diff options
author | JF Bastien <jfb@chromium.org> | 2013-07-30 16:38:26 -0700 |
---|---|---|
committer | JF Bastien <jfb@chromium.org> | 2013-07-30 16:38:26 -0700 |
commit | f75fd0a9f95109b9cb13a74aad6dcc98c3d5d625 (patch) | |
tree | 652c465d76008ef58f5c8d55827050a6f1d875e1 /lib | |
parent | 423b3bb89c78e96c59843aa7c6e55d01bde174d1 (diff) |
Rewrite ``asm("":::"memory")`` to ``fence seq_cst``
This is often used as a compiler barrier and should "just work" in user code.
BUG= https://code.google.com/p/nativeclient/issues/detail?id=2345
R=eliben@chromium.org
TEST= (cd ./pnacl/build/llvm_x86_64 && ninja check-all)
Review URL: https://codereview.chromium.org/21178002
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Transforms/NaCl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Transforms/NaCl/PNaClABISimplify.cpp | 4 | ||||
-rw-r--r-- | lib/Transforms/NaCl/RewriteAsmDirectives.cpp | 111 |
3 files changed, 115 insertions, 1 deletions
diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt index e91d79f184..909cd8c68d 100644 --- a/lib/Transforms/NaCl/CMakeLists.txt +++ b/lib/Transforms/NaCl/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_library(LLVMNaClTransforms PromoteIntegers.cpp ReplacePtrsWithInts.cpp ResolvePNaClIntrinsics.cpp + RewriteAsmDirectives.cpp RewriteAtomics.cpp RewriteLLVMIntrinsics.cpp RewritePNaClLibraryCalls.cpp diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp index 189d87f2cb..4f8e0e4f87 100644 --- a/lib/Transforms/NaCl/PNaClABISimplify.cpp +++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp @@ -30,8 +30,10 @@ void llvm::PNaClABISimplifyAddPreOptPasses(PassManager &PM) { // LowerExpect converts Intrinsic::expect into branch weights, // which can then be removed after BlockPlacement. PM.add(createLowerExpectIntrinsicPass()); - // Rewrite unsupported intrinsics to simpler constructs. + // Rewrite unsupported intrinsics and inline assembly directives to + // simpler and portable constructs. PM.add(createRewriteLLVMIntrinsicsPass()); + PM.add(createRewriteAsmDirectivesPass()); // LowerInvoke prevents use of C++ exception handling, which is not // yet supported in the PNaCl ABI. PM.add(createLowerInvokePass()); diff --git a/lib/Transforms/NaCl/RewriteAsmDirectives.cpp b/lib/Transforms/NaCl/RewriteAsmDirectives.cpp new file mode 100644 index 0000000000..cb726d2365 --- /dev/null +++ b/lib/Transforms/NaCl/RewriteAsmDirectives.cpp @@ -0,0 +1,111 @@ +//===- RewriteAsmDirectives.cpp - Handle Architecture-Independent Assembly-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass rewrites any inline assembly directive which is portable +// into LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Twine.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/InstVisitor.h" +#include "llvm/Pass.h" +#include <string> + +using namespace llvm; + +namespace { +class RewriteAsmDirectives : public FunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + RewriteAsmDirectives() : FunctionPass(ID) { + initializeRewriteAsmDirectivesPass(*PassRegistry::getPassRegistry()); + } + + virtual bool runOnFunction(Function &F); +}; + +class AsmDirectivesVisitor : public InstVisitor<AsmDirectivesVisitor> { +public: + AsmDirectivesVisitor(Function &F) + : F(F), C(F.getParent()->getContext()), ModifiedFunction(false) {} + ~AsmDirectivesVisitor() {} + bool modifiedFunction() const { return ModifiedFunction; } + + /// Only Call Instructions are ever inline assembly directives. + void visitCallInst(CallInst &CI); + +private: + Function &F; + LLVMContext &C; + bool ModifiedFunction; + + AsmDirectivesVisitor() LLVM_DELETED_FUNCTION; + AsmDirectivesVisitor(const AsmDirectivesVisitor &) LLVM_DELETED_FUNCTION; + AsmDirectivesVisitor &operator=(const AsmDirectivesVisitor &) LLVM_DELETED_FUNCTION; +}; +} + +char RewriteAsmDirectives::ID = 0; +INITIALIZE_PASS( + RewriteAsmDirectives, "rewrite-asm-directives", + "rewrite portable inline assembly directives into non-asm LLVM IR", + false, false) + +bool RewriteAsmDirectives::runOnFunction(Function &F) { + AsmDirectivesVisitor AV(F); + AV.visit(F); + return AV.modifiedFunction(); +} + +void AsmDirectivesVisitor::visitCallInst(CallInst &CI) { + if (!CI.isInlineAsm()) + return; + + Instruction *Replacement = NULL; + + InlineAsm *IA = cast<InlineAsm>(CI.getCalledValue()); + std::string AsmStr(IA->getAsmString()); + std::string ConstraintStr(IA->getConstraintString()); + Type *T = CI.getType(); + + bool isEmptyAsm = AsmStr.empty(); + // Different triples will encode "touch everything" differently, e.g.: + // - le32-unknown-nacl has "~{memory}". + // - x86 "~{memory},~{dirflag},~{fpsr},~{flags}". + // The following code therefore only searches for memory: this pass + // deals with portable assembly, touching anything else than memory in + // an empty assembly statement is meaningless. + bool touchesMemory = ConstraintStr.find("~{memory}") != std::string::npos; + + if (T->isVoidTy() && IA->hasSideEffects() && isEmptyAsm && touchesMemory) { + // asm("":::"memory") => fence seq_cst + // This transformation is safe and strictly stronger: the former is + // purely a compiler fence, whereas the latter is a compiler fence + // as well as a hardware fence which orders all loads and stores on + // the current thread of execution. + Replacement = new FenceInst(C, SequentiallyConsistent, CrossThread, &CI); + } + + if (Replacement) { + Replacement->setDebugLoc(CI.getDebugLoc()); + CI.replaceAllUsesWith(Replacement); + CI.eraseFromParent(); + ModifiedFunction = true; + } +} + +namespace llvm { +FunctionPass *createRewriteAsmDirectivesPass() { + return new RewriteAsmDirectives(); +} +} |