[clang] 8c3e6af - [WebAssembly] Add experimental multivalue calling ABI

Thomas Lively via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 4 21:20:42 PST 2020


Author: Thomas Lively
Date: 2020-02-04T21:09:49-08:00
New Revision: 8c3e6af71b8e827655c83997747a2042feddc845

URL: https://github.com/llvm/llvm-project/commit/8c3e6af71b8e827655c83997747a2042feddc845
DIFF: https://github.com/llvm/llvm-project/commit/8c3e6af71b8e827655c83997747a2042feddc845.diff

LOG: [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

Added: 
    

Modified: 
    clang/lib/Basic/Targets/WebAssembly.cpp
    clang/lib/Basic/Targets/WebAssembly.h
    clang/lib/CodeGen/TargetInfo.cpp
    clang/test/CodeGen/wasm-arguments.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index ef7936762152..6746768090f5 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<bool>(Feature)
       .Case("simd128", SIMDLevel >= SIMD128)

diff  --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index b022b4bb38a0..dd5584960304 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 94d0f3dac70a..a3e3a38a1033 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<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:

diff  --git a/clang/test/CodeGen/wasm-arguments.c b/clang/test/CodeGen/wasm-arguments.c
index c92028bae2db..25978d8a0990 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;
+}


        


More information about the cfe-commits mailing list