]> granicus.if.org Git - llvm/commitdiff
[WebAssembly] Add pass to infer prototypes for prototype-less functions
authorSam Clegg <sbc@chromium.org>
Wed, 11 Jul 2018 04:29:36 +0000 (04:29 +0000)
committerSam Clegg <sbc@chromium.org>
Wed, 11 Jul 2018 04:29:36 +0000 (04:29 +0000)
See https://bugs.llvm.org/show_bug.cgi?id=35385

Differential Revision: https://reviews.llvm.org/D48471

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@336759 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/WebAssembly/CMakeLists.txt
lib/Target/WebAssembly/WebAssembly.h
lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp [new file with mode: 0644]
lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
test/CodeGen/WebAssembly/add-prototypes.ll [new file with mode: 0644]

index 2fdb7f9d05f8bf9db4453eb82695895f0c5b9cc7..a928f110efe0d78b861b3aa02308f167f1159172 100644 (file)
@@ -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
index 0f045088d3ce0b506e95d15396fbec81a0d19a03..05b7b21fb597b91ee0bf879673b3fb3faacbe743 100644 (file)
@@ -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/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp b/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp
new file mode 100644 (file)
index 0000000..91b0b4a
--- /dev/null
@@ -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();
+}
index 02810d71b70a8bee267658422838bd50b23d09dd..9a5f459e9dd32867cdd8b8001037b2315ff733d7 100644 (file)
@@ -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());
 
diff --git a/test/CodeGen/WebAssembly/add-prototypes.ll b/test/CodeGen/WebAssembly/add-prototypes.ll
new file mode 100644 (file)
index 0000000..2b22767
--- /dev/null
@@ -0,0 +1,20 @@
+; RUN: opt -S -wasm-add-missing-prototypes %s | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+@foo_addr = global i64 (i32)* bitcast (i64 (...)* @foo to i64 (i32)*), align 8
+
+define void @bar(i32 %a) {
+entry:
+  %call = call i64 bitcast (i64 (...)* @foo to i64 (i32)*)(i32 42)
+  ret void
+}
+
+declare i64 @foo(...) #1
+
+attributes #1 = { "no-prototype" }
+
+; CHECK: %call = call i64 @foo(i32 42)
+; CHECK: declare i64 @foo(i32)
+; CHECK-NOT: attributes {{.*}} = { {{.*}}"no-prototype"{{.*}} }