xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
30  using namespace llvm;
31  
32  #define DEBUG_TYPE "wasm-add-missing-prototypes"
33  
34  namespace {
35  class WebAssemblyAddMissingPrototypes final : public ModulePass {
getPassName() const36    StringRef getPassName() const override {
37      return "Add prototypes to prototypes-less functions";
38    }
39  
getAnalysisUsage(AnalysisUsage & AU) const40    void getAnalysisUsage(AnalysisUsage &AU) const override {
41      AU.setPreservesCFG();
42      ModulePass::getAnalysisUsage(AU);
43    }
44  
45    bool runOnModule(Module &M) override;
46  
47  public:
48    static char ID;
WebAssemblyAddMissingPrototypes()49    WebAssemblyAddMissingPrototypes() : ModulePass(ID) {}
50  };
51  } // End anonymous namespace
52  
53  char WebAssemblyAddMissingPrototypes::ID = 0;
54  INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE,
55                  "Add prototypes to prototypes-less functions", false, false)
56  
createWebAssemblyAddMissingPrototypes()57  ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
58    return new WebAssemblyAddMissingPrototypes();
59  }
60  
runOnModule(Module & M)61  bool 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      // 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)
100              Calls.push_back(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(F.getFunctionType()->getReturnType(), false);
133      }
134  
135      Function *NewF =
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);
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(NewF);
148      OldF->replaceAllUsesWith(
149          ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF, OldF->getType()));
150      OldF->eraseFromParent();
151      NewF->setName(Name);
152    }
153  
154    return !Replacements.empty();
155  }
156