Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp @@ -103,14 +103,25 @@ // - 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 Args; @@ -118,34 +129,80 @@ 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); + 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; + } + } - // 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) { + 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; } Index: llvm/trunk/test/CodeGen/WebAssembly/call.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/call.ll +++ llvm/trunk/test/CodeGen/WebAssembly/call.ll @@ -153,7 +153,7 @@ ; 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{{$}} Index: llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll +++ llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll @@ -19,13 +19,13 @@ ; 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]+$}} Index: llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll +++ llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll @@ -16,17 +16,17 @@ 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 @@ ; 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 @@ ; 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 @@ 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 Index: llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll +++ llvm/trunk/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