summaryrefslogtreecommitdiffstats
path: root/llvm
diff options
context:
space:
mode:
Diffstat (limited to 'llvm')
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp107
-rw-r--r--llvm/test/CodeGen/WebAssembly/call.ll2
-rw-r--r--llvm/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll8
-rw-r--r--llvm/test/CodeGen/WebAssembly/function-bitcasts.ll32
-rw-r--r--llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll62
5 files changed, 150 insertions, 61 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
index d5e47ee8251..4f6dcb113b7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
@@ -103,14 +103,25 @@ static void FindUses(Value *V, Function &F,
// - Return value is not needed: drop it
// - Return value needed but not present: supply an undef
//
-// For now, return nullptr without creating a wrapper if the wrapper cannot
-// be generated due to incompatible types.
+// If the all the argument types of trivially castable to one another (i.e.
+// 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,
+// 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).
static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Module *M = F->getParent();
- Function *Wrapper =
- Function::Create(Ty, Function::PrivateLinkage, "bitcast", M);
+ Function *Wrapper = Function::Create(Ty, Function::PrivateLinkage,
+ F->getName() + "_bitcast", M);
BasicBlock *BB = BasicBlock::Create(M->getContext(), "body", Wrapper);
+ const DataLayout &DL = BB->getModule()->getDataLayout();
// Determine what arguments to pass.
SmallVector<Value *, 4> Args;
@@ -118,34 +129,80 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Function::arg_iterator AE = Wrapper->arg_end();
FunctionType::param_iterator PI = F->getFunctionType()->param_begin();
FunctionType::param_iterator PE = F->getFunctionType()->param_end();
+ bool TypeMismatch = false;
+ bool WrapperNeeded = false;
+
+ if ((F->getFunctionType()->getNumParams() != Ty->getNumParams()) ||
+ (F->getFunctionType()->isVarArg() != Ty->isVarArg()))
+ WrapperNeeded = true;
+
for (; AI != AE && PI != PE; ++AI, ++PI) {
- if (AI->getType() != *PI) {
- Wrapper->eraseFromParent();
- return nullptr;
+ Type *ArgType = AI->getType();
+ Type *ParamType = *PI;
+
+ if (ArgType == ParamType) {
+ Args.push_back(&*AI);
+ } else {
+ if (CastInst::isBitOrNoopPointerCastable(ArgType, ParamType, DL)) {
+ Instruction *PtrCast =
+ CastInst::CreateBitOrPointerCast(AI, ParamType, "cast");
+ BB->getInstList().push_back(PtrCast);
+ Args.push_back(PtrCast);
+ } else {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: arg type mismatch calling: "
+ << F->getName() << "\n");
+ LLVM_DEBUG(dbgs() << "Arg[" << Args.size() << "] Expected: "
+ << *ParamType << " Got: " << *ArgType << "\n");
+ TypeMismatch = true;
+ break;
+ }
}
- Args.push_back(&*AI);
}
- for (; PI != PE; ++PI)
- Args.push_back(UndefValue::get(*PI));
- if (F->isVarArg())
- for (; AI != AE; ++AI)
- Args.push_back(&*AI);
- CallInst *Call = CallInst::Create(F, Args, "", BB);
-
- // Determine what value to return.
- if (Ty->getReturnType()->isVoidTy())
- ReturnInst::Create(M->getContext(), BB);
- else if (F->getFunctionType()->getReturnType()->isVoidTy())
- ReturnInst::Create(M->getContext(), UndefValue::get(Ty->getReturnType()),
- BB);
- else if (F->getFunctionType()->getReturnType() == Ty->getReturnType())
- ReturnInst::Create(M->getContext(), Call, BB);
- else {
+ if (!TypeMismatch) {
+ for (; PI != PE; ++PI)
+ Args.push_back(UndefValue::get(*PI));
+ if (F->isVarArg())
+ for (; AI != AE; ++AI)
+ Args.push_back(&*AI);
+
+ CallInst *Call = CallInst::Create(F, Args, "", BB);
+
+ Type *ExpectedRtnType = F->getFunctionType()->getReturnType();
+ Type *RtnType = Ty->getReturnType();
+ // Determine what value to return.
+ if (RtnType->isVoidTy()) {
+ ReturnInst::Create(M->getContext(), BB);
+ WrapperNeeded = true;
+ } else if (ExpectedRtnType->isVoidTy()) {
+ 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,
+ DL)) {
+ Instruction *Cast =
+ CastInst::CreateBitOrPointerCast(Call, RtnType, "cast");
+ BB->getInstList().push_back(Cast);
+ ReturnInst::Create(M->getContext(), Cast, BB);
+ } else {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: return type mismatch calling: "
+ << F->getName() << "\n");
+ LLVM_DEBUG(dbgs() << "Expected: " << *ExpectedRtnType
+ << " Got: " << *RtnType << "\n");
+ TypeMismatch = true;
+ }
+ }
+
+ if (TypeMismatch) {
+ new UnreachableInst(M->getContext(), BB);
+ Wrapper->setName(F->getName() + "_bitcast_invalid");
+ } else if (!WrapperNeeded) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: no wrapper needed: " << F->getName()
+ << "\n");
Wrapper->eraseFromParent();
return nullptr;
}
-
return Wrapper;
}
diff --git a/llvm/test/CodeGen/WebAssembly/call.ll b/llvm/test/CodeGen/WebAssembly/call.ll
index ad494fbb83d..7662c472e25 100644
--- a/llvm/test/CodeGen/WebAssembly/call.ll
+++ b/llvm/test/CodeGen/WebAssembly/call.ll
@@ -153,7 +153,7 @@ define void @coldcc_tail_call_void_nullary() {
; CHECK-LABEL: call_constexpr:
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 2{{$}}
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, 3{{$}}
-; CHECK-NEXT: call .Lbitcast@FUNCTION, $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT: call .Lvararg_func_bitcast@FUNCTION, $pop[[L0]], $pop[[L1]]{{$}}
; CHECK-NEXT: call other_void_nullary@FUNCTION{{$}}
; CHECK-NEXT: call void_nullary@FUNCTION{{$}}
; CHECK-NEXT: return{{$}}
diff --git a/llvm/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll b/llvm/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll
index 5f1997b21e5..57f2a6dd210 100644
--- a/llvm/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll
+++ b/llvm/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll
@@ -19,13 +19,13 @@ declare void @specified(i32, i32)
; CHECK: callWithArgs:
; CHECK: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, 1
-; CHECK-NEXT: call .Lbitcast@FUNCTION, $pop1, $pop0
-; CHECK: call .Lbitcast.1@FUNCTION, $pop{{[0-9]+$}}
+; CHECK-NEXT: call .Lunderspecified_bitcast@FUNCTION, $pop1, $pop0
+; CHECK: call .Lspecified_bitcast@FUNCTION, $pop{{[0-9]+$}}
-; CHECK: .Lbitcast:
+; CHECK: .Lunderspecified_bitcast:
; CHECK-NEXT: .param i32, i32{{$}}
; CHECK: call underspecified@FUNCTION, $pop{{[0-9]+$}}
-; CHECK: .Lbitcast.1:
+; CHECK: .Lspecified_bitcast:
; CHECK-NEXT: .param i32{{$}}
; CHECK: call specified@FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
diff --git a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
index d3553a40b71..8e190b5c0ba 100644
--- a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
+++ b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
@@ -16,17 +16,17 @@ declare void @foo2()
declare void @foo3()
; CHECK-LABEL: test:
-; CHECK-NEXT: call .Lbitcast@FUNCTION{{$}}
-; CHECK-NEXT: call .Lbitcast@FUNCTION{{$}}
-; CHECK-NEXT: call .Lbitcast.1@FUNCTION{{$}}
+; CHECK-NEXT: call .Lhas_i32_arg_bitcast@FUNCTION{{$}}
+; CHECK-NEXT: call .Lhas_i32_arg_bitcast@FUNCTION{{$}}
+; CHECK-NEXT: call .Lhas_i32_ret_bitcast@FUNCTION{{$}}
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0
-; CHECK-NEXT: call .Lbitcast.4@FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: call .Lfoo0_bitcast@FUNCTION, $pop[[L0]]{{$}}
; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, 0
-; CHECK-NEXT: call .Lbitcast.4@FUNCTION, $pop[[L1]]{{$}}
+; CHECK-NEXT: call .Lfoo0_bitcast@FUNCTION, $pop[[L1]]{{$}}
; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, 0
-; CHECK-NEXT: call .Lbitcast.4@FUNCTION, $pop[[L2]]{{$}}
+; CHECK-NEXT: call .Lfoo0_bitcast@FUNCTION, $pop[[L2]]{{$}}
; CHECK-NEXT: call foo0@FUNCTION
-; CHECK-NEXT: i32.call $drop=, .Lbitcast.5@FUNCTION{{$}}
+; CHECK-NEXT: i32.call $drop=, .Lfoo1_bitcast@FUNCTION{{$}}
; CHECK-NEXT: call foo2@FUNCTION{{$}}
; CHECK-NEXT: call foo1@FUNCTION{{$}}
; CHECK-NEXT: call foo3@FUNCTION{{$}}
@@ -54,10 +54,10 @@ entry:
; CHECK-LABEL: test_varargs:
; CHECK: set_global
; CHECK: i32.const $push[[L3:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: call .Lbitcast.2@FUNCTION, $pop[[L3]]{{$}}
+; CHECK-NEXT: call .Lvararg_bitcast@FUNCTION, $pop[[L3]]{{$}}
; CHECK-NEXT: i32.const $push[[L4:[0-9]+]]=, 0{{$}}
; CHECK-NEXT: i32.store 0($[[L5:[0-9]+]]), $pop[[L4]]{{$}}
-; CHECK-NEXT: call .Lbitcast.3@FUNCTION, $[[L5]]{{$}}
+; CHECK-NEXT: call .Lplain_bitcast@FUNCTION, $[[L5]]{{$}}
define void @test_varargs() {
call void bitcast (void (...)* @vararg to void (i32)*)(i32 0)
call void (...) bitcast (void (i32)* @plain to void (...)*)(i32 0)
@@ -113,7 +113,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]+]]=, .Lbitcast@FUNCTION{{$}}
+; CHECK: i32.const $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast@FUNCTION{{$}}
; CHECK-NEXT: call __invoke_void@FUNCTION, $pop[[L4]]{{$}}
declare i32 @personality(...)
define void @test_invoke() personality i32 (...)* @personality {
@@ -138,28 +138,28 @@ end:
ret void
}
-; CHECK-LABEL: .Lbitcast:
+; CHECK-LABEL: .Lhas_i32_arg_bitcast:
; CHECK-NEXT: call has_i32_arg@FUNCTION, $0{{$}}
; CHECK-NEXT: end_function
-; CHECK-LABEL: .Lbitcast.1:
+; CHECK-LABEL: .Lhas_i32_ret_bitcast:
; CHECK-NEXT: call $drop=, has_i32_ret@FUNCTION{{$}}
; CHECK-NEXT: end_function
-; CHECK-LABEL: .Lbitcast.2:
+; CHECK-LABEL: .Lvararg_bitcast:
; CHECK: call vararg@FUNCTION, $1{{$}}
; CHECK: end_function
-; CHECK-LABEL: .Lbitcast.3:
+; CHECK-LABEL: .Lplain_bitcast:
; CHECK: call plain@FUNCTION, $1{{$}}
; CHECK: end_function
-; CHECK-LABEL: .Lbitcast.4:
+; CHECK-LABEL: .Lfoo0_bitcast:
; CHECK-NEXT: .param i32
; CHECK-NEXT: call foo0@FUNCTION{{$}}
; CHECK-NEXT: end_function
-; CHECK-LABEL: .Lbitcast.5:
+; CHECK-LABEL: .Lfoo1_bitcast:
; CHECK-NEXT: .result i32
; CHECK-NEXT: call foo1@FUNCTION{{$}}
; CHECK-NEXT: copy_local $push0=, $0
diff --git a/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
index 89ef73036df..7f4249681f0 100644
--- a/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
+++ b/llvm/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
@@ -1,27 +1,59 @@
; RUN: llc < %s -asm-verbose=false | FileCheck %s
-; Test that function pointer casts that require conversions are not converted
-; to wrappers. In theory some conversions could be supported, but currently no
-; conversions are implemented.
+; Test that function pointer casts that require conversions of arguments or
+; return types are converted to unreachable.
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
-; CHECK-LABEL: test:
+declare i32 @has_i64_arg(i64)
+declare i32 @has_ptr_arg(i8*)
+
+define void @test_invalid_rtn() {
+entry:
+ call i32 bitcast (i32 (i64)* @has_i64_arg to i32 (i32)*)(i32 0)
+ ret void
+}
+; CHECK-LABEL: test_invalid_rtn:
; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 0{{$}}
-; CHECK-NEXT: call has_i64_arg@FUNCTION, $pop[[L0]]{{$}}
-; CHECK-NEXT: i32.call $push{{[0-9]+}}=, has_i64_ret@FUNCTION{{$}}
-; CHECK-NEXT: drop
+; CHECK-NEXT: i32.call $push1=, .Lhas_i64_arg_bitcast_invalid@FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: drop $pop1
; CHECK-NEXT: end_function
-; CHECK-NOT: .Lbitcast
-
-declare void @has_i64_arg(i64)
-declare i64 @has_i64_ret()
-
-define void @test() {
+define void @test_invalid_arg() {
entry:
- call void bitcast (void (i64)* @has_i64_arg to void (i32)*)(i32 0)
- %t = call i32 bitcast (i64 ()* @has_i64_ret to i32 ()*)()
+ 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: 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: 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]]{{$}}
+; CHECK-NEXT: drop $pop[[L1]]{{$}}
+; CHECK-NEXT: i64.const $push[[L0:[0-9]+]]=, 3{{$}}
+; 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
+
+; CHECK-LABEL: .Lhas_i64_arg_bitcast_invalid:
+; CHECK-NEXT: .param i32
+; CHECK-NEXT: .result i32
+; CHECK-NEXT: unreachable
+; CHECK-NEXT: end_function
+
+; CHECK-LABEL: .Lhas_ptr_arg_bitcast_invalid:
+; CHECK-NEXT: .param i64
+; CHECK-NEXT: .result i32
+; CHECK-NEXT: unreachable
+; CHECK-NEXT: end_function
+
+; CHECK-LABEL: .Lhas_ptr_arg_bitcast_invalid.1:
+; CHECK-NEXT: .param i32
+; CHECK-NEXT: .result i32
+; CHECK-NEXT: unreachable
+; CHECK-NEXT: end_function
OpenPOWER on IntegriCloud