https://github.com/hoodmane updated https://github.com/llvm/llvm-project/pull/150921
>From 6caa2f7b749a9c655864afbbff07e9e78dd9b1b0 Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Mon, 28 Jul 2025 12:37:24 +0200 Subject: [PATCH 1/5] [clang,WebAssembly] Support reference types in test_function_pointer_signature --- .../CodeGen/TargetBuiltins/WebAssembly.cpp | 23 ++++---------- clang/lib/Sema/SemaWasm.cpp | 18 ----------- clang/test/CodeGen/builtins-wasm.c | 16 ++++++++-- clang/test/Sema/builtins-wasm.c | 4 --- .../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 9 ++++++ .../test/CodeGen/WebAssembly/ref-test-func.ll | 30 ++++++++++++++----- 6 files changed, 51 insertions(+), 49 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index 33a8d8f8d1754..ca709e27a44f8 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -253,28 +253,15 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Args.push_back(FuncRef); // Add the type information - 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"); - } - }; - - addType(LLVMFuncTy->getReturnType()); + llvm::Type *RetType = LLVMFuncTy->getReturnType(); + if (!RetType->isVoidTy()) { + Args.push_back(PoisonValue::get(RetType)); + } // The token type indicates the boundary between return types and param // types. Args.push_back(PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext()))); for (unsigned i = 0; i < NParams; i++) { - addType(LLVMFuncTy->getParamType(i)); + Args.push_back(PoisonValue::get(LLVMFuncTy->getParamType(i))); } Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_test_func); return Builder.CreateCall(Callee, Args); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 8998492a71619..c42b8677470e1 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -250,24 +250,6 @@ bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { << 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); diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index f201dfe704e7e..aeb03fd06268d 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -755,6 +755,8 @@ void *tp (void) { typedef void (*Fvoid)(void); typedef float (*Ffloats)(float, double, int); typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); +typedef __externref_t (*FExternRef)(__externref_t, __externref_t); +typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats); void use(int); @@ -764,11 +766,21 @@ void test_function_pointer_signature_void(Fvoid 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) + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison, token poison, float poison, double poison, i32 poison) 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) + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +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) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +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) use(__builtin_wasm_test_function_pointer_signature(func)); } diff --git a/clang/test/Sema/builtins-wasm.c b/clang/test/Sema/builtins-wasm.c index a3486b1aedb13..a93103e4804c8 100644 --- a/clang/test/Sema/builtins-wasm.c +++ b/clang/test/Sema/builtins-wasm.c @@ -57,8 +57,6 @@ void test_table_copy(int dst_idx, int src_idx, int 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 @@ -68,8 +66,6 @@ void test_function_pointer_signature() { // // 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); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index b03b35028c69c..fc852d0a12e14 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -136,6 +136,15 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, if (VT == MVT::f64) { return wasm::ValType::F64; } + if (VT == MVT::externref) { + return wasm::ValType::EXTERNREF; + } + if (VT == MVT::funcref) { + return wasm::ValType::FUNCREF; + } + if (VT == MVT::exnref) { + return wasm::ValType::EXNREF; + } LLVM_DEBUG(errs() << "Unhandled type for llvm.wasm.ref.test.func: " << VT << "\n"); llvm_unreachable("Unhandled type for llvm.wasm.ref.test.func"); diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index ea2453faaed90..4fda253d39fe3 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -31,7 +31,7 @@ define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 poison) tail call void @use(i32 noundef %res) #3 ret void } @@ -48,7 +48,7 @@ define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 poison) tail call void @use(i32 noundef %res) #3 ret void } @@ -65,7 +65,7 @@ define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison) tail call void @use(i32 noundef %res) #3 ret void } @@ -82,7 +82,7 @@ define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double 0.) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison) tail call void @use(i32 noundef %res) #3 ret void } @@ -100,7 +100,7 @@ define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, double 0.) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, double poison) tail call void @use(i32 noundef %res) #3 ret void } @@ -118,7 +118,7 @@ define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) local_unn ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i64 0, float 0., double 0., token poison, i64 0, float 0., i64 0) + %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 ret void } @@ -137,10 +137,26 @@ define void @test_fpsig_ptrs(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr null, token poison, ptr null, ptr null) + %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 ret void } +define void @test_reference_types(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_reference_types: +; CHK32: .functype test_reference_types (i32) -> () +; CHK64: .functype test_reference_types (i64) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-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 +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) + tail call void @use(i32 noundef %res) #3 + ret void +} declare void @use(i32 noundef) local_unnamed_addr #1 >From e7b8d8ebeeec8357be93b1e297b57ebf13d9c419 Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Mon, 28 Jul 2025 13:04:59 +0200 Subject: [PATCH 2/5] Fix handling of varargs function --- clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp | 6 +++++- clang/test/CodeGen/builtins-wasm.c | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index ca709e27a44f8..1a1889a4139d3 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -246,9 +246,10 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, llvm::FunctionType *LLVMFuncTy = cast<llvm::FunctionType>(ConvertType(QualType(FuncTy, 0))); + bool VarArg = LLVMFuncTy->isVarArg(); unsigned NParams = LLVMFuncTy->getNumParams(); std::vector<Value *> Args; - Args.reserve(NParams + 3); + Args.reserve(NParams + 3 + VarArg); // The only real argument is the FuncRef Args.push_back(FuncRef); @@ -263,6 +264,9 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, for (unsigned i = 0; i < NParams; i++) { Args.push_back(PoisonValue::get(LLVMFuncTy->getParamType(i))); } + if (VarArg) { + Args.push_back(PoisonValue::get(Builder.getPtrTy())); + } Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_test_func); return Builder.CreateCall(Callee, Args); } diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index aeb03fd06268d..61773db0dd2af 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -755,6 +755,7 @@ void *tp (void) { typedef void (*Fvoid)(void); typedef float (*Ffloats)(float, double, int); typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); +typedef void (*FVarArgs)(int, ...); typedef __externref_t (*FExternRef)(__externref_t, __externref_t); typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats); @@ -775,6 +776,11 @@ void test_function_pointer_signature_pointers(Fpointers func) { use(__builtin_wasm_test_function_pointer_signature(func)); } +void test_function_pointer_signature_varargs(FVarArgs func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + 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) use(__builtin_wasm_test_function_pointer_signature(func)); >From 6dafa4a31ca072ff194787671150fee2b2bb4d1a Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Tue, 29 Jul 2025 11:01:44 +0200 Subject: [PATCH 3/5] Add tests for struct and union abi --- clang/test/CodeGen/builtins-wasm.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index 61773db0dd2af..1b15c4a12a64f 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -752,41 +752,59 @@ void *tp (void) { // WEBASSEMBLY: call {{.*}} @llvm.thread.pointer.p0() } -typedef void (*Fvoid)(void); -typedef float (*Ffloats)(float, double, int); -typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); -typedef void (*FVarArgs)(int, ...); -typedef __externref_t (*FExternRef)(__externref_t, __externref_t); -typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats); void use(int); +typedef void (*Fvoid)(void); 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)); } +typedef float (*Ffloats)(float, double, int); void test_function_pointer_signature_floats(Ffloats func) { // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison, token poison, float poison, double poison, i32 poison) use(__builtin_wasm_test_function_pointer_signature(func)); } +typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); void test_function_pointer_signature_pointers(Fpointers func) { // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison) use(__builtin_wasm_test_function_pointer_signature(func)); } +typedef void (*FVarArgs)(int, ...); void test_function_pointer_signature_varargs(FVarArgs func) { // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 poison, ptr poison) use(__builtin_wasm_test_function_pointer_signature(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) 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) use(__builtin_wasm_test_function_pointer_signature(func)); } + +// Some tests that we get struct ABIs correct. There is no special code in +// __builtin_wasm_test_function_pointer_signature for this, it gets handled by +// the normal type lowering code. +// Single element structs are unboxed, multi element structs are passed on +// stack. +typedef struct {double x;} (*Fstructs1)(struct {double x;}, struct {float x;}, struct {double x; float y;}, union {double x; float y;}); +void test_function_pointer_structs1(Fstructs1 func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison, token poison, double poison, float poison, ptr poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +// Two element return struct ==> return ptr on stack +typedef struct {double x; double y;} (*Fstructs2)(void); +void test_function_pointer_structs2(Fstructs2 func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} >From ceb363a8b346da56cc965086f1a9f9f8f8dc2969 Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Tue, 29 Jul 2025 11:09:56 +0200 Subject: [PATCH 4/5] Add separate union test --- clang/test/CodeGen/builtins-wasm.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index 1b15c4a12a64f..ecab8e9a0fd3c 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -796,9 +796,9 @@ void test_function_pointer_funcref(FFuncRef func) { // the normal type lowering code. // Single element structs are unboxed, multi element structs are passed on // stack. -typedef struct {double x;} (*Fstructs1)(struct {double x;}, struct {float x;}, struct {double x; float y;}, union {double x; float y;}); +typedef struct {double x;} (*Fstructs1)(struct {double x;}, struct {float x;}, struct {double x; float y;}); void test_function_pointer_structs1(Fstructs1 func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison, token poison, double poison, float poison, ptr poison, ptr poison) + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison, token poison, double poison, float poison, ptr poison) use(__builtin_wasm_test_function_pointer_signature(func)); } @@ -808,3 +808,10 @@ void test_function_pointer_structs2(Fstructs2 func) { // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison) use(__builtin_wasm_test_function_pointer_signature(func)); } + +// Return union ==> return ptr on stack, one element union => unboxed +typedef union {double x; float y;} (*FUnions)(union {double x; float y;}, union {double x;}); +void test_function_pointer_unions(FUnions func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, double poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} >From 60e2c93b2caf8aa7d58bfaf4ddc428a4f2ba9c8e Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Thu, 7 Aug 2025 11:57:54 +0200 Subject: [PATCH 5/5] Reject struct/union param/return type in Sema with multivalue abi --- .../clang/Basic/DiagnosticSemaKinds.td | 6 +- clang/include/clang/Sema/SemaWasm.h | 3 +- clang/lib/Sema/SemaWasm.cpp | 30 +++++++- .../WebAssembly/builtins-test-fp-sig.c | 70 +++++++++++++++++++ clang/test/CodeGen/builtins-wasm.c | 64 ----------------- clang/test/Sema/builtins-wasm.c | 13 ++++ 6 files changed, 115 insertions(+), 71 deletions(-) create mode 100644 clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cf23594201143..116341f4b66d5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13234,9 +13234,9 @@ 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 " +def err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union + : Error<"not supported with the multivalue ABI for " + "function pointers with a struct/union as %select{return " "value|parameter}0">; // OpenACC diagnostics. diff --git a/clang/include/clang/Sema/SemaWasm.h b/clang/include/clang/Sema/SemaWasm.h index 8c0639fd7e76f..f82590755d183 100644 --- a/clang/include/clang/Sema/SemaWasm.h +++ b/clang/include/clang/Sema/SemaWasm.h @@ -37,7 +37,8 @@ class SemaWasm : public SemaBase { bool BuiltinWasmTableGrow(CallExpr *TheCall); bool BuiltinWasmTableFill(CallExpr *TheCall); bool BuiltinWasmTableCopy(CallExpr *TheCall); - bool BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall); + bool BuiltinWasmTestFunctionPointerSignature(const TargetInfo &TI, + CallExpr *TheCall); WebAssemblyImportNameAttr * mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index c42b8677470e1..e7731136720e8 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -17,6 +17,7 @@ #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/TargetBuiltins.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Sema/Attr.h" #include "clang/Sema/Sema.h" @@ -227,7 +228,8 @@ bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { return false; } -bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { +bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(const TargetInfo &TI, + CallExpr *TheCall) { if (SemaRef.checkArgCount(TheCall, 1)) return true; @@ -250,9 +252,31 @@ bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { << ArgType << FuncPtrArg->getSourceRange(); } + if (TI.getABI() == "experimental-mv") { + auto isStructOrUnion = [](QualType T) { + return T->isUnionType() || T->isStructureType(); + }; + if (isStructOrUnion(FuncTy->getReturnType())) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag:: + err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union) + << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange(); + } + auto NParams = FuncTy->getNumParams(); + for (unsigned I = 0; I < NParams; I++) { + if (isStructOrUnion(FuncTy->getParamType(I))) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag:: + err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union) + << 1 << FuncPtrArg->getSourceRange(); + } + } + } + // Set return type to int (the result of the test) TheCall->setType(getASTContext().IntTy); - return false; } @@ -279,7 +303,7 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, case WebAssembly::BI__builtin_wasm_table_copy: return BuiltinWasmTableCopy(TheCall); case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: - return BuiltinWasmTestFunctionPointerSignature(TheCall); + return BuiltinWasmTestFunctionPointerSignature(TI, TheCall); } return false; diff --git a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c new file mode 100644 index 0000000000000..88447f7fa232d --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +gc -O3 -emit-llvm -DSINGLE_VALUE -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY-SV +// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +gc -O3 -emit-llvm -DSINGLE_VALUE -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY-SV +// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +gc -target-abi experimental-mv -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes WEBASSEMBLY +// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-GC + +void use(int); + +typedef void (*Fvoid)(void); +void test_function_pointer_signature_void(Fvoid func) { + // MISSING-GC: error: '__builtin_wasm_test_function_pointer_signature' needs target feature gc + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +typedef float (*Ffloats)(float, double, int); +void test_function_pointer_signature_floats(Ffloats func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison, token poison, float poison, double poison, i32 poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); +void test_function_pointer_signature_pointers(Fpointers func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +typedef void (*FVarArgs)(int, ...); +void test_function_pointer_signature_varargs(FVarArgs func) { + // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(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) + 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) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +#ifdef SINGLE_VALUE +// Some tests that we get struct ABIs correct. There is no special code in +// __builtin_wasm_test_function_pointer_signature for this, it gets handled by +// the normal type lowering code. +// Single element structs are unboxed, multi element structs are passed on +// stack. +typedef struct {double x;} (*Fstructs1)(struct {double x;}, struct {float x;}, struct {double x; float y;}); +void test_function_pointer_structs1(Fstructs1 func) { + // WEBASSEMBLY-SV: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison, token poison, double poison, float poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +// Two element return struct ==> return ptr on stack +typedef struct {double x; double y;} (*Fstructs2)(void); +void test_function_pointer_structs2(Fstructs2 func) { + // WEBASSEMBLY-SV: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} + +// Return union ==> return ptr on stack, one element union => unboxed +typedef union {double x; float y;} (*FUnions)(union {double x; float y;}, union {double x;}); +void test_function_pointer_unions(FUnions func) { + // WEBASSEMBLY-SV: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, double poison) + use(__builtin_wasm_test_function_pointer_signature(func)); +} +#endif diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index ecab8e9a0fd3c..375664b852636 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -751,67 +751,3 @@ void *tp (void) { return __builtin_thread_pointer (); // WEBASSEMBLY: call {{.*}} @llvm.thread.pointer.p0() } - - -void use(int); - -typedef void (*Fvoid)(void); -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)); -} - -typedef float (*Ffloats)(float, double, int); -void test_function_pointer_signature_floats(Ffloats func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float poison, token poison, float poison, double poison, i32 poison) - use(__builtin_wasm_test_function_pointer_signature(func)); -} - -typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]); -void test_function_pointer_signature_pointers(Fpointers func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison, ptr poison) - use(__builtin_wasm_test_function_pointer_signature(func)); -} - -typedef void (*FVarArgs)(int, ...); -void test_function_pointer_signature_varargs(FVarArgs func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 poison, ptr poison) - use(__builtin_wasm_test_function_pointer_signature(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) - 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) - use(__builtin_wasm_test_function_pointer_signature(func)); -} - -// Some tests that we get struct ABIs correct. There is no special code in -// __builtin_wasm_test_function_pointer_signature for this, it gets handled by -// the normal type lowering code. -// Single element structs are unboxed, multi element structs are passed on -// stack. -typedef struct {double x;} (*Fstructs1)(struct {double x;}, struct {float x;}, struct {double x; float y;}); -void test_function_pointer_structs1(Fstructs1 func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double poison, token poison, double poison, float poison, ptr poison) - use(__builtin_wasm_test_function_pointer_signature(func)); -} - -// Two element return struct ==> return ptr on stack -typedef struct {double x; double y;} (*Fstructs2)(void); -void test_function_pointer_structs2(Fstructs2 func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison) - use(__builtin_wasm_test_function_pointer_signature(func)); -} - -// Return union ==> return ptr on stack, one element union => unboxed -typedef union {double x; float y;} (*FUnions)(union {double x; float y;}, union {double x;}); -void test_function_pointer_unions(FUnions func) { - // WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr poison, ptr poison, double poison) - use(__builtin_wasm_test_function_pointer_signature(func)); -} diff --git a/clang/test/Sema/builtins-wasm.c b/clang/test/Sema/builtins-wasm.c index a93103e4804c8..9075e9eaa5230 100644 --- a/clang/test/Sema/builtins-wasm.c +++ b/clang/test/Sema/builtins-wasm.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s +// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-abi experimental-mv -DMULTIVALUE -target-feature +reference-types %s #define EXPR_HAS_TYPE(expr, type) _Generic((expr), type : 1, default : 0) @@ -57,6 +58,8 @@ void test_table_copy(int dst_idx, int src_idx, int nelem) { typedef void (*F1)(void); typedef int (*F2)(int); +typedef void (*F3)(struct {int x; double y;}); +typedef struct {int x; double y;} (*F4)(void); void test_function_pointer_signature() { // Test argument count validation @@ -73,4 +76,14 @@ void test_function_pointer_signature() { // Test return type _Static_assert(EXPR_HAS_TYPE(__builtin_wasm_test_function_pointer_signature((F1)0), int), ""); + +#ifdef MULTIVALUE + // Test that struct arguments and returns are rejected with multivalue abi + (void)__builtin_wasm_test_function_pointer_signature((F3)0); // expected-error {{not supported with the multivalue ABI for function pointers with a struct/union as parameter}} + (void)__builtin_wasm_test_function_pointer_signature((F4)0); // expected-error {{not supported with the multivalue ABI for function pointers with a struct/union as return value}} +#else + // with default abi they are fine + (void)__builtin_wasm_test_function_pointer_signature((F3)0); + (void)__builtin_wasm_test_function_pointer_signature((F4)0); +#endif } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
