//===- NaClBitcodeHeader.cpp ----------------------------------------------===//
//     PNaCl bitcode header reader.
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of Bitcode abbrevations.
//
//===----------------------------------------------------------------------===//

#include "llvm/Bitcode/NaCl/NaClBitCodes.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"

using namespace llvm;

const bool NaClBitCodeAbbrevOp::HasValueArray[] = {
  true,  // Literal
  true,  // Fixed
  true,  // VBR
  false, // Array
  false  // Char6
};

const char *NaClBitCodeAbbrevOp::EncodingNameArray[] = {
  "Literal",
  "Fixed",
  "VBR",
  "Array",
  "Char6"
};

NaClBitCodeAbbrevOp::NaClBitCodeAbbrevOp(Encoding E, uint64_t Data)
    : Enc(E), Val(Data) {
  if (isValid(E, Data)) return;
  std::string Buffer;
  raw_string_ostream StrBuf(Buffer);
  StrBuf << "Invalid NaClBitCodeAbbrevOp(" << E << ", " << Data << ")";
  report_fatal_error(StrBuf.str());
}

bool NaClBitCodeAbbrevOp::isValid(Encoding E, uint64_t Val) {
  switch (NaClBitCodeAbbrevOp::Encoding(E)) {
  case Literal:
    return true;
  case Fixed:
  case VBR:
    return Val <= naclbitc::MaxAbbrevWidth;
  case Char6:
  case Array:
    return Val == 0;
  }
  llvm_unreachable("unhandled abbreviation");
}

void NaClBitCodeAbbrevOp::Print(raw_ostream& Stream) const {
  if (Enc == Literal) {
    Stream << getValue();
    return;
  }
  Stream << getEncodingName(Enc);
  if (!hasValue())
    return;
  Stream << "(" << Val << ")";
}

static void PrintExpression(raw_ostream &Stream,
                            const NaClBitCodeAbbrev *Abbrev,
                            unsigned &Index) {
  // Bail out early, in case we are incrementally building the
  // expression and the argument is not available yet.
  if (Index >= Abbrev->getNumOperandInfos()) return;

  const NaClBitCodeAbbrevOp &Op = Abbrev->getOperandInfo(Index);
  Op.Print(Stream);
  if (unsigned NumArgs = Op.NumArguments()) {
    Stream << "(";
    for (unsigned i = 0; i < NumArgs; ++i) {
      ++Index;
      if (i > 0) Stream << ",";
      PrintExpression(Stream, Abbrev, Index);
    }
    Stream << ")";
  }
}

void NaClBitCodeAbbrev::Print(raw_ostream &Stream, bool AddNewLine) const {
  Stream << "[";
  for (unsigned i = 0; i < getNumOperandInfos(); ++i) {
    if (i > 0) Stream << ", ";
    PrintExpression(Stream, this, i);
  }
  Stream << "]";
  if (AddNewLine) Stream << "\n";
}

NaClBitCodeAbbrev *NaClBitCodeAbbrev::Simplify() const {
  NaClBitCodeAbbrev *Abbrev = new NaClBitCodeAbbrev();
  for (unsigned i = 0; i < OperandList.size(); ++i) {
    const NaClBitCodeAbbrevOp &Op = OperandList[i];
    // Simplify if possible.  Currently, the only simplification known
    // is to remove unnecessary operands appearing immediately before an
    // array operator. That is, apply the simplification:
    //    Op Array(Op) -> Array(Op)
    assert(!Op.isArrayOp() || i == OperandList.size()-2);
    while (Op.isArrayOp() && !Abbrev->OperandList.empty() &&
           Abbrev->OperandList.back() == OperandList[i+1]) {
      Abbrev->OperandList.pop_back();
    }
    Abbrev->OperandList.push_back(Op);
  }
  return Abbrev;
}

bool NaClBitCodeAbbrev::isValid() const {
  // Verify that an array op appears can only appear if it is the
  // second to last element.
  unsigned NumOperands = getNumOperandInfos();
  if (NumOperands == 0) return false;
  for (unsigned i = 0; i < NumOperands; ++i) {
    const NaClBitCodeAbbrevOp &Op = getOperandInfo(i);
    if (Op.isArrayOp() && i + 2 != NumOperands)
      // Note: Unlike LLVM bitcode, we allow literals in arrays!
      return false;
  }
  return true;
}