pmatos created this revision.
pmatos added reviewers: asb, tlively.
Herald added subscribers: mattd, gchakrabarti, asavonic, StephenFan, wingo, 
ecnelises, sunfish, hiraditya, jgravelle-google, sbc100, dschuff.
Herald added a reviewer: aaron.ballman.
Herald added a project: All.
pmatos requested review of this revision.
Herald added subscribers: llvm-commits, cfe-commits, aheejin, jholewinski.
Herald added projects: clang, LLVM.

This is the funcref counterpart to 104bad5a. We introduce a new attribute
that marks a function pointer as a funcref. It also implements builtin
__builtin_wasm_ref_null_func(), that returns a null funcref value.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128440

Files:
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/AST/Type.h
  clang/include/clang/Basic/AddressSpaces.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/BuiltinsWebAssembly.def
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/TypePrinter.cpp
  clang/lib/Basic/Targets/DirectX.h
  clang/lib/Basic/Targets/NVPTX.h
  clang/lib/Basic/Targets/SPIR.h
  clang/lib/Basic/Targets/TCE.h
  clang/lib/Basic/Targets/X86.h
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/CodeGen/CodeGenTypes.cpp
  clang/lib/CodeGen/TargetInfo.cpp
  clang/lib/CodeGen/TargetInfo.h
  clang/lib/Sema/SemaChecking.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CodeGen/WebAssembly/wasm-funcref.c
  llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

Index: llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -404,11 +404,17 @@
 }
 
 //===----------------------------------------------------------------------===//
-// The following functions are called from lib/CodeGen/Passes.cpp to modify
-// the CodeGen pass sequence.
+// The following functions are called from lib/CodeGen/TargetPassConfig.cpp
+// to modify the CodeGen pass sequence.
 //===----------------------------------------------------------------------===//
 
 void WebAssemblyPassConfig::addIRPasses() {
+  // Run mem2reg to remove alloca references - needed for reference types
+  // FIXME: this should only be added when the subtarget has reference types 
+  // enabled but the subtarget is dependent on the function being compiled to 
+  // which we don't have access atm.
+  addPass(createPromoteMemoryToRegisterPass());
+
   // Add signatures to prototype-less function declarations
   addPass(createWebAssemblyAddMissingPrototypes());
 
Index: clang/test/CodeGen/WebAssembly/wasm-funcref.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/WebAssembly/wasm-funcref.c
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s
+
+typedef void (*funcref_t)() __attribute__((__funcref));
+
+funcref_t get_null() {
+// CHECK:      define i8 addrspace(20)* @get_null() #0 {
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %0 = call i8 addrspace(20)* @llvm.wasm.ref.null.func()
+// CHECK-NEXT:   ret i8 addrspace(20)* %0
+// CHECK-NEXT: }
+  return __builtin_wasm_ref_null_func();
+}
+
+void helper(funcref_t);
+
+void handle(funcref_t fn) {
+// CHECK:      define void @handle(i8 addrspace(20)* %fn) #0 {
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %fn.addr = alloca i8 addrspace(20)*, align 1
+// CHECK-NEXT:   store i8 addrspace(20)* %fn, i8 addrspace(20)** %fn.addr, align 1
+// CHECK-NEXT:   %0 = load i8 addrspace(20)*, i8 addrspace(20)** %fn.addr, align 1
+// CHECK-NEXT:   call void @helper(i8 addrspace(20)* %0)
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+  helper(fn);
+}
+
+typedef int (*fn_t)(int);
+typedef fn_t __attribute__((__funcref)) fn_funcref_t;
+
+// What happens when we move a function pointer into a funcref and then call it?
+fn_funcref_t get_ref(fn_t fnptr) {
+  return fnptr;
+}
+
+int call_fn(fn_funcref_t ref, int x) {
+  return ref(x);
+}
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -8106,6 +8106,27 @@
                               CurType, CurType);
 }
 
+static void HandleWebAssemblyFuncrefAttr(TypeProcessingState &State,
+                                         QualType &CurType,
+                                         ParsedAttr &Attr) {
+  if (!CurType->isFunctionPointerType()) {
+    State.getSema().Diag(Attr.getLoc(), 
+                         diag::err_attribute_webassembly_funcref);
+    Attr.setInvalid();
+    return;
+  }
+
+  // Add attribute
+  CurType =
+      State.getAttributedType(createSimpleAttr<WebAssemblyFuncrefAttr>(
+                                  State.getSema().Context, Attr),
+                              CurType, CurType);
+  
+  // Change address space to the funcref addrspace
+  Qualifiers Q = CurType.getQualifiers();
+  Q.setAddressSpace(LangAS::wasm_funcref);
+}
+
 /// Handle OpenCL Access Qualifier Attribute.
 static void HandleOpenCLAccessAttr(QualType &CurType, const ParsedAttr &Attr,
                                    Sema &S) {
@@ -8383,6 +8404,12 @@
       attr.setUsedAsTypeAttr();
       break;
 
+    case ParsedAttr::AT_WebAssemblyFuncref: {
+      HandleWebAssemblyFuncrefAttr(state, type, attr);
+      attr.setUsedAsTypeAttr();
+      break;
+    }
+
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
         attr.setUsedAsTypeAttr();
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -14283,7 +14283,10 @@
       // OpenCL allows function arguments declared to be an array of a type
       // to be qualified with an address space.
       !(getLangOpts().OpenCL &&
-        (T->isArrayType() || T.getAddressSpace() == LangAS::opencl_private))) {
+        (T->isArrayType() || T.getAddressSpace() == LangAS::opencl_private)) &&
+      // WebAssembly allows reference types as parameters. Funcref in particular
+      // lives in a different address space
+      !T->isWebAssemblyFuncrefType()) {
     Diag(NameLoc, diag::err_arg_with_address_space);
     New->setInvalidDecl();
   }
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -2640,7 +2640,14 @@
            diag::err_hip_invalid_args_builtin_mangled_name);
       return ExprError();
     }
+    break;
   }
+
+  case WebAssembly::BI__builtin_wasm_ref_null_func:
+    if (SemaBuiltinWasmRefNullFunc(TheCall))
+      return ExprError();
+    break;
+
   }
 
   // Since the target specific builtins for each arch overlap, only check those
@@ -6480,6 +6487,20 @@
   return false;
 }
 
+bool
+Sema::SemaBuiltinWasmRefNullFunc(CallExpr *TheCall) {
+  if (TheCall->getNumArgs() != 0)
+    return true;
+
+  // Set return type to a function pointer with the funcref attribute attached
+  QualType FnType = Context.getFunctionType(Context.VoidTy, {}, {});
+  QualType FnPtrType = Context.getPointerType(FnType);
+  
+  TheCall->setType(FnPtrType);
+
+  return false; 
+}
+
 /// We have a call to a function like __sync_fetch_and_add, which is an
 /// overloaded function based on the pointer type of its first argument.
 /// The main BuildCallExpr routines have already promoted the types of
Index: clang/lib/CodeGen/TargetInfo.h
===================================================================
--- clang/lib/CodeGen/TargetInfo.h
+++ clang/lib/CodeGen/TargetInfo.h
@@ -350,7 +350,14 @@
   }
 
   /// Return the WebAssembly externref reference type.
-  virtual llvm::Type *getWasmExternrefReferenceType() const { return nullptr; }
+  virtual llvm::Type *getWasmExternrefReferenceType() const {
+    return nullptr;
+  }
+  /// Return the WebAssembly funcref reference type.
+  virtual llvm::Type *getWasmFuncrefReferenceType() const {
+    return nullptr;
+  }
+  
   /// Emit the device-side copy of the builtin surface type.
   virtual bool emitCUDADeviceBuiltinSurfaceDeviceCopy(CodeGenFunction &CGF,
                                                       LValue Dst,
Index: clang/lib/CodeGen/TargetInfo.cpp
===================================================================
--- clang/lib/CodeGen/TargetInfo.cpp
+++ clang/lib/CodeGen/TargetInfo.cpp
@@ -894,6 +894,10 @@
   virtual llvm::Type *getWasmExternrefReferenceType() const override {
     return llvm::Type::getWasm_ExternrefTy(getABIInfo().getVMContext());
   }
+  /// Return the WebAssembly funcref reference type.
+  virtual llvm::Type *getWasmFuncrefReferenceType() const override {
+    return llvm::Type::getWasm_FuncrefTy(getABIInfo().getVMContext());
+  }  
 };
 
 /// Classify argument of given type \p Ty.
Index: clang/lib/CodeGen/CodeGenTypes.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenTypes.cpp
+++ clang/lib/CodeGen/CodeGenTypes.cpp
@@ -399,6 +399,7 @@
 
 /// ConvertType - Convert the specified type to its LLVM form.
 llvm::Type *CodeGenTypes::ConvertType(QualType T) {
+  const bool isWebAssemblyFuncref = T.getTypePtr()->isWebAssemblyFuncrefType();
   T = Context.getCanonicalType(T);
 
   const Type *Ty = T.getTypePtr();
@@ -417,6 +418,19 @@
     }
   }
 
+  if (isWebAssemblyFuncref) {
+    // Copies what we do later for PointerType but takes Funcref attribute into
+    // consideration. We cannot this reach caching which doesn't take the 
+    // funcref attribute into consideration.
+    const PointerType *PTy = cast<PointerType>(Ty);
+    QualType ETy = PTy->getPointeeType();
+    llvm::Type *PointeeType = ConvertTypeForMem(ETy);
+    if (PointeeType->isVoidTy())
+      PointeeType = llvm::Type::getInt8Ty(getLLVMContext());
+    llvm::Type *FuncTy = llvm::PointerType::get(PointeeType, 20 /*FixMe hardcoded AS*/);
+    return FuncTy;
+  }
+
   // RecordTypes are cached and processed specially.
   if (const RecordType *RT = dyn_cast<RecordType>(Ty))
     return ConvertRecordDeclType(RT->getDecl());
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -5103,6 +5103,15 @@
   const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
   llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
 
+  // If FI FunctionType is a WebAssembly funcref,
+  // then transform Ty so that it points to the funcref AS
+  if (D->getReturnType()->isWebAssemblyFuncrefType()) {
+    llvm::PointerType *PtrTy = dyn_cast<llvm::PointerType>(Ty->getReturnType());
+    assert(PtrTy);
+    Ty = llvm::FunctionType::get(llvm::PointerType::getWithSamePointeeType(PtrTy, 20),
+                                 Ty->params(), Ty->isVarArg()); 
+  }
+
   // Get or create the prototype for the function.
   if (!GV || (GV->getValueType() != Ty))
     GV = cast<llvm::GlobalValue>(GetAddrOfFunction(GD, Ty, /*ForVTable=*/false,
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -2036,6 +2036,17 @@
     Value *Src = Visit(const_cast<Expr*>(E));
     llvm::Type *SrcTy = Src->getType();
     llvm::Type *DstTy = ConvertType(DestTy);
+
+    // If the destination type is a WebAssembly Funcref then, we allow the
+    // types to have different address spaces and the one not in the 
+    // funcref address space is converted at this point.
+    if (DestTy->isWebAssemblyFuncrefType()) {
+      llvm::PointerType *PtrTy = cast<llvm::PointerType>(DstTy);
+      // FIXME: hardcoded addrspace, should be               
+      // llvm::WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF
+      DstTy = llvm::PointerType::getWithSamePointeeType(PtrTy, 20);
+    }
+
     if (SrcTy->isPtrOrPtrVectorTy() && DstTy->isPtrOrPtrVectorTy() &&
         SrcTy->getPointerAddressSpace() != DstTy->getPointerAddressSpace()) {
       llvm_unreachable("wrong cast for pointers in different address spaces"
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -18414,6 +18414,10 @@
     Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_extern);
     return Builder.CreateCall(Callee);
   }
+  case WebAssembly::BI__builtin_wasm_ref_null_func: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func);
+    return Builder.CreateCall(Callee);
+  }
   case WebAssembly::BI__builtin_wasm_swizzle_i8x16: {
     Value *Src = EmitScalarExpr(E->getArg(0));
     Value *Indices = EmitScalarExpr(E->getArg(1));
Index: clang/lib/Basic/Targets/X86.h
===================================================================
--- clang/lib/Basic/Targets/X86.h
+++ clang/lib/Basic/Targets/X86.h
@@ -45,6 +45,7 @@
     272, // ptr64
     1,   // wasm_var
     10,  // wasm_externref,
+    20,  // wasm_funcref
 };
 
 // X86 target abstract base class; x86-32 and x86-64 are very close, so
Index: clang/lib/Basic/Targets/TCE.h
===================================================================
--- clang/lib/Basic/Targets/TCE.h
+++ clang/lib/Basic/Targets/TCE.h
@@ -52,6 +52,7 @@
     0,  // ptr64
     1,  // wasm_var
     10, // wasm_externref
+    20, // wasm_funcref
 };
 
 class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo {
Index: clang/lib/Basic/Targets/SPIR.h
===================================================================
--- clang/lib/Basic/Targets/SPIR.h
+++ clang/lib/Basic/Targets/SPIR.h
@@ -45,6 +45,7 @@
     0,  // ptr64
     1,  // wasm_var
     10, // wasm_externref
+    20, // wasm_funcref
 };
 
 // Used by both the SPIR and SPIR-V targets.
@@ -64,18 +65,19 @@
     // cuda_constant pointer can be casted to default/"flat" pointer, but in
     // SPIR-V casts between constant and generic pointers are not allowed. For
     // this reason cuda_constant is mapped to SPIR-V CrossWorkgroup.
-    1,  // cuda_constant
-    3,  // cuda_shared
-    1,  // sycl_global
-    5,  // sycl_global_device
-    6,  // sycl_global_host
-    3,  // sycl_local
-    0,  // sycl_private
-    0,  // ptr32_sptr
-    0,  // ptr32_uptr
-    0,  // ptr64
-    1,  // wasm_var
-    10, // wasm_externref
+    1, // cuda_constant
+    3, // cuda_shared
+    1, // sycl_global
+    5, // sycl_global_device
+    6, // sycl_global_host
+    3, // sycl_local
+    0, // sycl_private
+    0, // ptr32_sptr
+    0, // ptr32_uptr
+    0, // ptr64
+    1, // wasm_var
+    10,// wasm_externref
+    20,// wasm_funcref
 };
 
 // Base class for SPIR and SPIR-V target info.
Index: clang/lib/Basic/Targets/NVPTX.h
===================================================================
--- clang/lib/Basic/Targets/NVPTX.h
+++ clang/lib/Basic/Targets/NVPTX.h
@@ -45,6 +45,7 @@
     0,  // ptr64
     1,  // wasm_var
     10, // wasm_externref
+    20, // wasm_funcref
 };
 
 /// The DWARF address class. Taken from
Index: clang/lib/Basic/Targets/DirectX.h
===================================================================
--- clang/lib/Basic/Targets/DirectX.h
+++ clang/lib/Basic/Targets/DirectX.h
@@ -43,6 +43,7 @@
     0,  // ptr64
     1,  // wasm_var
     10, // wasm_externref
+    20, // wasm_funcref
 };
 
 class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo {
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1737,6 +1737,8 @@
     // AttributedType nodes for them.
     break;
 
+  case attr::WebAssemblyFuncref: OS << "__funcref"; break;
+
   case attr::LifetimeBound:
   case attr::TypeNonNull:
   case attr::TypeNullable:
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -951,6 +951,7 @@
         12, // ptr64
         1,  // wasm_var
         10, // wasm_externref,
+        20, // wasm_funcref
     };
     return &FakeAddrSpaceMap;
   } else {
@@ -3981,6 +3982,14 @@
   return QualType();
 }
 
+/// getFuncrefType - Return a WebAssembly funcref type, which represents an
+/// opaque reference to a function.
+QualType ASTContext::getFuncrefType() const {
+  // FIXME: this cannot be written because there's no single funcref type 
+  // anymore
+  return QualType();
+}
+
 /// getScalableVectorType - Return the unique reference to a scalable vector
 /// type of the specified element type and size. VectorType must be a built-in
 /// type.
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -13043,6 +13043,9 @@
   ExprResult SemaBuiltinMatrixColumnMajorStore(CallExpr *TheCall,
                                                ExprResult CallResult);
 
+  // WebAssembly builtin handling
+  bool SemaBuiltinWasmRefNullFunc(CallExpr *TheCall);
+
 public:
   enum FormatStringType {
     FST_Scanf,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7314,6 +7314,8 @@
   "'__clang_arm_builtin_alias' attribute can only be applied to an ARM builtin">;
 def err_attribute_arm_mve_polymorphism : Error<
   "'__clang_arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">;
+def err_attribute_webassembly_funcref : Error<
+  "'__clang_webassembly_funcref' attribute can only be applied to function pointer types">;
 
 def warn_setter_getter_impl_required : Warning<
   "property %0 requires method %1 to be defined - "
Index: clang/include/clang/Basic/BuiltinsWebAssembly.def
===================================================================
--- clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -193,5 +193,9 @@
 // Some builtins are polymorphic - see 't' as part of the third argument,
 // in which case the argument spec (second argument) is unused.
 TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "e", "nc", "reference-types")
+// A funcref represented as a function pointer with the funcref attribute
+// attached to the type, therefore SemaChecking will check for the right
+// return type.
+TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types")
 #undef BUILTIN
 #undef TARGET_BUILTIN
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -4036,3 +4036,9 @@
   let LangOpts = [COnly];
 }
 def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
+
+def WebAssemblyFuncref : TypeAttr, TargetSpecificAttr<TargetWebAssembly> {
+  let Spellings = [Clang<"__funcref">];
+  let Args = [];
+  let Documentation = [Undocumented];
+}
Index: clang/include/clang/Basic/AddressSpaces.h
===================================================================
--- clang/include/clang/Basic/AddressSpaces.h
+++ clang/include/clang/Basic/AddressSpaces.h
@@ -58,6 +58,8 @@
 
   wasm_var,
   wasm_externref,
+  wasm_funcref,
+
   // This denotes the count of language-specific address spaces and also
   // the offset added to the target-specific address spaces, which are usually
   // specified by address space attributes __attribute__(address_space(n))).
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -1482,8 +1482,12 @@
     return Quals.getObjCLifetime();
   }
 
-  bool hasAddressSpace() const { return Quals.hasAddressSpace(); }
-  LangAS getAddressSpace() const { return Quals.getAddressSpace(); }
+  bool hasAddressSpace() const { 
+    return Quals.hasAddressSpace(); 
+  }
+  LangAS getAddressSpace() const {
+    return Quals.getAddressSpace(); 
+  }
 
   const Type *getBaseType() const { return BaseType; }
 
@@ -1971,6 +1975,8 @@
   /// Check if this is a WebAssembly Reference Type.
   bool isWebAssemblyReferenceType() const;
   bool isWebAssemblyExternrefType() const;
+  bool isWebAssemblyFuncrefType() const;
+
   /// Determines if this is a sizeless type supported by the
   /// 'arm_sve_vector_bits' type attribute, which can be applied to a single
   /// SVE vector or predicate, excluding tuple types such as svint32x4_t.
@@ -6684,6 +6690,9 @@
 
 /// Return the address space of this type.
 inline LangAS QualType::getAddressSpace() const {
+  if (getTypePtr()->isWebAssemblyFuncrefType()) {
+    return LangAS::wasm_funcref;
+  }
   return getQualifiers().getAddressSpace();
 }
 
@@ -6851,6 +6860,11 @@
     return false;
 }
 
+inline bool Type::isWebAssemblyFuncrefType() const {
+  return isFunctionPointerType() && 
+    hasAttr(attr::WebAssemblyFuncref);
+}
+
 inline bool Type::isFunctionReferenceType() const {
   if (const auto *T = getAs<ReferenceType>())
     return T->getPointeeType()->isFunctionType();
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -1495,6 +1495,9 @@
   /// Return a WebAssembly externref type
   QualType getExternrefType() const;
 
+  /// Return a WebAssembly funcref type
+  QualType getFuncrefType() const;
+  
   /// Return the unique reference to a vector type of the specified
   /// element type and size.
   ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to