forked from OSchip/llvm-project
[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
This commit is contained in:
parent
fcd1b66ae1
commit
92617559bb
|
@ -13,6 +13,7 @@ tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
|
||||||
add_public_tablegen_target(WebAssemblyCommonTableGen)
|
add_public_tablegen_target(WebAssemblyCommonTableGen)
|
||||||
|
|
||||||
add_llvm_target(WebAssemblyCodeGen
|
add_llvm_target(WebAssemblyCodeGen
|
||||||
|
WebAssemblyAddMissingPrototypes.cpp
|
||||||
WebAssemblyArgumentMove.cpp
|
WebAssemblyArgumentMove.cpp
|
||||||
WebAssemblyAsmPrinter.cpp
|
WebAssemblyAsmPrinter.cpp
|
||||||
WebAssemblyCallIndirectFixup.cpp
|
WebAssemblyCallIndirectFixup.cpp
|
||||||
|
|
|
@ -28,6 +28,7 @@ class FunctionPass;
|
||||||
// LLVM IR passes.
|
// LLVM IR passes.
|
||||||
ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);
|
ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);
|
||||||
ModulePass *createWebAssemblyLowerGlobalDtors();
|
ModulePass *createWebAssemblyLowerGlobalDtors();
|
||||||
|
ModulePass *createWebAssemblyAddMissingPrototypes();
|
||||||
ModulePass *createWebAssemblyFixFunctionBitcasts();
|
ModulePass *createWebAssemblyFixFunctionBitcasts();
|
||||||
FunctionPass *createWebAssemblyOptimizeReturned();
|
FunctionPass *createWebAssemblyOptimizeReturned();
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ FunctionPass *createWebAssemblyPeephole();
|
||||||
FunctionPass *createWebAssemblyCallIndirectFixup();
|
FunctionPass *createWebAssemblyCallIndirectFixup();
|
||||||
|
|
||||||
// PassRegistry initialization declarations.
|
// PassRegistry initialization declarations.
|
||||||
|
void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &);
|
||||||
void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
|
void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
|
||||||
void initializeLowerGlobalDtorsPass(PassRegistry &);
|
void initializeLowerGlobalDtorsPass(PassRegistry &);
|
||||||
void initializeFixFunctionBitcastsPass(PassRegistry &);
|
void initializeFixFunctionBitcastsPass(PassRegistry &);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -51,6 +51,7 @@ extern "C" void LLVMInitializeWebAssemblyTarget() {
|
||||||
|
|
||||||
// Register backend passes
|
// Register backend passes
|
||||||
auto &PR = *PassRegistry::getPassRegistry();
|
auto &PR = *PassRegistry::getPassRegistry();
|
||||||
|
initializeWebAssemblyAddMissingPrototypesPass(PR);
|
||||||
initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
|
initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
|
||||||
initializeLowerGlobalDtorsPass(PR);
|
initializeLowerGlobalDtorsPass(PR);
|
||||||
initializeFixFunctionBitcastsPass(PR);
|
initializeFixFunctionBitcastsPass(PR);
|
||||||
|
@ -214,6 +215,9 @@ void WebAssemblyPassConfig::addIRPasses() {
|
||||||
addPass(createAtomicExpandPass());
|
addPass(createAtomicExpandPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add signatures to prototype-less function declarations
|
||||||
|
addPass(createWebAssemblyAddMissingPrototypes());
|
||||||
|
|
||||||
// Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.
|
// Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.
|
||||||
addPass(createWebAssemblyLowerGlobalDtors());
|
addPass(createWebAssemblyLowerGlobalDtors());
|
||||||
|
|
||||||
|
|
|
@ -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"{{.*}} }
|
Loading…
Reference in New Issue