[WebAssembly] Support opaque pointers in FixFunctionBitcasts
authorNikita Popov <nikita.ppv@gmail.com>
Sat, 28 Aug 2021 21:38:12 +0000 (23:38 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 1 Sep 2021 20:17:24 +0000 (22:17 +0200)
With opaque pointers, no actual bitcasts will be present. Instead,
there will be a mismatch between the call FunctionType and the
function ValueType. Change the code to collect CallBases
specifically (rather than general Uses) and compare these types.

RAUW is no longer performed, as there would no longer be any
bitcasts that can be RAUWd.

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

llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
llvm/test/CodeGen/WebAssembly/function-bitcasts.ll
llvm/test/CodeGen/WebAssembly/main-declaration.ll

index 7abb6fa..2a4349e 100644 (file)
@@ -64,29 +64,21 @@ ModulePass *llvm::createWebAssemblyFixFunctionBitcasts() {
 // Recursively descend the def-use lists from V to find non-bitcast users of
 // bitcasts of V.
 static void findUses(Value *V, Function &F,
-                     SmallVectorImpl<std::pair<Use *, Function *>> &Uses,
-                     SmallPtrSetImpl<Constant *> &ConstantBCs) {
-  for (Use &U : V->uses()) {
-    if (auto *BC = dyn_cast<BitCastOperator>(U.getUser()))
-      findUses(BC, F, Uses, ConstantBCs);
-    else if (auto *A = dyn_cast<GlobalAlias>(U.getUser()))
-      findUses(A, F, Uses, ConstantBCs);
-    else if (U.get()->getType() != F.getType()) {
-      CallBase *CB = dyn_cast<CallBase>(U.getUser());
-      if (!CB)
-        // Skip uses that aren't immediately called
-        continue;
+                     SmallVectorImpl<std::pair<CallBase *, Function *>> &Uses) {
+  for (User *U : V->users()) {
+    if (auto *BC = dyn_cast<BitCastOperator>(U))
+      findUses(BC, F, Uses);
+    else if (auto *A = dyn_cast<GlobalAlias>(U))
+      findUses(A, F, Uses);
+    else if (auto *CB = dyn_cast<CallBase>(U)) {
       Value *Callee = CB->getCalledOperand();
       if (Callee != V)
         // Skip calls where the function isn't the callee
         continue;
-      if (isa<Constant>(U.get())) {
-        // Only add constant bitcasts to the list once; they get RAUW'd
-        auto C = ConstantBCs.insert(cast<Constant>(U.get()));
-        if (!C.second)
-          continue;
-      }
-      Uses.push_back(std::make_pair(&U, &F));
+      if (CB->getFunctionType() == F.getValueType())
+        // Skip uses that are immediately called
+        continue;
+      Uses.push_back(std::make_pair(CB, &F));
     }
   }
 }
@@ -238,8 +230,7 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
 
   Function *Main = nullptr;
   CallInst *CallMain = nullptr;
-  SmallVector<std::pair<Use *, Function *>, 0> Uses;
-  SmallPtrSet<Constant *, 2> ConstantBCs;
+  SmallVector<std::pair<CallBase *, Function *>, 0> Uses;
 
   // Collect all the places that need wrappers.
   for (Function &F : M) {
@@ -247,7 +238,7 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
     // bitcast type difference for swiftself and swifterror.
     if (F.getCallingConv() == CallingConv::Swift)
       continue;
-    findUses(&F, F, Uses, ConstantBCs);
+    findUses(&F, F, Uses);
 
     // If we have a "main" function, and its type isn't
     // "int main(int argc, char *argv[])", create an artificial call with it
@@ -268,8 +259,7 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
         Value *Casted =
             ConstantExpr::getBitCast(Main, PointerType::get(MainTy, 0));
         CallMain = CallInst::Create(MainTy, Casted, Args, "call_main");
-        Use *UseMain = &CallMain->getOperandUse(2);
-        Uses.push_back(std::make_pair(UseMain, &F));
+        Uses.push_back(std::make_pair(CallMain, &F));
       }
     }
   }
@@ -277,16 +267,9 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
   DenseMap<std::pair<Function *, FunctionType *>, Function *> Wrappers;
 
   for (auto &UseFunc : Uses) {
-    Use *U = UseFunc.first;
+    CallBase *CB = UseFunc.first;
     Function *F = UseFunc.second;
-    auto *PTy = cast<PointerType>(U->get()->getType());
-    auto *Ty = dyn_cast<FunctionType>(PTy->getElementType());
-
-    // If the function is casted to something like i8* as a "generic pointer"
-    // to be later casted to something else, we can't generate a wrapper for it.
-    // Just ignore such casts for now.
-    if (!Ty)
-      continue;
+    FunctionType *Ty = CB->getFunctionType();
 
     auto Pair = Wrappers.insert(std::make_pair(std::make_pair(F, Ty), nullptr));
     if (Pair.second)
@@ -296,10 +279,7 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
     if (!Wrapper)
       continue;
 
-    if (isa<Constant>(U->get()))
-      U->get()->replaceAllUsesWith(Wrapper);
-    else
-      U->set(Wrapper);
+    CB->setCalledOperand(Wrapper);
   }
 
   // If we created a wrapper for main, rename the wrapper so that it's the
index 36cd5d8..5ebf2bc 100644 (file)
@@ -1,7 +1,10 @@
-; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions | FileCheck %s --check-prefixes=CHECK,TYPED
+; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions -force-opaque-pointers | FileCheck %s --check-prefixes=CHECK,OPAQUE
 
 ; Test that function pointer casts are replaced with wrappers.
 
+; The TYPED and OPAQUE prefixes only differ in function ordering.
+
 target triple = "wasm32-unknown-unknown"
 
 define void @has_i32_arg(i32) {
@@ -20,8 +23,10 @@ declare void @foo2()
 declare void @foo3()
 
 ; CHECK-LABEL: test:
-; CHECK:      call        .Lhas_i32_arg_bitcast.2{{$}}
-; CHECK-NEXT: call        .Lhas_i32_arg_bitcast.2{{$}}
+; TYPED:      call        .Lhas_i32_arg_bitcast.2{{$}}
+; TYPED-NEXT: call        .Lhas_i32_arg_bitcast.2{{$}}
+; OPAQUE:      call       .Lhas_i32_arg_bitcast{{$}}
+; OPAQUE-NEXT: call       .Lhas_i32_arg_bitcast{{$}}
 ; CHECK-NEXT: call        .Lhas_i32_ret_bitcast{{$}}
 ; CHECK-NEXT: call        $drop=, has_i32_ret
 ; CHECK-NEXT: i32.const   $push[[L0:[0-9]+]]=, 0
@@ -62,7 +67,8 @@ entry:
 @alias_i32_arg = weak hidden alias void (i32), void (i32)* @has_i32_arg
 
 ; CHECK-LABEL: test_alias:
-; CHECK: call    .Lhas_i32_arg_bitcast.2
+; TYPED: call    .Lhas_i32_arg_bitcast.2
+; OPAQUE: call   .Lhas_i32_arg_bitcast
 define void @test_alias() {
 entry:
   call void bitcast (void (i32)* @alias_i32_arg to void ()*)()
@@ -71,8 +77,10 @@ entry:
 
 
 ; CHECK-LABEL: test_structs:
-; CHECK: call     .Lhas_i32_arg_bitcast.1, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
-; CHECK: call     .Lhas_i32_arg_bitcast, $0, $pop2
+; TYPED: call     .Lhas_i32_arg_bitcast.1, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
+; TYPED: call     .Lhas_i32_arg_bitcast, $0, $pop2
+; OPAQUE: call    .Lhas_i32_arg_bitcast.2, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
+; OPAQUE: call    .Lhas_i32_arg_bitcast.1, $0, $pop2
 ; CHECK: call     .Lhas_struct_arg_bitcast{{$}}
 define void @test_structs() {
 entry:
@@ -156,7 +164,8 @@ define void @test_argument() {
 ; CHECK:      i32.const   $push[[L3:[0-9]+]]=, call_func{{$}}
 ; CHECK-NEXT: i32.const   $push[[L2:[0-9]+]]=, has_i32_arg{{$}}
 ; CHECK-NEXT: call        invoke_vi, $pop[[L3]], $pop[[L2]]{{$}}
-; CHECK:      i32.const   $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast.2{{$}}
+; TYPED:      i32.const   $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast.2{{$}}
+; OPAQUE:     i32.const   $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast{{$}}
 ; CHECK-NEXT: call        invoke_v, $pop[[L4]]{{$}}
 declare i32 @personality(...)
 define void @test_invoke() personality i32 (...)* @personality {
@@ -181,19 +190,35 @@ end:
   ret void
 }
 
-; CHECK-LABEL: .Lhas_i32_arg_bitcast:
-; CHECK-NEXT: .functype .Lhas_i32_arg_bitcast (i32, i32) -> ()
-; CHECK-NEXT: call        has_i32_arg, $1{{$}}
-; CHECK-NEXT: end_function
-
-; CHECK-LABEL: .Lhas_i32_arg_bitcast.1:
-; CHECK-NEXT: .functype .Lhas_i32_arg_bitcast.1 (i32, i32) -> ()
-; CHECK-NEXT: call        has_i32_arg, $0{{$}}
-; CHECK-NEXT: end_function
-
-; CHECK-LABEL: .Lhas_i32_arg_bitcast.2:
-; CHECK:      call        has_i32_arg, $0{{$}}
-; CHECK-NEXT: end_function
+; TYPED-LABEL: .Lhas_i32_arg_bitcast:
+; TYPED-NEXT: .functype .Lhas_i32_arg_bitcast (i32, i32) -> ()
+; TYPED-NEXT: call        has_i32_arg, $1{{$}}
+; TYPED-NEXT: end_function
+
+; TYPED-LABEL: .Lhas_i32_arg_bitcast.1:
+; TYPED-NEXT: .functype .Lhas_i32_arg_bitcast.1 (i32, i32) -> ()
+; TYPED-NEXT: call        has_i32_arg, $0{{$}}
+; TYPED-NEXT: end_function
+
+; TYPED-LABEL: .Lhas_i32_arg_bitcast.2:
+; TYPED-NEXT: .functype        .Lhas_i32_arg_bitcast.2 () -> ()
+; TYPED-NEXT: call        has_i32_arg, $0{{$}}
+; TYPED-NEXT: end_function
+
+; OPAQUE-LABEL: .Lhas_i32_arg_bitcast:
+; OPAQUE-NEXT: .functype       .Lhas_i32_arg_bitcast () -> ()
+; OPAQUE-NEXT: call        has_i32_arg, $0{{$}}
+; OPAQUE-NEXT: end_function
+
+; OPAQUE-LABEL: .Lhas_i32_arg_bitcast.1:
+; OPAQUE-NEXT: .functype .Lhas_i32_arg_bitcast.1 (i32, i32) -> ()
+; OPAQUE-NEXT: call        has_i32_arg, $1{{$}}
+; OPAQUE-NEXT: end_function
+
+; OPAQUE-LABEL: .Lhas_i32_arg_bitcast.2:
+; OPAQUE-NEXT: .functype .Lhas_i32_arg_bitcast.2 (i32, i32) -> ()
+; OPAQUE-NEXT: call        has_i32_arg, $0{{$}}
+; OPAQUE-NEXT: end_function
 
 ; CHECK-LABEL: .Lhas_i32_ret_bitcast:
 ; CHECK:      call        $drop=, has_i32_ret{{$}}
index 445300f..81b5b1b 100644 (file)
@@ -1,4 +1,5 @@
 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -force-opaque-pointers | FileCheck %s
 
 ; Test main functions with alternate signatures.