aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Schimpf <kschimpf@google.com>2013-08-01 07:12:23 -0700
committerKarl Schimpf <kschimpf@google.com>2013-08-01 07:12:23 -0700
commitb9657234ee8b1951db5977a8ffb55a2e5df6d76c (patch)
treeda9f474ea4b35ff767b93715a13a3933f0355f39
parent365546bcef14965546dc39ebcef35f07a897b9c5 (diff)
Remove the inttoptr dependency from load instructions.
Elides inttoptr casts used (exclusively) in load instructions when PNaClVersion=2. This is an incremental start on removing the inttoptr instruction from the PNaCl wire format (See issue 3544 for more information on the strategy of removing ptrtoint). Also modifies PNaCl bitcode reader/writer to accept PNaClVersion=1 as supported, and PNaClVersion=2 as unsupported but readable (allowing pnacl-freeze and pnacl-thaw to work on such files). Also allows command-line option --pnacl-version for setting PNaClVersion in the PNaCl bitcode writer. Also fixes some problems on PNaCl bitcode headers, using common support to determine when the read/written PNaCl bitcode file is valid. BUG= https://code.google.com/p/nativeclient/issues/detail?id=3544 R=jvoung@chromium.org Review URL: https://codereview.chromium.org/5812155903377408
-rw-r--r--include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h5
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeHeader.cpp2
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp73
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h11
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp41
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp57
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h9
-rw-r--r--test/NaCl/Bitcode/inttoptr-elide.ll180
-rw-r--r--tools/pnacl-freeze/pnacl-freeze.cpp2
9 files changed, 351 insertions, 29 deletions
diff --git a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
index 5433ed5352..9b53e08ab8 100644
--- a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
+++ b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
@@ -286,7 +286,10 @@ namespace naclbitc {
// 17 is unused.
// 18 is unused.
FUNC_CODE_INST_ALLOCA = 19, // ALLOCA: [op, align]
- FUNC_CODE_INST_LOAD = 20, // LOAD: [op, align, vol]
+ FUNC_CODE_INST_LOAD = 20, // PNaCl version 1:
+ // LOAD: [op, align, vol]
+ // PNaCl version 2:
+ // LOAD: [op, align, vol, ty]
// 21 is unused.
// 22 is unused.
FUNC_CODE_INST_VAARG = 23, // Not used in PNaCl.
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeHeader.cpp b/lib/Bitcode/NaCl/Reader/NaClBitcodeHeader.cpp
index 2dd2af5dc3..37b79ad3d8 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeHeader.cpp
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeHeader.cpp
@@ -248,7 +248,7 @@ void NaClBitcodeHeader::InstallFields() {
}
if (PNaClVersion != 1) {
IsSupportedFlag = false;
- IsReadableFlag = false;
+ IsReadableFlag = (PNaClVersion == 2);
UpdatedUnsupportedMessage = true;
UnsupportedMessage.clear();
raw_string_ostream UnsupportedStream(UnsupportedMessage);
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
index 9c2f45ea56..40f8842b3b 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
@@ -1301,6 +1301,46 @@ bool NaClBitcodeReader::ParseBitcodeInto(Module *M) {
}
}
+// Returns true if error occured installing I into BB.
+bool NaClBitcodeReader::InstallInstruction(
+ BasicBlock *BB, Instruction *I) {
+ // Add instruction to end of current BB. If there is no current BB, reject
+ // this file.
+ if (BB == 0) {
+ delete I;
+ return Error("Invalid instruction with no BB");
+ }
+ BB->getInstList().push_back(I);
+ return false;
+}
+
+Value *NaClBitcodeReader::ConvertOpToType(Value *Op, Type *T, BasicBlock *BB) {
+ // Note: Currently only knows how to add inttoptr type conversion, since
+ // this is the only elided instruction in the bitcode writer.
+ // TODO(kschimpf): Generalize this as we expand elided conversions.
+ Value *Conversion = 0;
+ Type *OpTy = Op->getType();
+ if (OpTy == T) return Op;
+
+ // Following while loop is only run once. It is used to break on
+ // erroneous conditions.
+ while (true) {
+ if (!OpTy->isIntegerTy()) break;
+ if (!T->isPointerTy()) break;
+ Instruction *I = CastInst::Create(Instruction::IntToPtr, Op, T);
+ if (InstallInstruction(BB, I)) break;
+ Conversion = I;
+ break;
+ }
+ if (Conversion == 0) {
+ std::string Message;
+ raw_string_ostream StrM(Message);
+ StrM << "Can't convert " << *Op << " to type " << *T << "\n";
+ Error(StrM.str());
+ }
+ return Conversion;
+}
+
/// ParseFunctionBody - Lazily parse the specified function body block.
bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
DEBUG(dbgs() << "-> ParseFunctionBody\n");
@@ -1602,13 +1642,29 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
I = new AllocaInst(Type::getInt8Ty(Context), Size, (1 << Align) >> 1);
break;
}
- case naclbitc::FUNC_CODE_INST_LOAD: { // LOAD: [op, align, vol]
+ case naclbitc::FUNC_CODE_INST_LOAD: {
+ // PNaCl version 1: LOAD: [op, align, vol]
+ // PNaCl version 2: LOAD: [op, align, vol, ty]
unsigned OpNum = 0;
Value *Op;
- if (popValue(Record, &OpNum, NextValueNo, &Op) ||
- OpNum+2 != Record.size())
+ if (popValue(Record, &OpNum, NextValueNo, &Op))
return Error("Invalid LOAD record");
-
+ switch (GetPNaClVersion()) {
+ case 1:
+ if (Record.size() != 3)
+ return Error("Invalid LOAD record");
+ break;
+ case 2: {
+ if (Record.size() != 4)
+ return Error("Invalid LOAD record");
+ // Add pointer cast to op.
+ Type *T = getTypeByID(Record[3]);
+ if (T == 0)
+ return Error("Invalid type for load instruction");
+ Op = ConvertOpToType(Op, T->getPointerTo(), CurBB);
+ if (Op == 0) return true;
+ }
+ }
I = new LoadInst(Op, "", Record[OpNum+1], (1 << Record[OpNum]) >> 1);
break;
}
@@ -1677,13 +1733,8 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
continue;
}
- // Add instruction to end of current BB. If there is no current BB, reject
- // this file.
- if (CurBB == 0) {
- delete I;
- return Error("Invalid instruction with no BB");
- }
- CurBB->getInstList().push_back(I);
+ if (InstallInstruction(CurBB, I))
+ return true;
// If this was a terminator instruction, move to the next block.
if (isa<TerminatorInst>(I)) {
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
index 849ca7e506..805bddf0b2 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
@@ -267,6 +267,17 @@ private:
return getFnValueByID(ValNo);
}
+ /// \brief Add instructions to cast Op to the given type T into block BB.
+ /// Follows rules for pointer conversion as defined in
+ /// llvm/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp.
+ ///
+ /// Returns 0 if unable to generate conversion value (also generates
+ /// an appropriate error message and calls Error).
+ Value *ConvertOpToType(Value *Op, Type *T, BasicBlock *BB);
+
+ /// \brief Install instruction I into basic block BB.
+ bool InstallInstruction(BasicBlock *BB, Instruction *I);
+
bool ParseModule(bool Resume);
bool ParseTypeTable();
bool ParseTypeTableBody();
diff --git a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
index 9f3626557b..ac2a6a0acf 100644
--- a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
@@ -811,8 +811,9 @@ static void pushValue(const Value *V, unsigned InstID,
SmallVector<unsigned, 64> &Vals,
NaClValueEnumerator &VE,
NaClBitstreamWriter &Stream) {
- EmitFnForwardTypeRef(V, InstID, VE, Stream);
- unsigned ValID = VE.getValueID(V);
+ const Value *VElided = VE.ElideCasts(V);
+ EmitFnForwardTypeRef(VElided, InstID, VE, Stream);
+ unsigned ValID = VE.getValueID(VElided);
// Make encoding relative to the InstID.
Vals.push_back(InstID - ValID);
}
@@ -821,8 +822,9 @@ static void pushValue64(const Value *V, unsigned InstID,
SmallVector<uint64_t, 128> &Vals,
NaClValueEnumerator &VE,
NaClBitstreamWriter &Stream) {
- EmitFnForwardTypeRef(V, InstID, VE, Stream);
- uint64_t ValID = VE.getValueID(V);
+ const Value *VElided = VE.ElideCasts(V);
+ EmitFnForwardTypeRef(VElided, InstID, VE, Stream);
+ uint64_t ValID = VE.getValueID(VElided);
Vals.push_back(InstID - ValID);
}
@@ -830,14 +832,16 @@ static void pushValueSigned(const Value *V, unsigned InstID,
SmallVector<uint64_t, 128> &Vals,
NaClValueEnumerator &VE,
NaClBitstreamWriter &Stream) {
- EmitFnForwardTypeRef(V, InstID, VE, Stream);
- unsigned ValID = VE.getValueID(V);
+ const Value *VElided = VE.ElideCasts(V);
+ EmitFnForwardTypeRef(VElided, InstID, VE, Stream);
+ unsigned ValID = VE.getValueID(VElided);
int64_t diff = ((int32_t)InstID - (int32_t)ValID);
emitSignedInt64(Vals, diff);
}
/// WriteInstruction - Emit an instruction to the specified stream.
-static void WriteInstruction(const Instruction &I, unsigned InstID,
+/// Returns true if instruction actually emitted.
+static bool WriteInstruction(const Instruction &I, unsigned InstID,
NaClValueEnumerator &VE,
NaClBitstreamWriter &Stream,
SmallVector<unsigned, 64> &Vals) {
@@ -848,6 +852,8 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
default:
if (Instruction::isCast(I.getOpcode())) {
// CAST: [opval, destty, castopc]
+ if (VE.IsElidedCast(&I))
+ return false;
Code = naclbitc::FUNC_CODE_INST_CAST;
AbbrevToUse = FUNCTION_INST_CAST_ABBREV;
pushValue(I.getOperand(0), InstID, Vals, VE, Stream);
@@ -965,7 +971,7 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
// Also do expected action - clear external Vals collection:
Vals.clear();
- return;
+ return true;
}
break;
case Instruction::Unreachable:
@@ -988,7 +994,7 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
// Emit a Vals64 vector and exit.
Stream.EmitRecord(Code, Vals64, AbbrevToUse);
Vals64.clear();
- return;
+ return true;
}
case Instruction::Alloca:
@@ -998,13 +1004,17 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
pushValue(I.getOperand(0), InstID, Vals, VE, Stream); // size.
Vals.push_back(Log2_32(cast<AllocaInst>(I).getAlignment())+1);
break;
-
case Instruction::Load:
+ // PNaCl Version 1: LOAD: [op, align, vol]
+ // PNaCl Version 2: LOAD: [op, align, vol, ty]
Code = naclbitc::FUNC_CODE_INST_LOAD;
- pushValue(I.getOperand(0), InstID, Vals, VE, Stream); // ptr
+ pushValue(I.getOperand(0), InstID, Vals, VE, Stream);
AbbrevToUse = FUNCTION_INST_LOAD_ABBREV;
Vals.push_back(Log2_32(cast<LoadInst>(I).getAlignment())+1);
Vals.push_back(cast<LoadInst>(I).isVolatile());
+ if (PNaClVersion == 2) {
+ Vals.push_back(VE.getTypeID(I.getType()));
+ }
break;
case Instruction::Store:
Code = naclbitc::FUNC_CODE_INST_STORE;
@@ -1047,6 +1057,7 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
Stream.EmitRecord(Code, Vals, AbbrevToUse);
Vals.clear();
+ return true;
}
// Emit names for globals/functions etc.
@@ -1133,9 +1144,8 @@ static void WriteFunction(const Function &F, NaClValueEnumerator &VE,
for (Function::const_iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
for (BasicBlock::const_iterator I = BB->begin(), E = BB->end();
I != E; ++I) {
- WriteInstruction(*I, InstID, VE, Stream, Vals);
-
- if (!I->getType()->isVoidTy())
+ if (WriteInstruction(*I, InstID, VE, Stream, Vals) &&
+ !I->getType()->isVoidTy())
++InstID;
}
@@ -1233,6 +1243,9 @@ static void WriteBlockInfo(const NaClValueEnumerator &VE,
Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6)); // Ptr
Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 4)); // Align
Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, 1)); // volatile
+ if (PNaClVersion == 2) {
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 4)); // Typecast
+ }
if (Stream.EmitBlockInfoAbbrev(naclbitc::FUNCTION_BLOCK_ID,
Abbv) != FUNCTION_INST_LOAD_ABBREV)
llvm_unreachable("Unexpected abbrev ordering!");
diff --git a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
index 3ad224a04f..7da01f005e 100644
--- a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
@@ -231,7 +231,11 @@ void NaClValueEnumerator::EnumerateValueSymbolTable(const ValueSymbolTable &VST)
EnumerateValue(VI->getValue());
}
-void NaClValueEnumerator::EnumerateValue(const Value *V) {
+void NaClValueEnumerator::EnumerateValue(const Value *VIn) {
+ // Skip over elided values.
+ const Value *V = ElideCasts(VIn);
+ if (V != VIn) return;
+
assert(!V->getType()->isVoidTy() && "Can't insert void values!");
assert(!isa<MDNode>(V) && !isa<MDString>(V) &&
"EnumerateValue doesn't handle Metadata!");
@@ -423,3 +427,54 @@ void NaClValueEnumerator::purgeFunction() {
BasicBlocks.clear();
FnForwardTypeRefs.clear();
}
+
+// Returns true if the bitcode writer can assume that the given
+// argument of the given operation can accept a normalized pointer.
+// Note: This function is based on the concept of NormalizedPtr as
+// defined in llvm/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp.
+static bool AllowsNormalizedPtr(const Value *V, const Instruction *Arg) {
+ const Instruction *I = dyn_cast<Instruction>(V);
+ if (I == 0) return false;
+
+ // TODO(kschimpf) Expand this list to any operation that can handle
+ // normalized pointers. That is loads and stores, function calls, and
+ // instrinsic calls.
+ switch (I->getOpcode()) {
+ default:
+ return false;
+ case Instruction::Load:
+ return I->getOperand(0) == Arg;
+ }
+}
+
+// Returns true if the bitcode reader and writer can assume that the
+// uses of the given inttotpr I2P allow normalized pointers (as
+// defined in llvm/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp).
+static bool IntToPtrUsesAllowEliding(const Instruction *I2P) {
+ for (Value::const_use_iterator u = I2P->use_begin(), e = I2P->use_end();
+ u != e; ++u) {
+ if (!AllowsNormalizedPtr(cast<Value>(*u), I2P)) return false;
+ }
+ // If reached, either all uses have a normalized pointer (and hence
+ // we know how to automatically add it back), or there were no uses (and
+ // hence represents dead code).
+ return true;
+}
+
+// Note: This function is based on the comments in
+// llvm/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp.
+const Value *NaClValueEnumerator::ElideCasts(const Value *V) {
+ if (PNaClVersion == 1) return V;
+ // TODO(kschimpf): Expand this out to cover all cases.
+ if (const Instruction *I = dyn_cast<Instruction>(V)) {
+ switch (I->getOpcode()) {
+ default:
+ break;
+ case Instruction::IntToPtr:
+ if (IntToPtrUsesAllowEliding(I)) {
+ return ElideCasts(I->getOperand(0));
+ }
+ }
+ }
+ return V;
+}
diff --git a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
index 3e71740f92..22de263c4b 100644
--- a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
+++ b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
@@ -135,6 +135,15 @@ public:
void incorporateFunction(const Function &F);
void purgeFunction();
+ /// \brief Returns the value after elided (cast) operations have been
+ /// removed. Returns V if unable to elide the cast.
+ const Value *ElideCasts(const Value *V);
+
+ /// \brief Returns true if value V is an elided (cast) operation.
+ bool IsElidedCast(const Value *V) {
+ return V != ElideCasts(V);
+ }
+
private:
void OptimizeTypes(const Module *M);
void OptimizeConstants(unsigned CstStart, unsigned CstEnd);
diff --git a/test/NaCl/Bitcode/inttoptr-elide.ll b/test/NaCl/Bitcode/inttoptr-elide.ll
new file mode 100644
index 0000000000..2f35389fa5
--- /dev/null
+++ b/test/NaCl/Bitcode/inttoptr-elide.ll
@@ -0,0 +1,180 @@
+; Test how we handle eliding inttoptr instructions.
+; TODO(kschimpf) Expand these tests as further CL's are added for issue 3544.
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 | pnacl-bcanalyzer -dump \
+; RUN: | FileCheck %s -check-prefix=PF1
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 | pnacl-thaw \
+; RUN: | llvm-dis - | FileCheck %s -check-prefix=TD1
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=2 | pnacl-bcanalyzer -dump \
+; RUN: | FileCheck %s -check-prefix=PF2
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=2 | pnacl-thaw \
+; RUN: | llvm-dis - | FileCheck %s -check-prefix=TD2
+
+; ------------------------------------------------------
+
+; Test that we elide the simple case of inttoptr of a load.
+define void @SimpleLoad(i32 %i) {
+ %1 = inttoptr i32 %i to i32*
+ %2 = load i32* %1, align 4
+ ret void
+}
+
+; TD1: define void @SimpleLoad(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32*
+; TD1-NEXT: %2 = load i32* %1, align 4
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK NumWords=6 BlockCodeSize=4>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST abbrevid=7 op0=1 op1=1 op2=10/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0/>
+; PF1-NEXT: <INST_RET abbrevid=8/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @SimpleLoad(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32*
+; TD2-NEXT: %2 = load i32* %1, align 4
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK NumWords=5 BlockCodeSize=4>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_RET abbrevid=8/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+
+; Test that we don't elide an inttoptr if one of its uses is not a load.
+define i32* @NonsimpleLoad(i32 %i) {
+ %1 = inttoptr i32 %i to i32*
+ %2 = load i32* %1, align 4
+ ret i32* %1
+}
+
+; TD1: define i32* @NonsimpleLoad(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32*
+; TD1-NEXT: %2 = load i32* %1, align 4
+; TD1-NEXT: ret i32* %1
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK NumWords=6 BlockCodeSize=4>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST abbrevid=7 op0=1 op1=1 op2=10/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0/>
+; PF1-NEXT: <INST_RET abbrevid=9 op0=2/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define i32* @NonsimpleLoad(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32*
+; TD2-NEXT: %2 = load i32* %1, align 4
+; TD2-NEXT: ret i32* %1
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK NumWords=6 BlockCodeSize=4>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CAST abbrevid=7 op0=1 op1=1 op2=10/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_RET abbrevid=9 op0=2/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+
+; Test that we can handle multiple inttoptr of loads.
+define i32 @TwoLoads(i32 %i) {
+ %1 = inttoptr i32 %i to i32*
+ %2 = load i32* %1, align 4
+ %3 = inttoptr i32 %i to i32*
+ %4 = load i32* %3, align 4
+ %5 = add i32 %2, %4
+ ret i32 %5
+}
+
+; TD1: define i32 @TwoLoads(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32*
+; TD1-NEXT: %2 = load i32* %1, align 4
+; TD1-NEXT: %3 = inttoptr i32 %i to i32*
+; TD1-NEXT: %4 = load i32* %3, align 4
+; TD1-NEXT: %5 = add i32 %2, %4
+; TD1-NEXT: ret i32 %5
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK NumWords=8 BlockCodeSize=4>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST abbrevid=7 op0=1 op1=1 op2=10/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0/>
+; PF1-NEXT: <INST_CAST abbrevid=7 op0=3 op1=1 op2=10/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0/>
+; PF1-NEXT: <INST_BINOP abbrevid=5 op0=3 op1=1 op2=0/>
+; PF1-NEXT: <INST_RET abbrevid=9 op0=1/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define i32 @TwoLoads(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32*
+; TD2-NEXT: %2 = load i32* %1, align 4
+; TD2-NEXT: %3 = inttoptr i32 %i to i32*
+; TD2-NEXT: %4 = load i32* %3, align 4
+; TD2-NEXT: %5 = add i32 %2, %4
+; TD2-NEXT: ret i32 %5
+; TD2-NEXT: }
+
+
+; PF2: <FUNCTION_BLOCK NumWords=7 BlockCodeSize=4>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=2 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_BINOP abbrevid=5 op0=2 op1=1 op2=0/>
+; PF2-NEXT: <INST_RET abbrevid=9 op0=1/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+
+; Test how we duplicate inttoptrs, even if optimized in the input file.
+define i32 @TwoLoadOpt(i32 %i) {
+ %1 = inttoptr i32 %i to i32*
+ %2 = load i32* %1, align 4
+ %3 = load i32* %1, align 4
+ %4 = add i32 %2, %3
+ ret i32 %4
+}
+
+; TD1: define i32 @TwoLoadOpt(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32*
+; TD1-NEXT: %2 = load i32* %1, align 4
+; TD1-NEXT: %3 = load i32* %1, align 4
+; TD1-NEXT: %4 = add i32 %2, %3
+; TD1-NEXT: ret i32 %4
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK NumWords=7 BlockCodeSize=4>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST abbrevid=7 op0=1 op1=1 op2=10/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0/>
+; PF1-NEXT: <INST_LOAD abbrevid=4 op0=2 op1=3 op2=0/>
+; PF1-NEXT: <INST_BINOP abbrevid=5 op0=2 op1=1 op2=0/>
+; PF1-NEXT: <INST_RET abbrevid=9 op0=1/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define i32 @TwoLoadOpt(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32*
+; TD2-NEXT: %2 = load i32* %1, align 4
+; TD2-NEXT: %3 = inttoptr i32 %i to i32*
+; TD2-NEXT: %4 = load i32* %3, align 4
+; TD2-NEXT: %5 = add i32 %2, %4
+; TD2-NEXT: ret i32 %5
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK NumWords=7 BlockCodeSize=4>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=1 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_LOAD abbrevid=4 op0=2 op1=3 op2=0 op3=0/>
+; PF2-NEXT: <INST_BINOP abbrevid=5 op0=2 op1=1 op2=0/>
+; PF2-NEXT: <INST_RET abbrevid=9 op0=1/>
+; PF2-NEXT: <VALUE_SYMTAB NumWords=1 BlockCodeSize=3>
+; PF2-NEXT: <ENTRY abbrevid=6 op0=4 op1=105/>
+; PF2-NEXT: </VALUE_SYMTAB>
+; PF2: </FUNCTION_BLOCK>
diff --git a/tools/pnacl-freeze/pnacl-freeze.cpp b/tools/pnacl-freeze/pnacl-freeze.cpp
index 297edb85a7..a3bc1a68e4 100644
--- a/tools/pnacl-freeze/pnacl-freeze.cpp
+++ b/tools/pnacl-freeze/pnacl-freeze.cpp
@@ -47,7 +47,7 @@ static void WriteOutputFile(const Module *M) {
exit(1);
}
- NaClWriteBitcodeToFile(M, Out->os());
+ NaClWriteBitcodeToFile(M, Out->os(), /* AcceptSupportedOnly = */ false);
// Declare success.
Out->keep();