Add branch instructions to Subzero bitcode reader.
BUG= https://code.google.com/p/nativeclient/issues/detail?id=3894
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/545603003
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 2b92ad7..f315294 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -865,6 +865,18 @@
return Nodes[Index];
}
+ // Returns the Index-th basic block in the list of basic blocks.
+ // Assumes Index corresponds to a branch instruction. Hence, if
+ // the branch references the entry block, it also generates a
+ // corresponding error.
+ Ice::CfgNode *getBranchBasicBlock(uint32_t Index) {
+ if (Index == 0) {
+ Error("Branch to entry block not allowed");
+ // TODO(kschimpf) Remove error recovery once implementation complete.
+ }
+ return GetBasicBlock(Index);
+ }
+
// Generates the next available local variable using the given
// type. Note: if Ty is void, this function returns NULL.
Ice::Variable *NextInstVar(Ice::Type Ty) {
@@ -1203,7 +1215,12 @@
Node->appendInst(Ice::InstUnreachable::create(Func));
}
}
- getTranslator().translateFcn(Func);
+ // Note: Once any errors have been found, we turn off all
+ // translation of all remaining functions. This allows use to see
+ // multiple errors, without adding extra checks to the translator
+ // for such parsing errors.
+ if (Context->getNumErrors() == 0)
+ getTranslator().translateFcn(Func);
}
void FunctionParser::ReportInvalidBinaryOp(Ice::InstArithmetic::OpKind Op,
@@ -1455,7 +1472,6 @@
}
case naclbitc::FUNC_CODE_INST_RET: {
// RET: [opval?]
- InstIsTerminating = true;
if (!isValidRecordSizeInRange(0, 1, "function block ret"))
return;
if (Values.size() == 0) {
@@ -1463,6 +1479,35 @@
} else {
Inst = Ice::InstRet::create(Func, getRelativeOperand(Values[0]));
}
+ InstIsTerminating = true;
+ break;
+ }
+ case naclbitc::FUNC_CODE_INST_BR: {
+ if (Values.size() == 1) {
+ // BR: [bb#]
+ Ice::CfgNode *Block = getBranchBasicBlock(Values[0]);
+ if (Block == NULL)
+ return;
+ Inst = Ice::InstBr::create(Func, Block);
+ } else {
+ // BR: [bb#, bb#, opval]
+ if (!isValidRecordSize(3, "function block branch"))
+ return;
+ Ice::Operand *Cond = getRelativeOperand(Values[2]);
+ if (Cond->getType() != Ice::IceType_i1) {
+ std::string Buffer;
+ raw_string_ostream StrBuf(Buffer);
+ StrBuf << "Branch condition not i1";
+ Error(StrBuf.str());
+ return;
+ }
+ Ice::CfgNode *ThenBlock = getBranchBasicBlock(Values[0]);
+ Ice::CfgNode *ElseBlock = getBranchBasicBlock(Values[1]);
+ if (ThenBlock == NULL || ElseBlock == NULL)
+ return;
+ Inst = Ice::InstBr::create(Func, Cond, ThenBlock, ElseBlock);
+ }
+ InstIsTerminating = true;
break;
}
default:
diff --git a/tests_lit/reader_tests/branch.ll b/tests_lit/reader_tests/branch.ll
new file mode 100644
index 0000000..f57be6d
--- /dev/null
+++ b/tests_lit/reader_tests/branch.ll
@@ -0,0 +1,46 @@
+; Tests if we handle a branch instructions.
+
+; RUN: llvm-as < %s | pnacl-freeze \
+; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
+; RUN: -allow-pnacl-reader-error-recovery \
+; RUN: | FileCheck %s
+
+define void @SimpleBranch() {
+ br label %b3
+b1:
+ br label %b2
+b2:
+ ret void
+b3:
+ br label %b1
+}
+
+; CHECK: define void @SimpleBranch() {
+; CHECK-NEXT: __0:
+; CHECK-NEXT: br label %__3
+; CHECK-NEXT: __1:
+; CHECK-NEXT: br label %__2
+; CHECK-NEXT: __2:
+; CHECK-NEXT: ret void
+; CHECK-NEXT: __3:
+; CHECK-NEXT: br label %__1
+; CHECK-NEXT: }
+
+define void @CondBranch(i32 %p) {
+ %test = trunc i32 %p to i1
+ br i1 %test, label %b1, label %b2
+b1:
+ ret void
+b2:
+ br i1 %test, label %b2, label %b1
+}
+
+; CHECK-NEXT: define void @CondBranch(i32 %__0) {
+; CHECK-NEXT: __0:
+; CHECK-NEXT: %__1 = trunc i32 %__0 to i1
+; CHECK-NEXT: br i1 %__1, label %__1, label %__2
+; CHECK-NEXT: __1:
+; CHECK-NEXT: ret void
+; CHECK-NEXT: __2:
+; CHECK-NEXT: br i1 %__1, label %__2, label %__1
+; CHECK-NEXT: }