diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp index ef7936762152d8..6746768090f564 100644 --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -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(Feature) .Case("simd128", SIMDLevel >= SIMD128) diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index b022b4bb38a003..dd5584960304e0 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -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 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { IntPtrType = SignedLong; } + StringRef getABI() const override; + bool setABI(const std::string &Name) override; + protected: void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 94d0f3dac70ac5..a3e3a38a1033be 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -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 @@ class WebAssemblyABIInfo final : public SwiftABIInfo { 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(); + 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: diff --git a/clang/test/CodeGen/wasm-arguments.c b/clang/test/CodeGen/wasm-arguments.c index c92028bae2db0b..25978d8a0990fd 100644 --- a/clang/test/CodeGen/wasm-arguments.c +++ b/clang/test/CodeGen/wasm-arguments.c @@ -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) -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) { - s2 foo; +// 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; - 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; +} s2; + +// 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; } -// 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; +}