// 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));
}
}
}
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) {
// 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
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));
}
}
}
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)
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
-; 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) {
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
@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 ()*)()
; 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:
; 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 {
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{{$}}