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/WebAssembly | |
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/WebAssembly')
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()); |