Home | History | Annotate | Line # | Download | only in WebAssembly
      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 {
     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 
     47 public:
     48   static char ID;
     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 
     57 ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
     58   return new WebAssemblyAddMissingPrototypes();
     59 }
     60 
     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     // 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 = std::string(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