Index: llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp =================================================================== --- llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp +++ llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp @@ -86,27 +86,37 @@ F.getName()); } - // Create a function prototype based on the first call site (first bitcast) - // that we find. + // Find calls of this function, looking through bitcasts. + SmallVector Calls; + SmallVector Worklist; + Worklist.push_back(&F); + while (!Worklist.empty()) { + Value *V = Worklist.pop_back_val(); + for (User *U : V->users()) { + if (auto *BC = dyn_cast(U)) + Worklist.push_back(BC); + else if (auto *CB = dyn_cast(U)) + if (CB->getCalledOperand() == V) + Calls.push_back(CB); + } + } + + // Create a function prototype based on the first call site that we find. FunctionType *NewType = nullptr; - for (Use &U : F.uses()) { - LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n"); - LLVM_DEBUG(dbgs() << *U.getUser() << "\n"); - if (auto *BC = dyn_cast(U.getUser())) { - if (auto *DestType = dyn_cast( - BC->getDestTy()->getPointerElementType())) { - if (!NewType) { - // Create a new function with the correct type - NewType = DestType; - LLVM_DEBUG(dbgs() << "found function type: " << *NewType << "\n"); - } else if (NewType != DestType) { - errs() << "warning: prototype-less function used with " - "conflicting signatures: " - << F.getName() << "\n"; - LLVM_DEBUG(dbgs() << " " << *DestType << "\n"); - LLVM_DEBUG(dbgs() << " "<< *NewType << "\n"); - } - } + for (CallBase *CB : Calls) { + LLVM_DEBUG(dbgs() << "prototype-less call of " << F.getName() << ":\n"); + LLVM_DEBUG(dbgs() << *CB << "\n"); + FunctionType *DestType = CB->getFunctionType(); + if (!NewType) { + // Create a new function with the correct type + NewType = DestType; + LLVM_DEBUG(dbgs() << "found function type: " << *NewType << "\n"); + } else if (NewType != DestType) { + errs() << "warning: prototype-less function used with " + "conflicting signatures: " + << F.getName() << "\n"; + LLVM_DEBUG(dbgs() << " " << *DestType << "\n"); + LLVM_DEBUG(dbgs() << " "<< *NewType << "\n"); } } Index: llvm/test/CodeGen/WebAssembly/add-prototypes-conflict.ll =================================================================== --- llvm/test/CodeGen/WebAssembly/add-prototypes-conflict.ll +++ llvm/test/CodeGen/WebAssembly/add-prototypes-conflict.ll @@ -7,15 +7,15 @@ ; WARNING: warning: prototype-less function used with conflicting signatures: foo ; CHECK-LABEL: @call_with_conflicting_prototypes -; CHECK: %call1 = call i64 @foo(i32 42) -; CHECK: %call2 = call i64 bitcast (i64 (i32)* @foo to i64 (i32, i32)*)(i32 42, i32 43) +; CHECK: %call1 = call i64 bitcast (i64 (i32, i32)* @foo to i64 (i32)*)(i32 42) +; CHECK: %call2 = call i64 @foo(i32 42, i32 43) define void @call_with_conflicting_prototypes() { %call1 = call i64 bitcast (i64 (...)* @foo to i64 (i32)*)(i32 42) %call2 = call i64 bitcast (i64 (...)* @foo to i64 (i32, i32)*)(i32 42, i32 43) ret void } -; CHECK: declare extern_weak i64 @foo(i32) +; CHECK: declare extern_weak i64 @foo(i32, i32) declare extern_weak i64 @foo(...) #1 ; CHECK-NOT: attributes {{.*}} = { {{.*}}"no-prototype"{{.*}} } Index: llvm/test/CodeGen/WebAssembly/add-prototypes-opaque-ptrs.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/WebAssembly/add-prototypes-opaque-ptrs.ll @@ -0,0 +1,79 @@ +; RUN: opt -S -wasm-add-missing-prototypes -force-opaque-pointers %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK: @foo_addr = global ptr @foo, align 8 +@foo_addr = global i64 (i32)* bitcast (i64 (...)* @foo to i64 (i32)*), align 8 + +; CHECK: @foo_addr_i8 = global ptr @foo, align 8 +@foo_addr_i8 = global i8* bitcast (i64 (...)* @foo to i8*), align 8 + +; CHECK-LABEL: @call_foo +; CHECK: %call = call i64 @foo(i32 42) +define void @call_foo(i32 %a) { + %call = call i64 bitcast (i64 (...)* @foo to i64 (i32)*)(i32 42) + ret void +} + +; CHECK-LABEL: @call_foo_ptr +; CHECK: %1 = bitcast ptr @foo to ptr +; CHECK-NEXT: %call = call i64 %1(i32 43) +define i64 @call_foo_ptr(i32 %a) { + %1 = bitcast i64 (...)* @foo to i64 (i32)* + %call = call i64 (i32) %1(i32 43) + ret i64 %call +} + +; CHECK-LABEL: @to_intptr_inst +; CHECK: %1 = bitcast ptr @foo to ptr +; CHECK-NEXT: ret ptr %1 +define i8* @to_intptr_inst() { + %1 = bitcast i64 (...)* @foo to i8* + ret i8* %1 +} + +; CHECK-LABEL: @to_intptr_constexpr +; CHECK: ret ptr @foo +define i8* @to_intptr_constexpr() { + ret i8* bitcast (i64 (...)* @foo to i8*) +} + +; CHECK-LABEL: @null_compare +; CHECK: br i1 icmp eq (ptr @foo, ptr null), label %if.then, label %if.end +define i8 @null_compare() { + br i1 icmp eq (i64 (...)* @foo, i64 (...)* null), label %if.then, label %if.end +if.then: + ret i8 0 +if.end: + ret i8 1 +} + +; CHECK-LABEL: @as_paramater +; CHECK: call void @func_param(ptr @foo) +define void @as_paramater() { + call void @func_param(i64 (...)* @foo) + ret void +} + +; Check if a sret parameter works in a no-prototype function. +; CHECK-LABEL: @sret_param +; CHECK: call void @make_struct_foo(ptr sret(%struct.foo) %foo) +%struct.foo = type { i32, i32 } +declare void @make_struct_foo(%struct.foo* sret(%struct.foo), ...) #1 +define void @sret_param() { + %foo = alloca %struct.foo, align 4 + call void bitcast (void (%struct.foo*, ...)* @make_struct_foo to void (%struct.foo*)*)(%struct.foo* sret(%struct.foo) %foo) + ret void +} + +declare void @func_param(i64 (...)*) + +; CHECK: declare void @func_not_called() +declare void @func_not_called(...) #1 + +; CHECK: declare extern_weak i64 @foo(i32) +declare extern_weak i64 @foo(...) #1 + +; CHECK-NOT: attributes {{.*}} = { {{.*}}"no-prototype"{{.*}} } +attributes #1 = { "no-prototype" }