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