//===- subzero/src/WasmTranslator.cpp - WASM to Subzero Translation -------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines a driver for translating Wasm bitcode into PNaCl bitcode.
///
/// The translator uses V8's WebAssembly decoder to handle the binary Wasm
/// format but replaces the usual TurboFan builder with a new PNaCl builder.
///
//===----------------------------------------------------------------------===//

#if ALLOW_WASM

#include "WasmTranslator.h"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wcovered-switch-default"
#endif // __clang__
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif // defined(__GNUC__) && !defined(__clang__)

#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/zone.h"

#include "src/bit-vector.h"

#include "src/wasm/ast-decoder-impl.h"

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif // defined(__GNUC__) && !defined(__clang__)

#include "IceCfgNode.h"
#include "IceGlobalInits.h"

using namespace std;
using namespace Ice;
using namespace v8::internal;
using namespace v8::internal::wasm;
using v8::internal::wasm::DecodeWasmModule;

#undef LOG
#define LOG(Expr) log([&](Ostream & out) { Expr; })

namespace {
// 64KB
const uint32_t WASM_PAGE_SIZE = 64 << 10;

std::string toStdString(WasmName Name) {
  return std::string(Name.name, Name.length);
}

Ice::Type toIceType(wasm::LocalType Type) {
  switch (Type) {
  case MachineRepresentation::kNone:
    llvm::report_fatal_error("kNone type not supported");
  case MachineRepresentation::kBit:
    return IceType_i1;
  case MachineRepresentation::kWord8:
    return IceType_i8;
  case MachineRepresentation::kWord16:
    return IceType_i16;
  case MachineRepresentation::kWord32:
    return IceType_i32;
  case MachineRepresentation::kWord64:
    return IceType_i64;
  case MachineRepresentation::kFloat32:
    return IceType_f32;
  case MachineRepresentation::kFloat64:
    return IceType_f64;
  case MachineRepresentation::kSimd128:
    llvm::report_fatal_error("ambiguous SIMD type");
  case MachineRepresentation::kTagged:
    llvm::report_fatal_error("kTagged type not supported");
  }
  llvm::report_fatal_error("unexpected type");
}

Ice::Type toIceType(v8::internal::MachineType Type) {
  // TODO (eholk): reorder these based on expected call frequency.
  if (Type == MachineType::Int32()) {
    return IceType_i32;
  }
  if (Type == MachineType::Uint32()) {
    return IceType_i32;
  }
  if (Type == MachineType::Int8()) {
    return IceType_i8;
  }
  if (Type == MachineType::Uint8()) {
    return IceType_i8;
  }
  if (Type == MachineType::Int16()) {
    return IceType_i16;
  }
  if (Type == MachineType::Uint16()) {
    return IceType_i16;
  }
  if (Type == MachineType::Int64()) {
    return IceType_i64;
  }
  if (Type == MachineType::Uint64()) {
    return IceType_i64;
  }
  if (Type == MachineType::Float32()) {
    return IceType_f32;
  }
  if (Type == MachineType::Float64()) {
    return IceType_f64;
  }
  llvm::report_fatal_error("Unsupported MachineType");
}

std::string fnNameFromId(uint32_t Id) {
  return std::string("fn") + to_string(Id);
}

std::string getFunctionName(const WasmModule *Module, uint32_t func_index) {
  // Try to find the function name in the export table
  for (const auto Export : Module->export_table) {
    if (Export.func_index == func_index) {
      return "__szwasm_" + toStdString(Module->GetName(Export.name_offset,
                                                       Export.name_length));
    }
  }
  return fnNameFromId(func_index);
}

} // end of anonymous namespace

/// This class wraps either an Operand or a CfgNode.
///
/// Turbofan's sea of nodes representation only has nodes for values, control
/// flow, etc. In Subzero these concepts are all separate. This class lets V8's
/// Wasm decoder treat Subzero objects as though they are all the same.
class OperandNode {
  static constexpr uintptr_t NODE_FLAG = 1;
  static constexpr uintptr_t UNDEF_PTR = (uintptr_t)-1;

  uintptr_t Data = UNDEF_PTR;

public:
  OperandNode() = default;
  explicit OperandNode(Operand *Operand)
      : Data(reinterpret_cast<uintptr_t>(Operand)) {}
  explicit OperandNode(CfgNode *Node)
      : Data(reinterpret_cast<uintptr_t>(Node) | NODE_FLAG) {}
  explicit OperandNode(nullptr_t) : Data(UNDEF_PTR) {}

  operator Operand *() const {
    if (UNDEF_PTR == Data) {
      return nullptr;
    }
    if (!isOperand()) {
      llvm::report_fatal_error("This OperandNode is not an Operand");
    }
    return reinterpret_cast<Operand *>(Data);
  }

  operator CfgNode *() const {
    if (UNDEF_PTR == Data) {
      return nullptr;
    }
    if (!isCfgNode()) {
      llvm::report_fatal_error("This OperandNode is not a CfgNode");
    }
    return reinterpret_cast<CfgNode *>(Data & ~NODE_FLAG);
  }

  explicit operator bool() const { return (Data != UNDEF_PTR) && Data; }
  bool operator==(const OperandNode &Rhs) const {
    return (Data == Rhs.Data) ||
           (UNDEF_PTR == Data && (Rhs.Data == 0 || Rhs.Data == NODE_FLAG)) ||
           (UNDEF_PTR == Rhs.Data && (Data == 0 || Data == NODE_FLAG));
  }
  bool operator!=(const OperandNode &Rhs) const { return !(*this == Rhs); }

  bool isOperand() const { return (Data != UNDEF_PTR) && !(Data & NODE_FLAG); }
  bool isCfgNode() const { return (Data != UNDEF_PTR) && (Data & NODE_FLAG); }

  Operand *toOperand() const { return static_cast<Operand *>(*this); }

  CfgNode *toCfgNode() const { return static_cast<CfgNode *>(*this); }
};

Ostream &operator<<(Ostream &Out, const OperandNode &Op) {
  if (Op.isOperand()) {
    const auto *Oper = Op.toOperand();
    Out << "(Operand*)" << Oper;
    if (Oper) {
      Out << "::" << Oper->getType();
    }
  } else if (Op.isCfgNode()) {
    Out << "(CfgNode*)" << Op.toCfgNode();
  } else {
    Out << "nullptr";
  }
  return Out;
}

bool isComparison(wasm::WasmOpcode Opcode) {
  switch (Opcode) {
  case kExprI32Ne:
  case kExprI64Ne:
  case kExprI32Eq:
  case kExprI64Eq:
  case kExprI32LtS:
  case kExprI64LtS:
  case kExprI32LtU:
  case kExprI64LtU:
  case kExprI32GeS:
  case kExprI64GeS:
  case kExprI32GtS:
  case kExprI64GtS:
  case kExprI32GtU:
  case kExprI64GtU:
  case kExprF32Eq:
  case kExprF64Eq:
  case kExprF32Ne:
  case kExprF64Ne:
  case kExprF32Le:
  case kExprF64Le:
  case kExprF32Lt:
  case kExprF64Lt:
  case kExprF32Ge:
  case kExprF64Ge:
  case kExprF32Gt:
  case kExprF64Gt:
  case kExprI32LeS:
  case kExprI64LeS:
  case kExprI32GeU:
  case kExprI64GeU:
  case kExprI32LeU:
  case kExprI64LeU:
    return true;
  default:
    return false;
  }
}

class IceBuilder {
  using Node = OperandNode;
  using Variable = Ice::Variable;

  IceBuilder() = delete;
  IceBuilder(const IceBuilder &) = delete;
  IceBuilder &operator=(const IceBuilder &) = delete;

public:
  explicit IceBuilder(class Cfg *Func)
      : ControlPtr(nullptr), Func(Func), Ctx(Func->getContext()) {}

  /// Allocates a buffer of Nodes for use by V8.
  Node *Buffer(size_t Count) {
    LOG(out << "Buffer(" << Count << ")\n");
    return Func->allocateArrayOf<Node>(Count);
  }

  Node Error() { llvm::report_fatal_error("Error"); }
  Node Start(uint32_t Params) {
    LOG(out << "Start(" << Params << ") = ");
    auto *Entry = Func->getEntryNode();
    assert(Entry);
    LOG(out << Node(Entry) << "\n");

    // Load the WasmMemory address to make it available everywhere else in the
    // function.
    auto *WasmMemoryPtr =
        Ctx->getConstantExternSym(Ctx->getGlobalString("WASM_MEMORY"));
    assert(WasmMemory == nullptr);
    auto *WasmMemoryV = makeVariable(getPointerType());
    Entry->appendInst(InstLoad::create(Func, WasmMemoryV, WasmMemoryPtr));
    WasmMemory = WasmMemoryV;

    return OperandNode(Entry);
  }
  Node Param(uint32_t Index, wasm::LocalType Type) {
    LOG(out << "Param(" << Index << ") = ");
    auto *Arg = makeVariable(toIceType(Type));
    assert(Index == NextArg);
    Func->addArg(Arg);
    ++NextArg;
    LOG(out << Node(Arg) << "\n");
    return OperandNode(Arg);
  }
  Node Loop(CfgNode *Entry) {
    auto *Loop = Func->makeNode();
    LOG(out << "Loop(" << Entry << ") = " << Loop << "\n");
    Entry->appendInst(InstBr::create(Func, Loop));
    return OperandNode(Loop);
  }
  void Terminate(Node Effect, Node Control) {
    // TODO(eholk): this is almost certainly wrong
    LOG(out << "Terminate(" << Effect << ", " << Control << ")"
            << "\n");
  }
  Node Merge(uint32_t Count, Node *Controls) {
    LOG(out << "Merge(" << Count);
    for (uint32_t i = 0; i < Count; ++i) {
      LOG(out << ", " << Controls[i]);
    }
    LOG(out << ") = ");

    auto *MergedNode = Func->makeNode();

    for (uint32_t i = 0; i < Count; ++i) {
      CfgNode *Control = Controls[i];
      Control->appendInst(InstBr::create(Func, MergedNode));
    }
    LOG(out << (OperandNode)MergedNode << "\n");
    return OperandNode(MergedNode);
  }
  Node Phi(wasm::LocalType, uint32_t Count, Node *Vals, Node Control) {
    LOG(out << "Phi(" << Count << ", " << Control);
    for (uint32_t i = 0; i < Count; ++i) {
      LOG(out << ", " << Vals[i]);
    }
    LOG(out << ") = ");

    const auto &InEdges = Control.toCfgNode()->getInEdges();
    assert(Count == InEdges.size());

    assert(Count > 0);

    auto *Dest = makeVariable(Vals[0].toOperand()->getType(), Control);

    // Multiply by 200 in case more things get added later.

    // TODO(eholk): find a better way besides multiplying by some arbitrary
    // constant.
    auto *Phi = InstPhi::create(Func, Count * 200, Dest);
    for (uint32_t i = 0; i < Count; ++i) {
      auto *Op = Vals[i].toOperand();
      assert(Op);
      Phi->addArgument(Op, InEdges[i]);
    }
    setDefiningInst(Dest, Phi);
    Control.toCfgNode()->appendInst(Phi);
    LOG(out << Node(Dest) << "\n");
    return OperandNode(Dest);
  }
  Node EffectPhi(uint32_t Count, Node *Effects, Node Control) {
    // TODO(eholk): this function is almost certainly wrong.
    LOG(out << "EffectPhi(" << Count << ", " << Control << "):\n");
    for (uint32_t i = 0; i < Count; ++i) {
      LOG(out << "  " << Effects[i] << "\n");
    }
    return OperandNode(nullptr);
  }
  Node Int32Constant(int32_t Value) {
    LOG(out << "Int32Constant(" << Value << ") = ");
    auto *Const = Ctx->getConstantInt32(Value);
    assert(Const);
    assert(Control());
    LOG(out << Node(Const) << "\n");
    return OperandNode(Const);
  }
  Node Int64Constant(int64_t Value) {
    LOG(out << "Int64Constant(" << Value << ") = ");
    auto *Const = Ctx->getConstantInt64(Value);
    assert(Const);
    LOG(out << Node(Const) << "\n");
    return OperandNode(Const);
  }
  Node Float32Constant(float Value) {
    LOG(out << "Float32Constant(" << Value << ") = ");
    auto *Const = Ctx->getConstantFloat(Value);
    assert(Const);
    LOG(out << Node(Const) << "\n");
    return OperandNode(Const);
  }
  Node Float64Constant(double Value) {
    LOG(out << "Float64Constant(" << Value << ") = ");
    auto *Const = Ctx->getConstantDouble(Value);
    assert(Const);
    LOG(out << Node(Const) << "\n");
    return OperandNode(Const);
  }
  Node Binop(wasm::WasmOpcode Opcode, Node Left, Node Right) {
    LOG(out << "Binop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Left
            << ", " << Right << ") = ");
    BooleanVariable *BoolDest = nullptr;
    Variable *Dest = nullptr;
    if (isComparison(Opcode)) {
      BoolDest = makeVariable<BooleanVariable>(IceType_i32);
      Dest = BoolDest;
    } else {
      Dest = makeVariable(Left.toOperand()->getType());
    }
    switch (Opcode) {
    case kExprI32Add:
    case kExprI64Add:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Add, Dest, Left, Right));
      break;
    case kExprF32Add:
    case kExprF64Add:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Fadd,
                                                   Dest, Left, Right));
      break;
    case kExprI32Sub:
    case kExprI64Sub:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Sub, Dest, Left, Right));
      break;
    case kExprF32Sub:
    case kExprF64Sub:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Fsub,
                                                   Dest, Left, Right));
      break;
    case kExprI32Mul:
    case kExprI64Mul:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Mul, Dest, Left, Right));
      break;
    case kExprF32Mul:
    case kExprF64Mul:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Fmul,
                                                   Dest, Left, Right));
      break;
    case kExprI32DivS:
    case kExprI64DivS:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Sdiv,
                                                   Dest, Left, Right));
      break;
    case kExprI32DivU:
    case kExprI64DivU:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Udiv,
                                                   Dest, Left, Right));
      break;
    case kExprF32Div:
    case kExprF64Div:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Fdiv,
                                                   Dest, Left, Right));
      break;
    case kExprI32RemU:
    case kExprI64RemU:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Urem,
                                                   Dest, Left, Right));
      break;
    case kExprI32RemS:
    case kExprI64RemS:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Srem,
                                                   Dest, Left, Right));
      break;
    case kExprI32Ior:
    case kExprI64Ior:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Or, Dest, Left, Right));
      break;
    case kExprI32Xor:
    case kExprI64Xor:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Xor, Dest, Left, Right));
      break;
    case kExprI32Shl:
    case kExprI64Shl:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Shl, Dest, Left, Right));
      break;
    case kExprI32Rol:
    case kExprI64Rol: {
      // TODO(eholk): add rotate as an ICE instruction to make it easier to take
      // advantage of hardware support.

      const auto DestTy = Left.toOperand()->getType();
      const SizeT BitCount = typeWidthInBytes(DestTy) * CHAR_BIT;

      auto *Masked = makeVariable(DestTy);
      auto *Bottom = makeVariable(DestTy);
      auto *Top = makeVariable(DestTy);
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::And, Masked, Right,
                                 Ctx->getConstantInt(DestTy, BitCount - 1)));
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Shl, Top, Left, Masked));
      auto *RotShift = makeVariable(DestTy);
      Control()->appendInst(InstArithmetic::create(
          Func, InstArithmetic::Sub, RotShift,
          Ctx->getConstantInt(DestTy, BitCount), Masked));
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Lshr,
                                                   Bottom, Left, RotShift));
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Or, Dest, Top, Bottom));
      break;
    }
    case kExprI32Ror:
    case kExprI64Ror: {
      // TODO(eholk): add rotate as an ICE instruction to make it easier to take
      // advantage of hardware support.

      const auto DestTy = Left.toOperand()->getType();
      const SizeT BitCount = typeWidthInBytes(DestTy) * CHAR_BIT;

      auto *Masked = makeVariable(DestTy);
      auto *Bottom = makeVariable(DestTy);
      auto *Top = makeVariable(DestTy);
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::And, Masked, Right,
                                 Ctx->getConstantInt(DestTy, BitCount - 1)));
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Lshr,
                                                   Top, Left, Masked));
      auto *RotShift = makeVariable(DestTy);
      Control()->appendInst(InstArithmetic::create(
          Func, InstArithmetic::Sub, RotShift,
          Ctx->getConstantInt(DestTy, BitCount), Masked));
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Shl,
                                                   Bottom, Left, RotShift));
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::Or, Dest, Top, Bottom));
      break;
    }
    case kExprI32ShrU:
    case kExprI64ShrU:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Lshr,
                                                   Dest, Left, Right));
      break;
    case kExprI32ShrS:
    case kExprI64ShrS:
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Ashr,
                                                   Dest, Left, Right));
      break;
    case kExprI32And:
    case kExprI64And:
      Control()->appendInst(
          InstArithmetic::create(Func, InstArithmetic::And, Dest, Left, Right));
      break;
    case kExprI32Ne:
    case kExprI64Ne: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Ne, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32Eq:
    case kExprI64Eq: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Eq, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32LtS:
    case kExprI64LtS: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Slt, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32LeS:
    case kExprI64LeS: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Sle, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32GeU:
    case kExprI64GeU: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Uge, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32LeU:
    case kExprI64LeU: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Ule, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32LtU:
    case kExprI64LtU: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Ult, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32GeS:
    case kExprI64GeS: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Sge, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32GtS:
    case kExprI64GtS: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Sgt, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprI32GtU:
    case kExprI64GtU: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstIcmp::create(Func, InstIcmp::Ugt, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Eq:
    case kExprF64Eq: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Ueq, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Ne:
    case kExprF64Ne: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Une, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Le:
    case kExprF64Le: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Ule, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Lt:
    case kExprF64Lt: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Ult, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Ge:
    case kExprF64Ge: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Uge, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    case kExprF32Gt:
    case kExprF64Gt: {
      auto *TmpDest = makeVariable(IceType_i1);
      Control()->appendInst(
          InstFcmp::create(Func, InstFcmp::Ugt, TmpDest, Left, Right));
      BoolDest->setBoolSource(TmpDest);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, TmpDest));
      break;
    }
    default:
      LOG(out << "Unknown binop: " << WasmOpcodes::OpcodeName(Opcode) << "\n");
      llvm::report_fatal_error("Uncovered or invalid binop.");
      return OperandNode(nullptr);
    }
    LOG(out << Dest << "\n");
    return OperandNode(Dest);
  }
  Node Unop(wasm::WasmOpcode Opcode, Node Input) {
    LOG(out << "Unop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Input
            << ") = ");
    Variable *Dest = nullptr;
    switch (Opcode) {
    // TODO (eholk): merge these next two cases using getConstantInteger
    case kExprI32Eqz: {
      auto *BoolDest = makeVariable<BooleanVariable>(IceType_i32);
      Dest = BoolDest;
      auto *Tmp = makeVariable(IceType_i1);
      Control()->appendInst(InstIcmp::create(Func, InstIcmp::Eq, Tmp, Input,
                                             Ctx->getConstantInt32(0)));
      BoolDest->setBoolSource(Tmp);
      Control()->appendInst(InstCast::create(Func, InstCast::Zext, Dest, Tmp));
      break;
    }
    case kExprI64Eqz: {
      auto *BoolDest = makeVariable<BooleanVariable>(IceType_i32);
      Dest = BoolDest;
      auto *Tmp = makeVariable(IceType_i1);
      Control()->appendInst(InstIcmp::create(Func, InstIcmp::Eq, Tmp, Input,
                                             Ctx->getConstantInt64(0)));
      BoolDest->setBoolSource(Tmp);
      Control()->appendInst(InstCast::create(Func, InstCast::Zext, Dest, Tmp));
      break;
    }
    case kExprI32Ctz: {
      Dest = makeVariable(IceType_i32);
      const auto FnName = Ctx->getGlobalString("llvm.cttz.i32");
      bool BadInstrinsic = false;
      const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
      assert(!BadInstrinsic);
      assert(Info);

      auto *Call = InstIntrinsicCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF32Neg: {
      Dest = makeVariable(IceType_f32);
      Control()->appendInst(InstArithmetic::create(
          Func, InstArithmetic::Fsub, Dest, Ctx->getConstantFloat(0), Input));
      break;
    }
    case kExprF64Neg: {
      Dest = makeVariable(IceType_f64);
      Control()->appendInst(InstArithmetic::create(
          Func, InstArithmetic::Fsub, Dest, Ctx->getConstantDouble(0), Input));
      break;
    }
    case kExprF32Abs: {
      Dest = makeVariable(IceType_f32);
      const auto FnName = Ctx->getGlobalString("llvm.fabs.f32");
      bool BadInstrinsic = false;
      const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
      assert(!BadInstrinsic);
      assert(Info);

      auto *Call = InstIntrinsicCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF64Abs: {
      Dest = makeVariable(IceType_f64);
      const auto FnName = Ctx->getGlobalString("llvm.fabs.f64");
      bool BadInstrinsic = false;
      const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
      assert(!BadInstrinsic);
      assert(Info);

      auto *Call = InstIntrinsicCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF32Floor: {
      Dest = makeVariable(IceType_f64);
      const auto FnName = Ctx->getGlobalString("env$$floor_f");
      constexpr bool HasTailCall = false;

      auto *Call = InstCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), HasTailCall);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF64Floor: {
      Dest = makeVariable(IceType_f64);
      const auto FnName = Ctx->getGlobalString("env$$floor_d");
      constexpr bool HasTailCall = false;

      auto *Call = InstCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), HasTailCall);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF32Sqrt: {
      Dest = makeVariable(IceType_f32);
      const auto FnName = Ctx->getGlobalString("llvm.sqrt.f32");
      bool BadInstrinsic = false;
      const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
      assert(!BadInstrinsic);
      assert(Info);

      auto *Call = InstIntrinsicCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprF64Sqrt: {
      Dest = makeVariable(IceType_f64);
      const auto FnName = Ctx->getGlobalString("llvm.sqrt.f64");
      bool BadInstrinsic = false;
      const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
      assert(!BadInstrinsic);
      assert(Info);

      auto *Call = InstIntrinsicCall::create(
          Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
      Call->addArg(Input);
      Control()->appendInst(Call);
      break;
    }
    case kExprI64UConvertI32:
      Dest = makeVariable(IceType_i64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Zext, Dest, Input));
      break;
    case kExprI64SConvertI32:
      Dest = makeVariable(IceType_i64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Sext, Dest, Input));
      break;
    case kExprI32SConvertF32:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fptosi, Dest, Input));
      break;
    case kExprI32UConvertF32:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fptoui, Dest, Input));
      break;
    case kExprI32SConvertF64:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fptosi, Dest, Input));
      break;
    case kExprI32UConvertF64:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fptoui, Dest, Input));
      break;
    case kExprI32ReinterpretF32:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Bitcast, Dest, Input));
      break;
    case kExprI64ReinterpretF64:
      Dest = makeVariable(IceType_i64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Bitcast, Dest, Input));
      break;
    case kExprF64ReinterpretI64:
      Dest = makeVariable(IceType_f64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Bitcast, Dest, Input));
      break;
    case kExprI32ConvertI64:
      Dest = makeVariable(IceType_i32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Trunc, Dest, Input));
      break;
    case kExprF64SConvertI32:
      Dest = makeVariable(IceType_f64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Sitofp, Dest, Input));
      break;
    case kExprF64UConvertI32:
      Dest = makeVariable(IceType_f64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Uitofp, Dest, Input));
      break;
    case kExprF64ConvertF32:
      Dest = makeVariable(IceType_f64);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fpext, Dest, Input));
      break;
    case kExprF32SConvertI32:
      Dest = makeVariable(IceType_f32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Sitofp, Dest, Input));
      break;
    case kExprF32UConvertI32:
      Dest = makeVariable(IceType_f32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Uitofp, Dest, Input));
      break;
    case kExprF32ReinterpretI32:
      Dest = makeVariable(IceType_f32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Bitcast, Dest, Input));
      break;
    case kExprF32ConvertF64:
      Dest = makeVariable(IceType_f32);
      Control()->appendInst(
          InstCast::create(Func, InstCast::Fptrunc, Dest, Input));
      break;
    default:
      LOG(out << "Unknown unop: " << WasmOpcodes::OpcodeName(Opcode) << "\n");
      llvm::report_fatal_error("Uncovered or invalid unop.");
      return OperandNode(nullptr);
    }
    LOG(out << Dest << "\n");
    return OperandNode(Dest);
  }
  uint32_t InputCount(CfgNode *Node) const { return Node->getInEdges().size(); }
  bool IsPhiWithMerge(Node Phi, Node Merge) const {
    LOG(out << "IsPhiWithMerge(" << Phi << ", " << Merge << ")"
            << "\n");
    if (Phi && Phi.isOperand()) {
      LOG(out << "  ...is operand"
              << "\n");
      if (getDefiningInst(Phi)) {
        LOG(out << "  ...has defining instruction"
                << "\n");
        LOG(out << getDefNode(Phi) << "\n");
        LOG(out << "  ..." << (getDefNode(Phi) == Merge) << "\n");
        return getDefNode(Phi) == Merge;
      }
    }
    return false;
  }
  void AppendToMerge(CfgNode *Merge, CfgNode *From) const {
    From->appendInst(InstBr::create(Func, Merge));
  }
  void AppendToPhi(Node Merge, Node Phi, Node From) {
    LOG(out << "AppendToPhi(" << Merge << ", " << Phi << ", " << From << ")"
            << "\n");
    auto *Inst = getDefiningInst(Phi);
    assert(Inst->getDest()->getType() == From.toOperand()->getType());
    Inst->addArgument(From, getDefNode(From));
  }

  //-----------------------------------------------------------------------
  // Operations that read and/or write {control} and {effect}.
  //-----------------------------------------------------------------------
  Node Branch(Node Cond, Node *TrueNode, Node *FalseNode) {
    // true_node and false_node appear to be out parameters.
    LOG(out << "Branch(" << Cond << ", ");

    // save control here because true_node appears to alias control.
    auto *Ctrl = Control();

    *TrueNode = OperandNode(Func->makeNode());
    *FalseNode = OperandNode(Func->makeNode());

    LOG(out << *TrueNode << ", " << *FalseNode << ")"
            << "\n");

    auto *CondBool = Cond.toOperand()->asBoolean();
    if (CondBool == nullptr) {
      CondBool = makeVariable(IceType_i1);
      Ctrl->appendInst(InstIcmp::create(Func, InstIcmp::Ne, CondBool, Cond,
                                        Ctx->getConstantInt32(0)));
    }

    Ctrl->appendInst(InstBr::create(Func, CondBool, *TrueNode, *FalseNode));
    return OperandNode(nullptr);
  }
  InstSwitch *CurrentSwitch = nullptr;
  CfgNode *SwitchNode = nullptr;
  SizeT SwitchIndex = 0;
  Node Switch(uint32_t Count, Node Key) {
    LOG(out << "Switch(" << Count << ", " << Key << ")\n");

    assert(!CurrentSwitch);

    auto *Default = Func->makeNode();
    // Count - 1 because the decoder counts the default label but Subzero does
    // not.
    CurrentSwitch = InstSwitch::create(Func, Count - 1, Key, Default);
    SwitchIndex = 0;
    SwitchNode = Control();
    // We don't actually append the switch to the CfgNode here because not all
    // the branches are ready.
    return Node(nullptr);
  }
  Node IfValue(int32_t Value, Node) {
    LOG(out << "IfValue(" << Value << ") [Index = " << SwitchIndex << "]\n");
    assert(CurrentSwitch);
    auto *Target = Func->makeNode();
    CurrentSwitch->addBranch(SwitchIndex++, Value, Target);
    return Node(Target);
  }
  Node IfDefault(Node) {
    LOG(out << "IfDefault(...) [Index = " << SwitchIndex << "]\n");
    assert(CurrentSwitch);
    assert(CurrentSwitch->getLabelDefault());
    // Now we append the switch, since this should be the last edge.
    assert(SwitchIndex == CurrentSwitch->getNumCases());
    SwitchNode->appendInst(CurrentSwitch);
    SwitchNode = nullptr;
    auto Default = Node(CurrentSwitch->getLabelDefault());
    CurrentSwitch = nullptr;
    return Default;
  }
  Node Return(uint32_t Count, Node *Vals) {
    assert(1 >= Count);
    LOG(out << "Return(");
    if (Count > 0)
      LOG(out << Vals[0]);
    LOG(out << ")"
            << "\n");
    auto *Instr =
        1 == Count ? InstRet::create(Func, Vals[0]) : InstRet::create(Func);
    Control()->appendInst(Instr);
    Control()->setHasReturn();
    LOG(out << Node(nullptr) << "\n");
    return OperandNode(nullptr);
  }
  Node ReturnVoid() {
    LOG(out << "ReturnVoid() = ");
    auto *Instr = InstRet::create(Func);
    Control()->appendInst(Instr);
    Control()->setHasReturn();
    LOG(out << Node(nullptr) << "\n");
    return OperandNode(nullptr);
  }
  Node Unreachable() {
    LOG(out << "Unreachable() = ");
    auto *Instr = InstUnreachable::create(Func);
    Control()->appendInst(Instr);
    LOG(out << Node(nullptr) << "\n");
    return OperandNode(nullptr);
  }

  Node CallDirect(uint32_t Index, Node *Args) {
    LOG(out << "CallDirect(" << Index << ")"
            << "\n");
    assert(Module->IsValidFunction(Index));
    const auto *Module = this->Module->module;
    assert(Module);
    const auto &Target = Module->functions[Index];
    const auto *Sig = Target.sig;
    assert(Sig);
    const auto NumArgs = Sig->parameter_count();
    LOG(out << "  number of args: " << NumArgs << "\n");

    const auto TargetName = getFunctionName(Module, Index);
    LOG(out << "  target name: " << TargetName << "\n");

    assert(Sig->return_count() <= 1);

    auto TargetOperand =
        Ctx->getConstantSym(0, Ctx->getGlobalString(TargetName));

    auto *Dest = Sig->return_count() > 0
                     ? makeVariable(toIceType(Sig->GetReturn()))
                     : nullptr;
    auto *Call = InstCall::create(Func, NumArgs, Dest, TargetOperand,
                                  false /* HasTailCall */);
    for (uint32_t i = 0; i < NumArgs; ++i) {
      // The builder reserves the first argument for the code object.
      LOG(out << "  args[" << i << "] = " << Args[i + 1] << "\n");
      Call->addArg(Args[i + 1]);
    }

    Control()->appendInst(Call);
    LOG(out << "Call Result = " << Node(Dest) << "\n");
    return OperandNode(Dest);
  }
  Node CallImport(uint32_t Index, Node *Args) {
    LOG(out << "CallImport(" << Index << ")"
            << "\n");
    const auto *Module = this->Module->module;
    assert(Module);
    const auto *Sig = this->Module->GetImportSignature(Index);
    assert(Sig);
    const auto NumArgs = Sig->parameter_count();
    LOG(out << "  number of args: " << NumArgs << "\n");

    const auto &Target = Module->import_table[Index];
    const auto ModuleName = toStdString(
        Module->GetName(Target.module_name_offset, Target.module_name_length));
    const auto FnName = toStdString(Module->GetName(
        Target.function_name_offset, Target.function_name_length));

    const auto TargetName = Ctx->getGlobalString(ModuleName + "$$" + FnName);
    LOG(out << "  target name: " << TargetName << "\n");

    assert(Sig->return_count() <= 1);

    auto TargetOperand = Ctx->getConstantExternSym(TargetName);

    auto *Dest = Sig->return_count() > 0
                     ? makeVariable(toIceType(Sig->GetReturn()))
                     : nullptr;
    constexpr bool NoTailCall = false;
    auto *Call =
        InstCall::create(Func, NumArgs, Dest, TargetOperand, NoTailCall);
    for (uint32_t i = 0; i < NumArgs; ++i) {
      // The builder reserves the first argument for the code object.
      LOG(out << "  args[" << i << "] = " << Args[i + 1] << "\n");
      assert(Args[i + 1].toOperand()->getType() == toIceType(Sig->GetParam(i)));
      Call->addArg(Args[i + 1]);
    }

    Control()->appendInst(Call);
    LOG(out << "Call Result = " << Node(Dest) << "\n");
    return OperandNode(Dest);
  }
  Node CallIndirect(uint32_t SigIndex, Node *Args) {
    LOG(out << "CallIndirect(" << SigIndex << ")\n");
    // TODO(eholk): Compile to something better than a switch.
    const auto *Module = this->Module->module;
    assert(Module);
    const auto &IndirectTable = Module->function_table;

    auto *Abort = getIndirectFailTarget();

    assert(Args[0].toOperand());

    auto *Switch = InstSwitch::create(Func, IndirectTable.size(),
                                      Args[0].toOperand(), Abort);
    assert(Abort);

    const bool HasReturn = Module->signatures[SigIndex]->return_count() != 0;
    const Ice::Type DestTy =
        HasReturn ? toIceType(Module->signatures[SigIndex]->GetReturn())
                  : IceType_void;

    auto *Dest = HasReturn ? makeVariable(DestTy) : nullptr;

    auto *ExitNode = Func->makeNode();
    auto *PhiInst =
        HasReturn ? InstPhi::create(Func, IndirectTable.size(), Dest) : nullptr;

    for (uint32_t Index = 0; Index < IndirectTable.size(); ++Index) {
      const auto &Target = Module->functions[IndirectTable[Index]];

      if (SigIndex == Target.sig_index) {
        auto *CallNode = Func->makeNode();
        auto *SavedControl = Control();
        *ControlPtr = OperandNode(CallNode);
        auto *Tmp = CallDirect(Target.func_index, Args).toOperand();
        *ControlPtr = OperandNode(SavedControl);
        if (PhiInst) {
          PhiInst->addArgument(Tmp, CallNode);
        }
        CallNode->appendInst(InstBr::create(Func, ExitNode));
        Switch->addBranch(Index, Index, CallNode);
      } else {
        Switch->addBranch(Index, Index, Abort);
      }
    }

    if (PhiInst) {
      ExitNode->appendInst(PhiInst);
    }

    Control()->appendInst(Switch);
    *ControlPtr = OperandNode(ExitNode);
    return OperandNode(Dest);
  }
  Node Invert(Node Node) {
    (void)Node;
    llvm::report_fatal_error("Invert");
  }

  //-----------------------------------------------------------------------
  // Operations that concern the linear memory.
  //-----------------------------------------------------------------------
  Node MemSize(uint32_t Offset) {
    (void)Offset;
    llvm::report_fatal_error("MemSize");
  }
  Node LoadGlobal(uint32_t Index) {
    (void)Index;
    llvm::report_fatal_error("LoadGlobal");
  }
  Node StoreGlobal(uint32_t Index, Node Val) {
    (void)Index;
    (void)Val;
    llvm::report_fatal_error("StoreGlobal");
  }

  Node LoadMem(wasm::LocalType Type, MachineType MemType, Node Index,
               uint32_t Offset) {
    LOG(out << "LoadMem." << toIceType(MemType) << "(" << Index << "[" << Offset
            << "]) = ");

    auto *RealAddr = sanitizeAddress(Index, Offset);

    auto *LoadResult = makeVariable(toIceType(MemType));
    Control()->appendInst(InstLoad::create(Func, LoadResult, RealAddr));

    // and cast, if needed
    Variable *Result = nullptr;
    if (toIceType(Type) != toIceType(MemType)) {
      auto DestType = toIceType(Type);
      Result = makeVariable(DestType);
      // TODO(eholk): handle signs correctly.
      if (isScalarIntegerType(DestType)) {
        if (MemType.IsSigned()) {
          Control()->appendInst(
              InstCast::create(Func, InstCast::Sext, Result, LoadResult));
        } else {
          Control()->appendInst(
              InstCast::create(Func, InstCast::Zext, Result, LoadResult));
        }
      } else if (isScalarFloatingType(DestType)) {
        Control()->appendInst(
            InstCast::create(Func, InstCast::Sitofp, Result, LoadResult));
      } else {
        llvm::report_fatal_error("Unsupported type for memory load");
      }
    } else {
      Result = LoadResult;
    }

    LOG(out << Result << "\n");
    return OperandNode(Result);
  }
  void StoreMem(MachineType Type, Node Index, uint32_t Offset, Node Val) {
    LOG(out << "StoreMem." << toIceType(Type) << "(" << Index << "[" << Offset
            << "] = " << Val << ")"
            << "\n");

    auto *RealAddr = sanitizeAddress(Index, Offset);

    // cast the value to the right type, if needed
    Operand *StoreVal = nullptr;
    if (toIceType(Type) != Val.toOperand()->getType()) {
      auto *LocalStoreVal = makeVariable(toIceType(Type));
      Control()->appendInst(
          InstCast::create(Func, InstCast::Trunc, LocalStoreVal, Val));
      StoreVal = LocalStoreVal;
    } else {
      StoreVal = Val;
    }

    // then store the memory
    Control()->appendInst(InstStore::create(Func, StoreVal, RealAddr));
  }

  static void PrintDebugName(OperandNode Node) {
    (void)Node;
    llvm::report_fatal_error("PrintDebugName");
  }

  CfgNode *Control() {
    return ControlPtr ? ControlPtr->toCfgNode() : Func->getEntryNode();
  }
  Node Effect() { return *EffectPtr; }

  void set_module(wasm::ModuleEnv *Module) { this->Module = Module; }

  void set_control_ptr(Node *Control) { this->ControlPtr = Control; }

  void set_effect_ptr(Node *Effect) { this->EffectPtr = Effect; }

private:
  wasm::ModuleEnv *Module;
  Node *ControlPtr;
  Node *EffectPtr;

  class Cfg *Func;
  GlobalContext *Ctx;

  CfgNode *BoundsFailTarget = nullptr;
  CfgNode *IndirectFailTarget = nullptr;

  SizeT NextArg = 0;

  CfgUnorderedMap<Operand *, InstPhi *> PhiMap;
  CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap;

  Operand *WasmMemory = nullptr;

  InstPhi *getDefiningInst(Operand *Op) const {
    const auto &Iter = PhiMap.find(Op);
    if (Iter == PhiMap.end()) {
      return nullptr;
    }
    return Iter->second;
  }

  void setDefiningInst(Operand *Op, InstPhi *Phi) {
    LOG(out << "\n== setDefiningInst(" << Op << ", " << Phi << ") ==\n");
    PhiMap.emplace(Op, Phi);
  }

  template <typename T = Variable> T *makeVariable(Ice::Type Type) {
    return makeVariable<T>(Type, Control());
  }

  template <typename T = Variable>
  T *makeVariable(Ice::Type Type, CfgNode *DefNode) {
    auto *Var = Func->makeVariable<T>(Type);
    DefNodeMap.emplace(Var, DefNode);
    return Var;
  }

  CfgNode *getDefNode(Operand *Op) const {
    const auto &Iter = DefNodeMap.find(Op);
    if (Iter == DefNodeMap.end()) {
      return nullptr;
    }
    return Iter->second;
  }

  CfgNode *getBoundsFailTarget() {
    if (!BoundsFailTarget) {
      // TODO (eholk): Move this node to the end of the CFG, or even better,
      // have only one abort block for the whole module.
      BoundsFailTarget = Func->makeNode();
      BoundsFailTarget->appendInst(InstCall::create(
          Func, 0, nullptr,
          Ctx->getConstantExternSym(Ctx->getGlobalString("__Sz_bounds_fail")),
          false));
      BoundsFailTarget->appendInst(InstUnreachable::create(Func));
    }

    return BoundsFailTarget;
  }
  CfgNode *getIndirectFailTarget() {
    if (!IndirectFailTarget) {
      // TODO (eholk): Move this node to the end of the CFG, or even better,
      // have only one abort block for the whole module.
      IndirectFailTarget = Func->makeNode();
      IndirectFailTarget->appendInst(InstCall::create(
          Func, 0, nullptr,
          Ctx->getConstantExternSym(Ctx->getGlobalString("__Sz_indirect_fail")),
          false));
      IndirectFailTarget->appendInst(InstUnreachable::create(Func));
    }

    return IndirectFailTarget;
  }

  Operand *getWasmMemory() {
    assert(WasmMemory != nullptr);
    return WasmMemory;
  }

  Operand *sanitizeAddress(Operand *Base, uint32_t Offset) {
    SizeT MemSize = Module->module->min_mem_pages * WASM_PAGE_SIZE;

    bool ConstZeroBase = false;

    // first, add the index and the offset together.
    if (auto *ConstBase = llvm::dyn_cast<ConstantInteger32>(Base)) {
      uint32_t RealOffset = Offset + ConstBase->getValue();
      if (RealOffset >= MemSize) {
        // We've proven this will always be an out of bounds access, so insert
        // an unconditional trap.
        Control()->appendInst(InstUnreachable::create(Func));
        // It doesn't matter what we return here, so return something that will
        // allow the rest of code generation to happen.
        //
        // We might be tempted to just abort translation here, but out of bounds
        // memory access is a runtime trap, not a compile error.
        return Ctx->getConstantZero(getPointerType());
      }
      Base = Ctx->getConstantInt32(RealOffset);
      ConstZeroBase = (0 == RealOffset);
    } else if (0 != Offset) {
      auto *Addr = makeVariable(Ice::getPointerType());
      auto *OffsetConstant = Ctx->getConstantInt32(Offset);
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add,
                                                   Addr, Base, OffsetConstant));

      Base = Addr;
    }

    // Do the bounds check if enabled
    if (getFlags().getWasmBoundsCheck() &&
        !llvm::isa<ConstantInteger32>(Base)) {
      // TODO (eholk): creating a new basic block on every memory access is
      // terrible (see https://goo.gl/Zj7DTr). Try adding a new instruction that
      // encapsulates this "abort if false" pattern.
      auto *CheckPassed = Func->makeNode();
      auto *CheckFailed = getBoundsFailTarget();

      auto *Check = makeVariable(IceType_i1);
      Control()->appendInst(InstIcmp::create(Func, InstIcmp::Ult, Check, Base,
                                             Ctx->getConstantInt32(MemSize)));
      Control()->appendInst(
          InstBr::create(Func, Check, CheckPassed, CheckFailed));

      *ControlPtr = OperandNode(CheckPassed);
    }

    Ice::Operand *RealAddr = nullptr;
    auto MemBase = getWasmMemory();
    if (!ConstZeroBase) {
      auto RealAddrV = Func->makeVariable(Ice::getPointerType());
      Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add,
                                                   RealAddrV, Base, MemBase));

      RealAddr = RealAddrV;
    } else {
      RealAddr = MemBase;
    }
    return RealAddr;
  }

  template <typename F = std::function<void(Ostream &)>> void log(F Fn) const {
    if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) {
      Fn(Ctx->getStrDump());
      Ctx->getStrDump().flush();
    }
  }
};

std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone,
                                                       FunctionBody &Body) {
  OstreamLocker L1(Ctx);
  auto Func = Cfg::create(Ctx, getNextSequenceNumber());
  TimerMarker T(TimerStack::TT_wasmGenIce, Func.get());
  Ice::CfgLocalAllocatorScope L2(Func.get());

  // TODO(eholk): parse the function signature...

  Func->setEntryNode(Func->makeNode());

  IceBuilder Builder(Func.get());
  SR_WasmDecoder<OperandNode, IceBuilder> Decoder(Zone, &Builder, Body);

  LOG(out << getFlags().getDefaultGlobalPrefix() << "\n");
  Decoder.Decode();

  // We don't always know where the incoming branches are in phi nodes, so this
  // function finds them.
  Func->fixPhiNodes();

  Func->computeInOutEdges();

  return Func;
}

constexpr SizeT InitialBufferSize = 16 << 10; // 16KB

WasmTranslator::WasmTranslator(GlobalContext *Ctx)
    : Translator(Ctx), Buffer(InitialBufferSize) {}

void WasmTranslator::translate(
    const std::string &IRFilename,
    std::unique_ptr<llvm::DataStreamer> InputStream) {
  TimerMarker T(TimerStack::TT_wasm, Ctx);

  LOG(out << "Initializing v8/wasm stuff..."
          << "\n");
  Zone Zone;
  ZoneScope _(&Zone);

  SizeT BytesRead = 0;
  while (true) {
    BytesRead +=
        InputStream->GetBytes(&Buffer[BytesRead], Buffer.size() - BytesRead);
    LOG(out << "Read " << BytesRead << " bytes"
            << "\n");
    if (BytesRead < Buffer.size())
      break;
    Buffer.resize(Buffer.size() * 2);
  }

  LOG(out << "Decoding module " << IRFilename << "\n");

  constexpr v8::internal::Isolate *NoIsolate = nullptr;
  auto Result = DecodeWasmModule(NoIsolate, &Zone, Buffer.data(),
                                 Buffer.data() + BytesRead, false, kWasmOrigin);

  auto Module = Result.val;

  LOG(out << "Module info:"
          << "\n");
  LOG(out << "  min_mem_pages:           " << Module->min_mem_pages << "\n");
  LOG(out << "  max_mem_pages:           " << Module->max_mem_pages << "\n");
  LOG(out << "  number of globals:       " << Module->globals.size() << "\n");
  LOG(out << "  number of signatures:    " << Module->signatures.size()
          << "\n");
  LOG(out << "  number of functions:     " << Module->functions.size() << "\n");
  LOG(out << "  number of data_segments: " << Module->data_segments.size()
          << "\n");
  LOG(out << "  function table size:     " << Module->function_table.size()
          << "\n");
  LOG(out << "  import table size:       " << Module->import_table.size()
          << "\n");
  LOG(out << "  export table size:       " << Module->export_table.size()
          << "\n");

  LOG(out << "\n"
          << "Data segment information:"
          << "\n");
  uint32_t Id = 0;
  for (const auto Seg : Module->data_segments) {
    LOG(out << Id << ":  (" << Seg.source_offset << ", " << Seg.source_size
            << ") => " << Seg.dest_addr);
    if (Seg.init) {
      LOG(out << " init\n");
    } else {
      LOG(out << "\n");
    }
    Id++;
  }

  LOG(out << "\n"
          << "Import information:"
          << "\n");
  for (const auto Import : Module->import_table) {
    auto ModuleName = toStdString(
        Module->GetName(Import.module_name_offset, Import.module_name_length));
    auto FnName = toStdString(Module->GetName(Import.function_name_offset,
                                              Import.function_name_length));
    LOG(out << "  " << Import.sig_index << ": " << ModuleName << "::" << FnName
            << "\n");
  }

  LOG(out << "\n"
          << "Export information:"
          << "\n");
  for (const auto Export : Module->export_table) {
    LOG(out << "  " << Export.func_index << ": "
            << toStdString(
                   Module->GetName(Export.name_offset, Export.name_length))
            << " (" << Export.name_offset << ", " << Export.name_length << ")");
    LOG(out << "\n");
  }

  LOG(out << "\n"
          << "Function information:"
          << "\n");
  for (const auto F : Module->functions) {
    LOG(out << "  " << F.func_index << ": "
            << toStdString(Module->GetName(F.name_offset, F.name_length))
            << " (" << F.name_offset << ", " << F.name_length << ")");
    if (F.exported)
      LOG(out << " export");
    if (F.external)
      LOG(out << " extern");
    LOG(out << "\n");
  }

  LOG(out << "\n"
          << "Indirect table:"
          << "\n");
  for (uint32_t F : Module->function_table) {
    LOG(out << "  " << F << ": " << getFunctionName(Module, F) << "\n");
  }

  ModuleEnv ModuleEnv;
  ModuleEnv.module = Module;

  FunctionBody Body;
  Body.module = &ModuleEnv;

  LOG(out << "Translating " << IRFilename << "\n");

  {
    unique_ptr<VariableDeclarationList> Globals =
        makeUnique<VariableDeclarationList>();

    // Global variables, etc go here.
    auto *WasmMemory = VariableDeclaration::createExternal(Globals.get());
    WasmMemory->setName(Ctx->getGlobalString("WASM_DATA_INIT"));

    // Fill in the segments
    SizeT WritePtr = 0;
    for (const auto Seg : Module->data_segments) {
      // fill in gaps with zero.
      if (Seg.dest_addr > WritePtr) {
        WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create(
            Globals.get(), Seg.dest_addr - WritePtr));
        WritePtr = Seg.dest_addr;
      }

      // Add the data
      WasmMemory->addInitializer(VariableDeclaration::DataInitializer::create(
          Globals.get(), reinterpret_cast<const char *>(Module->module_start) +
                             Seg.source_offset,
          Seg.source_size));

      WritePtr += Seg.source_size;
    }

    // Save the size of the initialized data in a global variable so the runtime
    // can use it to determine the initial heap break.
    auto *GlobalDataSize = VariableDeclaration::createExternal(Globals.get());
    GlobalDataSize->setName(Ctx->getGlobalString("WASM_DATA_SIZE"));
    GlobalDataSize->addInitializer(VariableDeclaration::DataInitializer::create(
        Globals.get(), reinterpret_cast<const char *>(&WritePtr),
        sizeof(WritePtr)));

    // Save the number of pages for the runtime
    auto *GlobalNumPages = VariableDeclaration::createExternal(Globals.get());
    GlobalNumPages->setName(Ctx->getGlobalString("WASM_NUM_PAGES"));
    GlobalNumPages->addInitializer(VariableDeclaration::DataInitializer::create(
        Globals.get(), reinterpret_cast<const char *>(&Module->min_mem_pages),
        sizeof(Module->min_mem_pages)));

    Globals->push_back(WasmMemory);
    Globals->push_back(GlobalDataSize);
    Globals->push_back(GlobalNumPages);

    lowerGlobals(std::move(Globals));
  }

  // Translate each function.
  for (const auto Fn : Module->functions) {
    const auto FnName = getFunctionName(Module, Fn.func_index);

    LOG(out << "  " << Fn.func_index << ": " << FnName << "...");

    Body.sig = Fn.sig;
    Body.base = Buffer.data();
    Body.start = Buffer.data() + Fn.code_start_offset;
    Body.end = Buffer.data() + Fn.code_end_offset;

    std::unique_ptr<Cfg> Func = nullptr;
    {
      TimerMarker T_func(getContext(), FnName);
      Func = translateFunction(&Zone, Body);
      Func->setFunctionName(Ctx->getGlobalString(FnName));
    }
    Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func)));
    LOG(out << "done.\n");
  }

  return;
}

#endif // ALLOW_WASM