summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp34
-rw-r--r--llvm/test/CodeGen/WebAssembly/function-bitcasts.ll43
-rw-r--r--llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll40
3 files changed, 95 insertions, 22 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
index 742fe407223..4050bd9e7b6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
@@ -107,14 +107,18 @@ static void FindUses(Value *V, Function &F,
// I32 vs pointer type) then we don't create a wrapper at all (return nullptr
// instead).
//
-// If there is a type mismatch that would result in an invalid wasm module
-// being written then generate wrapper that contains unreachable (i.e. abort
-// at runtime). Such programs are deep into undefined behaviour territory,
+// If there is a type mismatch that we know would result in an invalid wasm
+// module then generate wrapper that contains unreachable (i.e. abort at
+// runtime). Such programs are deep into undefined behaviour territory,
// but we choose to fail at runtime rather than generate and invalid module
// or fail at compiler time. The reason we delay the error is that we want
// to support the CMake which expects to be able to compile and link programs
// that refer to functions with entirely incorrect signatures (this is how
// CMake detects the existence of a function in a toolchain).
+//
+// For bitcasts that involve struct types we don't know at this stage if they
+// would be equivalent at the wasm level and so we can't know if we need to
+// generate a wrapper.
static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Module *M = F->getParent();
@@ -132,8 +136,12 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
bool TypeMismatch = false;
bool WrapperNeeded = false;
+ Type *ExpectedRtnType = F->getFunctionType()->getReturnType();
+ Type *RtnType = Ty->getReturnType();
+
if ((F->getFunctionType()->getNumParams() != Ty->getNumParams()) ||
- (F->getFunctionType()->isVarArg() != Ty->isVarArg()))
+ (F->getFunctionType()->isVarArg() != Ty->isVarArg()) ||
+ (ExpectedRtnType != RtnType))
WrapperNeeded = true;
for (; AI != AE && PI != PE; ++AI, ++PI) {
@@ -148,6 +156,10 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
CastInst::CreateBitOrPointerCast(AI, ParamType, "cast");
BB->getInstList().push_back(PtrCast);
Args.push_back(PtrCast);
+ } else if (ArgType->isStructTy() || ParamType->isStructTy()) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: struct param type in bitcast: "
+ << F->getName() << "\n");
+ WrapperNeeded = false;
} else {
LLVM_DEBUG(dbgs() << "CreateWrapper: arg type mismatch calling: "
<< F->getName() << "\n");
@@ -159,7 +171,7 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
}
}
- if (!TypeMismatch) {
+ if (WrapperNeeded && !TypeMismatch) {
for (; PI != PE; ++PI)
Args.push_back(UndefValue::get(*PI));
if (F->isVarArg())
@@ -173,10 +185,9 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
// Determine what value to return.
if (RtnType->isVoidTy()) {
ReturnInst::Create(M->getContext(), BB);
- WrapperNeeded = true;
} else if (ExpectedRtnType->isVoidTy()) {
+ LLVM_DEBUG(dbgs() << "Creating dummy return: " << *RtnType << "\n");
ReturnInst::Create(M->getContext(), UndefValue::get(RtnType), BB);
- WrapperNeeded = true;
} else if (RtnType == ExpectedRtnType) {
ReturnInst::Create(M->getContext(), Call, BB);
} else if (CastInst::isBitOrNoopPointerCastable(ExpectedRtnType, RtnType,
@@ -185,6 +196,10 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
CastInst::CreateBitOrPointerCast(Call, RtnType, "cast");
BB->getInstList().push_back(Cast);
ReturnInst::Create(M->getContext(), Cast, BB);
+ } else if (RtnType->isStructTy() || ExpectedRtnType->isStructTy()) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: struct return type in bitcast: "
+ << F->getName() << "\n");
+ WrapperNeeded = false;
} else {
LLVM_DEBUG(dbgs() << "CreateWrapper: return type mismatch calling: "
<< F->getName() << "\n");
@@ -195,6 +210,10 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
}
if (TypeMismatch) {
+ // Create a new wrapper that simply contains `unreachable`.
+ Wrapper->eraseFromParent();
+ Wrapper = Function::Create(Ty, Function::PrivateLinkage, F->getName() + "_bitcast_invalid", M);
+ BasicBlock *BB = BasicBlock::Create(M->getContext(), "body", Wrapper);
new UnreachableInst(M->getContext(), BB);
Wrapper->setName(F->getName() + "_bitcast_invalid");
} else if (!WrapperNeeded) {
@@ -203,6 +222,7 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Wrapper->eraseFromParent();
return nullptr;
}
+ LLVM_DEBUG(dbgs() << "CreateWrapper: " << F->getName() << "\n");
return Wrapper;
}
diff --git a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
index 233a2b4468f..0e7fcd5d570 100644
--- a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
+++ b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
@@ -6,6 +6,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
declare void @has_i32_arg(i32)
+declare void @has_struct_arg({i32})
declare i32 @has_i32_ret()
declare void @vararg(...)
declare void @plain(i32)
@@ -16,9 +17,10 @@ declare void @foo2()
declare void @foo3()
; CHECK-LABEL: test:
-; CHECK-NEXT: call .Lhas_i32_arg_bitcast@FUNCTION{{$}}
-; CHECK-NEXT: call .Lhas_i32_arg_bitcast@FUNCTION{{$}}
+; CHECK-NEXT: call .Lhas_i32_arg_bitcast.2@FUNCTION{{$}}
+; CHECK-NEXT: call .Lhas_i32_arg_bitcast.2@FUNCTION{{$}}
; CHECK-NEXT: call .Lhas_i32_ret_bitcast@FUNCTION{{$}}
+; CHECK-NEXT: i32.call $drop=, has_i32_ret@FUNCTION
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0
; CHECK-NEXT: call .Lfoo0_bitcast@FUNCTION, $pop[[L0]]{{$}}
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, 0
@@ -36,6 +38,7 @@ entry:
call void bitcast (void (i32)* @has_i32_arg to void ()*)()
call void bitcast (void (i32)* @has_i32_arg to void ()*)()
call void bitcast (i32 ()* @has_i32_ret to void ()*)()
+ call i32 bitcast (i32 ()* @has_i32_ret to i32 ()*)()
call void bitcast (void ()* @foo0 to void (i32)*)(i32 0)
%p = bitcast void ()* @foo0 to void (i32)*
call void %p(i32 0)
@@ -51,6 +54,30 @@ entry:
ret void
}
+; CHECK-LABEL: test_structs:
+; CHECK: call .Lhas_i32_arg_bitcast.1@FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
+; CHECK: call .Lhas_i32_arg_bitcast@FUNCTION, $0, $pop2
+; CHECK: call .Lhas_struct_arg_bitcast@FUNCTION{{$}}
+define void @test_structs() {
+entry:
+ call void bitcast (void (i32)* @has_i32_arg to void (i32, {i32})*)(i32 5, {i32} {i32 6})
+ call {i32, i64} bitcast (void (i32)* @has_i32_arg to {i32, i64} (i32)*)(i32 7)
+ call void bitcast (void ({i32})* @has_struct_arg to void ()*)()
+ ret void
+}
+
+; CHECK-LABEL: test_structs_unhandled:
+; CHECK: call has_struct_arg@FUNCTION, $pop{{[0-9]+$}}
+; CHECK: call has_struct_arg@FUNCTION, $pop{{[0-9]+$}}
+; CHECK: call has_i32_ret@FUNCTION, $pop{{[0-9]+$}}
+define void @test_structs_unhandled() {
+entry:
+ call void @has_struct_arg({i32} {i32 3})
+ call void bitcast (void ({i32})* @has_struct_arg to void ({i64})*)({i64} {i64 4})
+ call {i32, i32} bitcast (i32 ()* @has_i32_ret to {i32, i32} ()*)()
+ ret void
+}
+
; CHECK-LABEL: test_varargs:
; CHECK: set_global
; CHECK: i32.const $push[[L3:[0-9]+]]=, 0{{$}}
@@ -113,7 +140,7 @@ define void @test_argument() {
; CHECK: i32.const $push[[L3:[0-9]+]]=, call_func@FUNCTION{{$}}
; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, has_i32_arg@FUNCTION{{$}}
; CHECK-NEXT: call "__invoke_void_i32()*"@FUNCTION, $pop[[L3]], $pop[[L2]]{{$}}
-; CHECK: i32.const $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast@FUNCTION{{$}}
+; CHECK: i32.const $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast.2@FUNCTION{{$}}
; CHECK-NEXT: call __invoke_void@FUNCTION, $pop[[L4]]{{$}}
declare i32 @personality(...)
define void @test_invoke() personality i32 (...)* @personality {
@@ -139,6 +166,16 @@ end:
}
; CHECK-LABEL: .Lhas_i32_arg_bitcast:
+; CHECK-NEXT: .param i32, i32
+; CHECK-NEXT: call has_i32_arg@FUNCTION, $1{{$}}
+; CHECK-NEXT: end_function
+
+; CHECK-LABEL: .Lhas_i32_arg_bitcast.1:
+; CHECK-NEXT: .param i32, i32
+; CHECK-NEXT: call has_i32_arg@FUNCTION, $0{{$}}
+; CHECK-NEXT: end_function
+
+; CHECK-LABEL: .Lhas_i32_arg_bitcast.2:
; CHECK-NEXT: call has_i32_arg@FUNCTION, $0{{$}}
; CHECK-NEXT: end_function
diff --git a/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
index 6bc478c596d..b8642b97501 100644
--- a/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
+++ b/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
@@ -9,28 +9,31 @@ target triple = "wasm32-unknown-unknown"
declare i32 @has_i64_arg(i64)
declare i32 @has_ptr_arg(i8*)
+; CHECK-LABEL: test_invalid_rtn:
+; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: i32.call $push[[L1:[0-9]+]]=, .Lhas_i64_arg_bitcast_invalid.2@FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: drop $pop[[L1]]{{$}}
+; CHECK-NEXT: i64.const $push[[L0:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: i64.call $push[[L1:[0-9]+]]=, .Lhas_i64_arg_bitcast_invalid@FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: drop $pop[[L1]]{{$}}
+; CHECK-NEXT: end_function
define void @test_invalid_rtn() {
entry:
call i32 bitcast (i32 (i64)* @has_i64_arg to i32 (i32)*)(i32 0)
+ call [1 x i64] bitcast (i32 (i64)* @has_i64_arg to [1 x i64] (i64)*)(i64 0)
ret void
}
-; CHECK-LABEL: test_invalid_rtn:
-; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: i32.call $push1=, .Lhas_i64_arg_bitcast_invalid@FUNCTION, $pop[[L0]]{{$}}
-; CHECK-NEXT: drop $pop1
-; CHECK-NEXT: end_function
-define void @test_invalid_arg() {
-entry:
- call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i8)*)(i8 2)
- call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i32)*)(i32 2)
- call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i64)*)(i64 3)
+; CHECK-LABEL: test_struct_rtn:
+; CHECK: call has_i64_arg@FUNCTION, $pop6, $pop0
+define void @test_struct_rtn() {
+ call {i32, i32} bitcast (i32 (i64)* @has_i64_arg to {i32, i32} (i64)*)(i64 0)
ret void
}
; CHECK-LABEL: test_invalid_arg:
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 2{{$}}
-; CHECK-NEXT: i32.call $push[[L1:[0-9]+]]=, .Lhas_ptr_arg_bitcast_invalid.1@FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: i32.call $push[[L1:[0-9]+]]=, .Lhas_ptr_arg_bitcast_invalid.4@FUNCTION, $pop[[L0]]{{$}}
; CHECK-NEXT: drop $pop[[L1]]{{$}}
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 2{{$}}
; CHECK-NEXT: i32.call $push[[L1:[0-9]+]]=, has_ptr_arg@FUNCTION, $pop[[L0]]{{$}}
@@ -39,8 +42,21 @@ entry:
; CHECK-NEXT: i32.call $push[[L1:[0-9]+]]=, .Lhas_ptr_arg_bitcast_invalid@FUNCTION, $pop[[L0]]{{$}}
; CHECK-NEXT: drop $pop[[L1]]{{$}}
; CHECK-NEXT: end_function
+define void @test_invalid_arg() {
+entry:
+ call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i8)*)(i8 2)
+ call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i32)*)(i32 2)
+ call i32 bitcast (i32 (i8*)* @has_ptr_arg to i32 (i64)*)(i64 3)
+ ret void
+}
; CHECK-LABEL: .Lhas_i64_arg_bitcast_invalid:
+; CHECK-NEXT: .param i64
+; CHECK-NEXT: .result i64
+; CHECK-NEXT: unreachable
+; CHECK-NEXT: end_function
+
+; CHECK-LABEL: .Lhas_i64_arg_bitcast_invalid.2:
; CHECK-NEXT: .param i32
; CHECK-NEXT: .result i32
; CHECK-NEXT: unreachable
@@ -52,7 +68,7 @@ entry:
; CHECK-NEXT: unreachable
; CHECK-NEXT: end_function
-; CHECK-LABEL: .Lhas_ptr_arg_bitcast_invalid.1:
+; CHECK-LABEL: .Lhas_ptr_arg_bitcast_invalid.4:
; CHECK-NEXT: .param i32
; CHECK-NEXT: .result i32
; CHECK-NEXT: unreachable
OpenPOWER on IntegriCloud