1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
| //===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Add prototypes to prototypes-less functions.
///
/// WebAssembly has strict function prototype checking so we need functions
/// declarations to match the call sites. Clang treats prototype-less functions
/// as varargs (foo(...)) which happens to work on existing platforms but
/// doesn't under WebAssembly. This pass will find all the call sites of each
/// prototype-less function, ensure they agree, and then set the signature
/// on the function declaration accordingly.
///
//===----------------------------------------------------------------------===//
#include "WebAssembly.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-add-missing-prototypes"
namespace {
class WebAssemblyAddMissingPrototypes final : public ModulePass {
StringRef getPassName() const override {
return "Add prototypes to prototypes-less functions";
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
ModulePass::getAnalysisUsage(AU);
}
bool runOnModule(Module &M) override;
public:
static char ID;
WebAssemblyAddMissingPrototypes() : ModulePass(ID) {}
};
} // End anonymous namespace
char WebAssemblyAddMissingPrototypes::ID = 0;
INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE,
"Add prototypes to prototypes-less functions", false, false)
ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
return new WebAssemblyAddMissingPrototypes();
}
bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n");
std::vector<std::pair<Function *, Function *>> Replacements;
// Find all the prototype-less function declarations
for (Function &F : M) {
if (!F.isDeclaration() || !F.hasFnAttribute("no-prototype"))
continue;
LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName()
<< "\n");
// When clang emits prototype-less C functions it uses (...), i.e. varargs
// function that take no arguments (have no sentinel). When we see a
// no-prototype attribute we expect the function have these properties.
if (!F.isVarArg())
report_fatal_error(
"Functions with 'no-prototype' attribute must take varargs: " +
F.getName());
unsigned NumParams = F.getFunctionType()->getNumParams();
if (NumParams != 0) {
if (!(NumParams == 1 && F.arg_begin()->hasStructRetAttr()))
report_fatal_error("Functions with 'no-prototype' attribute should "
"not have params: " +
F.getName());
}
// Create a function prototype based on the first call site (first bitcast)
// 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<BitCastOperator>(U.getUser())) {
if (auto *DestType = dyn_cast<FunctionType>(
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");
}
}
}
}
if (!NewType) {
LLVM_DEBUG(
dbgs() << "could not derive a function prototype from usage: " +
F.getName() + "\n");
// We could not derive a type for this function. In this case strip
// the isVarArg and make it a simple zero-arg function. This has more
// chance of being correct. The current signature of (...) is illegal in
// C since it doesn't have any arguments before the "...", we this at
// least makes it possible for this symbol to be resolved by the linker.
NewType = FunctionType::get(F.getFunctionType()->getReturnType(), false);
}
Function *NewF =
Function::Create(NewType, F.getLinkage(), F.getName() + ".fixed_sig");
NewF->setAttributes(F.getAttributes());
NewF->removeFnAttr("no-prototype");
Replacements.emplace_back(&F, NewF);
}
for (auto &Pair : Replacements) {
Function *OldF = Pair.first;
Function *NewF = Pair.second;
std::string Name = OldF->getName();
M.getFunctionList().push_back(NewF);
OldF->replaceAllUsesWith(
ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF, OldF->getType()));
OldF->eraseFromParent();
NewF->setName(Name);
}
return !Replacements.empty();
}
|