1//===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// Add prototypes to prototypes-less functions.
11///
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.
18///
19//===----------------------------------------------------------------------===//
20
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"
30using namespace llvm;
31
32#define DEBUG_TYPE "wasm-add-missing-prototypes"
33
34namespace {
35class WebAssemblyAddMissingPrototypes final : public ModulePass {
36 StringRef getPassName() const override {
37 return "Add prototypes to prototypes-less functions";
38 }
39
40 void getAnalysisUsage(AnalysisUsage &AU) const override {
41 AU.setPreservesCFG();
42 ModulePass::getAnalysisUsage(AU);
43 }
44
45 bool runOnModule(Module &M) override;
46
47public:
48 static char ID;
49 WebAssemblyAddMissingPrototypes() : ModulePass(ID) {}
50};
51} // End anonymous namespace
52
53char WebAssemblyAddMissingPrototypes::ID = 0;
54INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE,
55 "Add prototypes to prototypes-less functions", false, false)
56
57ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
58 return new WebAssemblyAddMissingPrototypes();
59}
60
61bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
62 LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n");
63
64 std::vector<std::pair<Function *, Function *>> Replacements;
65
66 // Find all the prototype-less function declarations
67 for (Function &F : M) {
68 if (!F.isDeclaration() || !F.hasFnAttribute(Kind: "no-prototype"))
69 continue;
70
71 LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName()
72 << "\n");
73
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.
77 if (!F.isVarArg())
78 report_fatal_error(
79 reason: "Functions with 'no-prototype' attribute must take varargs: " +
80 F.getName());
81 unsigned NumParams = F.getFunctionType()->getNumParams();
82 if (NumParams != 0) {
83 if (!(NumParams == 1 && F.arg_begin()->hasStructRetAttr()))
84 report_fatal_error(reason: "Functions with 'no-prototype' attribute should "
85 "not have params: " +
86 F.getName());
87 }
88
89 // Find calls of this function, looking through bitcasts.
90 SmallVector<CallBase *> Calls;
91 SmallVector<Value *> Worklist;
92 Worklist.push_back(Elt: &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>(Val: U))
97 Worklist.push_back(Elt: BC);
98 else if (auto *CB = dyn_cast<CallBase>(Val: U))
99 if (CB->getCalledOperand() == V)
100 Calls.push_back(Elt: CB);
101 }
102 }
103
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();
110 if (!NewType) {
111 // Create a new function with the correct type
112 NewType = DestType;
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");
120 }
121 }
122
123 if (!NewType) {
124 LLVM_DEBUG(
125 dbgs() << "could not derive a function prototype from usage: " +
126 F.getName() + "\n");
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(Result: F.getFunctionType()->getReturnType(), isVarArg: false);
133 }
134
135 Function *NewF =
136 Function::Create(Ty: NewType, Linkage: F.getLinkage(), N: F.getName() + ".fixed_sig");
137 NewF->setAttributes(F.getAttributes());
138 NewF->removeFnAttr(Kind: "no-prototype");
139 NewF->IsNewDbgInfoFormat = F.IsNewDbgInfoFormat;
140 Replacements.emplace_back(args: &F, args&: NewF);
141 }
142
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(val: NewF);
148 OldF->replaceAllUsesWith(
149 V: ConstantExpr::getPointerBitCastOrAddrSpaceCast(C: NewF, Ty: OldF->getType()));
150 OldF->eraseFromParent();
151 NewF->setName(Name);
152 }
153
154 return !Replacements.empty();
155}
156