Add forward instruction declaration to Subzero bitcode reader.
BUG= https://code.google.com/p/nativeclient/issues/detail?id=3892
R=jvoung@chromium.org, stichnot@chromium.org
Review URL: https://codereview.chromium.org/576853002
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index eac0760..d63232f 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -814,6 +814,7 @@
FcnId(Context->getNextFunctionBlockValueID()),
LLVMFunc(cast<Function>(Context->getGlobalValueByID(FcnId))),
CachedNumGlobalValueIDs(Context->getNumGlobalValueIDs()),
+ NextLocalInstIndex(Context->getNumGlobalValueIDs()),
InstIsTerminating(false) {
Func->setFunctionName(LLVMFunc->getName());
Func->setReturnType(Context->convertToIceType(LLVMFunc->getReturnType()));
@@ -830,7 +831,9 @@
~FunctionParser() LLVM_OVERRIDE;
// Set the next constant ID to the given constant C.
- void setNextConstantID(Ice::Constant *C) { LocalOperands.push_back(C); }
+ void setNextConstantID(Ice::Constant *C) {
+ setOperand(NextLocalInstIndex++, C);
+ }
private:
// Timer for reading function bitcode and converting to ICE.
@@ -845,11 +848,14 @@
unsigned FcnId;
// The corresponding LLVM function.
Function *LLVMFunc;
+ // Holds the dividing point between local and global absolute value indices.
+ uint32_t CachedNumGlobalValueIDs;
// Holds operands local to the function block, based on indices
// defined in the bitcode file.
std::vector<Ice::Operand *> LocalOperands;
- // Holds the dividing point between local and global absolute value indices.
- uint32_t CachedNumGlobalValueIDs;
+ // Holds the index within LocalOperands corresponding to the next
+ // instruction that generates a value.
+ uint32_t NextLocalInstIndex;
// True if the last processed instruction was a terminating
// instruction.
bool InstIsTerminating;
@@ -910,15 +916,42 @@
return getBasicBlock(Index);
}
- // Generates the next available local variable using the given type.
- Ice::Variable *getNextInstVar(Ice::Type Ty) {
+ // Generate an instruction variable with type Ty.
+ Ice::Variable *createInstVar(Ice::Type Ty) {
if (Ty == Ice::IceType_void) {
Error("Can't define instruction value using type void");
// Recover since we can't throw an exception.
Ty = Ice::IceType_i32;
}
- Ice::Variable *Var = Func->makeVariable(Ty, CurrentNode);
- LocalOperands.push_back(Var);
+ return Func->makeVariable(Ty, CurrentNode);
+ }
+
+ // Generates the next available local variable using the given type.
+ Ice::Variable *getNextInstVar(Ice::Type Ty) {
+ assert(NextLocalInstIndex >= CachedNumGlobalValueIDs);
+ // Before creating one, see if a forwardtyperef has already defined it.
+ uint32_t LocalIndex = NextLocalInstIndex - CachedNumGlobalValueIDs;
+ if (LocalIndex < LocalOperands.size()) {
+ Ice::Operand *Op = LocalOperands[LocalIndex];
+ if (Op != NULL) {
+ if (Ice::Variable *Var = dyn_cast<Ice::Variable>(Op)) {
+ if (Var->getType() == Ty) {
+ ++NextLocalInstIndex;
+ return Var;
+ }
+ }
+ std::string Buffer;
+ raw_string_ostream StrBuf(Buffer);
+ StrBuf << "Illegal forward referenced instruction ("
+ << NextLocalInstIndex << "): " << *Op;
+ Error(StrBuf.str());
+ // TODO(kschimpf) Remove error recovery once implementation complete.
+ ++NextLocalInstIndex;
+ return createInstVar(Ty);
+ }
+ }
+ Ice::Variable *Var = createInstVar(Ty);
+ setOperand(NextLocalInstIndex++, Var);
return Var;
}
@@ -947,12 +980,54 @@
if (LocalIndex >= LocalOperands.size()) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
- StrBuf << "Value index " << Index << " out of range. Must be less than "
- << (LocalOperands.size() + CachedNumGlobalValueIDs);
+ StrBuf << "Value index " << Index << " not defined!";
Error(StrBuf.str());
report_fatal_error("Unable to continue");
}
- return LocalOperands[LocalIndex];
+ Ice::Operand *Op = LocalOperands[LocalIndex];
+ if (Op == NULL) {
+ std::string Buffer;
+ raw_string_ostream StrBuf(Buffer);
+ StrBuf << "Value index " << Index << " not defined!";
+ Error(StrBuf.str());
+ report_fatal_error("Unable to continue");
+ }
+ return Op;
+ }
+
+ // Sets element Index (in the local operands list) to Op.
+ void setOperand(uint32_t Index, Ice::Operand *Op) {
+ assert(Op);
+ // Check if simple push works.
+ uint32_t LocalIndex = Index - CachedNumGlobalValueIDs;
+ if (LocalIndex == LocalOperands.size()) {
+ LocalOperands.push_back(Op);
+ return;
+ }
+
+ // Must be forward reference, expand vector to accommodate.
+ if (LocalIndex >= LocalOperands.size())
+ LocalOperands.resize(LocalIndex+1);
+
+ // If element not defined, set it.
+ Ice::Operand *OldOp = LocalOperands[LocalIndex];
+ if (OldOp == NULL) {
+ LocalOperands[LocalIndex] = Op;
+ return;
+ }
+
+ // See if forward reference matches.
+ if (OldOp == Op)
+ return;
+
+ // Error has occurred.
+ std::string Buffer;
+ raw_string_ostream StrBuf(Buffer);
+ StrBuf << "Multiple definitions for index " << Index
+ << ": " << *Op << " and " << *OldOp;
+ Error(StrBuf.str());
+ // TODO(kschimpf) Remove error recovery once implementation complete.
+ LocalOperands[LocalIndex] = Op;
}
// Returns the relative operand (wrt to BaseIndex) referenced by
@@ -963,7 +1038,7 @@
// Returns the absolute index of the next value generating instruction.
uint32_t getNextInstIndex() const {
- return CachedNumGlobalValueIDs + LocalOperands.size();
+ return NextLocalInstIndex;
}
// Generates type error message for binary operator Op
@@ -1702,10 +1777,17 @@
Ice::InstStore::create(Func, Value, Address, Alignment));
break;
}
+ case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: {
+ // FORWARDTYPEREF: [opval, ty]
+ if (!isValidRecordSize(2, "function block forward type ref"))
+ return;
+ setOperand(Values[0], createInstVar(
+ Context->convertToIceType(Context->getTypeByID(Values[1]))));
+ break;
+ }
case naclbitc::FUNC_CODE_INST_SWITCH:
case naclbitc::FUNC_CODE_INST_CALL:
case naclbitc::FUNC_CODE_INST_CALL_INDIRECT:
- case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF:
default:
// Generate error message!
BlockParserBaseClass::ProcessRecord();
diff --git a/tests_lit/lit.cfg b/tests_lit/lit.cfg
index 5e94f1e..f1d3c74 100644
--- a/tests_lit/lit.cfg
+++ b/tests_lit/lit.cfg
@@ -48,7 +48,8 @@
config.substitutions.append(('%szdiff', os.path.join(pydir, 'szdiff.py')))
llvmbintools = [r"\bFileCheck\b", r"\bllvm-as\b", r"\bllvm-mc\b",
- r"\bllvm-objdump\b", r"\bnot\b", r"\bpnacl-freeze\b"]
+ r"\bllvm-objdump\b", r"\bnot\b", r"\bpnacl-freeze\b",
+ r"\bpnacl-bcdis\b"]
for tool in llvmbintools:
# The re.sub() line is adapted from one of LLVM's lit.cfg files.
diff --git a/tests_lit/reader_tests/forwardref.ll b/tests_lit/reader_tests/forwardref.ll
new file mode 100644
index 0000000..21d33d5
--- /dev/null
+++ b/tests_lit/reader_tests/forwardref.ll
@@ -0,0 +1,122 @@
+; Test use forward type references in function blocks.
+
+; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
+; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
+; RUN: -allow-pnacl-reader-error-recovery \
+; RUN: -allow-local-symbol-tables \
+; RUN: | FileCheck %s
+
+; RUN: llvm-as < %s | pnacl-freeze | pnacl-bcdis -no-records \
+; RUN: | FileCheck --check-prefix=DUMP %s
+
+define void @LoopCarriedDep() {
+b0:
+ %v0 = add i32 1, 2
+ br label %b1
+b1:
+ %v1 = phi i32 [%v0, %b0], [%v2, %b1]
+ %v2 = add i32 %v1, 1
+ br label %b1
+}
+
+; CHECK: define void @LoopCarriedDep() {
+; CHECK-NEXT: b0:
+; CHECK-NEXT: %v0 = add i32 1, 2
+; CHECK-NEXT: br label %b1
+; CHECK-NEXT: b1:
+; CHECK-NEXT: %v1 = phi i32 [ %v0, %b0 ], [ %v2, %b1 ]
+; CHECK-NEXT: %v2 = add i32 %v1, 1
+; CHECK-NEXT: br label %b1
+; CHECK-NEXT: }
+
+; Snippet of bitcode objdump with forward type reference (see "declare").
+; DUMP: function void @f0() { // BlockID = 12
+; DUMP-NEXT: blocks 2;
+; DUMP-NEXT: constants { // BlockID = 11
+; DUMP-NEXT: i32: <@a0>
+; DUMP-NEXT: %c0 = i32 1; <@a1>
+; DUMP-NEXT: %c1 = i32 2; <@a1>
+; DUMP-NEXT: }
+; DUMP-NEXT: %b0:
+; DUMP-NEXT: %v0 = add i32 %c0, %c1; <@a1>
+; DUMP-NEXT: br label %b1;
+; DUMP-NEXT: %b1:
+; DUMP-NEXT: declare i32 %v2; <@a6>
+; DUMP-NEXT: %v1 = phi i32 [%v0, %b0], [%v2, %b1];
+; DUMP-NEXT: %v2 = add i32 %v1, %c0; <@a1>
+; DUMP-NEXT: br label %b1;
+; DUMP-NEXT: }
+
+define void @BackBranch(i32 %p0) {
+b0:
+ br label %b4
+b1:
+ %v0 = add i32 %p0, %v3
+ br label %b6
+b2:
+ %v1 = add i32 %p0, %v4
+ br label %b6
+b3:
+ %v2 = add i32 %p0, %v3 ; No forward declare, already done!
+ br label %b6
+b4:
+ %v3 = add i32 %p0, %p0
+ br i1 1, label %b1, label %b5
+b5:
+ %v4 = add i32 %v3, %p0
+ br i1 1, label %b2, label %b3
+b6:
+ ret void
+}
+
+; CHECK: define void @BackBranch(i32 %p0) {
+; CHECK-NEXT: b0:
+; CHECK-NEXT: br label %b4
+; CHECK-NEXT: b1:
+; CHECK-NEXT: %v0 = add i32 %p0, %v3
+; CHECK-NEXT: br label %b6
+; CHECK-NEXT: b2:
+; CHECK-NEXT: %v1 = add i32 %p0, %v4
+; CHECK-NEXT: br label %b6
+; CHECK-NEXT: b3:
+; CHECK-NEXT: %v2 = add i32 %p0, %v3
+; CHECK-NEXT: br label %b6
+; CHECK-NEXT: b4:
+; CHECK-NEXT: %v3 = add i32 %p0, %p0
+; CHECK-NEXT: br i1 true, label %b1, label %b5
+; CHECK-NEXT: b5:
+; CHECK-NEXT: %v4 = add i32 %v3, %p0
+; CHECK-NEXT: br i1 true, label %b2, label %b3
+; CHECK-NEXT: b6:
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+
+; Snippet of bitcode objdump with forward type references (see "declare").
+; DUMP: function void @f1(i32 %p0) { // BlockID = 12
+; DUMP-NEXT: blocks 7;
+; DUMP-NEXT: constants { // BlockID = 11
+; DUMP-NEXT: i1: <@a0>
+; DUMP-NEXT: %c0 = i1 1; <@a1>
+; DUMP-NEXT: }
+; DUMP-NEXT: %b0:
+; DUMP-NEXT: br label %b4;
+; DUMP-NEXT: %b1:
+; DUMP-NEXT: declare i32 %v3; <@a6>
+; DUMP-NEXT: %v0 = add i32 %p0, %v3; <@a1>
+; DUMP-NEXT: br label %b6;
+; DUMP-NEXT: %b2:
+; DUMP-NEXT: declare i32 %v4; <@a6>
+; DUMP-NEXT: %v1 = add i32 %p0, %v4; <@a1>
+; DUMP-NEXT: br label %b6;
+; DUMP-NEXT: %b3:
+; DUMP-NEXT: %v2 = add i32 %p0, %v3; <@a1>
+; DUMP-NEXT: br label %b6;
+; DUMP-NEXT: %b4:
+; DUMP-NEXT: %v3 = add i32 %p0, %p0; <@a1>
+; DUMP-NEXT: br i1 %c0, label %b1, label %b5;
+; DUMP-NEXT: %b5:
+; DUMP-NEXT: %v4 = add i32 %v3, %p0; <@a1>
+; DUMP-NEXT: br i1 %c0, label %b2, label %b3;
+; DUMP-NEXT: %b6:
+; DUMP-NEXT: ret void; <@a3>
+; DUMP-NEXT: }