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("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          "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("Functions with 'no-prototype' attribute should "
85                           "not have params: " +
86                           F.getName());
87    }
88
89    // Create a function prototype based on the first call site (first bitcast)
90    // that we find.
91    FunctionType *NewType = nullptr;
92    for (Use &U : F.uses()) {
93      LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n");
94      LLVM_DEBUG(dbgs() << *U.getUser() << "\n");
95      if (auto *BC = dyn_cast<BitCastOperator>(U.getUser())) {
96        if (auto *DestType = dyn_cast<FunctionType>(
97                BC->getDestTy()->getPointerElementType())) {
98          if (!NewType) {
99            // Create a new function with the correct type
100            NewType = DestType;
101            LLVM_DEBUG(dbgs() << "found function type: " << *NewType << "\n");
102          } else if (NewType != DestType) {
103            errs() << "warning: prototype-less function used with "
104                      "conflicting signatures: "
105                   << F.getName() << "\n";
106            LLVM_DEBUG(dbgs() << "  " << *DestType << "\n");
107            LLVM_DEBUG(dbgs() << "  "<<  *NewType << "\n");
108          }
109        }
110      }
111    }
112
113    if (!NewType) {
114      LLVM_DEBUG(
115          dbgs() << "could not derive a function prototype from usage: " +
116                        F.getName() + "\n");
117      // We could not derive a type for this function.  In this case strip
118      // the isVarArg and make it a simple zero-arg function.  This has more
119      // chance of being correct.  The current signature of (...) is illegal in
120      // C since it doesn't have any arguments before the "...", we this at
121      // least makes it possible for this symbol to be resolved by the linker.
122      NewType = FunctionType::get(F.getFunctionType()->getReturnType(), false);
123    }
124
125    Function *NewF =
126        Function::Create(NewType, F.getLinkage(), F.getName() + ".fixed_sig");
127    NewF->setAttributes(F.getAttributes());
128    NewF->removeFnAttr("no-prototype");
129    Replacements.emplace_back(&F, NewF);
130  }
131
132  for (auto &Pair : Replacements) {
133    Function *OldF = Pair.first;
134    Function *NewF = Pair.second;
135    std::string Name = OldF->getName();
136    M.getFunctionList().push_back(NewF);
137    OldF->replaceAllUsesWith(
138        ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF, OldF->getType()));
139    OldF->eraseFromParent();
140    NewF->setName(Name);
141  }
142
143  return !Replacements.empty();
144}
145