https://github.com/hoodmane updated https://github.com/llvm/llvm-project/pull/203165
>From 475bcb6fe19776b6a724648d16f29ebe3250f255 Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Wed, 10 Jun 2026 20:36:28 -0700 Subject: [PATCH] [WebAssembly] Represent reference types as TargetExtType 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. Co-authored-by: Hood Chatham <[email protected]> Co-authored-by: Paulo Matos <[email protected]> --- clang/lib/CodeGen/CGCall.cpp | 13 ++ clang/lib/CodeGen/CGExprScalar.cpp | 43 +++- clang/lib/CodeGen/CodeGenTypes.cpp | 4 + .../WebAssembly/builtins-table-externref.c | 28 +-- .../WebAssembly/builtins-table-funcref.c | 16 +- .../WebAssembly/builtins-test-fp-sig.c | 4 +- .../test/CodeGen/WebAssembly/wasm-externref.c | 8 +- .../WebAssembly/wasm-funcref-to-ptr-error.c | 21 ++ clang/test/CodeGen/WebAssembly/wasm-funcref.c | 69 ++++-- clang/test/CodeGen/builtins-wasm.c | 4 +- llvm/include/llvm/IR/Intrinsics.h | 2 + llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 11 + .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 23 +- llvm/lib/CodeGen/ValueTypes.cpp | 4 + llvm/lib/IR/Intrinsics.cpp | 17 +- llvm/lib/IR/Type.cpp | 12 +- llvm/lib/Target/WebAssembly/CMakeLists.txt | 1 - .../GISel/WebAssemblyCallLowering.cpp | 53 +++-- .../WebAssembly/Utils/WasmAddressSpaces.h | 6 +- .../Utils/WebAssemblyTypeUtilities.h | 10 +- llvm/lib/Target/WebAssembly/WebAssembly.h | 2 - .../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 21 ++ .../WebAssembly/WebAssemblyISelLowering.cpp | 49 +++-- .../WebAssembly/WebAssemblyISelLowering.h | 3 - .../WebAssemblyLowerRefTypesIntPtrConv.cpp | 85 -------- .../WebAssemblyRefTypeMem2Local.cpp | 2 + .../WebAssembly/WebAssemblyTargetMachine.cpp | 2 - .../GlobalISel/irtranslator/args.ll | 24 +-- .../GlobalISel/irtranslator/call-basics.ll | 56 +---- .../GlobalISel/irtranslator/ret-basics.ll | 30 +-- .../GlobalISel/reference-types-fallback.ll | 40 ++++ .../WebAssembly/externref-globalget.ll | 2 +- .../WebAssembly/externref-globalset.ll | 2 +- .../CodeGen/WebAssembly/externref-inttoptr.ll | 12 +- .../CodeGen/WebAssembly/externref-ptrtoint.ll | 11 +- .../CodeGen/WebAssembly/externref-tableget.ll | 2 +- .../CodeGen/WebAssembly/externref-tableset.ll | 2 +- .../WebAssembly/externref-unsized-load.ll | 2 +- .../WebAssembly/externref-unsized-store.ll | 2 +- llvm/test/CodeGen/WebAssembly/funcref-call.ll | 10 +- .../CodeGen/WebAssembly/funcref-globalget.ll | 2 +- .../CodeGen/WebAssembly/funcref-globalset.ll | 2 +- .../WebAssembly/funcref-ptr-conversion.ll | 28 +++ .../CodeGen/WebAssembly/funcref-table_call.ll | 6 +- .../CodeGen/WebAssembly/funcref-tableget.ll | 2 +- .../CodeGen/WebAssembly/funcref-tableset.ll | 2 +- .../WebAssembly/funcref-to-ptr-error.ll | 30 +++ llvm/test/CodeGen/WebAssembly/ref-null.ll | 4 +- .../test/CodeGen/WebAssembly/ref-test-func.ll | 201 ++++++++++++------ .../CodeGen/WebAssembly/ref-type-mem2local.ll | 44 ++-- .../CodeGen/WebAssembly/select-reftype.ll | 24 +-- llvm/test/CodeGen/WebAssembly/table-copy.ll | 2 +- llvm/test/CodeGen/WebAssembly/table-fill.ll | 2 +- llvm/test/CodeGen/WebAssembly/table-grow.ll | 2 +- llvm/test/CodeGen/WebAssembly/table-size.ll | 2 +- llvm/test/CodeGen/WebAssembly/table-types.ll | 4 +- .../llvm/lib/Target/WebAssembly/BUILD.gn | 1 - 57 files changed, 616 insertions(+), 450 deletions(-) create mode 100644 clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c delete mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll create mode 100644 llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll create mode 100644 llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll 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..18ed6570730f4 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,43 @@ 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: use wasm_funcref_to_ptr. This will probably crash + // later in codegen since we haven't implemented a way to actually get a + // function pointer from a funcref. + 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 +2885,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..3de3bad6affb5 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -645,6 +645,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { case Type::Pointer: { const PointerType *PTy = cast<PointerType>(Ty); QualType ETy = PTy->getPointeeType(); + 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..ce663a70dd3b1 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c @@ -0,0 +1,21 @@ +// 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 + +// We haven't implemented a way of converting a funcref to a function pointer. +// We can generate code for it if the result is immediately called, which avoids +// the need for creating a function pointer. If the resulting pointer escapes, +// we haven't implemented codegen for that. Diagnose it in the front end rather +// than crashing in 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 [[FN_ADDR]], align 4 +// CHECK-NEXT: call void @helper(target("wasm.funcref") noundef [[TMP0]]) // CHECK-NEXT: ret i32 0 // int handle(funcref_t fn) { @@ -60,29 +60,58 @@ int handle(funcref_t fn) { // CHECK-NEXT: [[FNPTR_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[FNPTR:%.*]], ptr [[FNPTR_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FNPTR_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = addrspacecast ptr [[TMP0]] to ptr addrspace(20) -// CHECK-NEXT: ret ptr addrspace(20) [[TMP1]] +// CHECK-NEXT: [[TMP1:%.*]] = call target("wasm.funcref") @llvm.wasm.ptr.to_funcref(ptr [[TMP0]]) +// CHECK-NEXT: ret target("wasm.funcref") [[TMP1]] // fn_funcref_t get_ref(fn_t fnptr) { return (fn_funcref_t) fnptr; } +// Casting a null function pointer to a funcref yields a null funcref +// (ref.null func), not a lookup of index 0 in the indirect function table. +// CHECK-LABEL: @get_null_from_fnptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret target("wasm.funcref") zeroinitializer +// +fn_funcref_t get_null_from_fnptr() { + return (fn_funcref_t)(fn_t)0; +} + // Call funcref // CHECK-LABEL: @call_fn( // CHECK-NEXT: entry: -// CHECK-NEXT: [[REF_ADDR:%.*]] = alloca ptr addrspace(20), align 4 +// CHECK-NEXT: [[REF_ADDR:%.*]] = alloca target("wasm.funcref"), align 4 // CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store ptr addrspace(20) [[REF:%.*]], ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: store target("wasm.funcref") [[REF:%.*]], ptr [[REF_ADDR]], align 4 // CHECK-NEXT: store i32 [[X:%.*]], ptr [[X_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(20), ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[REF_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call addrspace(20) i32 [[TMP0]](i32 noundef [[TMP1]]) +// CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.wasm.funcref.to_ptr(target("wasm.funcref") [[TMP0]]) +// CHECK-NEXT: [[CALL:%.*]] = call i32 [[TMP2]](i32 noundef [[TMP1]]) // CHECK-NEXT: ret i32 [[CALL]] // int call_fn(fn_funcref_t ref, int x) { return ref(x); } +// Explicitly casting a funcref to a plain function pointer and calling it +// immediately is allowed: the conversion feeds directly into the indirect call. +// CHECK-LABEL: @call_fn_via_cast( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[REF_ADDR:%.*]] = alloca target("wasm.funcref"), align 4 +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store target("wasm.funcref") [[REF:%.*]], ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: store i32 [[X:%.*]], ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.wasm.funcref.to_ptr(target("wasm.funcref") [[TMP0]]) +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call i32 [[TMP1]](i32 noundef [[TMP2]]) +// CHECK-NEXT: ret i32 [[CALL]] +// +int call_fn_via_cast(fn_funcref_t ref, int x) { + return ((fn_t)ref)(x); +} + typedef fn_funcref_t (*builtin_refnull_t)(); // Calling ref.null through a function pointer. @@ -91,8 +120,8 @@ typedef fn_funcref_t (*builtin_refnull_t)(); // CHECK-NEXT: [[REFNULL_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[REFNULL:%.*]], ptr [[REFNULL_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[REFNULL_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call ptr addrspace(20) [[TMP0]]() -// CHECK-NEXT: ret ptr addrspace(20) [[CALL]] +// CHECK-NEXT: [[CALL:%.*]] = call target("wasm.funcref") [[TMP0]]() +// CHECK-NEXT: ret target("wasm.funcref") [[CALL]] // fn_funcref_t get_null_fptr(builtin_refnull_t refnull) { return refnull(); diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index 375664b852636..40788b0afeb45 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -737,13 +737,13 @@ f16x8 pmax_f16x8(f16x8 a, f16x8 b) { } __externref_t externref_null() { return __builtin_wasm_ref_null_extern(); - // WEBASSEMBLY: tail call ptr addrspace(10) @llvm.wasm.ref.null.extern() + // WEBASSEMBLY: tail call target("wasm.externref") @llvm.wasm.ref.null.extern() // WEBASSEMBLY-NEXT: ret } int externref_is_null(__externref_t arg) { return __builtin_wasm_ref_is_null_extern(arg); - // WEBASSEMBLY: tail call i32 @llvm.wasm.ref.is_null.extern(ptr addrspace(10) %arg) + // WEBASSEMBLY: tail call i32 @llvm.wasm.ref.is_null.extern(target("wasm.externref") %arg) // WEBASSEMBLY-NEXT: ret } diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h index 5ccb18d7281bc..d78a66e45a3ad 100644 --- a/llvm/include/llvm/IR/Intrinsics.h +++ b/llvm/include/llvm/IR/Intrinsics.h @@ -177,6 +177,8 @@ struct IITDescriptor { AMX, PPCQuad, AArch64Svcount, + WasmExternref, + WasmFuncref, // Overloaded type. Overloaded, // AnyKind and overload index in OverloadInfo. diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td index c1e4b97e96bc8..a0e83cee9f055 100644 --- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td +++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td @@ -47,6 +47,17 @@ def int_wasm_ref_test_func : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_vararg_ty], [IntrNoMem]>; +//===----------------------------------------------------------------------===// +// funcref <--> pointer conversion intrinsics +//===----------------------------------------------------------------------===// +def int_wasm_funcref_to_ptr : + DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_funcref_ty], + [IntrNoMem], "llvm.wasm.funcref.to_ptr">; + +def int_wasm_ptr_to_funcref : + DefaultAttrsIntrinsic<[llvm_funcref_ty], [llvm_ptr_ty], + [IntrNoMem], "llvm.wasm.ptr.to_funcref">; + //===----------------------------------------------------------------------===// // Table intrinsics //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index 5ae52cae771fb..1b479c2b36058 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -230,9 +230,26 @@ static bool dontUseFastISelFor(const Function &Fn) { // Debug info on those is reliant on good Argument lowering, and FastISel is // not capable of lowering the entire function. Mixing the two selectors tend // to result in poor lowering of Arguments. - return any_of(Fn.args(), [](const Argument &Arg) { - return Arg.hasAttribute(Attribute::AttrKind::SwiftAsync); - }); + if (any_of(Fn.args(), [](const Argument &Arg) { + return Arg.hasAttribute(Attribute::AttrKind::SwiftAsync); + })) + return true; + + // A WebAssembly funcref call is expressed in IR as a call through the pointer + // produced by the llvm.wasm.funcref.to_ptr intrinsic. SelectionDAG fuses the + // intrinsic and the call into a table.set + call_indirect through the + // dedicated __funcref_call_table. FastISel selects each call as its own + // single-instruction block and therefore cannot perform this fusion, so fall + // back to SelectionDAG for the whole function when the intrinsic is present. + if (Fn.getParent()->getTargetTriple().isWasm()) { + for (const BasicBlock &BB : Fn) + for (const Instruction &I : BB) + if (const auto *II = dyn_cast<IntrinsicInst>(&I)) + if (II->getIntrinsicID() == Intrinsic::wasm_funcref_to_ptr) + return true; + } + + return false; } static bool maintainPGOProfile(const TargetMachine &TM, diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp index e74068e22f4cd..a8eb5f801a280 100644 --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -268,6 +268,10 @@ MVT MVT::getVT(Type *Ty, bool HandleUnknown){ TargetExtType *TargetExtTy = cast<TargetExtType>(Ty); if (TargetExtTy->getName() == "aarch64.svcount") return MVT(MVT::aarch64svcount); + else if (TargetExtTy->getName() == "wasm.externref") + return MVT(MVT::externref); + else if (TargetExtTy->getName() == "wasm.funcref") + return MVT(MVT::funcref); else if (TargetExtTy->getName().starts_with("spirv.")) return MVT(MVT::spirvbuiltin); if (TargetExtTy->getName() == "riscv.vector.tuple") { diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp index b0bad878f0c6f..ed4d37df33e7d 100644 --- a/llvm/lib/IR/Intrinsics.cpp +++ b/llvm/lib/IR/Intrinsics.cpp @@ -358,10 +358,10 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos, DecodeIITType(NextElt, Infos, OutputTable); return; case IIT_EXTERNREF: - OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 10)); + OutputTable.push_back(IITDescriptor::get(IITDescriptor::WasmExternref, 0)); return; case IIT_FUNCREF: - OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 20)); + OutputTable.push_back(IITDescriptor::get(IITDescriptor::WasmFuncref, 0)); return; case IIT_PTR: OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 0)); @@ -556,7 +556,10 @@ static Type *DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos, return Type::getPPC_FP128Ty(Context); case IITDescriptor::AArch64Svcount: return TargetExtType::get(Context, "aarch64.svcount"); - + case IITDescriptor::WasmExternref: + return TargetExtType::get(Context, "wasm.externref"); + case IITDescriptor::WasmFuncref: + return TargetExtType::get(Context, "wasm.funcref"); case IITDescriptor::Integer: return IntegerType::get(Context, D.IntegerWidth); case IITDescriptor::Vector: @@ -1029,6 +1032,14 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos, return PrintMsg(isa<TargetExtType>(Ty) && cast<TargetExtType>(Ty)->getName() == "aarch64.svcount", "aarch64.svcount"); + case IITDescriptor::WasmExternref: + return PrintMsg(isa<TargetExtType>(Ty) && + cast<TargetExtType>(Ty)->getName() == "wasm.externref", + "wasm.externref"); + case IITDescriptor::WasmFuncref: + return PrintMsg(isa<TargetExtType>(Ty) && + cast<TargetExtType>(Ty)->getName() == "wasm.funcref", + "wasm.funcref"); case IITDescriptor::Vector: { VectorType *VT = dyn_cast<VectorType>(Ty); StringRef Scalable = D.VectorWidth.isScalable() ? "vscale " : ""; diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 47b230d44285b..d5f4671487acb 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -334,13 +334,11 @@ Type *Type::getByteFromIntType(Type *Ty) { } Type *Type::getWasm_ExternrefTy(LLVMContext &C) { - // opaque pointer in addrspace(10) - return PointerType::get(C, 10); + return TargetExtType::get(C, "wasm.externref", {}, {}); } Type *Type::getWasm_FuncrefTy(LLVMContext &C) { - // opaque pointer in addrspace(20) - return PointerType::get(C, 20); + return TargetExtType::get(C, "wasm.funcref", {}, {}); } //===----------------------------------------------------------------------===// @@ -1134,6 +1132,12 @@ static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) { TargetExtType::CanBeVectorElement); } + // Opaque types in the WebAssembly name space. + if (Name == "wasm.funcref" || Name == "wasm.externref") + return TargetTypeInfo(PointerType::getUnqual(C), TargetExtType::HasZeroInit, + TargetExtType::CanBeGlobal, + TargetExtType::CanBeLocal); + return TargetTypeInfo(Type::getVoidTy(C)); } diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index ef5f7d11e7e49..a024a354890d2 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -51,7 +51,6 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyInstrInfo.cpp WebAssemblyLowerBrUnless.cpp WebAssemblyLowerEmscriptenEHSjLj.cpp - WebAssemblyLowerRefTypesIntPtrConv.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp WebAssemblyMCLowerPrePass.cpp diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp index 9f3a1d1ba7fa2..3241e30789b97 100644 --- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp +++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp @@ -14,7 +14,7 @@ #include "WebAssemblyCallLowering.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "Utils/WasmAddressSpaces.h" +#include "Utils/WebAssemblyTypeUtilities.h" #include "WebAssemblyISelLowering.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyRegisterInfo.h" @@ -58,22 +58,18 @@ static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) { return TargetOpcode::G_ANYEXT; } -static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) { - if (Ty == MVT::externref) { - return LLT::pointer( - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF, - DL.getPointerSizeInBits( - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF)); - } - - if (Ty == MVT::funcref) { - return LLT::pointer( - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF, - DL.getPointerSizeInBits( - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF)); - } - - return llvm::getLLTForMVT(Ty); +// GlobalISel doesn't handle reference types. We bail out of GlobalISel for +// functions passing/returning references and fall back to SDAG. +static bool typeContainsReference(const Type *Ty) { + if (WebAssembly::isWebAssemblyReferenceType(Ty)) + return true; + if (const auto *ArrTy = dyn_cast<ArrayType>(Ty)) + return typeContainsReference(ArrTy->getElementType()); + if (const auto *StructTy = dyn_cast<StructType>(Ty)) + return llvm::any_of(StructTy->elements(), [](const Type *ElemTy) { + return typeContainsReference(ElemTy); + }); + return false; } WebAssemblyCallLowering::WebAssemblyCallLowering( @@ -99,6 +95,9 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>(); const DataLayout &DL = F.getDataLayout(); + if (Val && typeContainsReference(Val->getType())) + return false; + MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN); assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) && @@ -134,7 +133,7 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT); const LLT OrigLLT = getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL); - const LLT NewLLT = getLLTForWasmMVT(NewVT, DL); + const LLT NewLLT = llvm::getLLTForMVT(NewVT); const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT); @@ -276,6 +275,12 @@ bool WebAssemblyCallLowering::lowerFormalArguments( if (!callingConvSupported(CallConv)) return false; + if (typeContainsReference(F.getReturnType())) + return false; + for (const Argument &Arg : F.args()) + if (typeContainsReference(Arg.getType())) + return false; + MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); MF.front().addLiveIn(WebAssembly::ARGUMENTS); @@ -308,7 +313,7 @@ bool WebAssemblyCallLowering::lowerFormalArguments( const MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT); const LLT OrigLLT = getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL); - const LLT NewLLT = getLLTForWasmMVT(NewVT, DL); + const LLT NewLLT = llvm::getLLTForMVT(NewVT); // If we need to split the type over multiple regs, check it's a scenario // we currently support. @@ -410,6 +415,12 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, if (!callingConvSupported(CallConv)) return false; + if (typeContainsReference(Info.OrigRet.Ty)) + return false; + for (const ArgInfo &Arg : Info.OrigArgs) + if (typeContainsReference(Arg.Ty)) + return false; + // TODO: tail calls if (Info.IsMustTailCall) return false; @@ -456,7 +467,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT); const LLT OrigLLT = getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL); - const LLT NewLLT = getLLTForWasmMVT(NewVT, DL); + const LLT NewLLT = llvm::getLLTForMVT(NewVT); const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT); @@ -552,7 +563,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT); const LLT OrigLLT = getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL); - const LLT NewLLT = getLLTForWasmMVT(NewVT, DL); + const LLT NewLLT = llvm::getLLTForMVT(NewVT); const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT); diff --git a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h index 2239badca69c3..d2ab2c0f9e777 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h +++ b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h @@ -24,11 +24,7 @@ enum WasmAddressSpace : unsigned { // linear memory: WebAssembly globals or WebAssembly locals. Loads and stores // to these pointers are lowered to global.get / global.set or local.get / // local.set, as appropriate. - WASM_ADDRESS_SPACE_VAR = 1, - // A non-integral address space for externref values - WASM_ADDRESS_SPACE_EXTERNREF = 10, - // A non-integral address space for funcref values - WASM_ADDRESS_SPACE_FUNCREF = 20, + WASM_ADDRESS_SPACE_VAR = 1 }; inline bool isDefaultAddressSpace(unsigned AS) { diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h index c5e0dcfffef9c..47ba91df81161 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h @@ -28,16 +28,14 @@ namespace WebAssembly { /// Return true if this is a WebAssembly Externref Type. inline bool isWebAssemblyExternrefType(const Type *Ty) { - return Ty->isPointerTy() && - Ty->getPointerAddressSpace() == - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF; + const TargetExtType *TargetTy = dyn_cast<TargetExtType>(Ty); + return TargetTy && TargetTy->getName() == "wasm.externref"; } /// Return true if this is a WebAssembly Funcref Type. inline bool isWebAssemblyFuncrefType(const Type *Ty) { - return Ty->isPointerTy() && - Ty->getPointerAddressSpace() == - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF; + const TargetExtType *TargetTy = dyn_cast<TargetExtType>(Ty); + return TargetTy && TargetTy->getName() == "wasm.funcref"; } /// Return true if this is a WebAssembly Reference Type. diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index 101348501bece..6556cb5e25c0e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -32,7 +32,6 @@ ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(); ModulePass *createWebAssemblyAddMissingPrototypes(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); -FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv(); FunctionPass *createWebAssemblyRefTypeMem2Local(); FunctionPass *createWebAssemblyReduceToAnyAllTrue(WebAssemblyTargetMachine &TM); @@ -94,7 +93,6 @@ void initializeWebAssemblyFixIrreducibleControlFlowPass(PassRegistry &); void initializeWebAssemblyLateEHPreparePass(PassRegistry &); void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &); void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &); -void initializeWebAssemblyLowerRefTypesIntPtrConvPass(PassRegistry &); void initializeWebAssemblyMCLowerPrePassPass(PassRegistry &); void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &); void initializeWebAssemblyNullifyDebugValueListsPass(PassRegistry &); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index c7b57588877b7..69e9e6b5a9717 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -275,6 +275,27 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { ReplaceNode(Node, TLSAlign); return; } + case Intrinsic::wasm_ptr_to_funcref: { + // Convert a function pointer to a funcref by reading the corresponding + // entry from the __indirect_function_table. + MachineFunction &MF = CurDAG->getMachineFunction(); + auto PtrVT = MVT::getIntegerVT(MF.getDataLayout().getPointerSizeInBits()); + MCSymbol *Table = WebAssembly::getOrCreateFunctionTableSymbol( + MF.getContext(), Subtarget); + SDValue TableSym = CurDAG->getMCSymbol(Table, PtrVT); + SDValue FuncPtr = Node->getOperand(1); + if (Subtarget->hasAddr64() && FuncPtr.getValueType() == MVT::i64) { + // table.get expects an i32 but on 64 bit platforms the function pointer + // is an i64. In that case, i32.wrap_i64 to convert. + FuncPtr = SDValue(CurDAG->getMachineNode(WebAssembly::I32_WRAP_I64, DL, + MVT::i32, FuncPtr), + 0); + } + MachineSDNode *FuncRef = CurDAG->getMachineNode( + WebAssembly::TABLE_GET_FUNCREF, DL, MVT::funcref, TableSym, FuncPtr); + ReplaceNode(Node, FuncRef); + return; + } case Intrinsic::wasm_ref_test_func: { // First emit the TABLE_GET instruction to convert function pointer ==> // funcref diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index bba3a34b08df8..7c0fc3fffea01 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -436,24 +436,6 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setMinimumJumpTableEntries(2); } -MVT WebAssemblyTargetLowering::getPointerTy(const DataLayout &DL, - uint32_t AS) const { - if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) - return MVT::externref; - if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) - return MVT::funcref; - return TargetLowering::getPointerTy(DL, AS); -} - -MVT WebAssemblyTargetLowering::getPointerMemTy(const DataLayout &DL, - uint32_t AS) const { - if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) - return MVT::externref; - if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) - return MVT::funcref; - return TargetLowering::getPointerMemTy(DL, AS); -} - TargetLowering::AtomicExpansionKind WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR( const AtomicRMWInst *AI) const { @@ -1294,6 +1276,17 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, MachineFunction &MF = DAG.getMachineFunction(); auto Layout = MF.getDataLayout(); + // A call through a funcref is expressed in IR as a call through the pointer + // produced by the llvm.wasm.funcref.to_ptr intrinsic. Detect this here and + // recover the underlying funcref value so the call can be lowered to a + // table.set + call_indirect through the dedicated __funcref_call_table. + bool IsFuncrefCall = false; + if (Callee.getOpcode() == ISD::INTRINSIC_WO_CHAIN && + Callee.getConstantOperandVal(0) == Intrinsic::wasm_funcref_to_ptr) { + Callee = Callee.getOperand(1); + IsFuncrefCall = true; + } + CallingConv::ID CallConv = CLI.CallConv; if (!callingConvSupported(CallConv)) fail(DL, DAG, @@ -1537,8 +1530,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, // Lastly, if this is a call to a funcref we need to add an instruction // table.set to the chain and transform the call. - if (CLI.CB && WebAssembly::isWebAssemblyFuncrefType( - CLI.CB->getCalledOperand()->getType())) { + if (IsFuncrefCall) { // In the absence of function references proposal where a funcref call is // lowered to call_ref, using reference types we generate a table.set to set // the funcref to a special table used solely for this purpose, followed by @@ -1554,11 +1546,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, SDValue TableSetOps[] = {Chain, Sym, TableSlot, Callee}; SDValue TableSet = DAG.getMemIntrinsicNode( WebAssemblyISD::TABLE_SET, DL, DAG.getVTList(MVT::Other), TableSetOps, - MVT::funcref, - // Machine Mem Operand args - MachinePointerInfo( - WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF), - CLI.CB->getCalledOperand()->getPointerAlignment(DAG.getDataLayout()), + MVT::funcref, MachinePointerInfo(), Align(1), MachineMemOperand::MOStore); Ops[0] = TableSet; // The new chain is the TableSet itself @@ -2277,6 +2265,17 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); } + case Intrinsic::wasm_funcref_to_ptr: { + // llvm.wasm.funcref.to_ptr only has a defined lowering when its result + // feeds directly into an indirect call. Reaching here means the pointer + // escapes a direct call. We haven't implemented conversion of a funcref + // into a real function pointer so we crash if we get here. + fail(DL, DAG, + "a funcref can only be converted to a pointer to be directly called; " + "the resulting pointer cannot otherwise be used"); + return DAG.getUNDEF(Op.getValueType()); + } + case Intrinsic::thread_pointer: { return SDValue(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h index 42f047840e504..04e6d6f2d9367 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -26,9 +26,6 @@ class WebAssemblyTargetLowering final : public TargetLowering { WebAssemblyTargetLowering(const TargetMachine &TM, const WebAssemblySubtarget &STI); - MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override; - MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const override; - private: /// Keep a pointer to the WebAssemblySubtarget around so that we can make the /// right decision when generating code for different targets. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp deleted file mode 100644 index be500de67e320..0000000000000 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp +++ /dev/null @@ -1,85 +0,0 @@ -//=== WebAssemblyLowerRefTypesIntPtrConv.cpp - -// Lower IntToPtr and PtrToInt on Reference Types ---===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Lowers IntToPtr and PtrToInt instructions on reference types to -/// Trap instructions since they have been allowed to operate -/// on non-integral pointers. -/// -//===----------------------------------------------------------------------===// - -#include "Utils/WebAssemblyTypeUtilities.h" -#include "WebAssembly.h" -#include "WebAssemblySubtarget.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/Pass.h" -#include <set> - -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower-reftypes-intptr-conv" - -namespace { -class WebAssemblyLowerRefTypesIntPtrConv final : public FunctionPass { - StringRef getPassName() const override { - return "WebAssembly Lower RefTypes Int-Ptr Conversions"; - } - - bool runOnFunction(Function &MF) override; - -public: - static char ID; // Pass identification - WebAssemblyLowerRefTypesIntPtrConv() : FunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyLowerRefTypesIntPtrConv::ID = 0; -INITIALIZE_PASS(WebAssemblyLowerRefTypesIntPtrConv, DEBUG_TYPE, - "WebAssembly Lower RefTypes Int-Ptr Conversions", false, false) - -FunctionPass *llvm::createWebAssemblyLowerRefTypesIntPtrConv() { - return new WebAssemblyLowerRefTypesIntPtrConv(); -} - -bool WebAssemblyLowerRefTypesIntPtrConv::runOnFunction(Function &F) { - LLVM_DEBUG(dbgs() << "********** Lower RefTypes IntPtr Convs **********\n" - "********** Function: " - << F.getName() << '\n'); - - // This function will check for uses of ptrtoint and inttoptr on reference - // types and replace them with a trap instruction. - // - // We replace the instruction by a trap instruction - // and its uses by null in the case of inttoptr and 0 in the - // case of ptrtoint. - std::set<Instruction *> worklist; - - for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { - PtrToIntInst *PTI = dyn_cast<PtrToIntInst>(&*I); - IntToPtrInst *ITP = dyn_cast<IntToPtrInst>(&*I); - if (!(PTI && WebAssembly::isWebAssemblyReferenceType( - PTI->getPointerOperand()->getType())) && - !(ITP && WebAssembly::isWebAssemblyReferenceType(ITP->getDestTy()))) - continue; - - I->replaceAllUsesWith(PoisonValue::get(I->getType())); - - Function *TrapIntrin = - Intrinsic::getOrInsertDeclaration(F.getParent(), Intrinsic::debugtrap); - CallInst::Create(TrapIntrin, {}, "", I->getIterator()); - - worklist.insert(&*I); - } - - // erase each instruction replaced by trap - for (Instruction *I : worklist) - I->eraseFromParent(); - - return !worklist.empty(); -} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp index 04b4c7d78aabb..d2ff9b264d576 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp @@ -64,6 +64,8 @@ void WebAssemblyRefTypeMem2Local::visitAllocaInst(AllocaInst &AI) { auto *NewAI = IRB.CreateAlloca(AI.getAllocatedType(), WebAssembly::WASM_ADDRESS_SPACE_VAR, nullptr, AI.getName() + ".var"); + // Preserve the original alloca's alignment. + NewAI->setAlignment(AI.getAlign()); // The below is basically equivalent to AI.replaceAllUsesWith(NewAI), but we // cannot use it because it requires the old and new types be the same, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 886ea0a8ab574..63bda89309dfc 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -119,7 +119,6 @@ LLVMInitializeWebAssemblyTarget() { initializeWebAssemblyDebugFixupPass(PR); initializeWebAssemblyPeepholePass(PR); initializeWebAssemblyMCLowerPrePassPass(PR); - initializeWebAssemblyLowerRefTypesIntPtrConvPass(PR); initializeWebAssemblyFixBrTableDefaultsPass(PR); initializeWebAssemblyDAGToDAGISelLegacyPass(PR); } @@ -670,7 +669,6 @@ void WebAssemblyPassConfig::addPreEmitPass() { bool WebAssemblyPassConfig::addPreISel() { TargetPassConfig::addPreISel(); - addPass(createWebAssemblyLowerRefTypesIntPtrConv()); return false; } diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll index 152a607b14d5a..97413111536be 100644 --- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll +++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll @@ -144,27 +144,9 @@ define void @test_f128_arg(fp128 %arg) { ret void } -%externref = type ptr addrspace(10) -define void @test_externref_arg(%externref %arg) { - ; CHECK-LABEL: name: test_externref_arg - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[ARGUMENT_externref:%[0-9]+]]:externref(p10) = ARGUMENT_externref 0, implicit $arguments - ; CHECK-NEXT: RETURN implicit-def $arguments - ret void -} - -%funcref = type ptr addrspace(20) -define void @test_funcref_arg(%funcref %arg) { - ; CHECK-LABEL: name: test_funcref_arg - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[ARGUMENT_funcref:%[0-9]+]]:funcref(p20) = ARGUMENT_funcref 0, implicit $arguments - ; CHECK-NEXT: RETURN implicit-def $arguments - ret void -} +; NOTE: Reference types (externref/funcref) are intentionally not tested here. +; GlobalISel currently falls back to SelectionDAG for them; see +; reference-types-fallback.ll. define void @test_multiple_args(ptr %arg1, float %arg2, i1 %arg3) { ; WASM32-LABEL: name: test_multiple_args diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll index d767ed5104dae..5a1a4fc8f3c68 100644 --- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll +++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll @@ -5,8 +5,6 @@ ; RUN: llc -mtriple=wasm64 -mattr=+simd128,+multivalue -target-abi=experimental-mv -global-isel -stop-after=irtranslator < %s | FileCheck %s -check-prefixes=CHECK,WASM64,MULTIVAL-SIMD,WASM64-MULTIVAL-SIMD -%externref = type ptr addrspace(10) -%funcref = type ptr addrspace(20) declare void @ret_void_args_none() define void @call_ret_void_args_none() { @@ -87,29 +85,9 @@ define ptr @call_ret_ptr_args_none() { ret ptr %ret } -declare %externref @ret_externref_args_none() -define %externref @call_ret_externref_args_none() { - ; CHECK-LABEL: name: call_ret_externref_args_none - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[CALL:%[0-9]+]]:externref(p10) = CALL @ret_externref_args_none, implicit-def $arguments, implicit $sp32, implicit $sp64 - ; CHECK-NEXT: RETURN [[CALL]](p10), implicit-def $arguments - %ret = call %externref @ret_externref_args_none() - ret %externref %ret -} - -declare %funcref @ret_funcref_args_none() -define %funcref @call_ret_funcref_args_none() { - ; CHECK-LABEL: name: call_ret_funcref_args_none - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[CALL:%[0-9]+]]:funcref(p20) = CALL @ret_funcref_args_none, implicit-def $arguments, implicit $sp32, implicit $sp64 - ; CHECK-NEXT: RETURN [[CALL]](p20), implicit-def $arguments - %ret = call %funcref @ret_funcref_args_none() - ret %funcref %ret -} +; NOTE: Reference-type (externref/funcref) returns are intentionally not tested +; here. GlobalISel currently falls back to SelectionDAG for them; see +; reference-types-fallback.ll. declare i128 @ret_i128_args_none() define i128 @call_ret_i128_args_none() { @@ -234,31 +212,9 @@ define void @call_ret_void_args_ptr(ptr %a) { ret void } -declare void @ret_void_args_externref(%externref) -define void @call_ret_void_args_externref(%externref %a) { - ; CHECK-LABEL: name: call_ret_void_args_externref - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[ARGUMENT_externref:%[0-9]+]]:externref(p10) = ARGUMENT_externref 0, implicit $arguments - ; CHECK-NEXT: CALL @ret_void_args_externref, [[ARGUMENT_externref]](p10), implicit-def $arguments, implicit $sp32, implicit $sp64 - ; CHECK-NEXT: RETURN implicit-def $arguments - call void @ret_void_args_externref(%externref %a) - ret void -} - -declare void @ret_void_args_funcref(%funcref) -define void @call_ret_void_args_funcref(%funcref %a) { - ; CHECK-LABEL: name: call_ret_void_args_funcref - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[ARGUMENT_funcref:%[0-9]+]]:funcref(p20) = ARGUMENT_funcref 0, implicit $arguments - ; CHECK-NEXT: CALL @ret_void_args_funcref, [[ARGUMENT_funcref]](p20), implicit-def $arguments, implicit $sp32, implicit $sp64 - ; CHECK-NEXT: RETURN implicit-def $arguments - call void @ret_void_args_funcref(%funcref %a) - ret void -} +; NOTE: Reference-type (externref/funcref) arguments are intentionally not +; tested here. GlobalISel currently falls back to SelectionDAG for them; see +; reference-types-fallback.ll. declare void @ret_void_args_i128(i128) define void @call_ret_void_args_i128(i128 %a) { diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll index 21e066e78c515..3bc6aaaa3d48c 100644 --- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll +++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll @@ -124,30 +124,6 @@ define double @test_ret_f64() { ret double 0.0 } -%externref = type ptr addrspace(10) -define %externref @test_ret_externref() { - ; CHECK-LABEL: name: test_ret_externref - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.ref_ptr - ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(p10) = G_LOAD [[FRAME_INDEX]](p0) :: (load (p10) from %ir.ref_ptr) - ; CHECK-NEXT: RETURN [[LOAD]](p10), implicit-def $arguments - %ref_ptr = alloca %externref - %ref = load %externref, ptr %ref_ptr - ret %externref %ref -} - -%funcref = type ptr addrspace(20) -define %funcref @test_ret_funcref() { - ; CHECK-LABEL: name: test_ret_funcref - ; CHECK: bb.1 (%ir-block.0): - ; CHECK-NEXT: liveins: $arguments - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.ref_ptr - ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(p20) = G_LOAD [[FRAME_INDEX]](p0) :: (load (p20) from %ir.ref_ptr) - ; CHECK-NEXT: RETURN [[LOAD]](p20), implicit-def $arguments - %ref_ptr = alloca %funcref - %ref = load %funcref, ptr %ref_ptr - ret %funcref %ref -} +; NOTE: Reference types (externref/funcref) are intentionally not tested here. +; GlobalISel currently falls back to SelectionDAG for them; see +; reference-types-fallback.ll. diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll new file mode 100644 index 0000000000000..bce9127a8969e --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll @@ -0,0 +1,40 @@ +; RUN: llc -mtriple=wasm32 -mattr=+reference-types -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s +; RUN: llc -mtriple=wasm64 -mattr=+reference-types -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s + +; GlobalISel does not yet correctly model WebAssembly reference types +; (externref/funcref): the generic getLLTForType derives an integer scalar LLT +; from their pointer layout type, which would round-trip references through +; invalid integer loads/bitcasts. Until that is fixed, the WebAssembly +; CallLowering bails out for any function passing or returning a reference type, +; so instruction selection falls back to SelectionDAG. This test verifies that +; fallback (rather than emission of incorrect code). + +define target("wasm.externref") @ret_externref(target("wasm.externref") %a) { +; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.externref +; CHECK-LABEL: warning: Instruction selection used fallback path for ret_externref + ret target("wasm.externref") %a +} + +define target("wasm.funcref") @ret_funcref(target("wasm.funcref") %a) { +; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.funcref +; CHECK-LABEL: warning: Instruction selection used fallback path for ret_funcref + ret target("wasm.funcref") %a +} + +declare void @take_externref(target("wasm.externref")) + +define void @call_take_externref(target("wasm.externref") %a) { +; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.externref +; CHECK-LABEL: warning: Instruction selection used fallback path for call_take_externref + call void @take_externref(target("wasm.externref") %a) + ret void +} + +declare target("wasm.externref") @produce_externref() + +define void @call_produce_externref() { +; CHECK: remark: {{.*}} unable to {{.*}} +; CHECK-LABEL: warning: Instruction selection used fallback path for call_produce_externref + %ref = call target("wasm.externref") @produce_externref() + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalget.ll b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll index 79d7932486e22..8d377cbfdad30 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-globalget.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll @@ -3,7 +3,7 @@ ; not error out. ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types -print-after=finalize-isel | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_global = local_unnamed_addr addrspace(1) global %externref undef diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalset.ll b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll index 5bfd673e89fa1..57cfffe82dee0 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-globalset.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_global = local_unnamed_addr addrspace(1) global %externref undef diff --git a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll index 64f955b6ed0f0..bdf04f73c54a7 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll @@ -1,16 +1,10 @@ -; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types 2>&1 | FileCheck %s +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR -%externref = type ptr addrspace(10) +%externref = type target("wasm.externref") define %externref @int_to_externref(i32 %i) { %ref = inttoptr i32 %i to %externref ret %externref %ref } - -; CHECK-LABEL: int_to_externref: -; CHECK-NEXT: .functype int_to_externref (i32) -> (externref) -; CHECK-NEXT: .local externref -; CHECK-NEXT: unreachable -; CHECK-NEXT: local.get 1 -; CHECK-NEXT: end_function +# CHECK-ERROR: error: invalid cast opcode for cast from 'i32' to 'target("wasm.externref")' diff --git a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll index 22558796f0624..f3cfe31445b60 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll @@ -1,15 +1,10 @@ -; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types 2>&1 | FileCheck %s +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR -%externref = type ptr addrspace(10) +%externref = type target("wasm.externref") define i32 @externref_to_int(%externref %ref) { %i = ptrtoint %externref %ref to i32 ret i32 %i } -; CHECK-LABEL: externref_to_int: -; CHECK-NEXT: .functype externref_to_int (externref) -> (i32) -; CHECK-NEXT: .local i32 -; CHECK-NEXT: unreachable -; CHECK-NEXT: local.get 1 -; CHECK-NEXT: end_function +# CHECK-ERROR: error: invalid cast opcode for cast from 'target("wasm.externref")' to 'i32' diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll index d9ae7c8f6c9b1..1189fc3571902 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll @@ -1,6 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll index 37c663869428e..f84ae4dba68eb 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll index 1f8f4d5140c51..98a0e801af387 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll @@ -1,6 +1,6 @@ ; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR -%externref = type ptr addrspace(10) +%externref = type target("wasm.externref") define void @load_extern(%externref %ref) { %e = load %extern, %externref %ref diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll index c7e062d1b0526..f3d3ee3685230 100644 --- a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll @@ -1,6 +1,6 @@ ; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR -%externref = type ptr addrspace(10) +%externref = type target("wasm.externref") define void @store_extern(%externref %ref) { store %extern undef, %externref %ref diff --git a/llvm/test/CodeGen/WebAssembly/funcref-call.ll b/llvm/test/CodeGen/WebAssembly/funcref-call.ll index 9904df2280e81..66692ac0eb0f0 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-call.ll @@ -2,7 +2,9 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=0 -mattr=+reference-types | FileCheck %s ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=1 -mattr=+reference-types | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") + +declare ptr @llvm.wasm.funcref.to_ptr(%funcref) nounwind ; CHECK: .tabletype __funcref_call_table, funcref, 1 @@ -19,7 +21,8 @@ define void @call_funcref(%funcref %ref) { ; CHECK-NEXT: ref.null_func ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: # fallthrough-return - call addrspace(20) void %ref() + %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + call void %refptr() ret void } @@ -41,6 +44,7 @@ define float @call_funcref_with_args(%funcref %ref) { ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: # fallthrough-return - %ret = call addrspace(20) float %ref(double 1.0, i32 2) + %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + %ret = call float %refptr(double 1.0, i32 2) ret float %ret } diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll index 9aa7fdabfdea9..2634e5a6fc57e 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll @@ -1,6 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") @funcref_global = local_unnamed_addr addrspace(1) global %funcref undef diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll index ca2feb6617996..791e8648537c0 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll @@ -1,6 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") @funcref_global = local_unnamed_addr addrspace(1) global %funcref undef diff --git a/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll b/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll new file mode 100644 index 0000000000000..b9792b661a742 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll @@ -0,0 +1,28 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s +; RUN: llc < %s --mtriple=wasm64-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s --check-prefix=CHECK64 + +%funcref = type target("wasm.funcref") + +declare %funcref @llvm.wasm.ptr.to_funcref(ptr) nounwind + +; CHECK: .tabletype __indirect_function_table, funcref + +; Converting a function pointer to a funcref is a table.get from the +; __indirect_function_table. +define %funcref @ptr_to_funcref(ptr %p) { +; CHECK-LABEL: ptr_to_funcref: +; CHECK: .functype ptr_to_funcref (i32) -> (funcref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: end_function +; +; On wasm64 the function pointer is an i64 and must be wrapped to i32 first. +; CHECK64-LABEL: ptr_to_funcref: +; CHECK64: .functype ptr_to_funcref (i64) -> (funcref) +; CHECK64-NEXT: local.get 0 +; CHECK64-NEXT: i32.wrap_i64 +; CHECK64-NEXT: table.get __indirect_function_table +; CHECK64-NEXT: end_function + %ref = call %funcref @llvm.wasm.ptr.to_funcref(ptr %p) + ret %funcref %ref +} diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll index 74bbc802ac077..e5343fa80d846 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll @@ -1,12 +1,13 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef ; CHECK: .tabletype __funcref_call_table, funcref, 1 declare %funcref @llvm.wasm.table.get.funcref(ptr addrspace(1), i32) nounwind +declare ptr @llvm.wasm.funcref.to_ptr(%funcref) nounwind define void @call_funcref_from_table(i32 %i) { ; CHECK-LABEL: call_funcref_from_table: @@ -22,7 +23,8 @@ define void @call_funcref_from_table(i32 %i) { ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: end_function %ref = call %funcref @llvm.wasm.table.get.funcref(ptr addrspace(1) @funcref_table, i32 %i) - call addrspace(20) void %ref() + %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + call void %refptr() ret void } diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll index 3df308c5ddf80..2621dcde6a79b 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll @@ -1,6 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll index 98e1b55613d7d..e62eb8a6f2d20 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%funcref = type target("wasm.funcref") @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef diff --git a/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll b/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll new file mode 100644 index 0000000000000..b756513ecbafa --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll @@ -0,0 +1,30 @@ +; RUN: not llc < %s --mtriple=wasm32-unknown-unknown -mattr=+reference-types 2>&1 | FileCheck %s + +; We have only implemented a lowering for llvm.wasm.funcref.to_ptr its result +; feeds directly into an indirect call. Check that we diagnose the case where we +; spill the result rather than crashing in the backend. + +%funcref = type target("wasm.funcref") + +declare ptr @llvm.wasm.funcref.to_ptr(%funcref) +declare void @sink(ptr) + +; CHECK: error: {{.*}}in function escape_via_store {{.*}}: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used +define void @escape_via_store(%funcref %ref, ptr %dst) { + %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + store ptr %p, ptr %dst + ret void +} + +; CHECK: error: {{.*}}in function escape_via_return {{.*}}: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used +define ptr @escape_via_return(%funcref %ref) { + %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + ret ptr %p +} + +; CHECK: error: {{.*}}in function escape_via_arg {{.*}}: a funcref can only be converted to a pointer to be directly called; the resulting pointer cannot otherwise be used +define void @escape_via_arg(%funcref %ref) { + %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref) + call void @sink(ptr %p) + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/ref-null.ll b/llvm/test/CodeGen/WebAssembly/ref-null.ll index af6ddfd8e0814..f2f2a0dad378b 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-null.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-null.ll @@ -1,8 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc --mtriple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%externref = type target("wasm.externref") +%funcref = type target("wasm.funcref") declare %externref @llvm.wasm.ref.null.extern() nounwind declare %funcref @llvm.wasm.ref.null.func() nounwind diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index 4fda253d39fe3..745f55a45f8d7 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -3,16 +3,24 @@ ; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types -mattr=+gc -verify-machineinstrs | FileCheck --check-prefixes CHECK,CHK64 %s define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_void_void: +; CHK32-LABEL: test_fpsig_void_void: ; CHK32: .functype test_fpsig_void_void (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test () -> () +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_void_void: ; CHK64: .functype test_fpsig_void_void (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> () -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test () -> () +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func) tail call void @use(i32 noundef %res) #3 @@ -20,16 +28,24 @@ entry: } define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_return_i32: +; CHK32-LABEL: test_fpsig_return_i32: ; CHK32: .functype test_fpsig_return_i32 (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test () -> (i32) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_return_i32: ; CHK64: .functype test_fpsig_return_i32 (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> (i32) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test () -> (i32) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 poison) tail call void @use(i32 noundef %res) #3 @@ -37,16 +53,24 @@ entry: } define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_return_i64: +; CHK32-LABEL: test_fpsig_return_i64: ; CHK32: .functype test_fpsig_return_i64 (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test () -> (i64) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_return_i64: ; CHK64: .functype test_fpsig_return_i64 (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> (i64) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test () -> (i64) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 poison) tail call void @use(i32 noundef %res) #3 @@ -54,16 +78,24 @@ entry: } define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_return_f32: +; CHK32-LABEL: test_fpsig_return_f32: ; CHK32: .functype test_fpsig_return_f32 (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test () -> (f32) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_return_f32: ; CHK64: .functype test_fpsig_return_f32 (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> (f32) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test () -> (f32) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison) tail call void @use(i32 noundef %res) #3 @@ -71,16 +103,24 @@ entry: } define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_return_f64: +; CHK32-LABEL: test_fpsig_return_f64: ; CHK32: .functype test_fpsig_return_f64 (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test () -> (f64) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_return_f64: ; CHK64: .functype test_fpsig_return_f64 (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> (f64) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test () -> (f64) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison) tail call void @use(i32 noundef %res) #3 @@ -89,16 +129,24 @@ entry: define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_param_i32: +; CHK32-LABEL: test_fpsig_param_i32: ; CHK32: .functype test_fpsig_param_i32 (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test (f64) -> () +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_param_i32: ; CHK64: .functype test_fpsig_param_i32 (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (f64) -> () -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test (f64) -> () +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, double poison) tail call void @use(i32 noundef %res) #3 @@ -107,16 +155,24 @@ entry: define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_multiple_params_and_returns: +; CHK32-LABEL: test_fpsig_multiple_params_and_returns: ; CHK32: .functype test_fpsig_multiple_params_and_returns (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test (i64, f32, i64) -> (i32, i64, f32, f64) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_multiple_params_and_returns: ; CHK64: .functype test_fpsig_multiple_params_and_returns (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (i64, f32, i64) -> (i32, i64, f32, f64) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test (i64, f32, i64) -> (i32, i64, f32, f64) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 poison, i64 poison, float poison, double poison, token poison, i64 poison, float poison, i64 poison) tail call void @use(i32 noundef %res) #3 @@ -125,17 +181,24 @@ entry: define void @test_fpsig_ptrs(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_ptrs: +; CHK32-LABEL: test_fpsig_ptrs: ; CHK32: .functype test_fpsig_ptrs (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test (i32, i32) -> (i32) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_fpsig_ptrs: ; CHK64: .functype test_fpsig_ptrs (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHK32-NEXT: ref.test (i32, i32) -> (i32) +; CHK64-NEXT: table.get __indirect_function_table ; CHK64-NEXT: ref.test (i64, i64) -> (i64) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr poison, token poison, ptr poison, ptr poison) tail call void @use(i32 noundef %res) #3 @@ -143,20 +206,30 @@ entry: } define void @test_reference_types(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_reference_types: +; CHK32-LABEL: test_reference_types: ; CHK32: .functype test_reference_types (i32) -> () +; CHK32-NEXT: # %bb.0: # %entry +; CHK32-NEXT: local.get 0 +; CHK32-NEXT: table.get __indirect_function_table +; CHK32-NEXT: ref.test (funcref, externref) -> (externref) +; CHK32-NEXT: call use +; CHK32-NEXT: # fallthrough-return +; +; CHK64-LABEL: test_reference_types: ; CHK64: .functype test_reference_types (i64) -> () -; CHECK-NEXT: # %bb.0: # %entry -; CHECK-NEXT: local.get 0 +; CHK64-NEXT: # %bb.0: # %entry +; CHK64-NEXT: local.get 0 ; CHK64-NEXT: i32.wrap_i64 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (funcref, externref) -> (externref) -; CHECK-NEXT: call use -; CHECK-NEXT: # fallthrough-return +; CHK64-NEXT: table.get __indirect_function_table +; CHK64-NEXT: ref.test (funcref, externref) -> (externref) +; CHK64-NEXT: call use +; CHK64-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr addrspace(10) poison, token poison, ptr addrspace(20) poison, ptr addrspace(10) poison) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, target("wasm.externref") poison, token poison, target("wasm.funcref") poison, target("wasm.externref") poison) tail call void @use(i32 noundef %res) #3 ret void } declare void @use(i32 noundef) local_unnamed_addr #1 +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} diff --git a/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll b/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll index 911e5bb516a2f..75b3002b3be76 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll @@ -3,8 +3,8 @@ target triple = "wasm32-unknown-unknown" -%externref = type ptr addrspace(10) -%funcref = type ptr addrspace(20) +%externref = type target("wasm.externref") +%funcref = type target("wasm.funcref") declare %externref @get_externref() declare %funcref @get_funcref() @@ -22,22 +22,22 @@ entry: store %externref %eref, ptr %alloc.externref, align 1 %eref.loaded = load %externref, ptr %alloc.externref, align 1 call void @take_externref(%externref %eref.loaded) - ; CHECK: %alloc.externref.var = alloca ptr addrspace(10), align 1, addrspace(1) - ; CHECK-NEXT: %eref = call ptr addrspace(10) @get_externref() - ; CHECK-NEXT: store ptr addrspace(10) %eref, ptr addrspace(1) %alloc.externref.var, align 1 - ; CHECK-NEXT: %eref.loaded = load ptr addrspace(10), ptr addrspace(1) %alloc.externref.var, align 1 - ; CHECK-NEXT: call void @take_externref(ptr addrspace(10) %eref.loaded) + ; CHECK: %alloc.externref.var = alloca target("wasm.externref"), align 1, addrspace(1) + ; CHECK-NEXT: %eref = call target("wasm.externref") @get_externref() + ; CHECK-NEXT: store target("wasm.externref") %eref, ptr addrspace(1) %alloc.externref.var, align 1 + ; CHECK-NEXT: %eref.loaded = load target("wasm.externref"), ptr addrspace(1) %alloc.externref.var, align 1 + ; CHECK-NEXT: call void @take_externref(target("wasm.externref") %eref.loaded) %alloc.funcref = alloca %funcref, align 1 %fref = call %funcref @get_funcref() store %funcref %fref, ptr %alloc.funcref, align 1 %fref.loaded = load %funcref, ptr %alloc.funcref, align 1 call void @take_funcref(%funcref %fref.loaded) - ; CHECK-NEXT: %alloc.funcref.var = alloca ptr addrspace(20), align 1, addrspace(1) - ; CHECK-NEXT: %fref = call ptr addrspace(20) @get_funcref() - ; CHECK-NEXT: store ptr addrspace(20) %fref, ptr addrspace(1) %alloc.funcref.var, align 1 - ; CHECK-NEXT: %fref.loaded = load ptr addrspace(20), ptr addrspace(1) %alloc.funcref.var, align 1 - ; CHECK-NEXT: call void @take_funcref(ptr addrspace(20) %fref.loaded) + ; CHECK-NEXT: %alloc.funcref.var = alloca target("wasm.funcref"), align 1, addrspace(1) + ; CHECK-NEXT: %fref = call target("wasm.funcref") @get_funcref() + ; CHECK-NEXT: store target("wasm.funcref") %fref, ptr addrspace(1) %alloc.funcref.var, align 1 + ; CHECK-NEXT: %fref.loaded = load target("wasm.funcref"), ptr addrspace(1) %alloc.funcref.var, align 1 + ; CHECK-NEXT: call void @take_funcref(target("wasm.funcref") %fref.loaded) ret void } @@ -68,22 +68,22 @@ entry: store %externref %eref, ptr %alloc.externref, align 1 %eref.loaded = load %externref, ptr %alloc.externref, align 1 call void @take_externref(%externref %eref.loaded) - ; ATTR: %alloc.externref.var = alloca ptr addrspace(10), align 1, addrspace(1) - ; ATTR-NEXT: %eref = call ptr addrspace(10) @get_externref() - ; ATTR-NEXT: store ptr addrspace(10) %eref, ptr addrspace(1) %alloc.externref.var, align 1 - ; ATTR-NEXT: %eref.loaded = load ptr addrspace(10), ptr addrspace(1) %alloc.externref.var, align 1 - ; ATTR-NEXT: call void @take_externref(ptr addrspace(10) %eref.loaded) + ; ATTR: %alloc.externref.var = alloca target("wasm.externref"), align 1, addrspace(1) + ; ATTR-NEXT: %eref = call target("wasm.externref") @get_externref() + ; ATTR-NEXT: store target("wasm.externref") %eref, ptr addrspace(1) %alloc.externref.var, align 1 + ; ATTR-NEXT: %eref.loaded = load target("wasm.externref"), ptr addrspace(1) %alloc.externref.var, align 1 + ; ATTR-NEXT: call void @take_externref(target("wasm.externref") %eref.loaded) %alloc.funcref = alloca %funcref, align 1 %fref = call %funcref @get_funcref() store %funcref %fref, ptr %alloc.funcref, align 1 %fref.loaded = load %funcref, ptr %alloc.funcref, align 1 call void @take_funcref(%funcref %fref.loaded) - ; ATTR-NEXT: %alloc.funcref.var = alloca ptr addrspace(20), align 1, addrspace(1) - ; ATTR-NEXT: %fref = call ptr addrspace(20) @get_funcref() - ; ATTR-NEXT: store ptr addrspace(20) %fref, ptr addrspace(1) %alloc.funcref.var, align 1 - ; ATTR-NEXT: %fref.loaded = load ptr addrspace(20), ptr addrspace(1) %alloc.funcref.var, align 1 - ; ATTR-NEXT: call void @take_funcref(ptr addrspace(20) %fref.loaded) + ; ATTR-NEXT: %alloc.funcref.var = alloca target("wasm.funcref"), align 1, addrspace(1) + ; ATTR-NEXT: %fref = call target("wasm.funcref") @get_funcref() + ; ATTR-NEXT: store target("wasm.funcref") %fref, ptr addrspace(1) %alloc.funcref.var, align 1 + ; ATTR-NEXT: %fref.loaded = load target("wasm.funcref"), ptr addrspace(1) %alloc.funcref.var, align 1 + ; ATTR-NEXT: call void @take_funcref(target("wasm.funcref") %fref.loaded) ret void } diff --git a/llvm/test/CodeGen/WebAssembly/select-reftype.ll b/llvm/test/CodeGen/WebAssembly/select-reftype.ll index baca3ca6258a3..985796048fb47 100644 --- a/llvm/test/CodeGen/WebAssembly/select-reftype.ll +++ b/llvm/test/CodeGen/WebAssembly/select-reftype.ll @@ -5,46 +5,46 @@ target triple = "wasm32-unknown-unknown" -define ptr addrspace(10) @select_externref_eq(i32 %a, ptr addrspace(10) %b, ptr addrspace(10) %c) { +define target("wasm.externref") @select_externref_eq(i32 %a, target("wasm.externref") %b, target("wasm.externref") %c) { ; CHECK-LABEL: select_externref_eq: ; CHECK: .functype select_externref_eq (i32, externref, externref) -> (externref) ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: externref.select $push0=, $2, $1, $0 ; CHECK-NEXT: return $pop0 %cmp = icmp eq i32 %a, 0 - %cond = select i1 %cmp, ptr addrspace(10) %b, ptr addrspace(10) %c - ret ptr addrspace(10) %cond + %cond = select i1 %cmp, target("wasm.externref") %b, target("wasm.externref") %c + ret target("wasm.externref") %cond } -define ptr addrspace(10) @select_externref_ne(i32 %a, ptr addrspace(10) %b, ptr addrspace(10) %c) { +define target("wasm.externref") @select_externref_ne(i32 %a, target("wasm.externref") %b, target("wasm.externref") %c) { ; CHECK-LABEL: select_externref_ne: ; CHECK: .functype select_externref_ne (i32, externref, externref) -> (externref) ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: externref.select $push0=, $1, $2, $0 ; CHECK-NEXT: return $pop0 %cmp = icmp ne i32 %a, 0 - %cond = select i1 %cmp, ptr addrspace(10) %b, ptr addrspace(10) %c - ret ptr addrspace(10) %cond + %cond = select i1 %cmp, target("wasm.externref") %b, target("wasm.externref") %c + ret target("wasm.externref") %cond } -define ptr addrspace(20) @select_funcref_eq(i32 %a, ptr addrspace(20) %b, ptr addrspace(20) %c) { +define target("wasm.funcref") @select_funcref_eq(i32 %a, target("wasm.funcref") %b, target("wasm.funcref") %c) { ; CHECK-LABEL: select_funcref_eq: ; CHECK: .functype select_funcref_eq (i32, funcref, funcref) -> (funcref) ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: funcref.select $push0=, $2, $1, $0 ; CHECK-NEXT: return $pop0 %cmp = icmp eq i32 %a, 0 - %cond = select i1 %cmp, ptr addrspace(20) %b, ptr addrspace(20) %c - ret ptr addrspace(20) %cond + %cond = select i1 %cmp, target("wasm.funcref") %b, target("wasm.funcref") %c + ret target("wasm.funcref") %cond } -define ptr addrspace(20) @select_funcref_ne(i32 %a, ptr addrspace(20) %b, ptr addrspace(20) %c) { +define target("wasm.funcref") @select_funcref_ne(i32 %a, target("wasm.funcref") %b, target("wasm.funcref") %c) { ; CHECK-LABEL: select_funcref_ne: ; CHECK: .functype select_funcref_ne (i32, funcref, funcref) -> (funcref) ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: funcref.select $push0=, $1, $2, $0 ; CHECK-NEXT: return $pop0 %cmp = icmp ne i32 %a, 0 - %cond = select i1 %cmp, ptr addrspace(20) %b, ptr addrspace(20) %c - ret ptr addrspace(20) %cond + %cond = select i1 %cmp, target("wasm.funcref") %b, target("wasm.funcref") %c + ret target("wasm.funcref") %cond } diff --git a/llvm/test/CodeGen/WebAssembly/table-copy.ll b/llvm/test/CodeGen/WebAssembly/table-copy.ll index 5c0647ada4ab0..d1387428c4368 100644 --- a/llvm/test/CodeGen/WebAssembly/table-copy.ll +++ b/llvm/test/CodeGen/WebAssembly/table-copy.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table1 = local_unnamed_addr addrspace(1) global [0 x %externref] undef @externref_table2 = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/table-fill.ll b/llvm/test/CodeGen/WebAssembly/table-fill.ll index 0b78124f038b1..7ed39361a1afd 100644 --- a/llvm/test/CodeGen/WebAssembly/table-fill.ll +++ b/llvm/test/CodeGen/WebAssembly/table-fill.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/table-grow.ll b/llvm/test/CodeGen/WebAssembly/table-grow.ll index 614c3400a782b..2b5801ea2fa17 100644 --- a/llvm/test/CodeGen/WebAssembly/table-grow.ll +++ b/llvm/test/CodeGen/WebAssembly/table-grow.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/table-size.ll b/llvm/test/CodeGen/WebAssembly/table-size.ll index 42cd2e8a909d7..2b8d0185bb049 100644 --- a/llvm/test/CodeGen/WebAssembly/table-size.ll +++ b/llvm/test/CodeGen/WebAssembly/table-size.ll @@ -1,6 +1,6 @@ ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral +%externref = type target("wasm.externref") @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef diff --git a/llvm/test/CodeGen/WebAssembly/table-types.ll b/llvm/test/CodeGen/WebAssembly/table-types.ll index cb5e54e2af230..da04ba35dd58e 100644 --- a/llvm/test/CodeGen/WebAssembly/table-types.ll +++ b/llvm/test/CodeGen/WebAssembly/table-types.ll @@ -1,7 +1,7 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral -%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral +%externref = type target("wasm.externref") +%funcref = type target("wasm.funcref") ; CHECK: .tabletype eref_table, externref ; CHECK-NEXT: .globl eref_table diff --git a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn index f8bd3fa48a92d..67f9a904bb1e7 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn @@ -106,7 +106,6 @@ static_library("LLVMWebAssemblyCodeGen") { "WebAssemblyLateEHPrepare.cpp", "WebAssemblyLowerBrUnless.cpp", "WebAssemblyLowerEmscriptenEHSjLj.cpp", - "WebAssemblyLowerRefTypesIntPtrConv.cpp", "WebAssemblyMCInstLower.cpp", "WebAssemblyMCLowerPrePass.cpp", "WebAssemblyMachineFunctionInfo.cpp", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
