Add ability to test parsing of bitcode records in Subzero.

Extends the NaCl bitcode munger so that the PNaClTranslator parser
can be applied to the defined sequence of record values.

BUG=None
R=jvoung@chromium.org, stichnot@chromium.org

Review URL: https://codereview.chromium.org/800883006
diff --git a/Makefile.standalone b/Makefile.standalone
index c1c5c47..13823da 100644
--- a/Makefile.standalone
+++ b/Makefile.standalone
@@ -120,7 +120,9 @@
 OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
 
 UNITTEST_SRCS = \
-	IceELFSectionTest.cpp
+	BitcodeMunge.cpp \
+	IceELFSectionTest.cpp \
+	IceParseInstsTest.cpp
 
 UNITTEST_OBJS = $(patsubst %.cpp, $(OBJDIR)/unittest/%.o, $(UNITTEST_SRCS))
 UNITTEST_LIB_OBJS = $(filter-out $(OBJDIR)/llvm2ice.o,$(OBJS))
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index 68255fb..b444dc0 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -27,6 +27,7 @@
         UseSandboxing(false), PhiEdgeSplit(false), DecorateAsm(false),
         DumpStats(false), AllowUninitializedGlobals(false),
         TimeEachFunction(false), DisableIRGeneration(false),
+        AllowErrorRecovery(false), GenerateUnitTestMessages(false),
         DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
         VerboseFocusOn(""), TranslateOnly("") {}
   bool DisableInternal;
@@ -43,6 +44,8 @@
   bool AllowUninitializedGlobals;
   bool TimeEachFunction;
   bool DisableIRGeneration;
+  bool AllowErrorRecovery;
+  bool GenerateUnitTestMessages;
   IceString DefaultGlobalPrefix;
   IceString DefaultFunctionPrefix;
   IceString TimingFocusOn;
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 9be47f5..482844f 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -37,12 +37,6 @@
 namespace {
 using namespace llvm;
 
-// TODO(kschimpf) Remove error recovery once implementation complete.
-static cl::opt<bool> AllowErrorRecovery(
-    "allow-pnacl-reader-error-recovery",
-    cl::desc("Allow error recovery when reading PNaCl bitcode."),
-    cl::init(false));
-
 // Models elements in the list of types defined in the types block.
 // These elements can be undefined, a (simple) type, or a function type
 // signature. Note that an extended type is undefined on construction.
@@ -440,7 +434,7 @@
   ErrorStatus = true;
   ++NumErrors;
   NaClBitcodeParser::Error(Message);
-  if (!AllowErrorRecovery)
+  if (!Translator.getFlags().AllowErrorRecovery)
     report_fatal_error("Unable to continue");
   return true;
 }
@@ -612,10 +606,11 @@
   StrBuf << "(" << format("%" PRIu64 ":%u", (Bit / 8),
                           static_cast<unsigned>(Bit % 8)) << ") ";
   // Note: If dump routines have been turned off, the error messages
-  // will not be readable. Hence, replace with simple error.
-  if (ALLOW_DUMP)
+  // will not be readable. Hence, replace with simple error. We also
+  // use the simple form for unit tests.
+  if (ALLOW_DUMP && !getFlags().GenerateUnitTestMessages) {
     StrBuf << Message;
-  else {
+  } else {
     StrBuf << "Invalid " << getBlockName() << " record: <" << Record.GetCode();
     for (const uint64_t Val : Record.GetValues()) {
       StrBuf << " " << Val;
@@ -2967,6 +2962,11 @@
   }
 
   std::unique_ptr<MemoryBuffer> MemBuf(ErrOrFile.get().release());
+  translateBuffer(IRFilename, MemBuf.get());
+}
+
+void PNaClTranslator::translateBuffer(const std::string &IRFilename,
+                                      MemoryBuffer *MemBuf) {
   if (MemBuf->getBufferSize() % 4 != 0) {
     errs() << IRFilename
            << ": Bitcode stream should be a multiple of 4 bytes in length.\n";
diff --git a/src/PNaClTranslator.h b/src/PNaClTranslator.h
index dea7d43..5738f11 100644
--- a/src/PNaClTranslator.h
+++ b/src/PNaClTranslator.h
@@ -19,6 +19,10 @@
 
 #include "IceTranslator.h"
 
+namespace llvm {
+class MemoryBuffer;
+} // end of namespace llvm
+
 namespace Ice {
 
 class PNaClTranslator : public Translator {
@@ -28,10 +32,15 @@
 public:
   PNaClTranslator(GlobalContext *Ctx, const ClFlags &Flags)
       : Translator(Ctx, Flags) {}
+
   // Reads the PNaCl bitcode file and translates to ICE, which is then
   // converted to machine code. Sets ErrorStatus to true if any
   // errors occurred.
   void translate(const std::string &IRFilename);
+
+  // Reads MemBuf, assuming it is the PNaCl bitcode contents of IRFilename.
+  void translateBuffer(const std::string &IRFilename,
+                       llvm::MemoryBuffer *MemBuf);
 };
 
 } // end of namespace Ice
diff --git a/src/llvm2ice.cpp b/src/llvm2ice.cpp
index 53c90ee..3b1813d 100644
--- a/src/llvm2ice.cpp
+++ b/src/llvm2ice.cpp
@@ -167,6 +167,10 @@
                 cl::desc("Build ICE instructions when reading bitcode"),
                 cl::init(true));
 
+static cl::opt<bool> AllowErrorRecovery(
+    "allow-pnacl-reader-error-recovery",
+    cl::desc("Allow error recovery when reading PNaCl bitcode."),
+    cl::init(false));
 
 static cl::opt<bool>
 LLVMVerboseErrors(
@@ -297,6 +301,7 @@
   Flags.VerboseFocusOn = VerboseFocusOn;
   Flags.TranslateOnly = TranslateOnly;
   Flags.DisableIRGeneration = DisableIRGeneration;
+  Flags.AllowErrorRecovery = AllowErrorRecovery;
 
   // Force -build-on-read=0 for .ll files.
   const std::string LLSuffix = ".ll";
diff --git a/unittest/BitcodeMunge.cpp b/unittest/BitcodeMunge.cpp
new file mode 100644
index 0000000..a6d2838
--- /dev/null
+++ b/unittest/BitcodeMunge.cpp
@@ -0,0 +1,40 @@
+//===- BitcodeMunge.cpp - Subzero Bitcode Munger ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Test harness for testing malformed bitcode files in Subzero.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BitcodeMunge.h"
+#include "IceCfg.h"
+#include "IceClFlags.h"
+#include "PNaClTranslator.h"
+#include "IceTypes.h"
+
+namespace IceTest {
+
+bool IceTest::SubzeroBitcodeMunger::runTest(
+    const char* TestName, const uint64_t Munges[], size_t MungeSize) {
+  const bool AddHeader = true;
+  setupTest(TestName, Munges, MungeSize, AddHeader);
+
+  Ice::ClFlags Flags;
+  Flags.AllowErrorRecovery = true;
+  Flags.GenerateUnitTestMessages = true;
+  Ice::GlobalContext Ctx(DumpStream, DumpStream, nullptr,
+                         Ice::IceV_Instructions, Ice::Target_X8632,
+                         Ice::Opt_m1, "", Flags);
+  Ice::PNaClTranslator Translator(&Ctx, Flags);
+  Translator.translateBuffer(TestName, MungedInput.get());
+
+  cleanupTest();
+  return Translator.getErrorStatus() == 0;
+}
+
+} // end of namespace IceTest
diff --git a/unittest/BitcodeMunge.h b/unittest/BitcodeMunge.h
new file mode 100644
index 0000000..ae46ae0
--- /dev/null
+++ b/unittest/BitcodeMunge.h
@@ -0,0 +1,47 @@
+//===- BitcodeMunge.h - Subzero Bitcode Munger ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Test harness for testing malformed bitcode files in Subzero. Uses NaCl's
+// bitcode munger to do this.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_UNITTEST_BITCODEMUNGE_H
+#define SUBZERO_UNITTEST_BITCODEMUNGE_H
+
+#include "llvm/Bitcode/NaCl/NaClBitcodeMunge.h"
+
+namespace IceTest {
+
+// Class to run tests on Subzero's bitcode parser. Runs a Subzero
+// translation, using (possibly) edited bitcode record values.  For
+// more details on how to represent the input arrays, see
+// NaClBitcodeMunge.h.
+class SubzeroBitcodeMunger : public llvm::NaClBitcodeMunger {
+public:
+  SubzeroBitcodeMunger(const uint64_t Records[], size_t RecordSize,
+                       uint64_t RecordTerminator)
+      : llvm::NaClBitcodeMunger(Records, RecordSize, RecordTerminator) {}
+
+  /// Runs PNaClTranslator to translate bitcode records (with defined
+  /// record Munges), and puts output into DumpResults. Returns true
+  /// if parse is successful.
+  bool runTest(const char* TestName, const uint64_t Munges[],
+               size_t MungeSize);
+
+  /// Same as above, but without any edits.
+  bool runTest(const char* TestName) {
+    uint64_t NoMunges[] = {0};
+    return runTest(TestName, NoMunges, 0);
+  }
+};
+
+} // end of namespace IceTest
+
+#endif // SUBZERO_UNITTEST_BITCODEMUNGE_H
diff --git a/unittest/IceParseInstsTest.cpp b/unittest/IceParseInstsTest.cpp
new file mode 100644
index 0000000..abb536c
--- /dev/null
+++ b/unittest/IceParseInstsTest.cpp
@@ -0,0 +1,79 @@
+//===- unittest/IceParseInstsTest.cpp - test instruction errors -----------===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
+#include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h"
+
+#include "BitcodeMunge.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+static const uint64_t Terminator = 0x5768798008978675LL;
+
+/// Test how we report a call arg that refers to nonexistent call argument
+TEST(IceParseInstsTest, NonexistentCallArg) {
+  const uint64_t BitcodeRecords[] = {
+    1, naclbitc::BLK_CODE_ENTER, naclbitc::MODULE_BLOCK_ID, 2, Terminator,
+    1, naclbitc::BLK_CODE_ENTER, naclbitc::TYPE_BLOCK_ID_NEW, 2, Terminator,
+    3, naclbitc::TYPE_CODE_NUMENTRY, 3, Terminator,
+    3, naclbitc::TYPE_CODE_INTEGER, 32, Terminator,
+    3, naclbitc::TYPE_CODE_VOID, Terminator,
+    3, naclbitc::TYPE_CODE_FUNCTION, 0, 1, 0, 0, Terminator,
+    0, naclbitc::BLK_CODE_EXIT, Terminator,
+    3, naclbitc::MODULE_CODE_FUNCTION, 2, 0, 1, 0, Terminator,
+    3, naclbitc::MODULE_CODE_FUNCTION, 2, 0, 0, 0, Terminator,
+    1, naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator,
+    3, naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator,
+    // Note: 100 is a bad value index in next line.
+    3, naclbitc::FUNC_CODE_INST_CALL, 0, 4, 2, 100, Terminator,
+    3, naclbitc::FUNC_CODE_INST_RET, Terminator,
+    0, naclbitc::BLK_CODE_EXIT, Terminator,
+    0, naclbitc::BLK_CODE_EXIT, Terminator
+  };
+
+
+  // Show bitcode objdump for BitcodeRecords.
+  NaClObjDumpMunger DumpMunger(BitcodeRecords,
+                               array_lengthof(BitcodeRecords), Terminator);
+  EXPECT_FALSE(DumpMunger.runTestForAssembly("Nonexistent call arg"));
+  EXPECT_EQ(
+      "module {  // BlockID = 8\n"
+      "  types {  // BlockID = 17\n"
+      "    count 3;\n"
+      "    @t0 = i32;\n"
+      "    @t1 = void;\n"
+      "    @t2 = void (i32, i32);\n"
+      "  }\n"
+      "  declare external void @f0(i32, i32);\n"
+      "  define external void @f1(i32, i32);\n"
+      "  function void @f1(i32 %p0, i32 %p1) {  // BlockID = 12\n"
+      "    blocks 1;\n"
+      "  %b0:\n"
+      "    call void @f0(i32 %p0, i32 @f0);\n"
+      "Error(66:4): Invalid relative value id: 100 (Must be <= 4)\n"
+      "    ret void;\n"
+      "  }\n"
+      "}\n",
+      DumpMunger.getTestResults());
+
+  // Show that we get appropriate error when parsing in Subzero.
+  IceTest::SubzeroBitcodeMunger Munger(
+      BitcodeRecords, array_lengthof(BitcodeRecords), Terminator);
+  EXPECT_FALSE(Munger.runTest("Nonexistent call arg"));
+  EXPECT_EQ(
+      "Error: (66:4) Invalid function record: <34 0 4 2 100>\n",
+      Munger.getTestResults());
+}
+
+} // end of anonymous namespace