aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/InitializePasses.h1
-rw-r--r--include/llvm/Transforms/NaCl.h9
-rw-r--r--lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp49
-rw-r--r--lib/Transforms/NaCl/CMakeLists.txt1
-rw-r--r--lib/Transforms/NaCl/PNaClABISimplify.cpp2
-rw-r--r--lib/Transforms/NaCl/PromoteI1Ops.cpp138
-rw-r--r--lib/Transforms/NaCl/ReplacePtrsWithInts.cpp8
-rw-r--r--test/NaCl/PNaClABI/abi-i1-operations.ll66
-rw-r--r--test/Transforms/NaCl/promote-i1-ops.ll77
-rw-r--r--tools/opt/opt.cpp1
10 files changed, 329 insertions, 23 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index 4e643c4d23..677c6253c4 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -294,6 +294,7 @@ void initializeInsertDivideCheckPass(PassRegistry&);
void initializeNaClCcRewritePass(PassRegistry&);
void initializePNaClABIVerifyModulePass(PassRegistry&);
void initializePNaClABIVerifyFunctionsPass(PassRegistry&);
+void initializePromoteI1OpsPass(PassRegistry&);
void initializePromoteIntegersPass(PassRegistry&);
void initializeReplacePtrsWithIntsPass(PassRegistry&);
void initializeResolveAliasesPass(PassRegistry&);
diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h
index 6924320cfe..2e2a7bc69d 100644
--- a/include/llvm/Transforms/NaCl.h
+++ b/include/llvm/Transforms/NaCl.h
@@ -35,6 +35,7 @@ ModulePass *createExpandTlsConstantExprPass();
ModulePass *createExpandVarArgsPass();
ModulePass *createFlattenGlobalsPass();
ModulePass *createGlobalCleanupPass();
+BasicBlockPass *createPromoteI1OpsPass();
FunctionPass *createPromoteIntegersPass();
ModulePass *createReplacePtrsWithIntsPass();
ModulePass *createResolveAliasesPass();
@@ -54,6 +55,14 @@ void PhiSafeReplaceUses(Use *U, Value *NewVal);
// Copy debug information from Original to NewInst, and return NewInst.
Instruction *CopyDebug(Instruction *NewInst, Instruction *Original);
+template <class InstType>
+static void CopyLoadOrStoreAttrs(InstType *Dest, InstType *Src) {
+ Dest->setVolatile(Src->isVolatile());
+ Dest->setAlignment(Src->getAlignment());
+ Dest->setOrdering(Src->getOrdering());
+ Dest->setSynchScope(Src->getSynchScope());
+}
+
// In order to change a function's type, the function must be
// recreated. RecreateFunction() recreates Func with type NewType.
// It copies or moves across everything except the argument values,
diff --git a/lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp b/lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp
index 9a96d19ed4..80d7da3f19 100644
--- a/lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp
+++ b/lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp
@@ -79,15 +79,21 @@ bool PNaClABIVerifyFunctions::IsWhitelistedMetadata(unsigned MDKind) {
}
// A valid pointer type is either:
-// * a pointer to a valid PNaCl scalar type, or
+// * a pointer to a valid PNaCl scalar type (except i1), or
// * a function pointer (with valid argument and return types).
+//
+// i1 is disallowed so that all loads and stores are a whole number of
+// bytes, and so that we do not need to define whether a store of i1
+// zero-extends.
static bool isValidPointerType(Type *Ty) {
if (PointerType *PtrTy = dyn_cast<PointerType>(Ty)) {
if (PtrTy->getAddressSpace() != 0)
return false;
- if (PNaClABITypeChecker::isValidScalarType(PtrTy->getElementType()))
+ Type *EltTy = PtrTy->getElementType();
+ if (PNaClABITypeChecker::isValidScalarType(EltTy) &&
+ !EltTy->isIntegerTy(1))
return true;
- if (FunctionType *FTy = dyn_cast<FunctionType>(PtrTy->getElementType()))
+ if (FunctionType *FTy = dyn_cast<FunctionType>(EltTy))
return PNaClABITypeChecker::isValidFunctionType(FTy);
}
return false;
@@ -201,22 +207,12 @@ const char *PNaClABIVerifyFunctions::checkInstruction(const Instruction *Inst) {
case Instruction::Br:
case Instruction::Unreachable:
// Binary operations
- case Instruction::Add:
case Instruction::FAdd:
- case Instruction::Sub:
case Instruction::FSub:
- case Instruction::Mul:
case Instruction::FMul:
- case Instruction::UDiv:
- case Instruction::SDiv:
case Instruction::FDiv:
- case Instruction::URem:
- case Instruction::SRem:
case Instruction::FRem:
// Bitwise binary operations
- case Instruction::Shl:
- case Instruction::LShr:
- case Instruction::AShr:
case Instruction::And:
case Instruction::Or:
case Instruction::Xor:
@@ -233,12 +229,30 @@ const char *PNaClABIVerifyFunctions::checkInstruction(const Instruction *Inst) {
case Instruction::UIToFP:
case Instruction::SIToFP:
// Other operations
- case Instruction::ICmp:
case Instruction::FCmp:
case Instruction::PHI:
case Instruction::Select:
break;
+ // The following operations are of dubious usefulness on 1-bit
+ // values. Use of the i1 type is disallowed here so that code
+ // generators do not need to support these corner cases.
+ case Instruction::ICmp:
+ // Binary operations
+ case Instruction::Add:
+ case Instruction::Sub:
+ case Instruction::Mul:
+ case Instruction::UDiv:
+ case Instruction::SDiv:
+ case Instruction::URem:
+ case Instruction::SRem:
+ case Instruction::Shl:
+ case Instruction::LShr:
+ case Instruction::AShr:
+ if (Inst->getOperand(0)->getType()->isIntegerTy(1))
+ return "arithmetic on i1";
+ break;
+
// Memory accesses.
case Instruction::Load: {
const LoadInst *Load = cast<LoadInst>(Inst);
@@ -290,8 +304,11 @@ const char *PNaClABIVerifyFunctions::checkInstruction(const Instruction *Inst) {
break;
case Instruction::Alloca: {
- if (!cast<AllocaInst>(Inst)->getAllocatedType()->isIntegerTy(8))
+ const AllocaInst *Alloca = cast<AllocaInst>(Inst);
+ if (!Alloca->getAllocatedType()->isIntegerTy(8))
return "non-i8 alloca";
+ if (!Alloca->getArraySize()->getType()->isIntegerTy(32))
+ return "alloca array size is not i32";
break;
}
@@ -345,6 +362,8 @@ const char *PNaClABIVerifyFunctions::checkInstruction(const Instruction *Inst) {
const SwitchInst *Switch = cast<SwitchInst>(Inst);
if (!isValidScalarOperand(Switch->getCondition()))
return "bad switch condition";
+ if (Switch->getCondition()->getType()->isIntegerTy(1))
+ return "switch on i1";
// SwitchInst requires the cases to be ConstantInts, but it
// doesn't require their types to be the same as the condition
diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt
index 02c5ac521d..49c5153bf3 100644
--- a/lib/Transforms/NaCl/CMakeLists.txt
+++ b/lib/Transforms/NaCl/CMakeLists.txt
@@ -15,6 +15,7 @@ add_llvm_library(LLVMNaClTransforms
FlattenGlobals.cpp
GlobalCleanup.cpp
PNaClABISimplify.cpp
+ PromoteI1Ops.cpp
PromoteIntegers.cpp
ReplacePtrsWithInts.cpp
RewriteLLVMIntrinsics.cpp
diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp
index a85829eb3b..056b0ae651 100644
--- a/lib/Transforms/NaCl/PNaClABISimplify.cpp
+++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp
@@ -65,6 +65,8 @@ void llvm::PNaClABISimplifyAddPostOptPasses(PassManager &PM) {
// run.
PM.add(createExpandSmallArgumentsPass());
+ PM.add(createPromoteI1OpsPass());
+
// We place StripMetadata after optimization passes because
// optimizations depend on the metadata.
PM.add(createStripMetadataPass());
diff --git a/lib/Transforms/NaCl/PromoteI1Ops.cpp b/lib/Transforms/NaCl/PromoteI1Ops.cpp
new file mode 100644
index 0000000000..dccf081e26
--- /dev/null
+++ b/lib/Transforms/NaCl/PromoteI1Ops.cpp
@@ -0,0 +1,138 @@
+//===- PromoteI1Ops.cpp - Promote various operations on the i1 type--------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass expands out various operations on the i1 type so that
+// these i1 operations do not need to be supported by the PNaCl
+// translator.
+//
+// This is similar to the PromoteIntegers pass in that it removes uses
+// of an unusual-size integer type. The difference is that i1 remains
+// a valid type in other operations. i1 can still be used in phi
+// nodes, "select" instructions, in "sext" and "zext", and so on. In
+// contrast, the integer types that PromoteIntegers removes are not
+// allowed in any context by PNaCl's ABI verifier.
+//
+// This pass expands out the following:
+//
+// * i1 loads and stores.
+// * All i1 comparisons and arithmetic operations, with the exception
+// of "and", "or" and "xor", because these are used in practice and
+// don't overflow.
+//
+// "switch" instructions on i1 are also disallowed by the PNaCl ABI
+// verifier, but they don't seem to be generated in practice and so
+// they are not currently expanded out by this pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+namespace {
+ class PromoteI1Ops : public BasicBlockPass {
+ public:
+ static char ID; // Pass identification, replacement for typeid
+ PromoteI1Ops() : BasicBlockPass(ID) {
+ initializePromoteI1OpsPass(*PassRegistry::getPassRegistry());
+ }
+
+ virtual bool runOnBasicBlock(BasicBlock &BB);
+ };
+}
+
+char PromoteI1Ops::ID = 0;
+INITIALIZE_PASS(PromoteI1Ops, "nacl-promote-i1-ops",
+ "Promote various operations on the i1 type",
+ false, false)
+
+static Value *promoteValue(Value *Val, bool SignExt, Instruction *InsertPt) {
+ Instruction::CastOps CastType =
+ SignExt ? Instruction::SExt : Instruction::ZExt;
+ return CopyDebug(CastInst::Create(CastType, Val,
+ Type::getInt8Ty(Val->getContext()),
+ Val->getName() + ".expand_i1_val",
+ InsertPt), InsertPt);
+}
+
+bool PromoteI1Ops::runOnBasicBlock(BasicBlock &BB) {
+ bool Changed = false;
+
+ Type *I1Ty = Type::getInt1Ty(BB.getContext());
+ Type *I8Ty = Type::getInt8Ty(BB.getContext());
+
+ for (BasicBlock::iterator Iter = BB.begin(), E = BB.end(); Iter != E; ) {
+ Instruction *Inst = Iter++;
+ if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) {
+ if (Load->getType() == I1Ty) {
+ Changed = true;
+ Value *Ptr = CopyDebug(
+ new BitCastInst(
+ Load->getPointerOperand(), I8Ty->getPointerTo(),
+ Load->getPointerOperand()->getName() + ".i8ptr", Load), Load);
+ LoadInst *NewLoad = new LoadInst(
+ Ptr, Load->getName() + ".pre_trunc", Load);
+ CopyDebug(NewLoad, Load);
+ CopyLoadOrStoreAttrs(NewLoad, Load);
+ Value *Result = CopyDebug(new TruncInst(NewLoad, I1Ty, "", Load), Load);
+ Result->takeName(Load);
+ Load->replaceAllUsesWith(Result);
+ Load->eraseFromParent();
+ }
+ } else if (StoreInst *Store = dyn_cast<StoreInst>(Inst)) {
+ if (Store->getValueOperand()->getType() == I1Ty) {
+ Changed = true;
+ Value *Ptr = CopyDebug(
+ new BitCastInst(
+ Store->getPointerOperand(), I8Ty->getPointerTo(),
+ Store->getPointerOperand()->getName() + ".i8ptr", Store),
+ Store);
+ Value *Val = promoteValue(Store->getValueOperand(), false, Store);
+ StoreInst *NewStore = new StoreInst(Val, Ptr, Store);
+ CopyDebug(NewStore, Store);
+ CopyLoadOrStoreAttrs(NewStore, Store);
+ Store->eraseFromParent();
+ }
+ } else if (BinaryOperator *Op = dyn_cast<BinaryOperator>(Inst)) {
+ if (Op->getType() == I1Ty &&
+ !(Op->getOpcode() == Instruction::And ||
+ Op->getOpcode() == Instruction::Or ||
+ Op->getOpcode() == Instruction::Xor)) {
+ Value *Arg1 = promoteValue(Op->getOperand(0), false, Op);
+ Value *Arg2 = promoteValue(Op->getOperand(1), false, Op);
+ Value *NewOp = CopyDebug(
+ BinaryOperator::Create(
+ Op->getOpcode(), Arg1, Arg2,
+ Op->getName() + ".pre_trunc", Op), Op);
+ Value *Result = CopyDebug(new TruncInst(NewOp, I1Ty, "", Op), Op);
+ Result->takeName(Op);
+ Op->replaceAllUsesWith(Result);
+ Op->eraseFromParent();
+ }
+ } else if (ICmpInst *Op = dyn_cast<ICmpInst>(Inst)) {
+ if (Op->getOperand(0)->getType() == I1Ty) {
+ Value *Arg1 = promoteValue(Op->getOperand(0), Op->isSigned(), Op);
+ Value *Arg2 = promoteValue(Op->getOperand(1), Op->isSigned(), Op);
+ Value *Result = CopyDebug(
+ new ICmpInst(Op, Op->getPredicate(), Arg1, Arg2, ""), Op);
+ Result->takeName(Op);
+ Op->replaceAllUsesWith(Result);
+ Op->eraseFromParent();
+ }
+ }
+ }
+ return Changed;
+}
+
+BasicBlockPass *llvm::createPromoteI1OpsPass() {
+ return new PromoteI1Ops();
+}
diff --git a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
index c812f4d432..6e8c6915e5 100644
--- a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
+++ b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
@@ -334,14 +334,6 @@ static AttributeSet RemovePointerAttrs(LLVMContext &Context,
return AttributeSet::get(Context, AttrList);
}
-template <class InstType>
-static void CopyLoadOrStoreAttrs(InstType *Dest, InstType *Src) {
- Dest->setVolatile(Src->isVolatile());
- Dest->setAlignment(Src->getAlignment());
- Dest->setOrdering(Src->getOrdering());
- Dest->setSynchScope(Src->getSynchScope());
-}
-
static void ConvertInstruction(DataLayout *DL, Type *IntPtrType,
FunctionConverter *FC, Instruction *Inst) {
if (ReturnInst *Ret = dyn_cast<ReturnInst>(Inst)) {
diff --git a/test/NaCl/PNaClABI/abi-i1-operations.ll b/test/NaCl/PNaClABI/abi-i1-operations.ll
new file mode 100644
index 0000000000..4c63683bb8
--- /dev/null
+++ b/test/NaCl/PNaClABI/abi-i1-operations.ll
@@ -0,0 +1,66 @@
+; RUN: pnacl-abicheck < %s | FileCheck %s
+
+; Most arithmetic operations are not very useful on i1, so use of i1
+; is restricted to a subset of operations.
+
+
+; i1 is allowed on these bitwise operations because:
+; * These operations never overflow.
+; * They do get generated in practice for combining conditions.
+define internal void @allowed_cases() {
+ %and = and i1 0, 0
+ %or = or i1 0, 0
+ %xor = xor i1 0, 0
+ ret void
+}
+; CHECK-NOT: disallowed
+
+
+define internal void @rejected_cases(i32 %ptr) {
+ ; Loads and stores of i1 are disallowed. This is done by rejecting
+ ; i1* as a pointer type.
+ %ptr.p = inttoptr i32 %ptr to i1*
+; CHECK: disallowed: bad result type: %ptr.p = inttoptr
+ load i1* %ptr.p, align 1
+; CHECK-NEXT: disallowed: bad pointer: {{.*}} load i1*
+
+ ; i1 arithmetic is of dubious usefulness, so it is rejected.
+ add i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} add
+ sub i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} sub
+ mul i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} mul
+ udiv i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} udiv
+ sdiv i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} sdiv
+ urem i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} urem
+ srem i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} srem
+ shl i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} shl
+ lshr i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} lshr
+ ashr i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} ashr
+
+ ; The same applies to i1 comparisons.
+ icmp eq i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} icmp eq
+ icmp ult i1 0, 0
+; CHECK-NEXT: disallowed: arithmetic on i1: {{.*}} icmp ult
+
+ ; There should be no implicit zero-extension in alloca.
+ alloca i8, i1 1
+; CHECK-NEXT: disallowed: alloca array size is not i32
+
+ ; Switch on i1 is not useful. "br" should be used instead.
+ switch i1 0, label %next [i1 0, label %next]
+; CHECK-NEXT: disallowed: switch on i1
+next:
+
+ ret void
+}
+; CHECK-NOT: disallowed
diff --git a/test/Transforms/NaCl/promote-i1-ops.ll b/test/Transforms/NaCl/promote-i1-ops.ll
new file mode 100644
index 0000000000..245004b681
--- /dev/null
+++ b/test/Transforms/NaCl/promote-i1-ops.ll
@@ -0,0 +1,77 @@
+; RUN: opt %s -nacl-promote-i1-ops -S | FileCheck %s
+
+; Test that the PromoteI1Ops pass expands out i1 loads/stores and i1
+; comparison and arithmetic operations, with the exception of "and",
+; "or" and "xor".
+
+
+; i1 loads and stores are converted to i8 load and stores with
+; explicit casts.
+
+define i1 @load(i1* %ptr) {
+ %val = load i1* %ptr
+ ret i1 %val
+}
+; CHECK: define i1 @load
+; CHECK-NEXT: %ptr.i8ptr = bitcast i1* %ptr to i8*
+; CHECK-NEXT: %val.pre_trunc = load i8* %ptr.i8ptr
+; CHECK-NEXT: %val = trunc i8 %val.pre_trunc to i1
+
+define void @store(i1 %val, i1* %ptr) {
+ store i1 %val, i1* %ptr
+ ret void
+}
+; CHECK: define void @store
+; CHECK-NEXT: %ptr.i8ptr = bitcast i1* %ptr to i8*
+; CHECK-NEXT: %val.expand_i1_val = zext i1 %val to i8
+; CHECK-NEXT: store i8 %val.expand_i1_val, i8* %ptr.i8ptr
+
+
+; i1 arithmetic and comparisons are converted to their i8 equivalents
+; with explicit casts.
+
+define i1 @add(i1 %x, i1 %y) {
+ %result = add i1 %x, %y
+ ret i1 %result
+}
+; CHECK: define i1 @add
+; CHECK-NEXT: %x.expand_i1_val = zext i1 %x to i8
+; CHECK-NEXT: %y.expand_i1_val = zext i1 %y to i8
+; CHECK-NEXT: %result.pre_trunc = add i8 %x.expand_i1_val, %y.expand_i1_val
+; CHECK-NEXT: %result = trunc i8 %result.pre_trunc to i1
+
+define i1 @compare(i1 %x, i1 %y) {
+ %result = icmp slt i1 %x, %y
+ ret i1 %result
+}
+; CHECK: define i1 @compare
+; CHECK-NEXT: %x.expand_i1_val = sext i1 %x to i8
+; CHECK-NEXT: %y.expand_i1_val = sext i1 %y to i8
+; CHECK-NEXT: %result = icmp slt i8 %x.expand_i1_val, %y.expand_i1_val
+
+
+; Non-shift bitwise operations should not be modified.
+define void @bitwise_ops(i1 %x, i1 %y) {
+ %and = and i1 %x, %y
+ %or = or i1 %x, %y
+ %xor = xor i1 %x, %y
+ ret void
+}
+; CHECK: define void @bitwise_ops
+; CHECK-NEXT: %and = and i1 %x, %y
+; CHECK-NEXT: %or = or i1 %x, %y
+; CHECK-NEXT: %xor = xor i1 %x, %y
+
+
+define void @unchanged_cases(i32 %x, i32 %y, i32* %ptr) {
+ %add = add i32 %x, %y
+ %cmp = icmp slt i32 %x, %y
+ %val = load i32* %ptr
+ store i32 %x, i32* %ptr
+ ret void
+}
+; CHECK: define void @unchanged_cases
+; CHECK-NEXT: %add = add i32 %x, %y
+; CHECK-NEXT: %cmp = icmp slt i32 %x, %y
+; CHECK-NEXT: %val = load i32* %ptr
+; CHECK-NEXT: store i32 %x, i32* %ptr
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 804382fca7..56ac3ec9a7 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -620,6 +620,7 @@ int main(int argc, char **argv) {
initializeInsertDivideCheckPass(Registry);
initializePNaClABIVerifyFunctionsPass(Registry);
initializePNaClABIVerifyModulePass(Registry);
+ initializePromoteI1OpsPass(Registry);
initializePromoteIntegersPass(Registry);
initializeReplacePtrsWithIntsPass(Registry);
initializeResolveAliasesPass(Registry);