1 //===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// Add prototypes to prototypes-less functions.
12 /// WebAssembly has strict function prototype checking so we need functions
13 /// declarations to match the call sites. Clang treats prototype-less functions
14 /// as varargs (foo(...)) which happens to work on existing platforms but
15 /// doesn't under WebAssembly. This pass will find all the call sites of each
16 /// prototype-less function, ensure they agree, and then set the signature
17 /// on the function declaration accordingly.
19 //===----------------------------------------------------------------------===//
21 #include "WebAssembly.h"
22 #include "llvm/IR/Constants.h"
23 #include "llvm/IR/IRBuilder.h"
24 #include "llvm/IR/Module.h"
25 #include "llvm/IR/Operator.h"
26 #include "llvm/Pass.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Transforms/Utils/Local.h"
29 #include "llvm/Transforms/Utils/ModuleUtils.h"
32 #define DEBUG_TYPE "wasm-add-missing-prototypes"
35 class WebAssemblyAddMissingPrototypes final
: public ModulePass
{
36 StringRef
getPassName() const override
{
37 return "Add prototypes to prototypes-less functions";
40 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
42 ModulePass::getAnalysisUsage(AU
);
45 bool runOnModule(Module
&M
) override
;
49 WebAssemblyAddMissingPrototypes() : ModulePass(ID
) {}
51 } // End anonymous namespace
53 char WebAssemblyAddMissingPrototypes::ID
= 0;
54 INITIALIZE_PASS(WebAssemblyAddMissingPrototypes
, DEBUG_TYPE
,
55 "Add prototypes to prototypes-less functions", false, false)
57 ModulePass
*llvm::createWebAssemblyAddMissingPrototypes() {
58 return new WebAssemblyAddMissingPrototypes();
61 bool WebAssemblyAddMissingPrototypes::runOnModule(Module
&M
) {
62 LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n");
64 std::vector
<std::pair
<Function
*, Function
*>> Replacements
;
66 // Find all the prototype-less function declarations
67 for (Function
&F
: M
) {
68 if (!F
.isDeclaration() || !F
.hasFnAttribute("no-prototype"))
71 LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F
.getName()
74 // When clang emits prototype-less C functions it uses (...), i.e. varargs
75 // function that take no arguments (have no sentinel). When we see a
76 // no-prototype attribute we expect the function have these properties.
79 "Functions with 'no-prototype' attribute must take varargs: " +
81 unsigned NumParams
= F
.getFunctionType()->getNumParams();
83 if (!(NumParams
== 1 && F
.arg_begin()->hasStructRetAttr()))
84 report_fatal_error("Functions with 'no-prototype' attribute should "
89 // Find calls of this function, looking through bitcasts.
90 SmallVector
<CallBase
*> Calls
;
91 SmallVector
<Value
*> Worklist
;
92 Worklist
.push_back(&F
);
93 while (!Worklist
.empty()) {
94 Value
*V
= Worklist
.pop_back_val();
95 for (User
*U
: V
->users()) {
96 if (auto *BC
= dyn_cast
<BitCastOperator
>(U
))
97 Worklist
.push_back(BC
);
98 else if (auto *CB
= dyn_cast
<CallBase
>(U
))
99 if (CB
->getCalledOperand() == V
)
104 // Create a function prototype based on the first call site that we find.
105 FunctionType
*NewType
= nullptr;
106 for (CallBase
*CB
: Calls
) {
107 LLVM_DEBUG(dbgs() << "prototype-less call of " << F
.getName() << ":\n");
108 LLVM_DEBUG(dbgs() << *CB
<< "\n");
109 FunctionType
*DestType
= CB
->getFunctionType();
111 // Create a new function with the correct type
113 LLVM_DEBUG(dbgs() << "found function type: " << *NewType
<< "\n");
114 } else if (NewType
!= DestType
) {
115 errs() << "warning: prototype-less function used with "
116 "conflicting signatures: "
117 << F
.getName() << "\n";
118 LLVM_DEBUG(dbgs() << " " << *DestType
<< "\n");
119 LLVM_DEBUG(dbgs() << " " << *NewType
<< "\n");
125 dbgs() << "could not derive a function prototype from usage: " +
127 // We could not derive a type for this function. In this case strip
128 // the isVarArg and make it a simple zero-arg function. This has more
129 // chance of being correct. The current signature of (...) is illegal in
130 // C since it doesn't have any arguments before the "...", we this at
131 // least makes it possible for this symbol to be resolved by the linker.
132 NewType
= FunctionType::get(F
.getFunctionType()->getReturnType(), false);
136 Function::Create(NewType
, F
.getLinkage(), F
.getName() + ".fixed_sig");
137 NewF
->setAttributes(F
.getAttributes());
138 NewF
->removeFnAttr("no-prototype");
139 NewF
->IsNewDbgInfoFormat
= F
.IsNewDbgInfoFormat
;
140 Replacements
.emplace_back(&F
, NewF
);
143 for (auto &Pair
: Replacements
) {
144 Function
*OldF
= Pair
.first
;
145 Function
*NewF
= Pair
.second
;
146 std::string Name
= std::string(OldF
->getName());
147 M
.getFunctionList().push_back(NewF
);
148 OldF
->replaceAllUsesWith(
149 ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF
, OldF
->getType()));
150 OldF
->eraseFromParent();
154 return !Replacements
.empty();