diff options
| author | Sam Clegg <sbc@chromium.org> | 2018-07-11 04:29:36 +0000 | 
|---|---|---|
| committer | Sam Clegg <sbc@chromium.org> | 2018-07-11 04:29:36 +0000 | 
| commit | 92617559bbb432e45efb462658020529c1ac9237 (patch) | |
| tree | 9396043b7c9da1e1fc1df8b78e2329a4c2163df1 /llvm/lib/Target | |
| parent | fcd1b66ae10520b2189df03405f7bfac687b3e70 (diff) | |
| download | bcm5719-llvm-92617559bbb432e45efb462658020529c1ac9237.tar.gz bcm5719-llvm-92617559bbb432e45efb462658020529c1ac9237.zip  | |
[WebAssembly] Add pass to infer prototypes for prototype-less functions
See https://bugs.llvm.org/show_bug.cgi?id=35385
Differential Revision: https://reviews.llvm.org/D48471
llvm-svn: 336759
Diffstat (limited to 'llvm/lib/Target')
4 files changed, 149 insertions, 0 deletions
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index 2fdb7f9d05f..a928f110efe 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -13,6 +13,7 @@ tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)  add_public_tablegen_target(WebAssemblyCommonTableGen)  add_llvm_target(WebAssemblyCodeGen +  WebAssemblyAddMissingPrototypes.cpp    WebAssemblyArgumentMove.cpp    WebAssemblyAsmPrinter.cpp    WebAssemblyCallIndirectFixup.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index 0f045088d3c..05b7b21fb59 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -28,6 +28,7 @@ class FunctionPass;  // LLVM IR passes.  ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);  ModulePass *createWebAssemblyLowerGlobalDtors(); +ModulePass *createWebAssemblyAddMissingPrototypes();  ModulePass *createWebAssemblyFixFunctionBitcasts();  FunctionPass *createWebAssemblyOptimizeReturned(); @@ -55,6 +56,7 @@ FunctionPass *createWebAssemblyPeephole();  FunctionPass *createWebAssemblyCallIndirectFixup();  // PassRegistry initialization declarations. +void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &);  void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);  void initializeLowerGlobalDtorsPass(PassRegistry &);  void initializeFixFunctionBitcastsPass(PassRegistry &); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp new file mode 100644 index 00000000000..91b0b4aac2b --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp @@ -0,0 +1,142 @@ +//===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \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/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.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() << "runnning AddMissingPrototypes\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()); +    if (F.getFunctionType()->getNumParams() != 0) +      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; +    Function* NewF = nullptr; +    for (Use &U : F.uses()) { +      LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n"); +      if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) { +        FunctionType *DestType = +            cast<FunctionType>(BC->getDestTy()->getPointerElementType()); + +        // Create a new function with the correct type +        NewType = DestType; +        NewF = Function::Create(NewType, F.getLinkage(), F.getName()); +        NewF->setAttributes(F.getAttributes()); +        NewF->removeFnAttr("no-prototype"); +        break; +      } +    } + +    if (!NewType) { +      LLVM_DEBUG( +          dbgs() << "could not derive a function prototype from usage: " + +                        F.getName() + "\n"); +      continue; +    } + +    for (Use &U : F.uses()) { +      if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) { +        FunctionType *DestType = +            cast<FunctionType>(BC->getDestTy()->getPointerElementType()); +        if (NewType != DestType) { +          report_fatal_error( +              "Prototypeless function used with conflicting signatures: " + +              F.getName()); +        } +        BC->replaceAllUsesWith(NewF); +        Replacements.emplace_back(&F, NewF); +      } else { +        dbgs() << *U.getUser()->getType() << "\n"; +        U.getUser()->dump(); +        report_fatal_error( +            "unexpected use of prototypeless function: " + F.getName() + "\n"); +      } +    } +  } + +  // Finally replace the old function declarations with the new ones +  for (auto &Pair : Replacements) { +    Function* Old = Pair.first; +    Function* New = Pair.second; +    Old->eraseFromParent(); +    M.getFunctionList().push_back(New); +  } + +  return !Replacements.empty(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 02810d71b70..9a5f459e9dd 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -51,6 +51,7 @@ extern "C" void LLVMInitializeWebAssemblyTarget() {    // Register backend passes    auto &PR = *PassRegistry::getPassRegistry(); +  initializeWebAssemblyAddMissingPrototypesPass(PR);    initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);    initializeLowerGlobalDtorsPass(PR);    initializeFixFunctionBitcastsPass(PR); @@ -214,6 +215,9 @@ void WebAssemblyPassConfig::addIRPasses() {      addPass(createAtomicExpandPass());    } +  // Add signatures to prototype-less function declarations +  addPass(createWebAssemblyAddMissingPrototypes()); +    // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.    addPass(createWebAssemblyLowerGlobalDtors());  | 

