llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Hood Chatham (hoodmane)
<details>
<summary>Changes</summary>
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
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]
``````````
</details>
https://github.com/llvm/llvm-project/pull/203165
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits