https://github.com/hoodmane updated https://github.com/llvm/llvm-project/pull/150201
>From 6c3c8a1d43b3b06c6b38e8e3d2e00ef2dc5fbc4e Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Mon, 7 Jul 2025 22:50:08 +0200 Subject: [PATCH 1/9] [WebAssembly,clang] Add __builtin_wasm_test_function_pointer_signature clang intrinsic Tests if the runtime type of the function pointer matches the static type. If this returns false, calling the function pointer will trap. Uses `@llvm.wasm.ref.test.func` added in #147486. --- .../clang/Basic/BuiltinsWebAssembly.def | 6 ++ .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/include/clang/Sema/SemaWasm.h | 1 + .../CodeGen/TargetBuiltins/WebAssembly.cpp | 57 +++++++++++++++++++ clang/lib/Sema/SemaWasm.cpp | 49 ++++++++++++++++ clang/test/CodeGen/builtins-wasm.c | 24 ++++++++ clang/test/Sema/builtins-wasm.c | 24 ++++++++ 7 files changed, 167 insertions(+) diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index e2afcc08064b2..1e03a40b1a22b 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -199,6 +199,12 @@ TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "ii", "nct", "reference-types" // return type. TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types") +// Check if the static type of a function pointer matches its static type. Used +// to avoid "function signature mismatch" traps. Takes a function pointer, uses +// table.get to look up the pointer in __indirect_function_table and then +// ref.test to test the type. +TARGET_BUILTIN(__builtin_wasm_test_function_pointer_signature, "i.", "nct", "reference-types") + // Table builtins TARGET_BUILTIN(__builtin_wasm_table_set, "viii", "t", "reference-types") TARGET_BUILTIN(__builtin_wasm_table_get, "iii", "t", "reference-types") diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b2ea65ae111be..745ac8cb5dc2e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7575,6 +7575,8 @@ def err_typecheck_illegal_increment_decrement : Error< "cannot %select{decrement|increment}1 value of type %0">; def err_typecheck_expect_int : Error< "used type %0 where integer is required">; +def err_typecheck_expect_function_pointer + : Error<"used type %0 where function pointer is required">; def err_typecheck_expect_hlsl_resource : Error< "used type %0 where __hlsl_resource_t is required">; def err_typecheck_arithmetic_incomplete_or_sizeless_type : Error< @@ -13202,6 +13204,10 @@ def err_wasm_builtin_arg_must_match_table_element_type : Error < "%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">; def err_wasm_builtin_arg_must_be_integer_type : Error < "%ordinal0 argument must be an integer">; +def err_wasm_builtin_test_fp_sig_cannot_include_reference_type + : Error<"not supported for " + "function pointers with a reference type %select{return " + "value|parameter}0">; // OpenACC diagnostics. def warn_acc_routine_unimplemented diff --git a/clang/include/clang/Sema/SemaWasm.h b/clang/include/clang/Sema/SemaWasm.h index 2123e073516cb..8c0639fd7e76f 100644 --- a/clang/include/clang/Sema/SemaWasm.h +++ b/clang/include/clang/Sema/SemaWasm.h @@ -37,6 +37,7 @@ class SemaWasm : public SemaBase { bool BuiltinWasmTableGrow(CallExpr *TheCall); bool BuiltinWasmTableFill(CallExpr *TheCall); bool BuiltinWasmTableCopy(CallExpr *TheCall); + bool BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall); WebAssemblyImportNameAttr * mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL); diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index b7fd70e855d40..5644792028013 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -12,7 +12,10 @@ #include "CGBuiltin.h" #include "clang/Basic/TargetBuiltins.h" +#include "llvm/ADT/APInt.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/IntrinsicsWebAssembly.h" +#include "llvm/Support/ErrorHandling.h" using namespace clang; using namespace CodeGen; @@ -218,6 +221,60 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func); return Builder.CreateCall(Callee); } + case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: { + Value *FuncRef = EmitScalarExpr(E->getArg(0)); + + // Get the function type from the argument's static type + QualType ArgType = E->getArg(0)->getType(); + const PointerType *PtrTy = ArgType->getAs<PointerType>(); + assert(PtrTy && "Sema should have ensured this is a function pointer"); + + const FunctionType *FuncTy = PtrTy->getPointeeType()->getAs<FunctionType>(); + assert(FuncTy && "Sema should have ensured this is a function pointer"); + + // In the llvm IR, we won't have access anymore to the type of the function + // pointer so we need to insert this type information somehow. We gave the + // @llvm.wasm.ref.test.func varargs and here we add an extra 0 argument of + // the type corresponding to the type of each argument of the function + // signature. When we lower from the IR we'll use the types of these + // arguments to determine the signature we want to test for. + + // Make a type index constant with 0. This gets replaced by the actual type + // in WebAssemblyMCInstLower.cpp. + llvm::FunctionType *LLVMFuncTy = + cast<llvm::FunctionType>(ConvertType(QualType(FuncTy, 0))); + + uint NParams = LLVMFuncTy->getNumParams(); + std::vector<Value *> Args; + Args.reserve(NParams + 2); + // The only real argument is the FuncRef + Args.push_back(FuncRef); + + // Add the type information + auto addType = [&Args](llvm::Type *T) { + if (T->isVoidTy()) { + // Do nothing + } else if (T->isFloatingPointTy()) { + Args.push_back(ConstantFP::get(T, 0)); + } else if (T->isIntegerTy()) { + Args.push_back(ConstantInt::get(T, 0)); + } else { + // TODO: Handle reference types here. For now, we reject them in Sema. + llvm_unreachable("Unhandled type"); + } + }; + + addType(LLVMFuncTy->getReturnType()); + // The token type indicates the boundary between return types and param + // types. + Args.push_back( + PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext()))); + for (uint i = 0; i < NParams; i++) { + addType(LLVMFuncTy->getParamType(i)); + } + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_test_func); + return Builder.CreateCall(Callee, Args); + } case WebAssembly::BI__builtin_wasm_swizzle_i8x16: { Value *Src = EmitScalarExpr(E->getArg(0)); Value *Indices = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 6faea24a46b09..8998492a71619 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -227,6 +227,53 @@ bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { return false; } +bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + + Expr *FuncPtrArg = TheCall->getArg(0); + QualType ArgType = FuncPtrArg->getType(); + + // Check that the argument is a function pointer + const PointerType *PtrTy = ArgType->getAs<PointerType>(); + if (!PtrTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + const FunctionProtoType *FuncTy = + PtrTy->getPointeeType()->getAs<FunctionProtoType>(); + if (!FuncTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + // Check that the function pointer doesn't use reference types + if (FuncTy->getReturnType().isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag::err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange(); + } + auto NParams = FuncTy->getNumParams(); + for (unsigned I = 0; I < NParams; I++) { + if (FuncTy->getParamType(I).isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag:: + err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 1 << FuncPtrArg->getSourceRange(); + } + } + + // Set return type to int (the result of the test) + TheCall->setType(getASTContext().IntTy); + + return false; +} + bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { @@ -249,6 +296,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, return BuiltinWasmTableFill(TheCall); case WebAssembly::BI__builtin_wasm_table_copy: return BuiltinWasmTableCopy(TheCall); + case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: + return BuiltinWasmTestFunctionPointerSignature(TheCall); } return false; diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index d8aff82b0c140..d52f5f88ed8cb 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -751,3 +751,27 @@ void *tp (void) { return __builtin_thread_pointer (); // WEBASSEMBLY: call {{.*}} @llvm.thread.pointer.p0() } + + +typedef void (*funcref_t)(); +typedef int (*funcref_int_t)(int); +typedef float (*F1)(float, double, int); +typedef int (*F2)(float, double, int); +typedef int (*F3)(int, int, int); +typedef void (*F4)(int, int, int); +typedef void (*F5)(void); + +void use(int); + +void test_function_pointer_signature_void(F1 func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, token poison, float 0.000000e+00, double 0.000000e+00, i32 0) + use(__builtin_wasm_test_function_pointer_signature(func)); + // WEBASSEMBLY: %1 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, token poison, float 0.000000e+00, double 0.000000e+00, i32 0) + use(__builtin_wasm_test_function_pointer_signature((F2)func)); + // WEBASSEMBLY: %2 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, token poison, i32 0, i32 0, i32 0) + use(__builtin_wasm_test_function_pointer_signature((F3)func)); + // WEBASSEMBLY: %3 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) + use(__builtin_wasm_test_function_pointer_signature((F4)func)); + // WEBASSEMBLY: %4 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + use(__builtin_wasm_test_function_pointer_signature((F5)func)); +} diff --git a/clang/test/Sema/builtins-wasm.c b/clang/test/Sema/builtins-wasm.c index 31e5291d3ae5e..a3486b1aedb13 100644 --- a/clang/test/Sema/builtins-wasm.c +++ b/clang/test/Sema/builtins-wasm.c @@ -54,3 +54,27 @@ void test_table_copy(int dst_idx, int src_idx, int nelem) { __builtin_wasm_table_copy(table, table, dst_idx, src_idx, table); // expected-error {{5th argument must be an integer}} __builtin_wasm_table_copy(table, table, dst_idx, src_idx, nelem); } + +typedef void (*F1)(void); +typedef int (*F2)(int); +typedef int (*F3)(__externref_t); +typedef __externref_t (*F4)(int); + +void test_function_pointer_signature() { + // Test argument count validation + (void)__builtin_wasm_test_function_pointer_signature(); // expected-error {{too few arguments to function call, expected 1, have 0}} + (void)__builtin_wasm_test_function_pointer_signature((F1)0, (F2)0); // expected-error {{too many arguments to function call, expected 1, have 2}} + + // // Test argument type validation - should require function pointer + (void)__builtin_wasm_test_function_pointer_signature((void*)0); // expected-error {{used type 'void *' where function pointer is required}} + (void)__builtin_wasm_test_function_pointer_signature((int)0); // expected-error {{used type 'int' where function pointer is required}} + (void)__builtin_wasm_test_function_pointer_signature((F3)0); // expected-error {{not supported for function pointers with a reference type parameter}} + (void)__builtin_wasm_test_function_pointer_signature((F4)0); // expected-error {{not supported for function pointers with a reference type return value}} + + // // Test valid usage + int res = __builtin_wasm_test_function_pointer_signature((F1)0); + res = __builtin_wasm_test_function_pointer_signature((F2)0); + + // Test return type + _Static_assert(EXPR_HAS_TYPE(__builtin_wasm_test_function_pointer_signature((F1)0), int), ""); +} >From 917850d9d185729b339d12fc62d6dfa45a3aa9b6 Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Wed, 23 Jul 2025 12:13:11 +0200 Subject: [PATCH 2/9] Fix comment --- clang/include/clang/Basic/BuiltinsWebAssembly.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index 1e03a40b1a22b..f5aadfd1cd46a 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -199,7 +199,7 @@ TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "ii", "nct", "reference-types" // return type. TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types") -// Check if the static type of a function pointer matches its static type. Used +// Check if the runtime type of a function pointer matches its static type. Used // to avoid "function signature mismatch" traps. Takes a function pointer, uses // table.get to look up the pointer in __indirect_function_table and then // ref.test to test the type. >From 27ad7828f061e7125848914e672538d220bcca02 Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Wed, 23 Jul 2025 12:13:39 +0200 Subject: [PATCH 3/9] clang-format --- clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index 5644792028013..d3c0d10581d45 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -267,8 +267,7 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, addType(LLVMFuncTy->getReturnType()); // The token type indicates the boundary between return types and param // types. - Args.push_back( - PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext()))); + Args.push_back(PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext()))); for (uint i = 0; i < NParams; i++) { addType(LLVMFuncTy->getParamType(i)); } >From d137fb7c6e28f43fef9c86719c8e45c838ffe96b Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Wed, 23 Jul 2025 12:17:44 +0200 Subject: [PATCH 4/9] Update comments --- .../CodeGen/TargetBuiltins/WebAssembly.cpp | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index d3c0d10581d45..f170ad44cea32 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -232,15 +232,17 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, const FunctionType *FuncTy = PtrTy->getPointeeType()->getAs<FunctionType>(); assert(FuncTy && "Sema should have ensured this is a function pointer"); - // In the llvm IR, we won't have access anymore to the type of the function - // pointer so we need to insert this type information somehow. We gave the - // @llvm.wasm.ref.test.func varargs and here we add an extra 0 argument of - // the type corresponding to the type of each argument of the function - // signature. When we lower from the IR we'll use the types of these - // arguments to determine the signature we want to test for. + // In the llvm IR, we won't have access any more to the type of the function + // pointer so we need to insert this type information somehow. The + // @llvm.wasm.ref.test.func takes varargs arguments whose values are unused + // to indicate the type of the function to test for. See the test here: + // llvm/test/CodeGen/WebAssembly/ref-test-func.ll + // + // The format is: first we include the return types (since this is a C + // function pointer, there will be 0 or one of these) then a token type to + // indicate the boundary between return types and param types, then the + // param types. - // Make a type index constant with 0. This gets replaced by the actual type - // in WebAssemblyMCInstLower.cpp. llvm::FunctionType *LLVMFuncTy = cast<llvm::FunctionType>(ConvertType(QualType(FuncTy, 0))); @@ -259,7 +261,7 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, } else if (T->isIntegerTy()) { Args.push_back(ConstantInt::get(T, 0)); } else { - // TODO: Handle reference types here. For now, we reject them in Sema. + // TODO: Handle reference types. For now, we reject them in Sema. llvm_unreachable("Unhandled type"); } }; >From 985dbdfe88f6cac3e4ebaba5fa25a372b3e79ae6 Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Wed, 23 Jul 2025 12:38:51 +0200 Subject: [PATCH 5/9] Handle pointer types, improve test --- .../CodeGen/TargetBuiltins/WebAssembly.cpp | 4 ++- clang/test/CodeGen/builtins-wasm.c | 33 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index f170ad44cea32..8901dad205fb4 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -253,13 +253,15 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Args.push_back(FuncRef); // Add the type information - auto addType = [&Args](llvm::Type *T) { + auto addType = [this, &Args](llvm::Type *T) { if (T->isVoidTy()) { // Do nothing } else if (T->isFloatingPointTy()) { Args.push_back(ConstantFP::get(T, 0)); } else if (T->isIntegerTy()) { Args.push_back(ConstantInt::get(T, 0)); + } else if (T->isPointerTy()) { + Args.push_back(ConstantPointerNull::get(llvm::PointerType::get(getLLVMContext(), T->getPointerAddressSpace()))); } else { // TODO: Handle reference types. For now, we reject them in Sema. llvm_unreachable("Unhandled type"); diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index d52f5f88ed8cb..59cecddaafca6 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -752,26 +752,23 @@ void *tp (void) { // WEBASSEMBLY: call {{.*}} @llvm.thread.pointer.p0() } - -typedef void (*funcref_t)(); -typedef int (*funcref_int_t)(int); -typedef float (*F1)(float, double, int); -typedef int (*F2)(float, double, int); -typedef int (*F3)(int, int, int); -typedef void (*F4)(int, int, int); -typedef void (*F5)(void); +typedef void (*Fvoid)(void); +typedef float (*Ffloats)(float, double, int); +typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); void use(int); -void test_function_pointer_signature_void(F1 func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, token poison, float 0.000000e+00, double 0.000000e+00, i32 0) +void test_function_pointer_signature_void(Fvoid func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +void test_function_pointer_signature_floats(Ffloats func) { + // WEBASSEMBLY: tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, token poison, float 0.000000e+00, double 0.000000e+00, i32 0) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +void test_function_pointer_signature_pointers(Fpointers func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr null, ptr null, ptr null, ptr null, ptr null, ptr null) use(__builtin_wasm_test_function_pointer_signature(func)); - // WEBASSEMBLY: %1 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, token poison, float 0.000000e+00, double 0.000000e+00, i32 0) - use(__builtin_wasm_test_function_pointer_signature((F2)func)); - // WEBASSEMBLY: %2 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, token poison, i32 0, i32 0, i32 0) - use(__builtin_wasm_test_function_pointer_signature((F3)func)); - // WEBASSEMBLY: %3 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) - use(__builtin_wasm_test_function_pointer_signature((F4)func)); - // WEBASSEMBLY: %4 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) - use(__builtin_wasm_test_function_pointer_signature((F5)func)); } >From 1e6e085399d7b81ade07fddc4fccd8afc75b24b1 Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Wed, 23 Jul 2025 12:50:13 +0200 Subject: [PATCH 6/9] clang-format --- clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index 8901dad205fb4..60c9b9e451d6d 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -261,7 +261,8 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, } else if (T->isIntegerTy()) { Args.push_back(ConstantInt::get(T, 0)); } else if (T->isPointerTy()) { - Args.push_back(ConstantPointerNull::get(llvm::PointerType::get(getLLVMContext(), T->getPointerAddressSpace()))); + Args.push_back(ConstantPointerNull::get(llvm::PointerType::get( + getLLVMContext(), T->getPointerAddressSpace()))); } else { // TODO: Handle reference types. For now, we reject them in Sema. llvm_unreachable("Unhandled type"); >From dade3c6221dc633177cc6f2a66f037bb29de254e Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Fri, 25 Jul 2025 11:07:55 +0200 Subject: [PATCH 7/9] Reserve NParams + 3 --- clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index 60c9b9e451d6d..33bdfa0a00b8e 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -248,7 +248,7 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, uint NParams = LLVMFuncTy->getNumParams(); std::vector<Value *> Args; - Args.reserve(NParams + 2); + Args.reserve(NParams + 3); // The only real argument is the FuncRef Args.push_back(FuncRef); >From e19af50e229d921d60884423a124acd212ecb5a3 Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Fri, 25 Jul 2025 11:23:48 +0200 Subject: [PATCH 8/9] Add gc target feature --- .../include/clang/Basic/BuiltinsWebAssembly.def | 2 +- clang/lib/Basic/Targets/WebAssembly.cpp | 16 ++++++++++++++++ clang/lib/Basic/Targets/WebAssembly.h | 1 + clang/test/CodeGen/builtins-wasm.c | 6 +++--- llvm/lib/Target/WebAssembly/WebAssembly.td | 15 ++++++++------- .../Target/WebAssembly/WebAssemblyInstrInfo.td | 3 +++ .../Target/WebAssembly/WebAssemblyInstrRef.td | 11 ++++------- .../Target/WebAssembly/WebAssemblySubtarget.cpp | 5 +++++ .../Target/WebAssembly/WebAssemblySubtarget.h | 2 ++ llvm/test/CodeGen/WebAssembly/ref-test-func.ll | 4 ++-- 10 files changed, 45 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index f5aadfd1cd46a..d31b72696ff4e 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -203,7 +203,7 @@ TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types") // to avoid "function signature mismatch" traps. Takes a function pointer, uses // table.get to look up the pointer in __indirect_function_table and then // ref.test to test the type. -TARGET_BUILTIN(__builtin_wasm_test_function_pointer_signature, "i.", "nct", "reference-types") +TARGET_BUILTIN(__builtin_wasm_test_function_pointer_signature, "i.", "nct", "gc") // Table builtins TARGET_BUILTIN(__builtin_wasm_table_set, "viii", "t", "reference-types") diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp index af25d25a3af3b..e362350ea678f 100644 --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -64,6 +64,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const { .Case("mutable-globals", HasMutableGlobals) .Case("nontrapping-fptoint", HasNontrappingFPToInt) .Case("reference-types", HasReferenceTypes) + .Case("gc", HasGC) .Case("relaxed-simd", SIMDLevel >= RelaxedSIMD) .Case("sign-ext", HasSignExt) .Case("simd128", SIMDLevel >= SIMD128) @@ -106,6 +107,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__wasm_nontrapping_fptoint__"); if (HasReferenceTypes) Builder.defineMacro("__wasm_reference_types__"); + if (HasGC) + Builder.defineMacro("__wasm_gc__"); if (SIMDLevel >= RelaxedSIMD) Builder.defineMacro("__wasm_relaxed_simd__"); if (HasSignExt) @@ -307,6 +310,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures( HasReferenceTypes = false; continue; } + if (Feature == "+gc") { + HasGC = true; + continue; + } + if (Feature == "-gc") { + HasGC = false; + continue; + } if (Feature == "+relaxed-simd") { SIMDLevel = std::max(SIMDLevel, RelaxedSIMD); continue; @@ -353,6 +364,11 @@ bool WebAssemblyTargetInfo::handleTargetFeatures( return false; } + // gc implies reference-types + if (HasGC) { + HasReferenceTypes = true; + } + // bulk-memory-opt is a subset of bulk-memory. if (HasBulkMemory) { HasBulkMemoryOpt = true; diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index 57b366cb9c750..c47c8cc1270a3 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -69,6 +69,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { bool HasMutableGlobals = false; bool HasNontrappingFPToInt = false; bool HasReferenceTypes = false; + bool HasGC = false; bool HasSignExt = false; bool HasTailCall = false; bool HasWideArithmetic = false; diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index 59cecddaafca6..f201dfe704e7e 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32 -// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64 -// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -flax-vector-conversions=none -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD +// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32 +// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64 +// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -flax-vector-conversions=none -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD // SIMD convenience types typedef signed char i8x16 __attribute((vector_size(16))); diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td index 13603f8181198..a6062097ba23c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.td +++ b/llvm/lib/Target/WebAssembly/WebAssembly.td @@ -71,6 +71,7 @@ def FeatureReferenceTypes : SubtargetFeature<"reference-types", "HasReferenceTypes", "true", "Enable reference types">; +def FeatureGC : SubtargetFeature<"gc", "HasGC", "true", "Enable wasm gc">; def FeatureRelaxedSIMD : SubtargetFeature<"relaxed-simd", "SIMDLevel", "RelaxedSIMD", "Enable relaxed-simd instructions">; @@ -136,13 +137,13 @@ def : ProcessorModel<"lime1", NoSchedModel, // Latest and greatest experimental version of WebAssembly. Bugs included! def : ProcessorModel<"bleeding-edge", NoSchedModel, - [FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt, - FeatureCallIndirectOverlong, FeatureExceptionHandling, - FeatureExtendedConst, FeatureFP16, FeatureMultiMemory, - FeatureMultivalue, FeatureMutableGlobals, - FeatureNontrappingFPToInt, FeatureRelaxedSIMD, - FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt, - FeatureTailCall]>; + [FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt, + FeatureCallIndirectOverlong, FeatureExceptionHandling, + FeatureExtendedConst, FeatureFP16, FeatureMultiMemory, + FeatureMultivalue, FeatureMutableGlobals, + FeatureNontrappingFPToInt, FeatureRelaxedSIMD, + FeatureReferenceTypes, FeatureGC, FeatureSIMD128, + FeatureSignExt, FeatureTailCall]>; //===----------------------------------------------------------------------===// // Target Declaration diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index b5e723e2a48d3..2b632fd6eef89 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -76,6 +76,9 @@ def HasReferenceTypes : Predicate<"Subtarget->hasReferenceTypes()">, AssemblerPredicate<(all_of FeatureReferenceTypes), "reference-types">; +def HasGC : Predicate<"Subtarget->hasGC()">, + AssemblerPredicate<(all_of FeatureGC), "gc">; + def HasRelaxedSIMD : Predicate<"Subtarget->hasRelaxedSIMD()">, AssemblerPredicate<(all_of FeatureRelaxedSIMD), "relaxed-simd">; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td index 40b87a084c687..fc82e5b4a61da 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td @@ -36,13 +36,10 @@ multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> { Requires<[HasReferenceTypes]>; } -defm REF_TEST_FUNCREF : - I<(outs I32: $res), - (ins TypeIndex:$type, FUNCREF: $ref), - (outs), - (ins TypeIndex:$type), - [], - "ref.test\t$type, $ref", "ref.test $type", 0xfb14>; +defm REF_TEST_FUNCREF : I<(outs I32:$res), (ins TypeIndex:$type, FUNCREF:$ref), + (outs), (ins TypeIndex:$type), [], + "ref.test\t$type, $ref", "ref.test $type", 0xfb14>, + Requires<[HasGC]>; defm "" : REF_I<FUNCREF, funcref, "func">; defm "" : REF_I<EXTERNREF, externref, "extern">; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp index 40ea48ab3ac48..a3ce40f0297ec 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp @@ -43,6 +43,11 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU, Bits.set(WebAssembly::FeatureBulkMemoryOpt); } + // gc implies reference-types + if (HasGC) { + HasReferenceTypes = true; + } + // reference-types implies call-indirect-overlong if (HasReferenceTypes) { HasCallIndirectOverlong = true; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h index 591ce25611e3e..f814274f057cd 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h @@ -51,6 +51,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { bool HasMutableGlobals = false; bool HasNontrappingFPToInt = false; bool HasReferenceTypes = false; + bool HasGC = false; bool HasSignExt = false; bool HasTailCall = false; bool HasWideArithmetic = false; @@ -107,6 +108,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { bool hasMutableGlobals() const { return HasMutableGlobals; } bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; } bool hasReferenceTypes() const { return HasReferenceTypes; } + bool hasGC() const { return HasGC; } bool hasRelaxedSIMD() const { return SIMDLevel >= RelaxedSIMD; } bool hasSignExt() const { return HasSignExt; } bool hasSIMD128() const { return SIMDLevel >= SIMD128; } diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index e3760a07c6445..661b71d7870d1 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 -; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=+reference-types | FileCheck --check-prefixes CHECK,CHK32 %s -; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types | FileCheck --check-prefixes CHECK,CHK64 %s +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=+reference-types -mattr=+gc | FileCheck --check-prefixes CHECK,CHK32 %s +; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types -mattr=+gc | 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: >From a98594662ce4638f4ecd11edf1444fd2892b52fd Mon Sep 17 00:00:00 2001 From: Hood Chatham <roberthoodchat...@gmail.com> Date: Fri, 25 Jul 2025 16:37:39 +0200 Subject: [PATCH 9/9] Turn on gc types in ref.test MC test --- llvm/test/MC/WebAssembly/reference-types.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/test/MC/WebAssembly/reference-types.s b/llvm/test/MC/WebAssembly/reference-types.s index 08aafb23969eb..7a838fc519493 100644 --- a/llvm/test/MC/WebAssembly/reference-types.s +++ b/llvm/test/MC/WebAssembly/reference-types.s @@ -1,5 +1,5 @@ -# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck %s -# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+reference-types < %s | FileCheck %s +# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+reference-types -mattr=+gc < %s | FileCheck %s +# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+reference-types -mattr=+gc < %s | FileCheck %s # CHECK-LABEL:ref_is_null: # CHECK: ref.is_null # encoding: [0xd1] _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits