diff options
-rw-r--r-- | include/llvm/InitializePasses.h | 1 | ||||
-rw-r--r-- | include/llvm/Transforms/NaCl.h | 1 | ||||
-rw-r--r-- | lib/Analysis/NaCl/PNaClABIVerifyModule.cpp | 11 | ||||
-rw-r--r-- | lib/Transforms/NaCl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Transforms/NaCl/CanonicalizeMemIntrinsics.cpp | 95 | ||||
-rw-r--r-- | lib/Transforms/NaCl/PNaClABISimplify.cpp | 5 | ||||
-rw-r--r-- | test/NaCl/PNaClABI/intrinsics.ll | 21 | ||||
-rw-r--r-- | test/Transforms/NaCl/canonicalize-mem-intrinsics.ll | 45 | ||||
-rw-r--r-- | tools/opt/opt.cpp | 1 |
9 files changed, 175 insertions, 6 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 677c6253c4..73622fa7c0 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -278,6 +278,7 @@ void initializeBBVectorizePass(PassRegistry&); void initializeMachineFunctionPrinterPassPass(PassRegistry&); // @LOCALMOD-BEGIN void initializeAddPNaClExternalDeclsPass(PassRegistry&); +void initializeCanonicalizeMemIntrinsicsPass(PassRegistry&); void initializeExpandArithWithOverflowPass(PassRegistry&); void initializeExpandByValPass(PassRegistry&); void initializeExpandConstantExprPass(PassRegistry&); diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h index 2e2a7bc69d..43adb237dd 100644 --- a/include/llvm/Transforms/NaCl.h +++ b/include/llvm/Transforms/NaCl.h @@ -23,6 +23,7 @@ class Use; class Value; ModulePass *createAddPNaClExternalDeclsPass(); +ModulePass *createCanonicalizeMemIntrinsicsPass(); ModulePass *createExpandArithWithOverflowPass(); ModulePass *createExpandByValPass(); FunctionPass *createExpandConstantExprPass(); diff --git a/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp index 7922d84a51..aece2f94a7 100644 --- a/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp +++ b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp @@ -198,9 +198,6 @@ bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, case Intrinsic::ctlz: case Intrinsic::cttz: return isWhitelistedCountBits(F, 2); case Intrinsic::ctpop: return isWhitelistedCountBits(F, 1); - case Intrinsic::memcpy: - case Intrinsic::memmove: - case Intrinsic::memset: case Intrinsic::nacl_read_tp: case Intrinsic::nacl_setjmp: case Intrinsic::nacl_longjmp: @@ -209,6 +206,14 @@ bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, case Intrinsic::stacksave: case Intrinsic::trap: return true; + case Intrinsic::memcpy: + case Intrinsic::memmove: + case Intrinsic::memset: + // Disallow the variant with the 64-bit "size" argument. + // TODO(mseaborn): Check all arguments' types and check the + // intrinsics' full names too. + // See https://code.google.com/p/nativeclient/issues/detail?id=3530 + return F->getFunctionType()->getParamType(2)->isIntegerTy(32); // (2) Known to be never allowed. case Intrinsic::not_intrinsic: diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt index 49c5153bf3..9cf164926b 100644 --- a/lib/Transforms/NaCl/CMakeLists.txt +++ b/lib/Transforms/NaCl/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_library(LLVMNaClTransforms AddPNaClExternalDecls.cpp + CanonicalizeMemIntrinsics.cpp ExpandArithWithOverflow.cpp ExpandByVal.cpp ExpandConstantExpr.cpp diff --git a/lib/Transforms/NaCl/CanonicalizeMemIntrinsics.cpp b/lib/Transforms/NaCl/CanonicalizeMemIntrinsics.cpp new file mode 100644 index 0000000000..fd44c65434 --- /dev/null +++ b/lib/Transforms/NaCl/CanonicalizeMemIntrinsics.cpp @@ -0,0 +1,95 @@ +//===- CanonicalizeMemIntrinsics.cpp - Make memcpy's "len" arg consistent--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass canonicalizes uses of the llvm.memset, llvm.memcpy and +// llvm.memmove intrinsics so that the variants with 64-bit "len" +// arguments aren't used, and the 32-bit variants are used instead. +// +// This means the PNaCl translator won't need to handle two versions +// of each of these intrinsics, and it won't need to do any implicit +// truncations from 64-bit to 32-bit. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/NaCl.h" + +using namespace llvm; + +namespace { + // This is a ModulePass because that makes it easier to find all + // uses of intrinsics efficiently. + class CanonicalizeMemIntrinsics : public ModulePass { + public: + static char ID; // Pass identification, replacement for typeid + CanonicalizeMemIntrinsics() : ModulePass(ID) { + initializeCanonicalizeMemIntrinsicsPass(*PassRegistry::getPassRegistry()); + } + + virtual bool runOnModule(Module &M); + }; +} + +char CanonicalizeMemIntrinsics::ID = 0; +INITIALIZE_PASS(CanonicalizeMemIntrinsics, "canonicalize-mem-intrinsics", + "Make memcpy() et al's \"len\" argument consistent", + false, false) + +static bool expandIntrinsic(Module *M, Intrinsic::ID ID) { + SmallVector<Type *, 3> Types; + Types.push_back(Type::getInt8PtrTy(M->getContext())); + if (ID != Intrinsic::memset) + Types.push_back(Type::getInt8PtrTy(M->getContext())); + unsigned LengthTypePos = Types.size(); + Types.push_back(Type::getInt64Ty(M->getContext())); + + std::string OldName = Intrinsic::getName(ID, Types); + Function *OldIntrinsic = M->getFunction(OldName); + if (!OldIntrinsic) + return false; + + Types[LengthTypePos] = Type::getInt32Ty(M->getContext()); + Function *NewIntrinsic = Intrinsic::getDeclaration(M, ID, Types); + + for (Value::use_iterator CallIter = OldIntrinsic->use_begin(), + E = OldIntrinsic->use_end(); CallIter != E; ) { + CallInst *Call = dyn_cast<CallInst>(*CallIter++); + if (!Call) { + report_fatal_error("CanonicalizeMemIntrinsics: Taking the address of an " + "intrinsic is not allowed: " + OldName); + } + // This temporarily leaves Call non-well-typed. + Call->setCalledFunction(NewIntrinsic); + // Truncate the "len" argument. No overflow check. + IRBuilder<> Builder(Call); + Value *Length = Builder.CreateTrunc(Call->getArgOperand(2), + Type::getInt32Ty(M->getContext()), + "mem_len_truncate"); + Call->setArgOperand(2, Length); + } + OldIntrinsic->eraseFromParent(); + return true; +} + +bool CanonicalizeMemIntrinsics::runOnModule(Module &M) { + bool Changed = false; + Changed |= expandIntrinsic(&M, Intrinsic::memset); + Changed |= expandIntrinsic(&M, Intrinsic::memcpy); + Changed |= expandIntrinsic(&M, Intrinsic::memmove); + return Changed; +} + +ModulePass *llvm::createCanonicalizeMemIntrinsicsPass() { + return new CanonicalizeMemIntrinsics(); +} diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp index 056b0ae651..81fce8ce13 100644 --- a/lib/Transforms/NaCl/PNaClABISimplify.cpp +++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp @@ -67,6 +67,11 @@ void llvm::PNaClABISimplifyAddPostOptPasses(PassManager &PM) { PM.add(createPromoteI1OpsPass()); + // Optimization passes and ExpandByVal introduce + // memset/memcpy/memmove intrinsics with a 64-bit size argument. + // This pass converts those arguments to 32-bit. + PM.add(createCanonicalizeMemIntrinsicsPass()); + // We place StripMetadata after optimization passes because // optimizations depend on the metadata. PM.add(createStripMetadataPass()); diff --git a/test/NaCl/PNaClABI/intrinsics.ll b/test/NaCl/PNaClABI/intrinsics.ll index 66113061ab..57edd4031f 100644 --- a/test/NaCl/PNaClABI/intrinsics.ll +++ b/test/NaCl/PNaClABI/intrinsics.ll @@ -19,9 +19,13 @@ declare void @llvm.dbg.value(metadata, i64, metadata) ; CHECK-NOT: Function llvm.memcpy.p0i8.p0i8.i32 is a disallowed LLVM intrinsic declare void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 %align, i1 %isvolatile) -; CHECK-NOT: Function llvm.memcpy.p0i8.p0i8.i64 is a disallowed LLVM intrinsic -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, - i64 %len, i32 %align, i1 %isvolatile) +; CHECK-NOT: Function llvm.memmove.p0i8.p0i8.i32 is a disallowed LLVM intrinsic +declare void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, + i32 %len, i32 %align, i1 %isvolatile) +; CHECK-NOT: Function llvm.memset.p0i8.i32 is a disallowed LLVM intrinsic +declare void @llvm.memset.p0i8.i32(i8* %dest, i8 %val, + i32 %len, i32 %align, i1 %isvolatile) + ; CHECK-NOT: Function llvm.nacl.read.tp is a disallowed LLVM intrinsic declare i8* @llvm.nacl.read.tp() @@ -104,3 +108,14 @@ declare i8* @llvm.frameaddress(i32 %level) ; CHECK: Function llvm.returnaddress is a disallowed LLVM intrinsic declare i8* @llvm.returnaddress(i32 %level) + +; The variants with 64-bit %len arguments are disallowed. +; CHECK: Function llvm.memcpy.p0i8.p0i8.i64 is a disallowed LLVM intrinsic +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, + i64 %len, i32 %align, i1 %isvolatile) +; CHECK: Function llvm.memmove.p0i8.p0i8.i64 is a disallowed LLVM intrinsic +declare void @llvm.memmove.p0i8.p0i8.i64(i8* %dest, i8* %src, + i64 %len, i32 %align, i1 %isvolatile) +; CHECK: Function llvm.memset.p0i8.i64 is a disallowed LLVM intrinsic +declare void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, + i64 %len, i32 %align, i1 %isvolatile) diff --git a/test/Transforms/NaCl/canonicalize-mem-intrinsics.ll b/test/Transforms/NaCl/canonicalize-mem-intrinsics.ll new file mode 100644 index 0000000000..9c263fd15e --- /dev/null +++ b/test/Transforms/NaCl/canonicalize-mem-intrinsics.ll @@ -0,0 +1,45 @@ +; RUN: opt %s -canonicalize-mem-intrinsics -S | FileCheck %s +; RUN: opt %s -canonicalize-mem-intrinsics -S \ +; RUN: | FileCheck %s -check-prefix=CLEANED + +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i32, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i32, i1) +declare void @llvm.memmove.p0i8.p0i8.i64(i8*, i8*, i64, i32, i1) +; CLEANED-NOT: @llvm.mem{{.*}}i64 + + +define void @memset_caller(i8* %dest, i8 %char, i64 %size) { + call void @llvm.memset.p0i8.i64(i8* %dest, i8 %char, i64 %size, i32 1, i1 0) + ret void +} +; CHECK: define void @memset_caller +; CHECK-NEXT: %mem_len_truncate = trunc i64 %size to i32 +; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %dest, i8 %char, i32 %mem_len_truncate, i32 1, i1 false) + + +define void @memcpy_caller(i8* %dest, i8* %src, i64 %size) { + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %size, i32 1, i1 0) + ret void +} +; CHECK: define void @memcpy_caller +; CHECK-NEXT: %mem_len_truncate = trunc i64 %size to i32 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %mem_len_truncate, i32 1, i1 false) + + +define void @memmove_caller(i8* %dest, i8* %src, i64 %size) { + call void @llvm.memmove.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %size, i32 1, i1 0) + ret void +} +; CHECK: define void @memmove_caller +; CHECK-NEXT: %mem_len_truncate = trunc i64 %size to i32 +; CHECK-NEXT: call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %mem_len_truncate, i32 1, i1 false) + + +; Check that constant sizes remain as constants. + +define void @memset_caller_const(i8* %dest, i8 %char) { + call void @llvm.memset.p0i8.i64(i8* %dest, i8 %char, i64 123, i32 1, i1 0) + ret void +} +; CHECK: define void @memset_caller +; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %dest, i8 %char, i32 123, i32 1, i1 false) diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 56ac3ec9a7..c5405de902 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -605,6 +605,7 @@ int main(int argc, char **argv) { initializeTarget(Registry); // @LOCALMOD-BEGIN initializeAddPNaClExternalDeclsPass(Registry); + initializeCanonicalizeMemIntrinsicsPass(Registry); initializeExpandArithWithOverflowPass(Registry); initializeExpandByValPass(Registry); initializeExpandConstantExprPass(Registry); |