 Chromium Code Reviews
 Chromium Code Reviews Issue 1837663002:
  Initial Subzero WASM prototype.  (Closed) 
  Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
    
  
    Issue 1837663002:
  Initial Subzero WASM prototype.  (Closed) 
  Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master| Index: src/WasmTranslator.cpp | 
| diff --git a/src/WasmTranslator.cpp b/src/WasmTranslator.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..84e5df94ce1d15c209525bf72159b381be755133 | 
| --- /dev/null | 
| +++ b/src/WasmTranslator.cpp | 
| @@ -0,0 +1,763 @@ | 
| +#include "WasmTranslator.h" | 
| 
JF
2016/03/28 18:17:13
Copyright.
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + | 
| +#include "IceGlobalInits.h" | 
| +#include "IceCfgNode.h" | 
| + | 
| +#include "src/wasm/module-decoder.h" | 
| +#include "src/wasm/wasm-opcodes.h" | 
| + | 
| +#include "src/zone.h" | 
| + | 
| +using namespace std; | 
| +using namespace Ice; | 
| +using namespace v8; | 
| +using namespace v8::internal; | 
| +using namespace v8::internal::wasm; | 
| +using v8::internal::wasm::DecodeWasmModule; | 
| + | 
| +#include "src/wasm/ast-decoder-impl.h" | 
| + | 
| +Ice::Type toIceType(v8::internal::MachineType) { | 
| + // TODO(eholk): actually convert this. | 
| + return IceType_i32; | 
| +} | 
| + | 
| +Ice::Type toIceType(wasm::LocalType Type) { | 
| + | 
| + switch (Type) { | 
| + default: | 
| + assert(false && "unexpected enum value"); | 
| + case MachineRepresentation::kNone: | 
| + assert(false && "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: | 
| + assert(false && "ambiguous SIMD type"); | 
| + case MachineRepresentation::kTagged: | 
| + assert(false && "kTagged type not supported"); | 
| 
JF
2016/03/28 18:17:13
llvm_unreachable for the few asserts above.
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + } | 
| +} | 
| + | 
| +/** | 
| + This class wraps either and Operand or a CfgNode. | 
| 
John
2016/03/28 18:44:09
an Operand
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + | 
| + Turbofan's sea of nodes representation only has nodes for values, control | 
| + flow, etc. In Subzero these concepts are all separate. This class let's 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; | 
| + | 
| +public: | 
| + uintptr_t Data; | 
| + | 
| + OperandNode() : Data(UNDEF_PTR) {} | 
| + OperandNode(Operand *Operand) : Data((uintptr_t)Operand) {} | 
| + OperandNode(CfgNode *Node) : Data((uintptr_t)Node | NODE_FLAG) {} | 
| + OperandNode(nullptr_t) : Data(UNDEF_PTR) {} | 
| + | 
| + operator Operand *() const { | 
| 
John
2016/03/28 18:44:07
explicit?
 
Eric Holk
2016/03/29 19:54:09
I'd rather not. The goal was to make something tha
 | 
| + if (UNDEF_PTR == Data) { | 
| + return nullptr; | 
| + } | 
| + assert(isOperand()); | 
| + return (Operand *)Data; | 
| 
John
2016/03/28 18:44:07
reinterpret_cast<Operand *>(Data);
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + } | 
| + | 
| + operator CfgNode *() const { | 
| + if (UNDEF_PTR == Data) { | 
| + return nullptr; | 
| + } | 
| + assert(isCfgNode()); | 
| + return (CfgNode *)(Data & ~NODE_FLAG); | 
| + } | 
| + | 
| + operator bool() const { return (Data != UNDEF_PTR) && Data; } | 
| 
John
2016/03/28 18:44:10
explicit operator? -- I personally do not like imp
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + bool operator!=(const OperandNode &Rhs) const { | 
| 
John
2016/03/28 18:44:07
why not:
bool operator==() { return Data == Rhs.D
 
Eric Holk
2016/03/29 19:54:12
It's probably cleaner to have operator== and then
 | 
| + return (UNDEF_PTR == Data && Rhs.Data != UNDEF_PTR && Rhs.Data) || | 
| + (UNDEF_PTR == Rhs.Data && Data != UNDEF_PTR && Data) || | 
| + Data != Rhs.Data; | 
| + } | 
| + | 
| + bool isOperand() const { return !(Data & NODE_FLAG); } | 
| + bool isCfgNode() const { return Data & NODE_FLAG; } | 
| +}; | 
| + | 
| +Ostream &operator<<(Ostream &Out, const OperandNode &Op) { | 
| + if (Op.isOperand()) { | 
| + auto Oper = (Operand *)Op; | 
| 
John
2016/03/28 18:44:09
static_cast -- or even better, Op.getOperand();
 | 
| + Out << "(Operand*)" << Oper; | 
| + } else if (Op.isCfgNode()) { | 
| + auto Node = (CfgNode *)Op; | 
| + Out << "(CfgNode*)" << Node; | 
| + } else { | 
| + Out << "nullptr"; | 
| + } | 
| + return Out; | 
| +} | 
| + | 
| +constexpr 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: | 
| + return true; | 
| + default: | 
| + return false; | 
| + } | 
| +} | 
| + | 
| +#define LOG(Expr) log([&](Ostream & out) { Expr; }) | 
| 
John
2016/03/28 18:44:10
default capture by reference -- even on a define -
 
Eric Holk
2016/03/29 19:54:11
Yeah... I don't super love this either. I don't re
 | 
| + | 
| +class IceBuilder { | 
| + using Node = OperandNode; | 
| + | 
| 
John
2016/03/28 18:44:10
Move the following to the end of the class declara
 
Eric Holk
2016/03/29 19:54:09
Done and Done.
 | 
| + wasm::ModuleEnv *module_; | 
| 
John
2016/03/28 18:44:08
Module
 | 
| + Node *control_; | 
| + Node *effect_; | 
| + | 
| + class Cfg *Cfg; | 
| + GlobalContext *Ctx; | 
| + | 
| + SizeT NextArg = 0; | 
| + | 
| + template <typename F = std::function<void(Ostream &)>> void log(F Fn) { | 
| + if (BuildDefs::dump() && (Ctx->getFlags().getVerbose() & IceV_Wasm)) { | 
| + Fn(Ctx->getStrDump()); | 
| 
John
2016/03/28 18:44:09
don't you want to lock the ostream before invoking
 
Eric Holk
2016/03/29 19:54:12
Done, and also in WasmTranslator.h
 | 
| + Ctx->getStrDump().flush(); | 
| + } | 
| + } | 
| + | 
| +public: | 
| + IceBuilder(class Cfg *Cfg) | 
| + : Cfg(Cfg), Ctx(Cfg->getContext()), control_(nullptr) {} | 
| 
John
2016/03/28 18:44:08
I would love member to be named all lower case, wi
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + | 
| + Node *Buffer(size_t Count) { | 
| 
John
2016/03/28 18:44:08
allocateBuffer -- the name Buffer() does not reall
 
Eric Holk
2016/03/29 19:54:11
This name was dictated by the WasmBuilder from V8.
 | 
| + LOG(out << "Buffer(" << Count << ")\n"); | 
| + return Cfg->allocateArrayOf<Node>(Count); | 
| + } | 
| + | 
| + Node Error() { llvm_unreachable("Error"); } | 
| + Node Start(unsigned params) { | 
| + LOG(out << "Start(" << params << ") = "); | 
| + auto Entry = Cfg->makeNode(); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:10
I left the * off because I remember your pointer r
 | 
| + Cfg->setEntryNode(Entry); | 
| + LOG(out << Node(Entry) << "\n"); | 
| + return Entry; | 
| + } | 
| + Node Param(unsigned Index, wasm::LocalType Type) { | 
| + LOG(out << "Param(" << Index << ") = "); | 
| + auto Arg = Cfg->makeVariable(toIceType(Type)); | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + assert(Index == NextArg); | 
| + Cfg->addArg(Arg); | 
| + Arg->setDefNode(Cfg->getEntryNode()); | 
| + NextArg++; | 
| + LOG(out << Node(Arg) << "\n"); | 
| + return Arg; | 
| + } | 
| + CfgNode *Loop(CfgNode *Entry) { | 
| + Node Loop = Cfg->makeNode(); | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + LOG(out << "Loop(" << Entry << ") = " << Loop << "\n"); | 
| + Entry->appendInst(InstBr::create(Cfg, Loop)); | 
| + return Loop; | 
| + } | 
| + void Terminate(Node Effect, Node Control) { | 
| + // TODO(eholk): this is almost certainly wrong | 
| + LOG(out << "Terminate(" << Effect << ", " << Control << ")" | 
| + << "\n"); | 
| + } | 
| + Node Merge(unsigned Count, Node *Controls) { | 
| + LOG(out << "Merge(" << Count); | 
| + for (auto i = 0; i < Count; ++i) { | 
| + LOG(out << ", " << Controls[i]); | 
| + } | 
| + LOG(out << ") = "); | 
| + | 
| + auto MergedNode = Cfg->makeNode(); | 
| 
John
2016/03/28 18:44:07
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + | 
| + for (auto i = 0; i < Count; ++i) { | 
| + CfgNode *Control = Controls[i]; | 
| + Control->appendInst(InstBr::create(Cfg, MergedNode)); | 
| + } | 
| + LOG(out << (OperandNode)MergedNode << "\n"); | 
| + return MergedNode; | 
| + } | 
| + Node Phi(wasm::LocalType Type, unsigned Count, Node *Vals, Node Control) { | 
| + LOG(out << "Phi(" << Count << ", " << Control); | 
| + for (int i = 0; i < Count; ++i) { | 
| + LOG(out << ", " << Vals[i]); | 
| + } | 
| + LOG(out << ") = "); | 
| + | 
| + const auto InEdges = ((CfgNode *)Control)->getInEdges(); | 
| 
John
2016/03/28 18:44:10
static_cast<>
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + assert(Count == InEdges.size()); | 
| + | 
| + assert(Count > 0); | 
| + | 
| + auto Dest = Cfg->makeVariable(((Operand *)Vals[0])->getType()); | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:11
Done. I also added a static_cast.
 | 
| + | 
| + // Multiply by 10 in case more things get added later. | 
| + | 
| + // TODO(eholk): find a better way besides multiplying by some arbitrary | 
| + // constant. | 
| + auto Phi = InstPhi::create(Cfg, Count * 10, Dest); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + for (int i = 0; i < Count; ++i) { | 
| + auto Op = (Operand *)Vals[i]; | 
| 
John
2016/03/28 18:44:08
static_cast<>
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + assert(Op); | 
| + Phi->addArgument(Op, InEdges[i]); | 
| + } | 
| + ((CfgNode *)Control)->appendInst(Phi); | 
| 
John
2016/03/28 18:44:08
static_cast<>
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + LOG(out << Node(Dest) << "\n"); | 
| + return Dest; | 
| + } | 
| + Node EffectPhi(unsigned Count, Node *Effects, Node Control) { | 
| + // TODO(eholk): this function is almost certainly wrong. | 
| + LOG(out << "EffectPhi(" << Count << ", " << Control << "):\n"); | 
| + for (auto i = 0; i < Count; ++i) { | 
| 
John
2016/03/28 18:44:09
no auto for integral types?...
 
Eric Holk
2016/03/29 19:54:11
Ok.
 | 
| + LOG(out << " " << Effects[i] << "\n"); | 
| + } | 
| + return nullptr; | 
| + } | 
| + Node Int32Constant(int32_t Value) { | 
| + LOG(out << "Int32Constant(" << Value << ") = "); | 
| + auto Const = Ctx->getConstantInt32(Value); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + assert(Const); | 
| + assert(Control()); | 
| + Const->setDefNode(Control()); | 
| + LOG(out << Node(Const) << "\n"); | 
| + return Const; | 
| + } | 
| + Node Int64Constant(int64_t Value) { | 
| + LOG(out << "Int64Constant(" << Value << ") = "); | 
| + auto Const = Ctx->getConstantInt64(Value); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + assert(Const); | 
| + Const->setDefNode(Control()); | 
| + LOG(out << Node(Const) << "\n"); | 
| + return Const; | 
| + } | 
| + Node Float32Constant(float Value) { | 
| + LOG(out << "Float32Constant(" << Value << ") = "); | 
| + auto Const = Ctx->getConstantFloat(Value); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + assert(Const); | 
| + Const->setDefNode(Control()); | 
| + LOG(out << Node(Const) << "\n"); | 
| + return Const; | 
| + } | 
| + Node Float64Constant(double Value) { | 
| + LOG(out << "Float64Constant(" << Value << ") = "); | 
| + auto Const = Ctx->getConstantDouble(Value); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + assert(Const); | 
| + Const->setDefNode(Control()); | 
| + LOG(out << Node(Const) << "\n"); | 
| + return Const; | 
| + } | 
| + Node Binop(wasm::WasmOpcode Opcode, Node Left, Node Right) { | 
| + LOG(out << "Binop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Left | 
| + << ", " << Right << ") = "); | 
| + Ice::Variable *Dest = Cfg->makeVariable( | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + isComparison(Opcode) ? IceType_i1 : ((Operand *)Left)->getType()); | 
| 
John
2016/03/28 18:44:08
static_cast<>
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + switch (Opcode) { | 
| + case kExprI32Add: | 
| + case kExprI64Add: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Add, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Sub: | 
| + case kExprI64Sub: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Sub, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Mul: | 
| + case kExprI64Mul: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Mul, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32DivU: | 
| + case kExprI64DivU: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Udiv, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32RemU: | 
| + case kExprI64RemU: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Urem, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Ior: | 
| + case kExprI64Ior: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Or, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Xor: | 
| + case kExprI64Xor: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Xor, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Shl: | 
| + case kExprI64Shl: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Shl, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32ShrU: | 
| + case kExprI64ShrU: | 
| + case kExprI32ShrS: | 
| + case kExprI64ShrS: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::Ashr, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32And: | 
| + case kExprI64And: | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Cfg, InstArithmetic::And, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Ne: | 
| + case kExprI64Ne: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Ne, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32Eq: | 
| + case kExprI64Eq: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Eq, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32LtS: | 
| + case kExprI64LtS: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Slt, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32LtU: | 
| + case kExprI64LtU: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Ult, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32GeS: | 
| + case kExprI64GeS: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Sge, Dest, Left, Right)); | 
| + case kExprI32GtS: | 
| + case kExprI64GtS: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Sgt, Dest, Left, Right)); | 
| + break; | 
| + case kExprI32GtU: | 
| + case kExprI64GtU: | 
| + Control()->appendInst( | 
| + InstIcmp::create(Cfg, InstIcmp::Ugt, Dest, Left, Right)); | 
| + break; | 
| + default: | 
| + LOG(out << "Unknown binop: " << WasmOpcodes::OpcodeName(Opcode) << "\n"); | 
| + assert(false && "Uncovered or invalid binop."); | 
| + return nullptr; | 
| + } | 
| + LOG(out << Dest << "\n"); | 
| + assert(Dest->getDefNode()); | 
| + return Dest; | 
| + } | 
| + Node Unop(wasm::WasmOpcode Opcode, Node Input) { | 
| + LOG(out << "Unop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Input | 
| + << ") = "); | 
| + Ice::Variable *Dest = nullptr; | 
| + switch (Opcode) { | 
| + case kExprF32Neg: { | 
| + Dest = Cfg->makeVariable(IceType_f32); | 
| + auto Zero = Cfg->makeVariable(IceType_f32); | 
| 
John
2016/03/28 18:44:10
auto *
 | 
| + Control()->appendInst(InstAssign::create(Cfg, Zero, Ctx->getConstantFloat(0))); | 
| 
John
2016/03/28 18:44:08
80 col
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + Control()->appendInst(InstArithmetic::create(Cfg, InstArithmetic::Fsub, Dest, Zero, Input)); | 
| 
John
2016/03/28 18:44:08
80 col
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + break; | 
| + } | 
| + case kExprF64Neg: { | 
| + Dest = Cfg->makeVariable(IceType_f64); | 
| + auto Zero = Cfg->makeVariable(IceType_f64); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + Control()->appendInst(InstAssign::create(Cfg, Zero, Ctx->getConstantDouble(0))); | 
| 
John
2016/03/28 18:44:09
80 col
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + Control()->appendInst(InstArithmetic::create(Cfg, InstArithmetic::Fsub, Dest, Zero, Input)); | 
| 
John
2016/03/28 18:44:08
80 col
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + break; | 
| + } | 
| + case kExprI64UConvertI32: | 
| + Dest = Cfg->makeVariable(IceType_i64); | 
| + Control()->appendInst(InstCast::create(Cfg, InstCast::Zext, Dest, Input)); | 
| + break; | 
| + default: | 
| + LOG(out << "Unknown unop: " << WasmOpcodes::OpcodeName(Opcode) << "\n"); | 
| + assert(false && "Uncovered or invalid unop."); | 
| + return nullptr; | 
| + } | 
| + LOG(out << Dest << "\n"); | 
| + return Dest; | 
| + } | 
| + unsigned InputCount(CfgNode *Node) { return Node->getInEdges().size(); } | 
| 
John
2016/03/28 18:44:07
this should not be a member class -- it is really
 
Eric Holk
2016/03/29 19:54:09
This is a function called from V8, so we have to m
 | 
| + bool IsPhiWithMerge(Node Phi, Node Merge) { | 
| + LOG(out << "IsPhiWithMerge(" << Phi << ", " << Merge << ")" | 
| + << "\n"); | 
| + if (Phi && Phi.isOperand()) { | 
| + LOG(out << " ...is operand" | 
| + << "\n"); | 
| + if (auto RealPhi = llvm::dyn_cast<Ice::Variable>((Operand *)Phi)) { | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + LOG(out << " ...is variable" | 
| + << "\n"); | 
| + if (auto Inst = RealPhi->getDefiningInst()) { | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + LOG(out << " ...has defining instruction" | 
| + << "\n"); | 
| + | 
| + if (Inst->getKind() != Inst::Phi) | 
| + return false; | 
| + LOG(out << " ...is phi" | 
| + << "\n"); | 
| + | 
| + LOG(out << " ..." << (Inst->getCfgNode() == Merge) << "\n"); | 
| + return Inst->getCfgNode() == Merge; | 
| + } | 
| + } | 
| + } | 
| + return false; | 
| + } | 
| + void AppendToMerge(CfgNode *Merge, CfgNode *From) { | 
| 
John
2016/03/28 18:44:09
AppendToMerge is a funny name; you're not really a
 
Eric Holk
2016/03/29 19:54:10
It's from V8 again. The idea is there is a merge n
 | 
| + From->appendInst(InstBr::create(Cfg, Merge)); | 
| + } | 
| + void AppendToPhi(Node Merge, Node Phi, Node From) { | 
| + LOG(out << "AppendToPhi(" << Merge << ", " << Phi << ", " << From << ")" | 
| + << "\n"); | 
| + auto Var = llvm::cast<Ice::Variable>((Operand *)Phi); | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + assert(Var->getDefiningInst()->getKind() == Inst::Phi); | 
| + auto Inst = llvm::cast<InstPhi>(Var->getDefiningInst()); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + auto FromVar = llvm::cast<Ice::Variable>((Operand *)From); | 
| 
John
2016/03/28 18:44:07
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + Inst->addArgument(From, FromVar->getDefiningInst()->getCfgNode()); | 
| + } | 
| + | 
| + //----------------------------------------------------------------------- | 
| + // Operations that read and/or write {control} and {effect}. | 
| + //----------------------------------------------------------------------- | 
| + nullptr_t Branch(Node Cond, Node *TrueNode, Node *FalseNode) { | 
| 
John
2016/03/28 18:44:08
void instead?
 
Eric Holk
2016/03/29 19:54:09
I tried, but V8 expects this function to return a
 | 
| + // 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(); | 
| 
John
2016/03/28 18:44:07
auto *?
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + | 
| + *TrueNode = Cfg->makeNode(); | 
| + *FalseNode = Cfg->makeNode(); | 
| + | 
| + LOG(out << *TrueNode << ", " << *FalseNode << ")" | 
| + << "\n"); | 
| + | 
| + Ctrl->appendInst(InstBr::create(Cfg, Cond, *TrueNode, *FalseNode)); | 
| + return nullptr; | 
| + } | 
| + Node Switch(unsigned Count, Node Key) { llvm_unreachable("Switch"); } | 
| + Node IfValue(int32_t Value, Node Sw) { llvm_unreachable("IfValue"); } | 
| + Node IfDefault(Node Sw) { llvm_unreachable("IfDefault"); } | 
| + Node Return(unsigned Count, Node *Vals) { | 
| + assert(1 >= Count); | 
| + LOG(out << "Return("); | 
| + if (Count > 0) | 
| + LOG(out << Vals[0]); | 
| + LOG(out << ")" | 
| + << "\n"); | 
| + auto Instr = | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + 1 == Count ? InstRet::create(Cfg, Vals[0]) : InstRet::create(Cfg); | 
| + Control()->appendInst(Instr); | 
| + Control()->setHasReturn(); | 
| + LOG(out << Node(nullptr) << "\n"); | 
| + return nullptr; | 
| + } | 
| + Node ReturnVoid() { | 
| + LOG(out << "ReturnVoid() = "); | 
| + auto Instr = InstRet::create(Cfg); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + Control()->appendInst(Instr); | 
| + Control()->setHasReturn(); | 
| + LOG(out << Node(nullptr) << "\n"); | 
| + return nullptr; | 
| + } | 
| + Node Unreachable() { | 
| + LOG(out << "Unreachable() = "); | 
| + auto Instr = InstUnreachable::create(Cfg); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + Control()->appendInst(Instr); | 
| + LOG(out << Node(nullptr) << "\n"); | 
| + return nullptr; | 
| + } | 
| + | 
| + Node CallDirect(uint32_t Index, Node *Args) { | 
| + LOG(out << "CallDirect(" << Index << ")" | 
| + << "\n"); | 
| + assert(module_->IsValidFunction(Index)); | 
| + const auto Module = module_->module; | 
| + assert(Module); | 
| + const auto &Target = Module->functions[Index]; | 
| + const auto Sig = Target.sig; | 
| 
John
2016/03/28 18:44:07
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + assert(Sig); | 
| + const auto NumArgs = Sig->parameter_count(); | 
| + LOG(out << " number of args: " << NumArgs << "\n"); | 
| + | 
| + const auto TargetName = Module->GetName(Target.name_offset); | 
| 
John
2016/03/28 18:44:07
std::string?
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + LOG(out << " target name: " << TargetName << "\n"); | 
| + | 
| + assert(Sig->return_count() <= 1); | 
| + | 
| + auto TargetOperand = Ctx->getConstantSym(0, TargetName); | 
| 
John
2016/03/28 18:44:09
auto *
 | 
| + | 
| + auto Dest = Sig->return_count() > 0 | 
| 
John
2016/03/28 18:44:08
auto *
 | 
| + ? Cfg->makeVariable(toIceType(Sig->GetReturn())) | 
| + : nullptr; | 
| + auto Call = InstCall::create(Cfg, NumArgs, Dest, TargetOperand, | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + false /* HasTailCall */); | 
| + for (int 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 Dest; | 
| + } | 
| + Node CallImport(uint32_t Index, Node *Args) { | 
| + LOG(out << "CallImport(" << Index << ")" | 
| + << "\n"); | 
| + // assert(module_->IsValidFunction(index)); | 
| + const auto Module = module_->module; | 
| + assert(Module); | 
| + const auto Sig = module_->GetImportSignature(Index); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + assert(Sig); | 
| + const auto NumArgs = Sig->parameter_count(); | 
| + LOG(out << " number of args: " << NumArgs << "\n"); | 
| + | 
| + const auto &Target = Module->import_table[Index]; | 
| + const auto TargetName = Module->GetName(Target.function_name_offset); | 
| + LOG(out << " target name: " << TargetName << "\n"); | 
| + | 
| + assert(Sig->return_count() <= 1); | 
| + | 
| + auto TargetOperand = Ctx->getConstantSym(0, TargetName); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + | 
| + auto Dest = Sig->return_count() > 0 | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + ? Cfg->makeVariable(toIceType(Sig->GetReturn())) | 
| + : nullptr; | 
| + auto Call = InstCall::create(Cfg, NumArgs, Dest, TargetOperand, | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + false /* HasTailCall */); | 
| + for (int 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 Dest; | 
| + } | 
| + Node CallIndirect(uint32_t Index, Node *Args) { | 
| + llvm_unreachable("CallIndirect"); | 
| + } | 
| + Node Invert(Node Node) { llvm_unreachable("Invert"); } | 
| + Node FunctionTable() { llvm_unreachable("FunctionTable"); } | 
| + | 
| + //----------------------------------------------------------------------- | 
| + // Operations that concern the linear memory. | 
| + //----------------------------------------------------------------------- | 
| + Node MemSize(uint32_t Offset) { llvm_unreachable("MemSize"); } | 
| + Node LoadGlobal(uint32_t Index) { llvm_unreachable("LoadGlobal"); } | 
| + Node StoreGlobal(uint32_t Index, Node Val) { | 
| + llvm_unreachable("StoreGlobal"); | 
| + } | 
| + Node LoadMem(wasm::LocalType Type, MachineType MemType, Node Index, | 
| + uint32_t Offset) { | 
| + LOG(out << "LoadMem(" << Index << "[" << Offset << "]) = "); | 
| + | 
| + // TODO(eholk): surely there is a better way to do this. | 
| + | 
| + // first, add the index and the offset together. | 
| + auto OffsetConstant = Ctx->getConstantInt32(Offset); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + auto Addr = Cfg->makeVariable(IceType_i32); | 
| 
John
2016/03/28 18:44:08
auto *
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + Control()->appendInst(InstArithmetic::create(Cfg, InstArithmetic::Add, Addr, | 
| + Index, OffsetConstant)); | 
| + | 
| + // then load the memory | 
| + auto LoadResult = Cfg->makeVariable(toIceType(MemType)); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + Control()->appendInst(InstLoad::create(Cfg, LoadResult, Addr)); | 
| + | 
| + // and cast, if needed | 
| + Ice::Variable *Result = nullptr; | 
| + if (toIceType(Type) != toIceType(MemType)) { | 
| + Result = Cfg->makeVariable(toIceType(Type)); | 
| + // TODO(eholk): handle signs correctly. | 
| + Control()->appendInst( | 
| + InstCast::create(Cfg, InstCast::Sext, Result, LoadResult)); | 
| + } else { | 
| + Result = LoadResult; | 
| + } | 
| + | 
| + LOG(out << Result << "\n"); | 
| + return Result; | 
| + } | 
| + void StoreMem(MachineType Type, Node Index, uint32_t Offset, Node Val) { | 
| + LOG(out << "StoreMem(" << Index << "[" << Offset << "] = " << Val << ")" | 
| + << "\n"); | 
| + | 
| + // TODO(eholk): surely there is a better way to do this. | 
| + | 
| + // first, add the index and the offset together. | 
| + auto OffsetConstant = Ctx->getConstantInt32(Offset); | 
| 
John
2016/03/28 18:44:07
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + auto Addr = Cfg->makeVariable(IceType_i32); | 
| 
John
2016/03/28 18:44:10
auto *
 
Eric Holk
2016/03/29 19:54:09
Done.
 | 
| + Control()->appendInst(InstArithmetic::create(Cfg, InstArithmetic::Add, Addr, | 
| + Index, OffsetConstant)); | 
| + | 
| + // cast the value to the right type, if needed | 
| + Operand *StoreVal = nullptr; | 
| + if (toIceType(Type) != ((Operand *)Val)->getType()) { | 
| + auto LocalStoreVal = Cfg->makeVariable(toIceType(Type)); | 
| 
John
2016/03/28 18:44:09
auto *
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + Control()->appendInst( | 
| + InstCast::create(Cfg, InstCast::Trunc, LocalStoreVal, Val)); | 
| + StoreVal = LocalStoreVal; | 
| + } else { | 
| + StoreVal = Val; | 
| + } | 
| + | 
| + // then store the memory | 
| + Control()->appendInst(InstStore::create(Cfg, StoreVal, Addr)); | 
| + } | 
| + | 
| + static void PrintDebugName(Node node) { llvm_unreachable("PrintDebugName"); } | 
| + | 
| + CfgNode *Control() { | 
| + return control_ ? (CfgNode *)*control_ : Cfg->getEntryNode(); | 
| 
John
2016/03/28 18:44:08
static_cast<>
 | 
| + } | 
| + Node Effect() { return *effect_; } | 
| + | 
| + void set_module(wasm::ModuleEnv *Module) { this->module_ = Module; } | 
| 
John
2016/03/28 18:44:10
setModule
 
Eric Holk
2016/03/29 19:54:09
This name is set by V8, so I can't change it.
 | 
| + | 
| + void set_control_ptr(Node *Control) { this->control_ = Control; } | 
| 
John
2016/03/28 18:44:09
setControlPtr
 
Eric Holk
2016/03/29 19:54:11
This name is set by V8, so I can't change it.
 | 
| + | 
| + void set_effect_ptr(Node *Effect) { this->effect_ = Effect; } | 
| 
John
2016/03/28 18:44:08
setEffectPtr
 
Eric Holk
2016/03/29 19:54:10
This name is set by V8, so I can't change it.
 | 
| +}; | 
| + | 
| +IceString fnNameFromId(uint32_t Id) { return IceString("fn") + to_string(Id); } | 
| + | 
| +std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, | 
| + FunctionEnv *Env, | 
| + const byte *Base, | 
| + const byte *Start, | 
| + const byte *End) { | 
| + auto Cfg = Cfg::create(Ctx, getNextSequenceNumber()); | 
| + Ice::CfgLocalAllocatorScope _(Cfg.get()); | 
| + | 
| + // TODO: parse the function signature... | 
| + | 
| + IceBuilder Builder(Cfg.get()); | 
| + LR_WasmDecoder<OperandNode, IceBuilder> Decoder(Zone, &Builder); | 
| + | 
| + LOG(out << Ctx->getFlags().getDefaultGlobalPrefix() << "\n"); | 
| + Decoder.Decode(Env, Base, Start, End); | 
| + | 
| + // We don't always know where the incoming branches are in phi nodes, so this | 
| + // function finds them. | 
| + Cfg->fixPhiNodes(); | 
| + | 
| + return Cfg; | 
| +} | 
| + | 
| +constexpr auto BUFFER_SIZE = 24 << 10; // 24k, bigger than the test cases I have | 
| + // so far. | 
| +uint8_t Buffer[BUFFER_SIZE]; | 
| 
JF
2016/03/28 18:17:13
Can you allocate this instead?
 
Eric Holk
2016/03/29 19:54:12
Done.
 | 
| + | 
| +void WasmTranslator::translate( | 
| + const std::string &IRFilename, | 
| + std::unique_ptr<llvm::DataStreamer> &&InputStream) { | 
| + LOG(out << "Initializing v8/wasm stuff..." | 
| + << "\n"); | 
| + Zone Zone; | 
| + ZoneScope _(&Zone); | 
| + | 
| + auto BytesRead = InputStream->GetBytes(Buffer, sizeof(Buffer)); | 
| 
John
2016/03/28 18:44:08
integral type? don't use auto?
 
Eric Holk
2016/03/29 19:54:10
Done.
 | 
| + LOG(out << "Read " << BytesRead << " bytes" | 
| + << "\n"); | 
| + | 
| + LOG(out << "Decoding module " << IRFilename << "\n"); | 
| + | 
| + auto Result = | 
| + DecodeWasmModule(nullptr /* DecodeWasmModule ignores the Isolate | 
| + * parameter, so we just pass a null | 
| + * one rather than go through the | 
| + * hullabaloo of making one. */, &Zone, | 
| + Buffer, Buffer + BytesRead, false, kWasmOrigin); | 
| + | 
| + auto Module = Result.val; | 
| + | 
| + LOG(out << "Module info:" | 
| + << "\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"); | 
| + | 
| + ModuleEnv ModuleEnv; | 
| + ModuleEnv.module = Module; | 
| + | 
| + LOG(out << "\n" | 
| + << "Function information:" | 
| + << "\n"); | 
| + for (const auto F : Module->functions) { | 
| + LOG(out << " " << F.name_offset << ": " << Module->GetName(F.name_offset)); | 
| + if (F.exported) | 
| + LOG(out << " export"); | 
| + if (F.external) | 
| + LOG(out << " extern"); | 
| + LOG(out << "\n"); | 
| + } | 
| + | 
| + FunctionEnv Fenv; | 
| + Fenv.module = &ModuleEnv; | 
| + | 
| + LOG(out << "Translating " << IRFilename << "\n"); | 
| + | 
| + // Translate each function. | 
| + uint32_t Id = 0; | 
| + for (const auto Fn : Module->functions) { | 
| + auto NewName = fnNameFromId(Id++); | 
| 
John
2016/03/28 18:44:07
std::string?
 
Eric Holk
2016/03/29 19:54:11
Done.
 | 
| + LOG(out << " " << Fn.name_offset << ": " << Module->GetName(Fn.name_offset) | 
| + << " -> " << NewName << "..."); | 
| + | 
| + Fenv.sig = Fn.sig; | 
| + Fenv.local_i32_count = Fn.local_i32_count; | 
| + Fenv.local_i64_count = Fn.local_i64_count; | 
| + Fenv.local_f32_count = Fn.local_f32_count; | 
| + Fenv.local_f64_count = Fn.local_f64_count; | 
| + Fenv.SumLocals(); | 
| + | 
| + auto Cfg = | 
| 
John
2016/03/28 18:44:07
auto *
 
Eric Holk
2016/03/29 19:54:09
translateFunction returns a std::unique_ptr, so au
 | 
| + translateFunction(&Zone, &Fenv, Buffer, Buffer + Fn.code_start_offset, | 
| + Buffer + Fn.code_end_offset); | 
| + // auto FnName = module->GetName(Fn.name_offset); | 
| + Cfg->setFunctionName(NewName); | 
| + | 
| + Ctx->optQueueBlockingPush(std::move(Cfg)); | 
| + LOG(out << "done." | 
| + << "\n"); | 
| + } | 
| + | 
| + return; | 
| +} |