This adds an llvm intrinsic for WebAssembly to test the type of a function. It is intended for adding a future clang builtin ` __builtin_wasm_test_function_pointer_signature` so we can test whether calling a function pointer will fail with function signature mismatch. Since the type of a function pointer is just `ptr` we can't figure out the expected type from that. The way I figured out to encode the type was by passing 0's of the appropriate type to the intrinsic. The first argument gives the expected type of the return type and the later values give the expected type of the arguments. So ```llvm @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, double 0.000000e+00, i32 0) ``` tests if `%func` is of type `(double, i32) -> (i32)`. It will lower to: ```wat local.get $func table.get $__indirect_function_table ref.test (double, i32) -> (i32) ``` To indicate the function should be void, I somewhat arbitrarily picked `token poison`, so the following tests for `(i32) -> ()`: ```llvm @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0) ``` To lower this intrinsic, we need some place to put the type information. With `encodeFunctionSignature()` we encode the signature information into an `APInt`. We decode it in `lowerEncodedFunctionSignature` in `WebAssemblyMCInstLower.cpp`.
121 lines
4.6 KiB
LLVM
121 lines
4.6 KiB
LLVM
; 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
|
|
|
|
define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_void_void:
|
|
; CHK32: .functype test_fpsig_void_void (i32) -> ()
|
|
; CHK64: .functype test_fpsig_void_void (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test () -> ()
|
|
; CHECK-NEXT: call use
|
|
; CHECK-NEXT: # fallthrough-return
|
|
entry:
|
|
%res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_return_i32:
|
|
; CHK32: .functype test_fpsig_return_i32 (i32) -> ()
|
|
; CHK64: .functype test_fpsig_return_i32 (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test () -> (i32)
|
|
; CHECK-NEXT: call use
|
|
; CHECK-NEXT: # fallthrough-return
|
|
entry:
|
|
%res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_return_i64:
|
|
; CHK32: .functype test_fpsig_return_i64 (i32) -> ()
|
|
; CHK64: .functype test_fpsig_return_i64 (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test () -> (i64)
|
|
; CHECK-NEXT: call use
|
|
; CHECK-NEXT: # fallthrough-return
|
|
entry:
|
|
%res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 0)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_return_f32:
|
|
; CHK32: .functype test_fpsig_return_f32 (i32) -> ()
|
|
; CHK64: .functype test_fpsig_return_f32 (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test () -> (f32)
|
|
; CHECK-NEXT: call use
|
|
; CHECK-NEXT: # fallthrough-return
|
|
entry:
|
|
%res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_return_f64:
|
|
; CHK32: .functype test_fpsig_return_f64 (i32) -> ()
|
|
; CHK64: .functype test_fpsig_return_f64 (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test () -> (f64)
|
|
; CHECK-NEXT: call use
|
|
; CHECK-NEXT: # fallthrough-return
|
|
entry:
|
|
%res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double 0.)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
|
|
define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_param_i32:
|
|
; CHK32: .functype test_fpsig_param_i32 (i32) -> ()
|
|
; CHK64: .functype test_fpsig_param_i32 (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: table.get __indirect_function_table
|
|
; CHECK-NEXT: ref.test (f64) -> ()
|
|
; 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.)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
|
|
define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) local_unnamed_addr #0 {
|
|
; CHECK-LABEL: test_fpsig_multiple_params_and_returns:
|
|
; CHK32: .functype test_fpsig_multiple_params_and_returns (i32) -> ()
|
|
; CHK64: .functype test_fpsig_multiple_params_and_returns (i64) -> ()
|
|
; CHECK-NEXT: # %bb.0: # %entry
|
|
; CHECK-NEXT: local.get 0
|
|
; 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
|
|
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)
|
|
tail call void @use(i32 noundef %res) #3
|
|
ret void
|
|
}
|
|
|
|
|
|
declare void @use(i32 noundef) local_unnamed_addr #1
|