forked from OSchip/llvm-project
[WebAssembly] Add experimental multivalue calling ABI
Summary: For now, this ABI simply expands all possible aggregate arguments and returns all possible aggregates directly. This ABI will change rapidly as we prototype and benchmark a new ABI that takes advantage of multivalue return and possibly other changes from the MVP ABI. Reviewers: aheejin, dschuff Subscribers: sbc100, jgravelle-google, sunfish, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D72972
This commit is contained in:
parent
649aba93a2
commit
8c3e6af71b
|
@ -33,6 +33,16 @@ const Builtin::Info WebAssemblyTargetInfo::BuiltinInfo[] = {
|
|||
static constexpr llvm::StringLiteral ValidCPUNames[] = {
|
||||
{"mvp"}, {"bleeding-edge"}, {"generic"}};
|
||||
|
||||
StringRef WebAssemblyTargetInfo::getABI() const { return ABI; }
|
||||
|
||||
bool WebAssemblyTargetInfo::setABI(const std::string &Name) {
|
||||
if (Name != "mvp" && Name != "experimental-mv")
|
||||
return false;
|
||||
|
||||
ABI = Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
|
||||
return llvm::StringSwitch<bool>(Feature)
|
||||
.Case("simd128", SIMDLevel >= SIMD128)
|
||||
|
|
|
@ -40,6 +40,8 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
|
|||
bool HasTailCall = false;
|
||||
bool HasReferenceTypes = false;
|
||||
|
||||
std::string ABI;
|
||||
|
||||
public:
|
||||
explicit WebAssemblyTargetInfo(const llvm::Triple &T, const TargetOptions &)
|
||||
: TargetInfo(T) {
|
||||
|
@ -59,6 +61,9 @@ public:
|
|||
IntPtrType = SignedLong;
|
||||
}
|
||||
|
||||
StringRef getABI() const override;
|
||||
bool setABI(const std::string &Name) override;
|
||||
|
||||
protected:
|
||||
void getTargetDefines(const LangOptions &Opts,
|
||||
MacroBuilder &Builder) const override;
|
||||
|
|
|
@ -726,11 +726,19 @@ ABIArgInfo DefaultABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class WebAssemblyABIInfo final : public SwiftABIInfo {
|
||||
public:
|
||||
enum ABIKind {
|
||||
MVP = 0,
|
||||
ExperimentalMV = 1,
|
||||
};
|
||||
|
||||
private:
|
||||
DefaultABIInfo defaultInfo;
|
||||
ABIKind Kind;
|
||||
|
||||
public:
|
||||
explicit WebAssemblyABIInfo(CodeGen::CodeGenTypes &CGT)
|
||||
: SwiftABIInfo(CGT), defaultInfo(CGT) {}
|
||||
explicit WebAssemblyABIInfo(CodeGen::CodeGenTypes &CGT, ABIKind Kind)
|
||||
: SwiftABIInfo(CGT), defaultInfo(CGT), Kind(Kind) {}
|
||||
|
||||
private:
|
||||
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
||||
|
@ -761,8 +769,9 @@ private:
|
|||
|
||||
class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
|
||||
public:
|
||||
explicit WebAssemblyTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
|
||||
: TargetCodeGenInfo(new WebAssemblyABIInfo(CGT)) {}
|
||||
explicit WebAssemblyTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT,
|
||||
WebAssemblyABIInfo::ABIKind K)
|
||||
: TargetCodeGenInfo(new WebAssemblyABIInfo(CGT, K)) {}
|
||||
|
||||
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
|
||||
CodeGen::CodeGenModule &CGM) const override {
|
||||
|
@ -813,6 +822,20 @@ ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
|
|||
// though watch out for things like bitfields.
|
||||
if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
|
||||
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
|
||||
// For the experimental multivalue ABI, fully expand all other aggregates
|
||||
if (Kind == ABIKind::ExperimentalMV) {
|
||||
const RecordType *RT = Ty->getAs<RecordType>();
|
||||
assert(RT);
|
||||
bool HasBitField = false;
|
||||
for (auto *Field : RT->getDecl()->fields()) {
|
||||
if (Field->isBitField()) {
|
||||
HasBitField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!HasBitField)
|
||||
return ABIArgInfo::getExpand();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise just do the default thing.
|
||||
|
@ -832,6 +855,9 @@ ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
// ABIArgInfo::getDirect().
|
||||
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
|
||||
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
|
||||
// For the experimental multivalue ABI, return all other aggregates
|
||||
if (Kind == ABIKind::ExperimentalMV)
|
||||
return ABIArgInfo::getDirect();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9828,8 +9854,12 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
|
|||
}
|
||||
|
||||
case llvm::Triple::wasm32:
|
||||
case llvm::Triple::wasm64:
|
||||
return SetCGInfo(new WebAssemblyTargetCodeGenInfo(Types));
|
||||
case llvm::Triple::wasm64: {
|
||||
WebAssemblyABIInfo::ABIKind Kind = WebAssemblyABIInfo::MVP;
|
||||
if (getTarget().getABI() == "experimental-mv")
|
||||
Kind = WebAssemblyABIInfo::ExperimentalMV;
|
||||
return SetCGInfo(new WebAssemblyTargetCodeGenInfo(Types, Kind));
|
||||
}
|
||||
|
||||
case llvm::Triple::arm:
|
||||
case llvm::Triple::armeb:
|
||||
|
|
|
@ -2,91 +2,139 @@
|
|||
// RUN: | FileCheck %s -check-prefix=WEBASSEMBLY32
|
||||
// RUN: %clang_cc1 -triple wasm64-unknown-unknown %s -emit-llvm -o - \
|
||||
// RUN: | FileCheck %s -check-prefix=WEBASSEMBLY64
|
||||
// RUN: %clang_cc1 -triple wasm32-unknown-unknown %s -target-abi experimental-mv -emit-llvm -o - \
|
||||
// RUN: | FileCheck %s -check-prefix=EXPERIMENTAL-MV
|
||||
|
||||
// Basic argument/attribute tests for WebAssembly
|
||||
// Basic argument/attribute and return tests for WebAssembly
|
||||
|
||||
// WEBASSEMBLY32: define void @f0(i32 %i, i32 %j, i64 %k, double %l, fp128 %m)
|
||||
// WEBASSEMBLY64: define void @f0(i32 %i, i64 %j, i64 %k, double %l, fp128 %m)
|
||||
void f0(int i, long j, long long k, double l, long double m) {}
|
||||
// WEBASSEMBLY32: define void @misc_args(i32 %i, i32 %j, i64 %k, double %l, fp128 %m)
|
||||
// WEBASSEMBLY64: define void @misc_args(i32 %i, i64 %j, i64 %k, double %l, fp128 %m)
|
||||
void misc_args(int i, long j, long long k, double l, long double m) {}
|
||||
|
||||
typedef struct {
|
||||
int aa;
|
||||
int bb;
|
||||
} s1;
|
||||
|
||||
// Structs should be passed byval and not split up.
|
||||
// WEBASSEMBLY32: define void @f1(%struct.s1* byval(%struct.s1) align 4 %i)
|
||||
// WEBASSEMBLY64: define void @f1(%struct.s1* byval(%struct.s1) align 4 %i)
|
||||
void f1(s1 i) {}
|
||||
// WEBASSEMBLY32: define void @struct_arg(%struct.s1* byval(%struct.s1) align 4 %i)
|
||||
// WEBASSEMBLY64: define void @struct_arg(%struct.s1* byval(%struct.s1) align 4 %i)
|
||||
|
||||
// Except in the experimental multivalue ABI, where structs are passed in args
|
||||
// EXPERIMENTAL-MV: define void @struct_arg(i32 %i.0, i32 %i.1)
|
||||
void struct_arg(s1 i) {}
|
||||
|
||||
// Structs should be returned sret and not simplified by the frontend.
|
||||
// WEBASSEMBLY32: define void @struct_ret(%struct.s1* noalias sret %agg.result)
|
||||
// WEBASSEMBLY32: ret void
|
||||
// WEBASSEMBLY64: define void @struct_ret(%struct.s1* noalias sret %agg.result)
|
||||
// WEBASSEMBLY64: ret void
|
||||
|
||||
// Except with the experimental multivalue ABI, which returns structs by value
|
||||
// EXPERIMENTAL-MV: define %struct.s1 @struct_ret()
|
||||
// EXPERIMENTAL-MV: ret %struct.s1 %0
|
||||
s1 struct_ret() {
|
||||
s1 foo;
|
||||
return foo;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int cc;
|
||||
} s2;
|
||||
// Single-element structs should be returned as the one element.
|
||||
// WEBASSEMBLY32: define i32 @f2()
|
||||
// WEBASSEMBLY64: define i32 @f2()
|
||||
s2 f2(void) {
|
||||
|
||||
// Single-element structs should be passed as the one element.
|
||||
// WEBASSEMBLY32: define void @single_elem_arg(i32 %i.coerce)
|
||||
// WEBASSEMBLY64: define void @single_elem_arg(i32 %i.coerce)
|
||||
// EXPERIMENTAL-MV: define void @single_elem_arg(i32 %i.coerce)
|
||||
void single_elem_arg(s2 i) {}
|
||||
|
||||
// Single-element structs should be passed as the one element.
|
||||
// WEBASSEMBLY32: define i32 @single_elem_ret()
|
||||
// WEBASSEMBLY32: ret i32
|
||||
// WEBASSEMBLY64: define i32 @single_elem_ret()
|
||||
// EXPERIMENTAL-MV: define i32 @single_elem_ret()
|
||||
s2 single_elem_ret() {
|
||||
s2 foo;
|
||||
return foo;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int cc;
|
||||
int dd;
|
||||
} s3;
|
||||
// Structs should be returned sret and not simplified by the frontend.
|
||||
// WEBASSEMBLY32: define void @f3(%struct.s3* noalias sret %agg.result)
|
||||
// WEBASSEMBLY64: define void @f3(%struct.s3* noalias sret %agg.result)
|
||||
s3 f3(void) {
|
||||
s3 foo;
|
||||
return foo;
|
||||
}
|
||||
|
||||
// WEBASSEMBLY32: define void @f4(i64 %i)
|
||||
// WEBASSEMBLY64: define void @f4(i64 %i)
|
||||
void f4(long long i) {}
|
||||
// WEBASSEMBLY32: define void @long_long_arg(i64 %i)
|
||||
// WEBASSEMBLY64: define void @long_long_arg(i64 %i)
|
||||
void long_long_arg(long long i) {}
|
||||
|
||||
// i8/i16 should be signext, i32 and higher should not.
|
||||
// WEBASSEMBLY32: define void @f5(i8 signext %a, i16 signext %b)
|
||||
// WEBASSEMBLY64: define void @f5(i8 signext %a, i16 signext %b)
|
||||
void f5(char a, short b) {}
|
||||
// WEBASSEMBLY32: define void @char_short_arg(i8 signext %a, i16 signext %b)
|
||||
// WEBASSEMBLY64: define void @char_short_arg(i8 signext %a, i16 signext %b)
|
||||
void char_short_arg(char a, short b) {}
|
||||
|
||||
// WEBASSEMBLY32: define void @f6(i8 zeroext %a, i16 zeroext %b)
|
||||
// WEBASSEMBLY64: define void @f6(i8 zeroext %a, i16 zeroext %b)
|
||||
void f6(unsigned char a, unsigned short b) {}
|
||||
// WEBASSEMBLY32: define void @uchar_ushort_arg(i8 zeroext %a, i16 zeroext %b)
|
||||
// WEBASSEMBLY64: define void @uchar_ushort_arg(i8 zeroext %a, i16 zeroext %b)
|
||||
void uchar_ushort_arg(unsigned char a, unsigned short b) {}
|
||||
|
||||
enum my_enum {
|
||||
ENUM1,
|
||||
ENUM2,
|
||||
ENUM3,
|
||||
};
|
||||
|
||||
// Enums should be treated as the underlying i32.
|
||||
// WEBASSEMBLY32: define void @f7(i32 %a)
|
||||
// WEBASSEMBLY64: define void @f7(i32 %a)
|
||||
void f7(enum my_enum a) {}
|
||||
// WEBASSEMBLY32: define void @enum_arg(i32 %a)
|
||||
// WEBASSEMBLY64: define void @enum_arg(i32 %a)
|
||||
void enum_arg(enum my_enum a) {}
|
||||
|
||||
enum my_big_enum {
|
||||
ENUM4 = 0xFFFFFFFFFFFFFFFF,
|
||||
};
|
||||
|
||||
// Big enums should be treated as the underlying i64.
|
||||
// WEBASSEMBLY32: define void @f8(i64 %a)
|
||||
// WEBASSEMBLY64: define void @f8(i64 %a)
|
||||
void f8(enum my_big_enum a) {}
|
||||
// WEBASSEMBLY32: define void @big_enum_arg(i64 %a)
|
||||
// WEBASSEMBLY64: define void @big_enum_arg(i64 %a)
|
||||
void big_enum_arg(enum my_big_enum a) {}
|
||||
|
||||
union simple_union {
|
||||
int a;
|
||||
char b;
|
||||
};
|
||||
|
||||
// Unions should be passed as byval structs.
|
||||
// WEBASSEMBLY32: define void @f9(%union.simple_union* byval(%union.simple_union) align 4 %s)
|
||||
// WEBASSEMBLY64: define void @f9(%union.simple_union* byval(%union.simple_union) align 4 %s)
|
||||
void f9(union simple_union s) {}
|
||||
// WEBASSEMBLY32: define void @union_arg(%union.simple_union* byval(%union.simple_union) align 4 %s)
|
||||
// WEBASSEMBLY64: define void @union_arg(%union.simple_union* byval(%union.simple_union) align 4 %s)
|
||||
// EXPERIMENTAL-MV: define void @union_arg(i32 %s.0)
|
||||
void union_arg(union simple_union s) {}
|
||||
|
||||
// Unions should be returned sret and not simplified by the frontend.
|
||||
// WEBASSEMBLY32: define void @union_ret(%union.simple_union* noalias sret %agg.result)
|
||||
// WEBASSEMBLY32: ret void
|
||||
// WEBASSEMBLY64: define void @union_ret(%union.simple_union* noalias sret %agg.result)
|
||||
// WEBASSEMBLY64: ret void
|
||||
|
||||
// The experimental multivalue ABI returns them by value, though.
|
||||
// EXPERIMENTAL-MV: define %union.simple_union @union_ret()
|
||||
// EXPERIMENTAL-MV: ret %union.simple_union %0
|
||||
union simple_union union_ret() {
|
||||
union simple_union bar;
|
||||
return bar;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int b4 : 4;
|
||||
int b3 : 3;
|
||||
int b8 : 8;
|
||||
} bitfield1;
|
||||
|
||||
// Bitfields should be passed as byval structs.
|
||||
// WEBASSEMBLY32: define void @f10(%struct.bitfield1* byval(%struct.bitfield1) align 4 %bf1)
|
||||
// WEBASSEMBLY64: define void @f10(%struct.bitfield1* byval(%struct.bitfield1) align 4 %bf1)
|
||||
void f10(bitfield1 bf1) {}
|
||||
// WEBASSEMBLY32: define void @bitfield_arg(%struct.bitfield1* byval(%struct.bitfield1) align 4 %bf1)
|
||||
// WEBASSEMBLY64: define void @bitfield_arg(%struct.bitfield1* byval(%struct.bitfield1) align 4 %bf1)
|
||||
// EXPERIMENTAL-MV: define void @bitfield_arg(%struct.bitfield1* byval(%struct.bitfield1) align 4 %bf1)
|
||||
void bitfield_arg(bitfield1 bf1) {}
|
||||
|
||||
// And returned via sret pointers.
|
||||
// WEBASSEMBLY32: define void @bitfield_ret(%struct.bitfield1* noalias sret %agg.result)
|
||||
// WEBASSEMBLY64: define void @bitfield_ret(%struct.bitfield1* noalias sret %agg.result)
|
||||
|
||||
// Except, of course, in the experimental multivalue ABI
|
||||
// EXPERIMENTAL-MV: define %struct.bitfield1 @bitfield_ret()
|
||||
bitfield1 bitfield_ret() {
|
||||
bitfield1 baz;
|
||||
return baz;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue