[llvm] r338744 - [WebAssembly] Ensure bitcasts that would result in invalid wasm are removed by FixFunctionBitcasts

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 2 10:38:06 PDT 2018


Author: sbc
Date: Thu Aug  2 10:38:06 2018
New Revision: 338744

URL: http://llvm.org/viewvc/llvm-project?rev=338744&view=rev
Log:
[WebAssembly] Ensure bitcasts that would result in invalid wasm are removed by FixFunctionBitcasts

Rather than allowing invalid bitcasts to be lowered to wasm
call instructions that won't validate, generate wrappers that
contain unreachable thereby delaying the error until runtime.

Differential Revision: https://reviews.llvm.org/D49517

Modified:
    llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
    llvm/trunk/test/CodeGen/WebAssembly/call.ll
    llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll
    llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll
    llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll

Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp?rev=338744&r1=338743&r2=338744&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp Thu Aug  2 10:38:06 2018
@@ -103,14 +103,25 @@ static void FindUses(Value *V, Function
 //  - 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
   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;
 }
 

Modified: llvm/trunk/test/CodeGen/WebAssembly/call.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/call.ll?rev=338744&r1=338743&r2=338744&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/call.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/call.ll Thu Aug  2 10:38:06 2018
@@ -153,7 +153,7 @@ define void @coldcc_tail_call_void_nulla
 ; 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 at FUNCTION, $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT: call .Lvararg_func_bitcast at FUNCTION, $pop[[L0]], $pop[[L1]]{{$}}
 ; CHECK-NEXT: call other_void_nullary at FUNCTION{{$}}
 ; CHECK-NEXT: call void_nullary at FUNCTION{{$}}
 ; CHECK-NEXT: return{{$}}

Modified: llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll?rev=338744&r1=338743&r2=338744&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts-varargs.ll Thu Aug  2 10:38:06 2018
@@ -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 at FUNCTION, $pop1, $pop0
-; CHECK: call    	.Lbitcast.1 at FUNCTION, $pop{{[0-9]+$}}
+; CHECK-NEXT: call    	.Lunderspecified_bitcast at FUNCTION, $pop1, $pop0
+; CHECK: call    	.Lspecified_bitcast at FUNCTION, $pop{{[0-9]+$}}
 
-; CHECK: .Lbitcast:
+; CHECK: .Lunderspecified_bitcast:
 ; CHECK-NEXT: .param  	i32, i32{{$}}
 ; CHECK: call    	underspecified at FUNCTION, $pop{{[0-9]+$}}
 
-; CHECK: .Lbitcast.1:
+; CHECK: .Lspecified_bitcast:
 ; CHECK-NEXT: .param  	i32{{$}}
 ; CHECK: call    	specified at FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+$}}

Modified: llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll?rev=338744&r1=338743&r2=338744&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/function-bitcasts.ll Thu Aug  2 10:38:06 2018
@@ -16,17 +16,17 @@ declare void @foo2()
 declare void @foo3()
 
 ; CHECK-LABEL: test:
-; CHECK-NEXT: call        .Lbitcast at FUNCTION{{$}}
-; CHECK-NEXT: call        .Lbitcast at FUNCTION{{$}}
-; CHECK-NEXT: call        .Lbitcast.1 at FUNCTION{{$}}
+; CHECK-NEXT: call        .Lhas_i32_arg_bitcast at FUNCTION{{$}}
+; CHECK-NEXT: call        .Lhas_i32_arg_bitcast at FUNCTION{{$}}
+; CHECK-NEXT: call        .Lhas_i32_ret_bitcast at FUNCTION{{$}}
 ; CHECK-NEXT: i32.const   $push[[L0:[0-9]+]]=, 0
-; CHECK-NEXT: call        .Lbitcast.4 at FUNCTION, $pop[[L0]]{{$}}
+; CHECK-NEXT: call        .Lfoo0_bitcast at FUNCTION, $pop[[L0]]{{$}}
 ; CHECK-NEXT: i32.const   $push[[L1:[0-9]+]]=, 0
-; CHECK-NEXT: call        .Lbitcast.4 at FUNCTION, $pop[[L1]]{{$}}
+; CHECK-NEXT: call        .Lfoo0_bitcast at FUNCTION, $pop[[L1]]{{$}}
 ; CHECK-NEXT: i32.const   $push[[L2:[0-9]+]]=, 0
-; CHECK-NEXT: call        .Lbitcast.4 at FUNCTION, $pop[[L2]]{{$}}
+; CHECK-NEXT: call        .Lfoo0_bitcast at FUNCTION, $pop[[L2]]{{$}}
 ; CHECK-NEXT: call        foo0 at FUNCTION
-; CHECK-NEXT: i32.call    $drop=, .Lbitcast.5 at FUNCTION{{$}}
+; CHECK-NEXT: i32.call    $drop=, .Lfoo1_bitcast at FUNCTION{{$}}
 ; CHECK-NEXT: call        foo2 at FUNCTION{{$}}
 ; CHECK-NEXT: call        foo1 at FUNCTION{{$}}
 ; CHECK-NEXT: call        foo3 at 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 at FUNCTION, $pop[[L3]]{{$}}
+; CHECK-NEXT: call        .Lvararg_bitcast at 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 at FUNCTION, $[[L5]]{{$}}
+; CHECK-NEXT: call        .Lplain_bitcast at 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 at FUNCTION{{$}}
 ; CHECK-NEXT: i32.const   $push[[L2:[0-9]+]]=, has_i32_arg at FUNCTION{{$}}
 ; CHECK-NEXT: call        "__invoke_void_i32()*"@FUNCTION, $pop[[L3]], $pop[[L2]]{{$}}
-; CHECK:      i32.const   $push[[L4:[0-9]+]]=, .Lbitcast at FUNCTION{{$}}
+; CHECK:      i32.const   $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast at FUNCTION{{$}}
 ; CHECK-NEXT: call        __invoke_void at 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 at FUNCTION, $0{{$}}
 ; CHECK-NEXT: end_function
 
-; CHECK-LABEL: .Lbitcast.1:
+; CHECK-LABEL: .Lhas_i32_ret_bitcast:
 ; CHECK-NEXT: call        $drop=, has_i32_ret at FUNCTION{{$}}
 ; CHECK-NEXT: end_function
 
-; CHECK-LABEL: .Lbitcast.2:
+; CHECK-LABEL: .Lvararg_bitcast:
 ; CHECK: call        vararg at FUNCTION, $1{{$}}
 ; CHECK: end_function
 
-; CHECK-LABEL: .Lbitcast.3:
+; CHECK-LABEL: .Lplain_bitcast:
 ; CHECK: call        plain at FUNCTION, $1{{$}}
 ; CHECK: end_function
 
-; CHECK-LABEL: .Lbitcast.4:
+; CHECK-LABEL: .Lfoo0_bitcast:
 ; CHECK-NEXT: .param      i32
 ; CHECK-NEXT: call        foo0 at FUNCTION{{$}}
 ; CHECK-NEXT: end_function
 
-; CHECK-LABEL: .Lbitcast.5:
+; CHECK-LABEL: .Lfoo1_bitcast:
 ; CHECK-NEXT: .result     i32
 ; CHECK-NEXT: call        foo1 at FUNCTION{{$}}
 ; CHECK-NEXT: copy_local  $push0=, $0

Modified: llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll?rev=338744&r1=338743&r2=338744&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/unsupported-function-bitcasts.ll Thu Aug  2 10:38:06 2018
@@ -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 at FUNCTION, $pop[[L0]]{{$}}
-; CHECK-NEXT: i32.call    $push{{[0-9]+}}=, has_i64_ret at FUNCTION{{$}}
-; CHECK-NEXT: drop
+; CHECK-NEXT: i32.call $push1=, .Lhas_i64_arg_bitcast_invalid at 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 at 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 at 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 at 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




More information about the llvm-commits mailing list