//===- 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 <string>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
#include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h"
#pragma clang diagnostic pop

#include "BitcodeMunge.h"
#include "unittests/Bitcode/NaClMungeTest.h"

using namespace llvm;
using namespace naclmungetest;

namespace {

// The ParseError constant is passed to the BitcodeMunger to prevent translation
// when we expect a Parse error.
constexpr bool ParseError = true;

// Note: alignment stored as 0 or log2(Alignment)+1.
uint64_t getEncAlignPower(unsigned Power) {
  return Power + 1;
}
uint64_t getEncAlignZero() { return 0; }

/// 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, 3, Terminator,
    3, naclbitc::MODULE_CODE_FUNCTION, 2, 0, 0, 3, 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(ARRAY_TERM(BitcodeRecords));
  EXPECT_FALSE(DumpMunger.runTest());
  EXPECT_EQ("      66:4|    3: <34, 0, 4, 2, 100>    |    call void @f0(i32 "
            "%p0, i32 @f0);\n"
            "Error(66:4): Invalid relative value id: 100 (Must be <= 4)\n",
            DumpMunger.getLinesWithSubstring("66:4"));

  // Show that we get appropriate error when parsing in Subzero.
  IceTest::SubzeroBitcodeMunger Munger(ARRAY_TERM(BitcodeRecords));
  EXPECT_FALSE(Munger.runTest(ParseError));
  EXPECT_EQ("Error(66:4): Invalid function record: <34 0 4 2 100>\n",
            Munger.getTestResults());

  // Show that we generate a fatal error when not allowing error recovery.
  Ice::ClFlags::Flags.setAllowErrorRecovery(false);
  EXPECT_DEATH(Munger.runTest(ParseError), ".*ERROR: Unable to continue.*");
}

/// Test how we recognize alignments in alloca instructions.
TEST(IceParseInstsTests, AllocaAlignment) {
  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, 4, Terminator,
    3,     naclbitc::TYPE_CODE_INTEGER, 32, Terminator,
    3,     naclbitc::TYPE_CODE_VOID, Terminator,
    3,     naclbitc::TYPE_CODE_FUNCTION, 0, 1, 0, Terminator,
    3,     naclbitc::TYPE_CODE_INTEGER, 8, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    3,   naclbitc::MODULE_CODE_FUNCTION, 2, 0, 0, 3, Terminator,
    1,   naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator,
    3,     naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator,
    3,     naclbitc::FUNC_CODE_INST_ALLOCA, 1, getEncAlignPower(0), Terminator,
    3,     naclbitc::FUNC_CODE_INST_RET, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    0, naclbitc::BLK_CODE_EXIT, Terminator};

  const uint64_t ReplaceIndex = 11; // index for FUNC_CODE_INST_ALLOCA

  // Show text when alignment is 1.
  NaClObjDumpMunger DumpMunger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(DumpMunger.runTest());
  EXPECT_EQ("      62:4|    3: <19, 1, 1>            |    %v0 = alloca i8, i32 "
            "%p0, align 1;\n",
            DumpMunger.getLinesWithSubstring("62:4"));

  // Show that we can handle alignment of 1.
  IceTest::SubzeroBitcodeMunger Munger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(Munger.runTest());

  // Show what happens when changing alignment to 0.
  const uint64_t Align0[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_ALLOCA, 1, getEncAlignZero(), Terminator,
  };
  EXPECT_TRUE(Munger.runTest(ARRAY(Align0)));
  EXPECT_TRUE(DumpMunger.runTestForAssembly(ARRAY(Align0)));
  EXPECT_EQ("    %v0 = alloca i8, i32 %p0, align 0;\n",
            DumpMunger.getLinesWithSubstring("alloca"));

  // Show what happens when changing alignment to 2**30.
  const uint64_t Align30[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_ALLOCA, 1, getEncAlignPower(30), Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align30), ParseError));
  EXPECT_EQ("Error(62:4): Invalid function record: <19 1 31>\n",
            Munger.getTestResults());

  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align30)));
  EXPECT_EQ("    %v0 = alloca i8, i32 %p0, align 0;\n",
            DumpMunger.getLinesWithSubstring("alloca"));
  EXPECT_EQ(
      "Error(62:4): Alignment can't be greater than 2**29. Found: 2**30\n",
      DumpMunger.getLinesWithSubstring("Error"));

  // Show what happens when changing alignment to 2**29.
  const uint64_t Align29[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_ALLOCA, 1, getEncAlignPower(29), Terminator,
  };
  EXPECT_TRUE(Munger.runTest(ARRAY(Align29)));
  EXPECT_TRUE(DumpMunger.runTestForAssembly(ARRAY(Align29)));
  EXPECT_EQ("    %v0 = alloca i8, i32 %p0, align 536870912;\n",
            DumpMunger.getLinesWithSubstring("alloca"));
}

// Test how we recognize alignments in load i32 instructions.
TEST(IceParseInstsTests, LoadI32Alignment) {
  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, 2, Terminator,
    3,     naclbitc::TYPE_CODE_INTEGER, 32, Terminator,
    3,     naclbitc::TYPE_CODE_FUNCTION, 0, 0, 0, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    3,   naclbitc::MODULE_CODE_FUNCTION, 1, 0, 0, 3, Terminator,
    1,   naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator,
    3,     naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator,
    3,     naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(0), 0, Terminator,
    3,     naclbitc::FUNC_CODE_INST_RET, 1, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    0, naclbitc::BLK_CODE_EXIT, Terminator};

  const uint64_t ReplaceIndex = 9; // index for FUNC_CODE_INST_LOAD

  // Show text when alignment is 1.
  NaClObjDumpMunger DumpMunger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(DumpMunger.runTest());
  EXPECT_EQ("      58:4|    3: <20, 1, 1, 0>         |    %v0 = load i32* %p0, "
            "align 1;\n",
            DumpMunger.getLinesWithSubstring("58:4"));
  IceTest::SubzeroBitcodeMunger Munger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(Munger.runTest());

  // Show what happens when changing alignment to 0.
  const uint64_t Align0[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignZero(), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align0), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 0 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align0)));
  EXPECT_EQ("    %v0 = load i32* %p0, align 0;\n"
            "Error(58:4): load: Illegal alignment for i32. Expects: 1\n",
            DumpMunger.getLinesWithSubstring("load"));

  // Show what happens when changing alignment to 4.
  const uint64_t Align4[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(2), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align4), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 3 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align4)));
  EXPECT_EQ("    %v0 = load i32* %p0, align 4;\n"
            "Error(58:4): load: Illegal alignment for i32. Expects: 1\n",
            DumpMunger.getLinesWithSubstring("load"));

  // Show what happens when changing alignment to 2**29.
  const uint64_t Align29[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(29), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align29), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 30 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align29)));
  EXPECT_EQ("    %v0 = load i32* %p0, align 536870912;\n"
            "Error(58:4): load: Illegal alignment for i32. Expects: 1\n",
            DumpMunger.getLinesWithSubstring("load"));

  // Show what happens when changing alignment to 2**30.
  const uint64_t Align30[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(30), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align30), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 31 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align30)));
  EXPECT_EQ("    %v0 = load i32* %p0, align 0;\n"
            "Error(58:4): load: Illegal alignment for i32. Expects: 1\n",
            DumpMunger.getLinesWithSubstring("load"));
}

// Test how we recognize alignments in load float instructions.
TEST(IceParseInstsTests, LoadFloatAlignment) {
  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_FLOAT, Terminator,
    3,     naclbitc::TYPE_CODE_INTEGER, 32, Terminator,
    3,     naclbitc::TYPE_CODE_FUNCTION, 0, 0, 1, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    3,   naclbitc::MODULE_CODE_FUNCTION, 2, 0, 0, 3, Terminator,
    1,   naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator,
    3,     naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator,
    3,     naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(0), 0, Terminator,
    3,     naclbitc::FUNC_CODE_INST_RET, 1, Terminator,
    0,   naclbitc::BLK_CODE_EXIT, Terminator,
    0, naclbitc::BLK_CODE_EXIT, Terminator};

  const uint64_t ReplaceIndex = 10; // index for FUNC_CODE_INST_LOAD

  // Show text when alignment is 1.
  NaClObjDumpMunger DumpMunger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(DumpMunger.runTest());
  EXPECT_EQ("      58:4|    3: <20, 1, 1, 0>         |    %v0 = load float* "
            "%p0, align 1;\n",
            DumpMunger.getLinesWithSubstring("58:4"));
  IceTest::SubzeroBitcodeMunger Munger(ARRAY_TERM(BitcodeRecords));
  EXPECT_TRUE(Munger.runTest());

  // Show what happens when changing alignment to 0.
  const uint64_t Align0[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignZero(), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align0), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 0 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align0)));
  EXPECT_EQ(
      "    %v0 = load float* %p0, align 0;\n"
            "Error(58:4): load: Illegal alignment for float. Expects: 1 or 4\n",
            DumpMunger.getLinesWithSubstring("load"));

  // Show what happens when changing alignment to 4.
  const uint64_t Align4[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(2), 0, Terminator,
  };
  EXPECT_TRUE(Munger.runTest(ARRAY(Align4)));
  EXPECT_TRUE(DumpMunger.runTestForAssembly(ARRAY(Align4)));
  EXPECT_EQ("    %v0 = load float* %p0, align 4;\n",
            DumpMunger.getLinesWithSubstring("load"));

  const uint64_t Align29[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(29), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align29), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 30 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align29)));
  EXPECT_EQ(
      "    %v0 = load float* %p0, align 536870912;\n"
            "Error(58:4): load: Illegal alignment for float. Expects: 1 or 4\n",
            DumpMunger.getLinesWithSubstring("load"));

  // Show what happens when changing alignment to 2**30.
  const uint64_t Align30[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_LOAD, 1, getEncAlignPower(30), 0, Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align30), ParseError));
  EXPECT_EQ("Error(58:4): Invalid function record: <20 1 31 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align30)));
  EXPECT_EQ(
      "    %v0 = load float* %p0, align 0;\n"
            "Error(58:4): load: Illegal alignment for float. Expects: 1 or 4\n",
            DumpMunger.getLinesWithSubstring("load"));
}

// Test how we recognize alignments in store instructions.
TEST(NaClParseInstsTests, StoreAlignment) {
  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_FLOAT, Terminator,
  3,     naclbitc::TYPE_CODE_INTEGER, 32, Terminator,
  3,     naclbitc::TYPE_CODE_FUNCTION, 0, 0, 1, 0, Terminator,
  0,   naclbitc::BLK_CODE_EXIT, Terminator,
  3,   naclbitc::MODULE_CODE_FUNCTION, 2, 0, 0, 3, Terminator,
  1,   naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator,
  3,     naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator,
  3,     naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignPower(0), Terminator,
  3,     naclbitc::FUNC_CODE_INST_RET, 1, Terminator,
  0,   naclbitc::BLK_CODE_EXIT, Terminator,
  0, naclbitc::BLK_CODE_EXIT, Terminator};

  const uint64_t ReplaceIndex = 10; // index for FUNC_CODE_INST_STORE

  // Show text when alignment is 1.
  NaClObjDumpMunger DumpMunger(BitcodeRecords, array_lengthof(BitcodeRecords),
                               Terminator);
  EXPECT_TRUE(DumpMunger.runTest("Good Store Alignment 1"));
  EXPECT_EQ("      62:4|    3: <24, 2, 1, 1>         |    store float %p1, "
            "float* %p0, \n",
            DumpMunger.getLinesWithSubstring("62:4"));
  IceTest::SubzeroBitcodeMunger Munger(
      BitcodeRecords, array_lengthof(BitcodeRecords), Terminator);
  EXPECT_TRUE(Munger.runTest());

  // Show what happens when changing alignment to 0.
  const uint64_t Align0[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignZero(), Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align0), ParseError));
  EXPECT_EQ("Error(62:4): Invalid function record: <24 2 1 0>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align0)));
  EXPECT_EQ(
      "    store float %p1, float* %p0, align 0;\n"
      "Error(62:4): store: Illegal alignment for float. Expects: 1 or 4\n",
      DumpMunger.getLinesWithSubstring("store"));

  // Show what happens when changing alignment to 4.
  const uint64_t Align4[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignPower(2), Terminator,
  };
  EXPECT_TRUE(Munger.runTest(ARRAY(Align4)));
  EXPECT_TRUE(DumpMunger.runTestForAssembly(ARRAY(Align4)));

  // Show what happens when changing alignment to 8.
  const uint64_t Align8[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignPower(3), Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align8), ParseError));
  EXPECT_EQ("Error(62:4): Invalid function record: <24 2 1 4>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align8)));
  EXPECT_EQ(
      "    store float %p1, float* %p0, align 8;\n"
      "Error(62:4): store: Illegal alignment for float. Expects: 1 or 4\n",
      DumpMunger.getLinesWithSubstring("store"));

  // Show what happens when changing alignment to 2**29.
  const uint64_t Align29[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      3, naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignPower(29), Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align29), ParseError));
  EXPECT_EQ("Error(62:4): Invalid function record: <24 2 1 30>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align29)));
  EXPECT_EQ(
      "    store float %p1, float* %p0, align 536870912;\n"
      "Error(62:4): store: Illegal alignment for float. Expects: 1 or 4\n",
      DumpMunger.getLinesWithSubstring("store"));

  const uint64_t Align30[] = {
      ReplaceIndex, NaClMungedBitcode::Replace,
      // Note: alignment stored as 0 or log2(Alignment)+1.
      3, naclbitc::FUNC_CODE_INST_STORE, 2, 1, getEncAlignPower(30), Terminator,
  };
  EXPECT_FALSE(Munger.runTest(ARRAY(Align30), ParseError));
  EXPECT_EQ("Error(62:4): Invalid function record: <24 2 1 31>\n",
            Munger.getTestResults());
  EXPECT_FALSE(DumpMunger.runTestForAssembly(ARRAY(Align30)));
  EXPECT_EQ(
      "    store float %p1, float* %p0, align 0;\n"
      "Error(62:4): store: Illegal alignment for float. Expects: 1 or 4\n",
      DumpMunger.getLinesWithSubstring("store"));
}

} // end of anonymous namespace
