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:   }