Skip to content

[WebAssembly] Represent reference types as TargetExtType - #203165

Merged
dschuff merged 13 commits into
llvm:mainfrom
hoodmane:RefTypesAsTargetExtTypes
Jun 25, 2026
Merged

[WebAssembly] Represent reference types as TargetExtType#203165
dschuff merged 13 commits into
llvm:mainfrom
hoodmane:RefTypesAsTargetExtTypes

Conversation

@hoodmane

@hoodmane hoodmane commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Originally #71540 by Paolo Matos, I picked it up and finished it. Resolves #69894.

Model WebAssembly externref and funcref as target("wasm.externref") / target("wasm.funcref") TargetExtTypes instead of pointers in non-integral address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled GlobalISel handling for functions that use them.

I added intrinsics for wasm.ptr.to_funcref and wasm.funcref.to_ptr. ptr.to_funcref does a table.get from the indirect function pointer table. As a special case, 0 is converted to the null funcref rather than doing table.get on 0. wasm.funcref.to_ptr is only handled when we call it immediately, otherwise it will fail to lower. We could dynamically put the funcref into the table to make it work but that would require a stack of spilled funcrefs and isn't worth the effort.

In the process of looking into this, I noticed that clang used to allow casting from funcref to function pointer but if the cast result escaped it would hit Cannot select: FUNCREF_TO_PTR in the backend. I added a diagnostic that says "a funcref can only be converted to a pointer to be directly called" to make this a little cleaner.

cc @QuantumSegfault

@llvmorg-github-actions llvmorg-github-actions Bot added backend:WebAssembly clang:codegen IR generation bugs: mangling, exceptions, etc. llvm:codegen llvm:globalisel llvm:SelectionDAG SelectionDAGISel as well llvm:ir labels Jun 11, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-backend-webassembly
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-llvm-globalisel

Author: Hood Chatham (hoodmane)

Changes

Originally #71540 by Paolo Matos, I picked it up and finished it.

Model WebAssembly externref and funcref as target("wasm.externref") / target("wasm.funcref") TargetExtTypes instead of pointers in non-integral address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled GlobalISel handling for functions that use them.

I added intrinsics for wasm.ptr.to_funcref and wasm.funcref.to_ptr. ptr.to_funcref does a table.get from the indirect function pointer table. As a special case, 0 is converted to the null funcref rather than doing table.get on 0. wasm.funcref.to_ptr is only handled when we call it immediately, otherwise it will fail to lower. We could dynamically put the funcref into the table to make it work but that would require a stack of spilled funcrefs and isn't worth the effort.

In the process of looking into this, I noticed that clang used to allow casting from funcref to function pointer but if the cast result escaped it would hit Cannot select: FUNCREF_TO_PTR in the backend. I added a diagnostic that says "a funcref can only be converted to a pointer to be directly called" to make this a little cleaner.

cc @QuantumSegfault


Patch is 98.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203165.diff

57 Files Affected:

  • (modified) clang/lib/CodeGen/CGCall.cpp (+13)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+38-3)
  • (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+6)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-externref.c (+14-14)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-funcref.c (+8-8)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c (+2-2)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-externref.c (+4-4)
  • (added) clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c (+22)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-funcref.c (+49-20)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+2-2)
  • (modified) llvm/include/llvm/IR/Intrinsics.h (+2)
  • (modified) llvm/include/llvm/IR/IntrinsicsWebAssembly.td (+11)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+20-3)
  • (modified) llvm/lib/CodeGen/ValueTypes.cpp (+4)
  • (modified) llvm/lib/IR/Intrinsics.cpp (+14-3)
  • (modified) llvm/lib/IR/Type.cpp (+8-4)
  • (modified) llvm/lib/Target/WebAssembly/CMakeLists.txt (-1)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp (+32-21)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h (+1-5)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h (+4-6)
  • (modified) llvm/lib/Target/WebAssembly/WebAssembly.h (-2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (+21)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+25-25)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h (-3)
  • (removed) llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp (-85)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp (+2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (-2)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll (+3-21)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll (+6-50)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll (+3-27)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll (+40)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll (+3-9)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll (+3-8)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-call.ll (+7-3)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll (+28)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-table_call.ll (+4-2)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll (+32)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-null.ll (+2-2)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-test-func.ll (+137-64)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll (+22-22)
  • (modified) llvm/test/CodeGen/WebAssembly/select-reftype.ll (+12-12)
  • (modified) llvm/test/CodeGen/WebAssembly/table-copy.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-fill.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-grow.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-size.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-types.ll (+2-2)
  • (modified) llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn (-1)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..3b91a210248ff 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -49,6 +49,7 @@
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/Type.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include <optional>
@@ -5972,6 +5973,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   const CGCallee &ConcreteCallee = Callee.prepareConcreteCallee(*this);
   llvm::Value *CalleePtr = ConcreteCallee.getFunctionPointer();
 
+  // A WebAssembly funcref is an opaque reference type and llvm only accepts
+  // function pointers as the call target. To make an indirect call through a
+  // reference type, first use the llvm.wasm.funcref.to_ptr intrinsic to make a
+  // fake function pointer to it. The backend lowers the resulting indirect call
+  // to a table.set into a single element dummy table + call_indirect 0.
+  if (auto *TET = dyn_cast<llvm::TargetExtType>(CalleePtr->getType());
+      TET && TET->getName() == "wasm.funcref") {
+    llvm::Function *ToPtr =
+        CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+    CalleePtr = Builder.CreateCall(ToPtr, {CalleePtr});
+  }
+
   // If we're using inalloca, set up that argument.
   if (ArgMemory.isValid()) {
     llvm::Value *Arg = ArgMemory.getPointer();
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 3a3dff7bec347..cb167d2b8f449 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -47,6 +47,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsPowerPC.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/MatrixBuilder.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TypeSize.h"
@@ -2839,6 +2840,41 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     return CGF.authPointerToPointerCast(Result, E->getType(), DestTy);
   }
   case CK_AddressSpaceConversion: {
+    llvm::Type *DestLTy = ConvertType(DestTy);
+    // WebAssembly reference types are opaque target extension types so an
+    // "address space conversion" involving them is not a real pointer cast.
+    auto IsWasmFuncref = [](llvm::Type *T) {
+      auto *TET = dyn_cast<llvm::TargetExtType>(T);
+      return TET && TET->getName() == "wasm.funcref";
+    };
+    bool SrcIsFuncref = IsWasmFuncref(ConvertType(E->getType()));
+    bool DestIsFuncref = IsWasmFuncref(DestLTy);
+    if (SrcIsFuncref && DestIsFuncref) {
+      // funcref -> funcref (e.g. between differently-typed funcrefs) is the
+      // identity on the opaque reference value.
+      return Visit(E);
+    }
+    if (SrcIsFuncref && !DestIsFuncref) {
+      // funcref -> pointer: recover the underlying function pointer.
+      llvm::Function *ToPtr =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+      return CGF.Builder.CreateCall(ToPtr, {Visit(E)});
+    }
+    if (!SrcIsFuncref && DestIsFuncref) {
+      // A null function pointer converts to a null funcref (ref.null func),
+      // rather than a table lookup at index 0.
+      Expr::EvalResult NullResult;
+      if (E->EvaluateAsRValue(NullResult, CGF.getContext()) &&
+          NullResult.Val.isNullPointer()) {
+        if (NullResult.HasSideEffects)
+          Visit(E);
+        return llvm::Constant::getNullValue(DestLTy);
+      }
+      // pointer -> funcref: do a table.get from the indirect function table.
+      llvm::Function *ToFuncref =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_ptr_to_funcref);
+      return CGF.Builder.CreateCall(ToFuncref, {Visit(E)});
+    }
     Expr::EvalResult Result;
     if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
         Result.Val.isNullPointer()) {
@@ -2847,12 +2883,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
       // eliminate the useless instructions emitted during translating E.
       if (Result.HasSideEffects)
         Visit(E);
-      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(
-          ConvertType(DestTy)), DestTy);
+      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(DestLTy), DestTy);
     }
     // Since target may map different address spaces in AST to the same address
     // space, an address space conversion may end up as a bitcast.
-    return CGF.performAddrSpaceCast(Visit(E), ConvertType(DestTy));
+    return CGF.performAddrSpaceCast(Visit(E), DestLTy);
   }
   case CK_AtomicToNonAtomic:
   case CK_NonAtomicToAtomic:
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index b28a0eb82f302..70c1d4f6a6ba9 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -645,6 +645,12 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
   case Type::Pointer: {
     const PointerType *PTy = cast<PointerType>(Ty);
     QualType ETy = PTy->getPointeeType();
+    // A WebAssembly funcref is represented as the opaque reference type
+    // target("wasm.funcref").
+    if (ETy.getAddressSpace() == LangAS::wasm_funcref) {
+      ResultType = CGM.getTargetCodeGenInfo().getWasmFuncrefReferenceType();
+      break;
+    }
     unsigned AS = getTargetAddressSpace(ETy);
     ResultType = llvm::PointerType::get(getLLVMContext(), AS);
     break;
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
index 7600a53ba3aa2..454fc31a5f53f 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
@@ -8,8 +8,8 @@ static const __externref_t const_table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
@@ -18,18 +18,18 @@ __externref_t test_builtin_wasm_table_get(int index) {
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get_const
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get_const(const int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(const int index, __externref_t ref) {
@@ -38,10 +38,10 @@ void test_builtin_wasm_table_set(const int index, __externref_t ref) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set_const
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set_const(const int index, const __externref_t ref) {
@@ -60,9 +60,9 @@ int test_builtin_wasm_table_size() {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
@@ -70,9 +70,9 @@ int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
index b4f729669a795..f80e9b10c4941 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
@@ -8,17 +8,17 @@ static funcref_t table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(int index, funcref_t ref) {
@@ -37,9 +37,9 @@ int test_builtin_wasm_table_size() {
 
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
@@ -47,9 +47,9 @@ int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, funcref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
index 88447f7fa232d..c8825028e1789 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
@@ -32,13 +32,13 @@ void test_function_pointer_signature_varargs(FVarArgs func) {
 
 typedef __externref_t (*FExternRef)(__externref_t, __externref_t);
 void test_function_pointer_externref(FExternRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(10) poison, token poison, ptr addrspace(10) poison, ptr addrspace(10) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.externref") poison, token poison, target("wasm.externref") poison, target("wasm.externref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
 typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats);
 void test_function_pointer_funcref(FFuncRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(20) poison, token poison, ptr addrspace(20) poison, ptr addrspace(20) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.funcref") poison, token poison, target("wasm.funcref") poison, target("wasm.funcref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..d226c51b7fd4e 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -7,10 +7,10 @@ void helper(externref_t);
 
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca ptr addrspace(10), align 1
-// CHECK-NEXT:    store ptr addrspace(10) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    call void @helper(ptr addrspace(10) [[TMP0]])
+// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca target("wasm.externref"), align 1
+// CHECK-NEXT:    store target("wasm.externref") [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.externref"), ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    call void @helper(target("wasm.externref") [[TMP0]])
 // CHECK-NEXT:    ret void
 //
 void handle(externref_t obj) {
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
new file mode 100644
index 0000000000000..7923196f7478e
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
@@ -0,0 +1,22 @@
+// RUN: not %clang_cc1 -triple wasm32 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple wasm64 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+
+// A WebAssembly funcref is an opaque reference type. Converting one to a plain
+// function pointer is only meaningful when the result is immediately called
+// (which lowers to a table.set + call_indirect through the funcref call table).
+// If the resulting pointer escapes (is stored, returned, or passed along), the
+// conversion is not representable and is diagnosed rather than crashing the
+// backend.
+
+typedef void (*__funcref funcref_t)(void);
+typedef void (*fn_t)(void);
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+void store_funcref_as_ptr(funcref_t f, fn_t *out) {
+  *out = (fn_t)f;
+}
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+fn_t return_funcref_as_ptr(funcref_t f) {
+  return (fn_t)f;
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref.c b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
index f01af0db321dd..63e9099b08a15 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
@@ -8,8 +8,8 @@ typedef int (*fn_t)(int);
 // Null funcref builtin call
 // CHECK-LABEL: @get_null(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t get_null() {
   return __builtin_wasm_ref_null_func();
@@ -19,8 +19,8 @@ funcref_t get_null() {
 // default return value for builtin is a funcref with function type () -> ().
 // CHECK-LABEL: @get_null_ii(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 fn_funcref_t get_null_ii() {
   return (fn_funcref_t) __builtin_wasm_ref_null_func();
@@ -29,10 +29,10 @@ fn_funcref_t get_null_ii() {
 // Identity function for funcref.
 // CHECK-LABEL: @identity(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t identity(funcref_t fn) {
   return fn;
@@ -43,10 +43,10 @@ void helper(funcref_t);
 // Pass funcref ref as an argument to a helper function.
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    call void @helper(ptr addrspace(20) noundef [[TMP0]])
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-clang-codegen

Author: Hood Chatham (hoodmane)

Changes

Originally #71540 by Paolo Matos, I picked it up and finished it.

Model WebAssembly externref and funcref as target("wasm.externref") / target("wasm.funcref") TargetExtTypes instead of pointers in non-integral address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled GlobalISel handling for functions that use them.

I added intrinsics for wasm.ptr.to_funcref and wasm.funcref.to_ptr. ptr.to_funcref does a table.get from the indirect function pointer table. As a special case, 0 is converted to the null funcref rather than doing table.get on 0. wasm.funcref.to_ptr is only handled when we call it immediately, otherwise it will fail to lower. We could dynamically put the funcref into the table to make it work but that would require a stack of spilled funcrefs and isn't worth the effort.

In the process of looking into this, I noticed that clang used to allow casting from funcref to function pointer but if the cast result escaped it would hit Cannot select: FUNCREF_TO_PTR in the backend. I added a diagnostic that says "a funcref can only be converted to a pointer to be directly called" to make this a little cleaner.

cc @QuantumSegfault


Patch is 98.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203165.diff

57 Files Affected:

  • (modified) clang/lib/CodeGen/CGCall.cpp (+13)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+38-3)
  • (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+6)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-externref.c (+14-14)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-funcref.c (+8-8)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c (+2-2)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-externref.c (+4-4)
  • (added) clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c (+22)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-funcref.c (+49-20)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+2-2)
  • (modified) llvm/include/llvm/IR/Intrinsics.h (+2)
  • (modified) llvm/include/llvm/IR/IntrinsicsWebAssembly.td (+11)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+20-3)
  • (modified) llvm/lib/CodeGen/ValueTypes.cpp (+4)
  • (modified) llvm/lib/IR/Intrinsics.cpp (+14-3)
  • (modified) llvm/lib/IR/Type.cpp (+8-4)
  • (modified) llvm/lib/Target/WebAssembly/CMakeLists.txt (-1)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp (+32-21)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h (+1-5)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h (+4-6)
  • (modified) llvm/lib/Target/WebAssembly/WebAssembly.h (-2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (+21)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+25-25)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h (-3)
  • (removed) llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp (-85)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp (+2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (-2)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll (+3-21)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll (+6-50)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll (+3-27)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll (+40)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll (+3-9)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll (+3-8)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-call.ll (+7-3)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll (+28)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-table_call.ll (+4-2)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll (+32)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-null.ll (+2-2)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-test-func.ll (+137-64)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll (+22-22)
  • (modified) llvm/test/CodeGen/WebAssembly/select-reftype.ll (+12-12)
  • (modified) llvm/test/CodeGen/WebAssembly/table-copy.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-fill.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-grow.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-size.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-types.ll (+2-2)
  • (modified) llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn (-1)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..3b91a210248ff 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -49,6 +49,7 @@
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/Type.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include <optional>
@@ -5972,6 +5973,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   const CGCallee &ConcreteCallee = Callee.prepareConcreteCallee(*this);
   llvm::Value *CalleePtr = ConcreteCallee.getFunctionPointer();
 
+  // A WebAssembly funcref is an opaque reference type and llvm only accepts
+  // function pointers as the call target. To make an indirect call through a
+  // reference type, first use the llvm.wasm.funcref.to_ptr intrinsic to make a
+  // fake function pointer to it. The backend lowers the resulting indirect call
+  // to a table.set into a single element dummy table + call_indirect 0.
+  if (auto *TET = dyn_cast<llvm::TargetExtType>(CalleePtr->getType());
+      TET && TET->getName() == "wasm.funcref") {
+    llvm::Function *ToPtr =
+        CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+    CalleePtr = Builder.CreateCall(ToPtr, {CalleePtr});
+  }
+
   // If we're using inalloca, set up that argument.
   if (ArgMemory.isValid()) {
     llvm::Value *Arg = ArgMemory.getPointer();
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 3a3dff7bec347..cb167d2b8f449 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -47,6 +47,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsPowerPC.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/MatrixBuilder.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TypeSize.h"
@@ -2839,6 +2840,41 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     return CGF.authPointerToPointerCast(Result, E->getType(), DestTy);
   }
   case CK_AddressSpaceConversion: {
+    llvm::Type *DestLTy = ConvertType(DestTy);
+    // WebAssembly reference types are opaque target extension types so an
+    // "address space conversion" involving them is not a real pointer cast.
+    auto IsWasmFuncref = [](llvm::Type *T) {
+      auto *TET = dyn_cast<llvm::TargetExtType>(T);
+      return TET && TET->getName() == "wasm.funcref";
+    };
+    bool SrcIsFuncref = IsWasmFuncref(ConvertType(E->getType()));
+    bool DestIsFuncref = IsWasmFuncref(DestLTy);
+    if (SrcIsFuncref && DestIsFuncref) {
+      // funcref -> funcref (e.g. between differently-typed funcrefs) is the
+      // identity on the opaque reference value.
+      return Visit(E);
+    }
+    if (SrcIsFuncref && !DestIsFuncref) {
+      // funcref -> pointer: recover the underlying function pointer.
+      llvm::Function *ToPtr =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+      return CGF.Builder.CreateCall(ToPtr, {Visit(E)});
+    }
+    if (!SrcIsFuncref && DestIsFuncref) {
+      // A null function pointer converts to a null funcref (ref.null func),
+      // rather than a table lookup at index 0.
+      Expr::EvalResult NullResult;
+      if (E->EvaluateAsRValue(NullResult, CGF.getContext()) &&
+          NullResult.Val.isNullPointer()) {
+        if (NullResult.HasSideEffects)
+          Visit(E);
+        return llvm::Constant::getNullValue(DestLTy);
+      }
+      // pointer -> funcref: do a table.get from the indirect function table.
+      llvm::Function *ToFuncref =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_ptr_to_funcref);
+      return CGF.Builder.CreateCall(ToFuncref, {Visit(E)});
+    }
     Expr::EvalResult Result;
     if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
         Result.Val.isNullPointer()) {
@@ -2847,12 +2883,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
       // eliminate the useless instructions emitted during translating E.
       if (Result.HasSideEffects)
         Visit(E);
-      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(
-          ConvertType(DestTy)), DestTy);
+      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(DestLTy), DestTy);
     }
     // Since target may map different address spaces in AST to the same address
     // space, an address space conversion may end up as a bitcast.
-    return CGF.performAddrSpaceCast(Visit(E), ConvertType(DestTy));
+    return CGF.performAddrSpaceCast(Visit(E), DestLTy);
   }
   case CK_AtomicToNonAtomic:
   case CK_NonAtomicToAtomic:
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index b28a0eb82f302..70c1d4f6a6ba9 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -645,6 +645,12 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
   case Type::Pointer: {
     const PointerType *PTy = cast<PointerType>(Ty);
     QualType ETy = PTy->getPointeeType();
+    // A WebAssembly funcref is represented as the opaque reference type
+    // target("wasm.funcref").
+    if (ETy.getAddressSpace() == LangAS::wasm_funcref) {
+      ResultType = CGM.getTargetCodeGenInfo().getWasmFuncrefReferenceType();
+      break;
+    }
     unsigned AS = getTargetAddressSpace(ETy);
     ResultType = llvm::PointerType::get(getLLVMContext(), AS);
     break;
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
index 7600a53ba3aa2..454fc31a5f53f 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
@@ -8,8 +8,8 @@ static const __externref_t const_table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
@@ -18,18 +18,18 @@ __externref_t test_builtin_wasm_table_get(int index) {
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get_const
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get_const(const int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(const int index, __externref_t ref) {
@@ -38,10 +38,10 @@ void test_builtin_wasm_table_set(const int index, __externref_t ref) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set_const
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set_const(const int index, const __externref_t ref) {
@@ -60,9 +60,9 @@ int test_builtin_wasm_table_size() {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
@@ -70,9 +70,9 @@ int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
index b4f729669a795..f80e9b10c4941 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
@@ -8,17 +8,17 @@ static funcref_t table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(int index, funcref_t ref) {
@@ -37,9 +37,9 @@ int test_builtin_wasm_table_size() {
 
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
@@ -47,9 +47,9 @@ int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, funcref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
index 88447f7fa232d..c8825028e1789 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
@@ -32,13 +32,13 @@ void test_function_pointer_signature_varargs(FVarArgs func) {
 
 typedef __externref_t (*FExternRef)(__externref_t, __externref_t);
 void test_function_pointer_externref(FExternRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(10) poison, token poison, ptr addrspace(10) poison, ptr addrspace(10) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.externref") poison, token poison, target("wasm.externref") poison, target("wasm.externref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
 typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats);
 void test_function_pointer_funcref(FFuncRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(20) poison, token poison, ptr addrspace(20) poison, ptr addrspace(20) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.funcref") poison, token poison, target("wasm.funcref") poison, target("wasm.funcref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..d226c51b7fd4e 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -7,10 +7,10 @@ void helper(externref_t);
 
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca ptr addrspace(10), align 1
-// CHECK-NEXT:    store ptr addrspace(10) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    call void @helper(ptr addrspace(10) [[TMP0]])
+// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca target("wasm.externref"), align 1
+// CHECK-NEXT:    store target("wasm.externref") [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.externref"), ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    call void @helper(target("wasm.externref") [[TMP0]])
 // CHECK-NEXT:    ret void
 //
 void handle(externref_t obj) {
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
new file mode 100644
index 0000000000000..7923196f7478e
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
@@ -0,0 +1,22 @@
+// RUN: not %clang_cc1 -triple wasm32 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple wasm64 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+
+// A WebAssembly funcref is an opaque reference type. Converting one to a plain
+// function pointer is only meaningful when the result is immediately called
+// (which lowers to a table.set + call_indirect through the funcref call table).
+// If the resulting pointer escapes (is stored, returned, or passed along), the
+// conversion is not representable and is diagnosed rather than crashing the
+// backend.
+
+typedef void (*__funcref funcref_t)(void);
+typedef void (*fn_t)(void);
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+void store_funcref_as_ptr(funcref_t f, fn_t *out) {
+  *out = (fn_t)f;
+}
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+fn_t return_funcref_as_ptr(funcref_t f) {
+  return (fn_t)f;
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref.c b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
index f01af0db321dd..63e9099b08a15 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
@@ -8,8 +8,8 @@ typedef int (*fn_t)(int);
 // Null funcref builtin call
 // CHECK-LABEL: @get_null(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t get_null() {
   return __builtin_wasm_ref_null_func();
@@ -19,8 +19,8 @@ funcref_t get_null() {
 // default return value for builtin is a funcref with function type () -> ().
 // CHECK-LABEL: @get_null_ii(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 fn_funcref_t get_null_ii() {
   return (fn_funcref_t) __builtin_wasm_ref_null_func();
@@ -29,10 +29,10 @@ fn_funcref_t get_null_ii() {
 // Identity function for funcref.
 // CHECK-LABEL: @identity(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t identity(funcref_t fn) {
   return fn;
@@ -43,10 +43,10 @@ void helper(funcref_t);
 // Pass funcref ref as an argument to a helper function.
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    call void @helper(ptr addrspace(20) noundef [[TMP0]])
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-selectiondag

Author: Hood Chatham (hoodmane)

Changes

Originally #71540 by Paolo Matos, I picked it up and finished it.

Model WebAssembly externref and funcref as target("wasm.externref") / target("wasm.funcref") TargetExtTypes instead of pointers in non-integral address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled GlobalISel handling for functions that use them.

I added intrinsics for wasm.ptr.to_funcref and wasm.funcref.to_ptr. ptr.to_funcref does a table.get from the indirect function pointer table. As a special case, 0 is converted to the null funcref rather than doing table.get on 0. wasm.funcref.to_ptr is only handled when we call it immediately, otherwise it will fail to lower. We could dynamically put the funcref into the table to make it work but that would require a stack of spilled funcrefs and isn't worth the effort.

In the process of looking into this, I noticed that clang used to allow casting from funcref to function pointer but if the cast result escaped it would hit Cannot select: FUNCREF_TO_PTR in the backend. I added a diagnostic that says "a funcref can only be converted to a pointer to be directly called" to make this a little cleaner.

cc @QuantumSegfault


Patch is 98.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203165.diff

57 Files Affected:

  • (modified) clang/lib/CodeGen/CGCall.cpp (+13)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+38-3)
  • (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+6)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-externref.c (+14-14)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-table-funcref.c (+8-8)
  • (modified) clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c (+2-2)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-externref.c (+4-4)
  • (added) clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c (+22)
  • (modified) clang/test/CodeGen/WebAssembly/wasm-funcref.c (+49-20)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+2-2)
  • (modified) llvm/include/llvm/IR/Intrinsics.h (+2)
  • (modified) llvm/include/llvm/IR/IntrinsicsWebAssembly.td (+11)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+20-3)
  • (modified) llvm/lib/CodeGen/ValueTypes.cpp (+4)
  • (modified) llvm/lib/IR/Intrinsics.cpp (+14-3)
  • (modified) llvm/lib/IR/Type.cpp (+8-4)
  • (modified) llvm/lib/Target/WebAssembly/CMakeLists.txt (-1)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp (+32-21)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h (+1-5)
  • (modified) llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h (+4-6)
  • (modified) llvm/lib/Target/WebAssembly/WebAssembly.h (-2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (+21)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+25-25)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h (-3)
  • (removed) llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp (-85)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp (+2)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (-2)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll (+3-21)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll (+6-50)
  • (modified) llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll (+3-27)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll (+40)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-globalset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll (+3-9)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll (+3-8)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-tableset.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-call.ll (+7-3)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-globalset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll (+28)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-table_call.ll (+4-2)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableget.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/funcref-tableset.ll (+1-1)
  • (added) llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll (+32)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-null.ll (+2-2)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-test-func.ll (+137-64)
  • (modified) llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll (+22-22)
  • (modified) llvm/test/CodeGen/WebAssembly/select-reftype.ll (+12-12)
  • (modified) llvm/test/CodeGen/WebAssembly/table-copy.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-fill.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-grow.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-size.ll (+1-1)
  • (modified) llvm/test/CodeGen/WebAssembly/table-types.ll (+2-2)
  • (modified) llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn (-1)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..3b91a210248ff 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -49,6 +49,7 @@
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/Type.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include <optional>
@@ -5972,6 +5973,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   const CGCallee &ConcreteCallee = Callee.prepareConcreteCallee(*this);
   llvm::Value *CalleePtr = ConcreteCallee.getFunctionPointer();
 
+  // A WebAssembly funcref is an opaque reference type and llvm only accepts
+  // function pointers as the call target. To make an indirect call through a
+  // reference type, first use the llvm.wasm.funcref.to_ptr intrinsic to make a
+  // fake function pointer to it. The backend lowers the resulting indirect call
+  // to a table.set into a single element dummy table + call_indirect 0.
+  if (auto *TET = dyn_cast<llvm::TargetExtType>(CalleePtr->getType());
+      TET && TET->getName() == "wasm.funcref") {
+    llvm::Function *ToPtr =
+        CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+    CalleePtr = Builder.CreateCall(ToPtr, {CalleePtr});
+  }
+
   // If we're using inalloca, set up that argument.
   if (ArgMemory.isValid()) {
     llvm::Value *Arg = ArgMemory.getPointer();
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 3a3dff7bec347..cb167d2b8f449 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -47,6 +47,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsPowerPC.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/MatrixBuilder.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TypeSize.h"
@@ -2839,6 +2840,41 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     return CGF.authPointerToPointerCast(Result, E->getType(), DestTy);
   }
   case CK_AddressSpaceConversion: {
+    llvm::Type *DestLTy = ConvertType(DestTy);
+    // WebAssembly reference types are opaque target extension types so an
+    // "address space conversion" involving them is not a real pointer cast.
+    auto IsWasmFuncref = [](llvm::Type *T) {
+      auto *TET = dyn_cast<llvm::TargetExtType>(T);
+      return TET && TET->getName() == "wasm.funcref";
+    };
+    bool SrcIsFuncref = IsWasmFuncref(ConvertType(E->getType()));
+    bool DestIsFuncref = IsWasmFuncref(DestLTy);
+    if (SrcIsFuncref && DestIsFuncref) {
+      // funcref -> funcref (e.g. between differently-typed funcrefs) is the
+      // identity on the opaque reference value.
+      return Visit(E);
+    }
+    if (SrcIsFuncref && !DestIsFuncref) {
+      // funcref -> pointer: recover the underlying function pointer.
+      llvm::Function *ToPtr =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+      return CGF.Builder.CreateCall(ToPtr, {Visit(E)});
+    }
+    if (!SrcIsFuncref && DestIsFuncref) {
+      // A null function pointer converts to a null funcref (ref.null func),
+      // rather than a table lookup at index 0.
+      Expr::EvalResult NullResult;
+      if (E->EvaluateAsRValue(NullResult, CGF.getContext()) &&
+          NullResult.Val.isNullPointer()) {
+        if (NullResult.HasSideEffects)
+          Visit(E);
+        return llvm::Constant::getNullValue(DestLTy);
+      }
+      // pointer -> funcref: do a table.get from the indirect function table.
+      llvm::Function *ToFuncref =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_ptr_to_funcref);
+      return CGF.Builder.CreateCall(ToFuncref, {Visit(E)});
+    }
     Expr::EvalResult Result;
     if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
         Result.Val.isNullPointer()) {
@@ -2847,12 +2883,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
       // eliminate the useless instructions emitted during translating E.
       if (Result.HasSideEffects)
         Visit(E);
-      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(
-          ConvertType(DestTy)), DestTy);
+      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(DestLTy), DestTy);
     }
     // Since target may map different address spaces in AST to the same address
     // space, an address space conversion may end up as a bitcast.
-    return CGF.performAddrSpaceCast(Visit(E), ConvertType(DestTy));
+    return CGF.performAddrSpaceCast(Visit(E), DestLTy);
   }
   case CK_AtomicToNonAtomic:
   case CK_NonAtomicToAtomic:
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index b28a0eb82f302..70c1d4f6a6ba9 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -645,6 +645,12 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
   case Type::Pointer: {
     const PointerType *PTy = cast<PointerType>(Ty);
     QualType ETy = PTy->getPointeeType();
+    // A WebAssembly funcref is represented as the opaque reference type
+    // target("wasm.funcref").
+    if (ETy.getAddressSpace() == LangAS::wasm_funcref) {
+      ResultType = CGM.getTargetCodeGenInfo().getWasmFuncrefReferenceType();
+      break;
+    }
     unsigned AS = getTargetAddressSpace(ETy);
     ResultType = llvm::PointerType::get(getLLVMContext(), AS);
     break;
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
index 7600a53ba3aa2..454fc31a5f53f 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
@@ -8,8 +8,8 @@ static const __externref_t const_table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
@@ -18,18 +18,18 @@ __externref_t test_builtin_wasm_table_get(int index) {
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get_const
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get_const(const int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(const int index, __externref_t ref) {
@@ -38,10 +38,10 @@ void test_builtin_wasm_table_set(const int index, __externref_t ref) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set_const
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) @const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set_const(const int index, const __externref_t ref) {
@@ -60,9 +60,9 @@ int test_builtin_wasm_table_size() {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
@@ -70,9 +70,9 @@ int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
index b4f729669a795..f80e9b10c4941 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
@@ -8,17 +8,17 @@ static funcref_t table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(int index, funcref_t ref) {
@@ -37,9 +37,9 @@ int test_builtin_wasm_table_size() {
 
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
@@ -47,9 +47,9 @@ int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, funcref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
index 88447f7fa232d..c8825028e1789 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
@@ -32,13 +32,13 @@ void test_function_pointer_signature_varargs(FVarArgs func) {
 
 typedef __externref_t (*FExternRef)(__externref_t, __externref_t);
 void test_function_pointer_externref(FExternRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(10) poison, token poison, ptr addrspace(10) poison, ptr addrspace(10) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.externref") poison, token poison, target("wasm.externref") poison, target("wasm.externref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
 typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats);
 void test_function_pointer_funcref(FFuncRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(20) poison, token poison, ptr addrspace(20) poison, ptr addrspace(20) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.funcref") poison, token poison, target("wasm.funcref") poison, target("wasm.funcref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..d226c51b7fd4e 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -7,10 +7,10 @@ void helper(externref_t);
 
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca ptr addrspace(10), align 1
-// CHECK-NEXT:    store ptr addrspace(10) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[OBJ_ADDR]], align 1
-// CHECK-NEXT:    call void @helper(ptr addrspace(10) [[TMP0]])
+// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca target("wasm.externref"), align 1
+// CHECK-NEXT:    store target("wasm.externref") [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.externref"), ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    call void @helper(target("wasm.externref") [[TMP0]])
 // CHECK-NEXT:    ret void
 //
 void handle(externref_t obj) {
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
new file mode 100644
index 0000000000000..7923196f7478e
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
@@ -0,0 +1,22 @@
+// RUN: not %clang_cc1 -triple wasm32 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple wasm64 -target-feature +reference-types -S -o /dev/null %s 2>&1 | FileCheck %s
+
+// A WebAssembly funcref is an opaque reference type. Converting one to a plain
+// function pointer is only meaningful when the result is immediately called
+// (which lowers to a table.set + call_indirect through the funcref call table).
+// If the resulting pointer escapes (is stored, returned, or passed along), the
+// conversion is not representable and is diagnosed rather than crashing the
+// backend.
+
+typedef void (*__funcref funcref_t)(void);
+typedef void (*fn_t)(void);
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+void store_funcref_as_ptr(funcref_t f, fn_t *out) {
+  *out = (fn_t)f;
+}
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
+fn_t return_funcref_as_ptr(funcref_t f) {
+  return (fn_t)f;
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref.c b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
index f01af0db321dd..63e9099b08a15 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
@@ -8,8 +8,8 @@ typedef int (*fn_t)(int);
 // Null funcref builtin call
 // CHECK-LABEL: @get_null(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t get_null() {
   return __builtin_wasm_ref_null_func();
@@ -19,8 +19,8 @@ funcref_t get_null() {
 // default return value for builtin is a funcref with function type () -> ().
 // CHECK-LABEL: @get_null_ii(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") @llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 fn_funcref_t get_null_ii() {
   return (fn_funcref_t) __builtin_wasm_ref_null_func();
@@ -29,10 +29,10 @@ fn_funcref_t get_null_ii() {
 // Identity function for funcref.
 // CHECK-LABEL: @identity(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t identity(funcref_t fn) {
   return fn;
@@ -43,10 +43,10 @@ void helper(funcref_t);
 // Pass funcref ref as an argument to a helper function.
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    call void @helper(ptr addrspace(20) noundef [[TMP0]])
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[...
[truncated]

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

Originally llvm#71540 by Paolo Matos, I picked it up and finished it.

Model WebAssembly externref and funcref as target("wasm.externref") /
target("wasm.funcref") TargetExtTypes instead of pointers in non-integral
address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled
GlobalISel handling for functions that use them.

I added intrinsics for `wasm.ptr.to_funcref` and `wasm.funcref.to_ptr`.
ptr.to_funcref does a table.get from the indirect function pointer table. As a
special case, 0 is converted to the null funcref rather than doing table.get on
0. `wasm.funcref.to_ptr` is only handled when we call it immediately, otherwise
it will fail to lower. We could dynamically put the funcref into the table to
make it work but that would require a stack of spilled funcrefs and isn't worth
the effort.

In the process of looking into this, I noticed that clang used to allow casting
from funcref to function pointer but if the cast result escaped it would hit
`Cannot select: FUNCREF_TO_PTR` in the backend. I added a diagnostic that says
"a funcref can only be converted to a pointer to be directly called" to make
this a little cleaner.

Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Paulo Matos <pmatos@igalia.com>
@hoodmane
hoodmane force-pushed the RefTypesAsTargetExtTypes branch from ae3f67d to 475bcb6 Compare June 11, 2026 04:07
@hoodmane

Copy link
Copy Markdown
Contributor Author

Incidentally, I noticed while writing this that we never emit call_ref. We should ideally check whether the gc target feature is present and if so emit a call_ref to call a function pointer rather than using a dummy table. I'll try to do this in a separate PR.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 206147 tests passed
  • 6731 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 137778 tests passed
  • 4832 tests skipped

✅ The build succeeded and all tests passed.

@QuantumSegfault

Copy link
Copy Markdown
Contributor

Incidentally, I noticed while writing this that we never emit call_ref. We should ideally check whether the gc target feature is present and if so emit a call_ref to call a function pointer rather than using a dummy table. I'll try to do this in a separate PR.

Way ahead of you. But maybe you can help push things forward.

#162227

It works, but in order to properly test it, I wanted to check against status quo. But status quo doesn't work at all. In order to fix existing thing, we need to move forward with #180900. Perhaps you have an opinion?

@QuantumSegfault QuantumSegfault left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good overall.

It's unfortunate we have to do that weird cast hack to call now...but probably the best that can be done at the moment

Comment thread llvm/test/CodeGen/WebAssembly/ref-test-func.ll Outdated
Comment thread llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp Outdated
Comment thread llvm/lib/IR/Type.cpp
Comment thread llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp Outdated
Comment thread clang/lib/CodeGen/CGExprScalar.cpp
@hoodmane
hoodmane force-pushed the RefTypesAsTargetExtTypes branch from e82c582 to e01a780 Compare June 11, 2026 17:58
@hoodmane

Copy link
Copy Markdown
Contributor Author

cc @dschuff @tlively @sbc100

Comment thread llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

@QuantumSegfault QuantumSegfault left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now.

@hoodmane

Copy link
Copy Markdown
Contributor Author

I claim this resolves #69894 so I should probably add a test for it.

dschuff added a commit that referenced this pull request Jun 25, 2026
Fix test from #203165 when no wasm target is configured
@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-sie-win running on sie-win-worker while building clang,llvm at step 7 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/46/builds/37101

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not z:\b\llvm-clang-x86_64-sie-win\build\bin\clang.exe -cc1 -internal-isystem Z:\b\llvm-clang-x86_64-sie-win\build\bin\../lib/clang\include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null Z:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c 2>&1 | z:\b\llvm-clang-x86_64-sie-win\build\bin\filecheck.exe Z:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# executed command: not 'z:\b\llvm-clang-x86_64-sie-win\build\bin\clang.exe' -cc1 -internal-isystem 'Z:\b\llvm-clang-x86_64-sie-win\build\bin\../lib/clang\include' -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null 'Z:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# note: command had no output on stdout or stderr
# executed command: 'z:\b\llvm-clang-x86_64-sie-win\build\bin\filecheck.exe' 'Z:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# .---command stderr------------
# | �[1mZ:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c:13:11: �[0m�[0;1;31merror: �[0m�[1mCHECK: expected string not found in input
�[0m# | �[1m�[0m// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# | �[0;1;32m          ^
�[0m# | �[0;1;32m�[0m�[1m<stdin>:1:1: �[0m�[0;1;30mnote: �[0m�[1mscanning from here
�[0m# | �[1m�[0merror: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | �[0;1;32m^
�[0m# | �[0;1;32m�[0m
# | Input file: <stdin>
# | Check file: Z:\b\llvm-clang-x86_64-sie-win\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# | �[1m�[0m�[0;1;30m          1: �[0m�[1m�[0;1;46merror: unable to create target: 'No available targets are compatible with triple "wasm32"' �[0m
# | �[0;1;31mcheck:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
�[0m# | �[0;1;31m�[0m�[0;1;30m          2: �[0m�[1m�[0;1;46m1 error generated. �[0m
# | �[0;1;31mcheck:13     ~~~~~~~~~~~~~~~~~~~
�[0m# | �[0;1;31m�[0m>>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building clang,llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/36320

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/bin/clang -cc1 -internal-isystem /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/bin/clang -cc1 -internal-isystem /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-aarch64-darwin running on doug-worker-4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/44709

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
...
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/wasm-ld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld.lld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/ld.lld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using lld-link: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/lld-link
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld64.lld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/ld64.lld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/wasm-ld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld.lld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/ld.lld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using lld-link: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/lld-link
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld64.lld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/ld64.lld
llvm-lit: /Users/buildbot/buildbot-root/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/wasm-ld
-- Testing: 94396 tests, 12 workers --
command timed out: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=1241.208210

@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-m68k-linux-cross running on suse-gary-m68k-cross while building clang,llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/743

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/bin/clang -cc1 -internal-isystem /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/bin/FileCheck /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/bin/clang -cc1 -internal-isystem /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/bin/FileCheck /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-armv8-quick running on linaro-clang-armv8-quick while building clang,llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/154/builds/33089

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/bin/clang -cc1 -internal-isystem /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/bin/clang -cc1 -internal-isystem /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-ubuntu-x-aarch64-pauthtest running on as-builder-11 while building clang,llvm at step 9 "test-check-clang".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/227/builds/2802

Here is the relevant piece of the build log for the reference
Step 9 (test-check-clang) failure: Test just built components: check-clang completed (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/bin/clang -cc1 -internal-isystem /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/bin/FileCheck /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/bin/clang -cc1 -internal-isystem /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/build/bin/FileCheck /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/buildbot/worker/as-builder-11/x-aarch64-pauthtest/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-ubuntu-x-aarch64-pac-ret running on as-builder-11 while building clang,llvm at step 16 "test-check-clang".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/229/builds/2486

Here is the relevant piece of the build log for the reference
Step 16 (test-check-clang) failure: Test just built components: check-clang completed (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/bin/clang -cc1 -internal-isystem /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/bin/FileCheck /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/bin/clang -cc1 -internal-isystem /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/build/bin/FileCheck /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/buildbot/worker/as-builder-11/x-aarch64-pac-ret/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-darwin running on doug-worker-3 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/23/builds/20673

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/bin/FileCheck /Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# note: command had no output on stdout or stderr
# executed command: /Volumes/RAMDisk/buildbot-root/x86_64-darwin/build/bin/FileCheck /Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | �[1m/Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: �[0m�[0;1;31merror: �[0m�[1mCHECK: expected string not found in input
�[0m# | �[1m�[0m// CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# | �[0;1;32m          ^
�[0m# | �[0;1;32m�[0m�[1m<stdin>:1:1: �[0m�[0;1;30mnote: �[0m�[1mscanning from here
�[0m# | �[1m�[0merror: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | �[0;1;32m^
�[0m# | �[0;1;32m�[0m
# | Input file: <stdin>
# | Check file: /Volumes/RAMDisk/buildbot-root/x86_64-darwin/llvm-project/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# | �[1m�[0m�[0;1;30m          1: �[0m�[1m�[0;1;46merror: unable to create target: 'No available targets are compatible with triple "wasm32"' �[0m
# | �[0;1;31mcheck:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
�[0m# | �[0;1;31m�[0m�[0;1;30m          2: �[0m�[1m�[0;1;46m1 error generated. �[0m
# | �[0;1;31mcheck:13     ~~~~~~~~~~~~~~~~~~~
�[0m# | �[0;1;31m�[0m>>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@hoodmane

Copy link
Copy Markdown
Contributor Author

Looks like my new test isn't properly skipped when the wasm target isn't built.

@QuantumSegfault

QuantumSegfault commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Looks like my new test isn't properly skipped when the wasm target isn't built.

Already taken care of: #205722

@hoodmane

Copy link
Copy Markdown
Contributor Author

Thanks @dschuff and @QuantumSegfault! Also, we should probably close these:
#71540
#93428

@QuantumSegfault

Copy link
Copy Markdown
Contributor

Thanks @dschuff and @QuantumSegfault! Also, we should probably close these: #71540 #93428

Done.

@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder llvm-clang-win-x-aarch64 running on as-builder-2 while building clang,llvm at step 10 "test-check-clang".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/193/builds/16978

Here is the relevant piece of the build log for the reference
Step 10 (test-check-clang) failure: Test just built components: check-clang completed (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not c:\buildbot\as-builder-2\x-aarch64\build\bin\clang.exe -cc1 -internal-isystem C:\buildbot\as-builder-2\x-aarch64\build\lib\clang\23\include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c 2>&1 | c:\buildbot\as-builder-2\x-aarch64\build\bin\filecheck.exe C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# executed command: not 'c:\buildbot\as-builder-2\x-aarch64\build\bin\clang.exe' -cc1 -internal-isystem 'C:\buildbot\as-builder-2\x-aarch64\build\lib\clang\23\include' -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null 'C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# executed command: 'c:\buildbot\as-builder-2\x-aarch64\build\bin\filecheck.exe' 'C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# .---command stderr------------
# | C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: C:\buildbot\as-builder-2\x-aarch64\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder openmp-s390x-linux running on systemz-1 while building clang,llvm at step 6 "test-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/23921

Here is the relevant piece of the build log for the reference
Step 6 (test-openmp) failure: test (failure)
******************** TEST 'libomp :: tasking/issue-94260-2.c' FAILED ********************
Exit Code: -11

Command Output (stdout):
--
# RUN: at line 1
/home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp   -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic && /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# note: command had no output on stdout or stderr
# error: command failed with exit status: -11

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-aarch64-sve2-vla running on linaro-g4-02 while building clang,llvm at step 7 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/198/builds/13892

Here is the relevant piece of the build log for the reference
Step 7 (ninja check 1) failure: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
...
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/flang/test/lit.cfg.py:203: note: using flang implicit search paths: -fintrinsic-modules-path=/home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/lib/clang/23/finclude/flang/aarch64-unknown-linux-gnu
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/flang/test/lit.cfg.py:206: note: using flang extra search paths: 
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/llvm/config.py:569: note: using clang: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/clang
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/discovery.py:273: warning: input '/home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/interception/Unit' contained no tests
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/discovery.py:273: warning: input '/home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/Unit' contained no tests
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/llvm/config.py:569: note: using ld.lld: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/ld.lld
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/llvm/config.py:569: note: using lld-link: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/lld-link
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/llvm/config.py:569: note: using ld64.lld: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/ld64.lld
llvm-lit: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/wasm-ld
-- Testing: 110824 tests, 48 workers --
command timed out: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=1320.799783

@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-arm64-windows-msvc running on linaro-armv8-windows-msvc-04 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/161/builds/12043

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not c:\users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\bin\clang.exe -cc1 -internal-isystem C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\lib\clang\23\include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c 2>&1 | c:\users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\bin\filecheck.exe C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# executed command: not 'c:\users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\bin\clang.exe' -cc1 -internal-isystem 'C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\lib\clang\23\include' -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null 'C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# executed command: 'c:\users\tcwg\llvm-worker\clang-arm64-windows-msvc\build\bin\filecheck.exe' 'C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c'
# .---command stderr------------
# | C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: C:\Users\tcwg\llvm-worker\clang-arm64-windows-msvc\llvm-project\clang\test\CodeGen\WebAssembly\wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


@llvm-ci

llvm-ci commented Jun 25, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder clang-solaris11-sparcv9 running on solaris11-sparcv9 while building clang,llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/13/builds/13746

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clang :: CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
not /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/bin/clang -cc1 -internal-isystem /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 2>&1 | /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/bin/FileCheck /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: not /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/bin/clang -cc1 -internal-isystem /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/lib/clang/23/include -nostdsysteminc -triple wasm32 -target-feature +reference-types -S -o /dev/null /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# executed command: /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/stage1/bin/FileCheck /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# .---command stderr------------
# | /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c:13:11: error: CHECK: expected string not found in input
# | // CHECK: error: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used
# |           ^
# | <stdin>:1:1: note: scanning from here
# | error: unable to create target: 'No available targets are compatible with triple "wasm32"'
# | ^
# | 
# | Input file: <stdin>
# | Check file: /opt/llvm-buildbot/home/solaris11-sparcv9/clang-solaris11-sparcv9/llvm/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           1: error: unable to create target: 'No available targets are compatible with triple "wasm32"' 
# | check:13     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |           2: 1 error generated. 
# | check:13     ~~~~~~~~~~~~~~~~~~~
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

********************


maarcosrmz pushed a commit to maarcosrmz/llvm-project that referenced this pull request Jul 1, 2026
Originally llvm#71540 by Paolo Matos, I picked it up and finished it.
Resolves llvm#69894.

Model WebAssembly externref and funcref as target("wasm.externref") /
target("wasm.funcref") TargetExtTypes instead of pointers in
non-integral address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled
GlobalISel handling for functions that use them.

I added intrinsics for `wasm.ptr.to_funcref` and `wasm.funcref.to_ptr`.
ptr.to_funcref does a table.get from the indirect function pointer
table. As a special case, 0 is converted to the null funcref rather than
doing table.get on 0. `wasm.funcref.to_ptr` is only handled when we call
it immediately, otherwise it will fail to lower. We could dynamically
put the funcref into the table to make it work but that would require a
stack of spilled funcrefs and isn't worth the effort.

In the process of looking into this, I noticed that clang used to allow
casting from funcref to function pointer but if the cast result escaped
it would hit `Cannot select: FUNCREF_TO_PTR` in the backend. I added a
diagnostic that says "a funcref can only be converted to a pointer to be
directly called" to make this a little cleaner.

cc @QuantumSegfault

---------

Co-authored-by: Paulo Matos <pmatos@igalia.com>
maarcosrmz pushed a commit to maarcosrmz/llvm-project that referenced this pull request Jul 1, 2026
AnthonyLatsis added a commit to bnbarham/swift that referenced this pull request Aug 3, 2026
…etExtType (#203165)"

```
/Users/alatsis/Desktop/rebranch/swift-2/lib/AST/Builtins.cpp:2659:11: error: enumeration values 'WasmExternref' and 'WasmFuncref' not handled in switch [-Werror,-Wswitch]
  switch (D.Kind) {
          ^
```

The LLVM commit models WebAssembly externref/funcref as
target("wasm.externref") / target("wasm.funcref") TargetExtTypes and
adds the corresponding WasmExternref and WasmFuncref enumerators to
IITDescriptor::Kind. These are opaque WebAssembly reference types with
no Swift representation, so they join the leading group of kinds that
"cannot be expressed in swift yet" and return an empty Type().

See llvm/llvm-project@765f0d1 (llvm/llvm-project#203165).

Assisted-by: Claude Code
AnthonyLatsis added a commit to AnthonyLatsis/swift that referenced this pull request Aug 4, 2026
…etExtType (#203165)"

```
/Users/alatsis/Desktop/rebranch/swift-2/lib/AST/Builtins.cpp:2659:11: error: enumeration values 'WasmExternref' and 'WasmFuncref' not handled in switch [-Werror,-Wswitch]
  switch (D.Kind) {
          ^
```

The LLVM commit models WebAssembly externref/funcref as
target("wasm.externref") / target("wasm.funcref") TargetExtTypes and
adds the corresponding WasmExternref and WasmFuncref enumerators to
IITDescriptor::Kind. These are opaque WebAssembly reference types with
no Swift representation, so they join the leading group of kinds that
"cannot be expressed in swift yet" and return an empty Type().

See llvm/llvm-project@765f0d1 (llvm/llvm-project#203165).

Assisted-by: Claude Code
AnthonyLatsis added a commit to AnthonyLatsis/swift that referenced this pull request Aug 5, 2026
…etExtType (#203165)"

```
/Users/alatsis/Desktop/rebranch/swift-2/lib/AST/Builtins.cpp:2659:11: error: enumeration values 'WasmExternref' and 'WasmFuncref' not handled in switch [-Werror,-Wswitch]
  switch (D.Kind) {
          ^
```

The LLVM commit models WebAssembly externref/funcref as
target("wasm.externref") / target("wasm.funcref") TargetExtTypes and
adds the corresponding WasmExternref and WasmFuncref enumerators to
IITDescriptor::Kind. These are opaque WebAssembly reference types with
no Swift representation, so they join the leading group of kinds that
"cannot be expressed in swift yet" and return an empty Type().

See llvm/llvm-project@765f0d1 (llvm/llvm-project#203165).

Assisted-by: Claude Code
AnthonyLatsis added a commit to AnthonyLatsis/swift that referenced this pull request Aug 6, 2026
…tType (#203165)"

Add the new `IITDescriptor::Kind` cases to the "cannot be expressed in
Swift yet" bucket.

See llvm/llvm-project@765f0d1 (llvm/llvm-project#203165).
AnthonyLatsis added a commit to AnthonyLatsis/swift that referenced this pull request Aug 6, 2026
…etExtType (#203165)"

```
/Users/alatsis/Desktop/rebranch/swift-2/lib/AST/Builtins.cpp:2659:11: error: enumeration values 'WasmExternref' and 'WasmFuncref' not handled in switch [-Werror,-Wswitch]
  switch (D.Kind) {
          ^
```

The LLVM commit models WebAssembly externref/funcref as
target("wasm.externref") / target("wasm.funcref") TargetExtTypes and
adds the corresponding WasmExternref and WasmFuncref enumerators to
IITDescriptor::Kind. These are opaque WebAssembly reference types with
no Swift representation, so they join the leading group of kinds that
"cannot be expressed in swift yet" and return an empty Type().

See llvm/llvm-project@765f0d1 (llvm/llvm-project#203165).

Assisted-by: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compilation crash targetting wasm with -O2 and reference types

4 participants