 Chromium Code Reviews
 Chromium Code Reviews Issue 1876413002:
  Subzero. WASM. Additional progress.  (Closed) 
  Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
    
  
    Issue 1876413002:
  Subzero. WASM. Additional progress.  (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 | 
| index b42ad7761442d9adee2475aaa18f9eefa8cb1f0a..853b520391c9e0665e97dc0de4d9d40c8baef8e3 100644 | 
| --- a/src/WasmTranslator.cpp | 
| +++ b/src/WasmTranslator.cpp | 
| @@ -17,14 +17,33 @@ | 
| #if ALLOW_WASM | 
| -#include "llvm/Support/StreamingMemoryObject.h" | 
| - | 
| #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" | 
| @@ -35,21 +54,17 @@ using namespace v8::internal; | 
| using namespace v8::internal::wasm; | 
| using v8::internal::wasm::DecodeWasmModule; | 
| -#include "src/wasm/ast-decoder-impl.h" | 
| - | 
| +#undef LOG | 
| #define LOG(Expr) log([&](Ostream & out) { Expr; }) | 
| namespace { | 
| -Ice::Type toIceType(v8::internal::MachineType) { | 
| - // TODO(eholk): actually convert this. | 
| - return IceType_i32; | 
| +std::string toStdString(WasmName Name) { | 
| + return std::string(Name.name, Name.length); | 
| } | 
| Ice::Type toIceType(wasm::LocalType Type) { | 
| switch (Type) { | 
| - default: | 
| - llvm::report_fatal_error("unexpected enum value"); | 
| case MachineRepresentation::kNone: | 
| llvm::report_fatal_error("kNone type not supported"); | 
| case MachineRepresentation::kBit: | 
| @@ -71,6 +86,58 @@ Ice::Type toIceType(wasm::LocalType Type) { | 
| case MachineRepresentation::kTagged: | 
| llvm::report_fatal_error("kTagged type not supported"); | 
| } | 
| + llvm_unreachable("unexpected type"); | 
| 
Jim Stichnoth
2016/04/14 20:03:44
Might as well make this report_fatal_error like ab
 
Eric Holk
2016/04/15 15:24:27
Done.
 | 
| +} | 
| + | 
| +Ice::Type toIceType(v8::internal::MachineType Type) { | 
| + if (Type == MachineType::Int8()) { | 
| + return IceType_i8; | 
| + } else if (Type == MachineType::Uint8()) { | 
| 
Jim Stichnoth
2016/04/14 20:03:44
All these "else if" should just be "if" because of
 
Eric Holk
2016/04/15 15:24:27
I changed the `else if` to `if`.
I moved the 32-b
 | 
| + return IceType_i8; | 
| + } else if (Type == MachineType::Int16()) { | 
| + return IceType_i16; | 
| + } else if (Type == MachineType::Uint16()) { | 
| + return IceType_i16; | 
| + } else if (Type == MachineType::Int32()) { | 
| + return IceType_i32; | 
| + } else if (Type == MachineType::Uint32()) { | 
| + return IceType_i32; | 
| + } else if (Type == MachineType::Int64()) { | 
| + return IceType_i64; | 
| + } else if (Type == MachineType::Uint64()) { | 
| + return IceType_i64; | 
| + } else if (Type == MachineType::Float32()) { | 
| + return IceType_f32; | 
| + } else if (Type == MachineType::Float64()) { | 
| + return IceType_f64; | 
| + } else { | 
| + 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) { | 
| + std::string FnName; | 
| + bool NameFound = false; | 
| + | 
| + // Try to find the function name in the export table | 
| + for (const auto Export : Module->export_table) { | 
| + if (Export.func_index == func_index) { | 
| + NameFound = true; | 
| + FnName = "__szwasm_" + toStdString(Module->GetName(Export.name_offset, | 
| + Export.name_length)); | 
| + break; | 
| 
Jim Stichnoth
2016/04/14 20:03:44
Can you just return FnName here, and remove the Na
 
Eric Holk
2016/04/15 15:24:27
Done.
 | 
| + } | 
| + } | 
| + | 
| + if (!NameFound) { | 
| + FnName = fnNameFromId(func_index); | 
| + } | 
| + | 
| + return FnName; | 
| } | 
| } // end of anonymous namespace | 
| @@ -132,7 +199,11 @@ public: | 
| Ostream &operator<<(Ostream &Out, const OperandNode &Op) { | 
| if (Op.isOperand()) { | 
| - Out << "(Operand*)" << Op.toOperand(); | 
| + auto *Oper = Op.toOperand(); | 
| 
Jim Stichnoth
2016/04/14 20:03:44
const auto * ?
 
Eric Holk
2016/04/15 15:24:27
Done.
 | 
| + Out << "(Operand*)" << Oper; | 
| + if (Oper) { | 
| + Out << "::" << Oper->getType(); | 
| + } | 
| } else if (Op.isCfgNode()) { | 
| Out << "(CfgNode*)" << Op.toCfgNode(); | 
| } else { | 
| @@ -141,7 +212,7 @@ Ostream &operator<<(Ostream &Out, const OperandNode &Op) { | 
| return Out; | 
| } | 
| -constexpr bool isComparison(wasm::WasmOpcode Opcode) { | 
| +bool isComparison(wasm::WasmOpcode Opcode) { | 
| switch (Opcode) { | 
| case kExprI32Ne: | 
| case kExprI64Ne: | 
| @@ -157,6 +228,16 @@ constexpr bool isComparison(wasm::WasmOpcode Opcode) { | 
| case kExprI64GtS: | 
| case kExprI32GtU: | 
| case kExprI64GtU: | 
| + case kExprF32Ne: | 
| + case kExprF64Ne: | 
| + case kExprF32Le: | 
| + case kExprF64Le: | 
| + case kExprI32LeS: | 
| + case kExprI64LeS: | 
| + case kExprI32GeU: | 
| + case kExprI64GeU: | 
| + case kExprI32LeU: | 
| + case kExprI64LeU: | 
| return true; | 
| default: | 
| return false; | 
| @@ -172,7 +253,7 @@ class IceBuilder { | 
| public: | 
| explicit IceBuilder(class Cfg *Func) | 
| - : Func(Func), Ctx(Func->getContext()), ControlPtr(nullptr) {} | 
| + : ControlPtr(nullptr), Func(Func), Ctx(Func->getContext()) {} | 
| /// Allocates a buffer of Nodes for use by V8. | 
| Node *Buffer(size_t Count) { | 
| @@ -183,8 +264,8 @@ public: | 
| Node Error() { llvm::report_fatal_error("Error"); } | 
| Node Start(unsigned Params) { | 
| LOG(out << "Start(" << Params << ") = "); | 
| - auto *Entry = Func->makeNode(); | 
| - Func->setEntryNode(Entry); | 
| + auto *Entry = Func->getEntryNode(); | 
| + assert(Entry); | 
| LOG(out << Node(Entry) << "\n"); | 
| return OperandNode(Entry); | 
| } | 
| @@ -224,9 +305,9 @@ public: | 
| LOG(out << (OperandNode)MergedNode << "\n"); | 
| return OperandNode(MergedNode); | 
| } | 
| - Node Phi(wasm::LocalType Type, unsigned Count, Node *Vals, Node Control) { | 
| + Node Phi(wasm::LocalType, unsigned Count, Node *Vals, Node Control) { | 
| 
Jim Stichnoth
2016/04/14 20:03:44
Can all these unsigneds be uint32_t instead?  That
 
Eric Holk
2016/04/15 15:24:27
Done.
 | 
| LOG(out << "Phi(" << Count << ", " << Control); | 
| - for (int i = 0; i < Count; ++i) { | 
| + for (uint32_t i = 0; i < Count; ++i) { | 
| LOG(out << ", " << Vals[i]); | 
| } | 
| LOG(out << ") = "); | 
| @@ -243,7 +324,7 @@ public: | 
| // TODO(eholk): find a better way besides multiplying by some arbitrary | 
| // constant. | 
| auto *Phi = InstPhi::create(Func, Count * 10, Dest); | 
| - for (int i = 0; i < Count; ++i) { | 
| + for (uint32_t i = 0; i < Count; ++i) { | 
| auto *Op = Vals[i].toOperand(); | 
| assert(Op); | 
| Phi->addArgument(Op, InEdges[i]); | 
| @@ -294,13 +375,18 @@ public: | 
| LOG(out << "Binop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Left | 
| << ", " << Right << ") = "); | 
| auto *Dest = makeVariable( | 
| - isComparison(Opcode) ? IceType_i1 : Left.toOperand()->getType()); | 
| + isComparison(Opcode) ? IceType_i32 : 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( | 
| @@ -321,6 +407,11 @@ public: | 
| 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( | 
| @@ -336,6 +427,28 @@ public: | 
| Control()->appendInst( | 
| InstArithmetic::create(Func, InstArithmetic::Shl, Dest, Left, Right)); | 
| break; | 
| + case kExprI32Rol: { | 
| + // TODO(eholk): add rotate as an ICE instruction to make it easier to take | 
| + // advantage of hardware support. | 
| + | 
| + // TODO(eholk): don't hardcode so many numbers. | 
| + auto *Masked = makeVariable(IceType_i32); | 
| + auto *Bottom = makeVariable(IceType_i32); | 
| + auto *Top = makeVariable(IceType_i32); | 
| + Control()->appendInst(InstArithmetic::create( | 
| + Func, InstArithmetic::And, Masked, Right, Ctx->getConstantInt32(31))); | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Func, InstArithmetic::Shl, Top, Left, Masked)); | 
| + auto *RotShift = makeVariable(IceType_i32); | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Func, InstArithmetic::Sub, RotShift, | 
| + Ctx->getConstantInt32(32), Masked)); | 
| + Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Lshr, | 
| + Bottom, Left, Masked)); | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Func, InstArithmetic::Or, Dest, Top, Bottom)); | 
| + break; | 
| + } | 
| case kExprI32ShrU: | 
| case kExprI64ShrU: | 
| case kExprI32ShrS: | 
| @@ -349,39 +462,112 @@ public: | 
| InstArithmetic::create(Func, InstArithmetic::And, Dest, Left, Right)); | 
| break; | 
| case kExprI32Ne: | 
| - case kExprI64Ne: | 
| + case kExprI64Ne: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Ne, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Ne, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| case kExprI32Eq: | 
| - case kExprI64Eq: | 
| + case kExprI64Eq: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Eq, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Eq, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| case kExprI32LtS: | 
| - case kExprI64LtS: | 
| + case kExprI64LtS: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Slt, Dest, Left, Right)); | 
| + InstIcmp::create(Func, InstIcmp::Slt, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| + case kExprI32LeS: | 
| + case kExprI64LeS: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Sle, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + break; | 
| + } | 
| + case kExprI32GeU: | 
| + case kExprI64GeU: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Uge, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + break; | 
| + } | 
| + case kExprI32LeU: | 
| + case kExprI64LeU: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Ule, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + break; | 
| + } | 
| case kExprI32LtU: | 
| - case kExprI64LtU: | 
| + case kExprI64LtU: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Ult, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Ult, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| case kExprI32GeS: | 
| - case kExprI64GeS: | 
| + case kExprI64GeS: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Sge, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Sge, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + } | 
| case kExprI32GtS: | 
| - case kExprI64GtS: | 
| + case kExprI64GtS: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Sgt, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Sgt, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| case kExprI32GtU: | 
| - case kExprI64GtU: | 
| + case kExprI64GtU: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstIcmp::create(Func, InstIcmp::Ugt, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + break; | 
| + } | 
| + case kExprF32Ne: | 
| + case kExprF64Ne: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstFcmp::create(Func, InstFcmp::Une, TmpDest, Left, Right)); | 
| + Control()->appendInst( | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| + break; | 
| + } | 
| + case kExprF32Le: | 
| + case kExprF64Le: { | 
| + auto *TmpDest = makeVariable(IceType_i1); | 
| + Control()->appendInst( | 
| + InstFcmp::create(Func, InstFcmp::Ule, TmpDest, Left, Right)); | 
| Control()->appendInst( | 
| - InstIcmp::create(Func, InstIcmp::Ugt, Dest, Left, Right)); | 
| + InstCast::create(Func, InstCast::Sext, Dest, TmpDest)); | 
| break; | 
| + } | 
| default: | 
| LOG(out << "Unknown binop: " << WasmOpcodes::OpcodeName(Opcode) << "\n"); | 
| llvm::report_fatal_error("Uncovered or invalid binop."); | 
| @@ -395,6 +581,14 @@ public: | 
| << ") = "); | 
| Ice::Variable *Dest = nullptr; | 
| switch (Opcode) { | 
| + case kExprI32Eqz: { | 
| + Dest = makeVariable(IceType_i32); | 
| + auto *Tmp = makeVariable(IceType_i1); | 
| + Control()->appendInst(InstIcmp::create(Func, InstIcmp::Eq, Tmp, Input, | 
| + Ctx->getConstantInt32(0))); | 
| + Control()->appendInst(InstCast::create(Func, InstCast::Sext, Dest, Tmp)); | 
| + break; | 
| + } | 
| case kExprF32Neg: { | 
| Dest = makeVariable(IceType_f32); | 
| Control()->appendInst(InstArithmetic::create( | 
| @@ -412,6 +606,21 @@ public: | 
| 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 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; | 
| default: | 
| LOG(out << "Unknown unop: " << WasmOpcodes::OpcodeName(Opcode) << "\n"); | 
| llvm::report_fatal_error("Uncovered or invalid unop."); | 
| @@ -427,7 +636,7 @@ public: | 
| if (Phi && Phi.isOperand()) { | 
| LOG(out << " ...is operand" | 
| << "\n"); | 
| - if (auto *Inst = getDefiningInst(Phi)) { | 
| + if (getDefiningInst(Phi)) { | 
| LOG(out << " ...has defining instruction" | 
| << "\n"); | 
| LOG(out << getDefNode(Phi) << "\n"); | 
| @@ -444,6 +653,7 @@ public: | 
| LOG(out << "AppendToPhi(" << Merge << ", " << Phi << ", " << From << ")" | 
| << "\n"); | 
| auto *Inst = getDefiningInst(Phi); | 
| + assert(Inst->getDest()->getType() == From.toOperand()->getType()); | 
| Inst->addArgument(From, getDefNode(From)); | 
| } | 
| @@ -463,12 +673,49 @@ public: | 
| LOG(out << *TrueNode << ", " << *FalseNode << ")" | 
| << "\n"); | 
| - Ctrl->appendInst(InstBr::create(Func, Cond, *TrueNode, *FalseNode)); | 
| + auto *CondBool = makeVariable(IceType_i1); | 
| + Ctrl->appendInst(InstCast::create(Func, InstCast::Trunc, CondBool, Cond)); | 
| + | 
| + Ctrl->appendInst(InstBr::create(Func, CondBool, *TrueNode, *FalseNode)); | 
| return OperandNode(nullptr); | 
| } | 
| - Node Switch(unsigned Count, Node Key) { llvm::report_fatal_error("Switch"); } | 
| - Node IfValue(int32_t Value, Node Sw) { llvm::report_fatal_error("IfValue"); } | 
| - Node IfDefault(Node Sw) { llvm::report_fatal_error("IfDefault"); } | 
| + InstSwitch *CurrentSwitch = nullptr; | 
| + CfgNode *SwitchNode = nullptr; | 
| + SizeT SwitchIndex = 0; | 
| + Node Switch(unsigned 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(unsigned Count, Node *Vals) { | 
| assert(1 >= Count); | 
| LOG(out << "Return("); | 
| @@ -511,20 +758,20 @@ public: | 
| const auto NumArgs = Sig->parameter_count(); | 
| LOG(out << " number of args: " << NumArgs << "\n"); | 
| - const auto TargetName = | 
| - Ctx->getGlobalString(Module->GetName(Target.name_offset)); | 
| + const auto TargetName = getFunctionName(Module, Index); | 
| LOG(out << " target name: " << TargetName << "\n"); | 
| assert(Sig->return_count() <= 1); | 
| - auto *TargetOperand = Ctx->getConstantSym(0, TargetName); | 
| + 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 (int i = 0; i < NumArgs; ++i) { | 
| + 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]); | 
| @@ -545,13 +792,17 @@ public: | 
| LOG(out << " number of args: " << NumArgs << "\n"); | 
| const auto &Target = Module->import_table[Index]; | 
| - const auto TargetName = | 
| - Ctx->getGlobalString(Module->GetName(Target.function_name_offset)); | 
| + 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->getConstantSym(0, TargetName); | 
| + auto TargetOperand = Ctx->getConstantExternSym(TargetName); | 
| auto *Dest = Sig->return_count() > 0 | 
| ? makeVariable(toIceType(Sig->GetReturn())) | 
| @@ -559,9 +810,10 @@ public: | 
| constexpr bool NoTailCall = false; | 
| auto *Call = | 
| InstCall::create(Func, NumArgs, Dest, TargetOperand, NoTailCall); | 
| - for (int i = 0; i < NumArgs; ++i) { | 
| + 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]); | 
| } | 
| @@ -569,41 +821,141 @@ public: | 
| LOG(out << "Call Result = " << Node(Dest) << "\n"); | 
| return OperandNode(Dest); | 
| } | 
| - Node CallIndirect(uint32_t Index, Node *Args) { | 
| - llvm::report_fatal_error("CallIndirect"); | 
| + 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; | 
| + | 
| + // TODO(eholk): This should probably actually call abort instead. | 
| + auto *Abort = Func->makeNode(); | 
| + Abort->appendInst(InstUnreachable::create(Func)); | 
| + | 
| + 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(InstBreakpoint::create(Func)); | 
| 
Jim Stichnoth
2016/04/14 20:03:44
remove?
 
Eric Holk
2016/04/15 15:24:27
Done.
 | 
| + Control()->appendInst(Switch); | 
| + *ControlPtr = OperandNode(ExitNode); | 
| + return OperandNode(Dest); | 
| + } | 
| + Node Invert(Node Node) { | 
| + (void)Node; | 
| + llvm::report_fatal_error("Invert"); | 
| } | 
| - Node Invert(Node Node) { llvm::report_fatal_error("Invert"); } | 
| - Node FunctionTable() { llvm::report_fatal_error("FunctionTable"); } | 
| //----------------------------------------------------------------------- | 
| // Operations that concern the linear memory. | 
| //----------------------------------------------------------------------- | 
| - Node MemSize(uint32_t Offset) { llvm::report_fatal_error("MemSize"); } | 
| - Node LoadGlobal(uint32_t Index) { llvm::report_fatal_error("LoadGlobal"); } | 
| + 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"); | 
| } | 
| + | 
| + Operand *sanitizeAddress(Operand *Base, uint32_t Offset) { | 
| + // first, add the index and the offset together. | 
| + if (0 != Offset) { | 
| + auto *Addr = makeVariable(IceType_i32); | 
| + auto *OffsetConstant = Ctx->getConstantInt32(Offset); | 
| + Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 
| + Addr, Base, OffsetConstant)); | 
| + Base = Addr; | 
| + } | 
| + | 
| + SizeT MemSize = Module->module->min_mem_pages * (16 << 10); | 
| + auto *WrappedAddr = makeVariable(IceType_i32); | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Func, InstArithmetic::Add, WrappedAddr, Base, | 
| + Ctx->getConstantInt32(MemSize))); | 
| + | 
| + auto ClampedAddr = makeVariable(IceType_i32); | 
| + Control()->appendInst( | 
| + InstArithmetic::create(Func, InstArithmetic::And, ClampedAddr, Base, | 
| + Ctx->getConstantInt32(MemSize - 1))); | 
| + | 
| + auto RealAddr = Func->makeVariable(IceType_i32); | 
| + auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY")); | 
| + Control()->appendInst(InstArithmetic::create( | 
| + Func, InstArithmetic::Add, RealAddr, ClampedAddr, MemBase)); | 
| + return RealAddr; | 
| + } | 
| + | 
| Node LoadMem(wasm::LocalType Type, MachineType MemType, Node Index, | 
| uint32_t Offset) { | 
| LOG(out << "LoadMem(" << Index << "[" << Offset << "]) = "); | 
| - // first, add the index and the offset together. | 
| - auto *OffsetConstant = Ctx->getConstantInt32(Offset); | 
| - auto *Addr = makeVariable(IceType_i32); | 
| - Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 
| - Addr, Index, OffsetConstant)); | 
| + auto *RealAddr = sanitizeAddress(Index, Offset); | 
| - // then load the memory | 
| auto *LoadResult = makeVariable(toIceType(MemType)); | 
| - Control()->appendInst(InstLoad::create(Func, LoadResult, Addr)); | 
| + Control()->appendInst(InstLoad::create(Func, LoadResult, RealAddr)); | 
| // and cast, if needed | 
| Ice::Variable *Result = nullptr; | 
| if (toIceType(Type) != toIceType(MemType)) { | 
| - Result = makeVariable(toIceType(Type)); | 
| + auto DestType = toIceType(Type); | 
| + Result = makeVariable(DestType); | 
| // TODO(eholk): handle signs correctly. | 
| - Control()->appendInst( | 
| - InstCast::create(Func, InstCast::Sext, Result, LoadResult)); | 
| + 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_unreachable("Unsupported type for memory load"); | 
| + } | 
| } else { | 
| Result = LoadResult; | 
| } | 
| @@ -615,13 +967,7 @@ public: | 
| 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); | 
| - auto *Addr = makeVariable(IceType_i32); | 
| - Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 
| - Addr, Index, OffsetConstant)); | 
| + auto *RealAddr = sanitizeAddress(Index, Offset); | 
| // cast the value to the right type, if needed | 
| Operand *StoreVal = nullptr; | 
| @@ -635,10 +981,11 @@ public: | 
| } | 
| // then store the memory | 
| - Control()->appendInst(InstStore::create(Func, StoreVal, Addr)); | 
| + Control()->appendInst(InstStore::create(Func, StoreVal, RealAddr)); | 
| } | 
| - static void PrintDebugName(Node node) { | 
| + static void PrintDebugName(OperandNode Node) { | 
| + (void)Node; | 
| llvm::report_fatal_error("PrintDebugName"); | 
| } | 
| @@ -705,36 +1052,33 @@ private: | 
| } | 
| }; | 
| -std::string fnNameFromId(uint32_t Id) { | 
| - return std::string("fn") + to_string(Id); | 
| -} | 
| - | 
| std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, | 
| - FunctionEnv *Env, | 
| - const byte *Base, | 
| - const byte *Start, | 
| - const byte *End) { | 
| + FunctionBody &Body) { | 
| OstreamLocker L1(Ctx); | 
| auto Func = Cfg::create(Ctx, getNextSequenceNumber()); | 
| Ice::CfgLocalAllocatorScope L2(Func.get()); | 
| - // TODO: parse the function signature... | 
| + // TODO(eholk): parse the function signature... | 
| + | 
| + Func->setEntryNode(Func->makeNode()); | 
| IceBuilder Builder(Func.get()); | 
| - LR_WasmDecoder<OperandNode, IceBuilder> Decoder(Zone, &Builder); | 
| + SR_WasmDecoder<OperandNode, IceBuilder> Decoder(Zone, &Builder, Body); | 
| LOG(out << getFlags().getDefaultGlobalPrefix() << "\n"); | 
| - Decoder.Decode(Env, Base, Start, End); | 
| + 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; | 
| } | 
| WasmTranslator::WasmTranslator(GlobalContext *Ctx) | 
| - : Translator(Ctx), BufferSize(24 << 10), Buffer(new uint8_t[24 << 10]) { | 
| + : Translator(Ctx), Buffer(new uint8_t[24 << 10]), BufferSize(24 << 10) { | 
| // TODO(eholk): compute the correct buffer size. This uses 24k by default, | 
| // which has been big enough for testing but is not a general solution. | 
| } | 
| @@ -761,6 +1105,8 @@ void WasmTranslator::translate( | 
| 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"); | 
| @@ -769,15 +1115,56 @@ void WasmTranslator::translate( | 
| << "\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"); | 
| - ModuleEnv ModuleEnv; | 
| - ModuleEnv.module = Module; | 
| + 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.name_offset << ": " << Module->GetName(F.name_offset)); | 
| + 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) | 
| @@ -785,29 +1172,76 @@ void WasmTranslator::translate( | 
| LOG(out << "\n"); | 
| } | 
| - FunctionEnv Fenv; | 
| - Fenv.module = &ModuleEnv; | 
| + 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_MEMORY")); | 
| + | 
| + // 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; | 
| + } | 
| + | 
| + // Pad the rest with zeros | 
| + SizeT DataSize = Module->min_mem_pages * (64 << 10); | 
| + if (WritePtr < DataSize) { | 
| + WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( | 
| + Globals.get(), DataSize - WritePtr)); | 
| + } | 
| + | 
| + WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( | 
| + Globals.get(), Module->min_mem_pages * (64 << 10))); | 
| + | 
| + Globals->push_back(WasmMemory); | 
| + | 
| + lowerGlobals(std::move(Globals)); | 
| + } | 
| + | 
| // Translate each function. | 
| - uint32_t Id = 0; | 
| for (const auto Fn : Module->functions) { | 
| - std::string NewName = fnNameFromId(Id++); | 
| - 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 Func = translateFunction(&Zone, &Fenv, Buffer.get(), | 
| - Buffer.get() + Fn.code_start_offset, | 
| - Buffer.get() + Fn.code_end_offset); | 
| - Func->setFunctionName(Ctx->getGlobalString(NewName)); | 
| + std::string FnName = getFunctionName(Module, Fn.func_index); | 
| 
Jim Stichnoth
2016/04/14 20:03:44
use "auto", like above?
 
Eric Holk
2016/04/15 15:24:27
Done. I went ahead and made it const too.
 | 
| + | 
| + LOG(out << " " << Fn.func_index << ": " << FnName << "..."); | 
| + | 
| + Body.sig = Fn.sig; | 
| + Body.base = Buffer.get(); | 
| + Body.start = Buffer.get() + Fn.code_start_offset; | 
| + Body.end = Buffer.get() + Fn.code_end_offset; | 
| + | 
| + auto Func = translateFunction(&Zone, Body); | 
| + Func->setFunctionName(Ctx->getGlobalString(FnName)); | 
| Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func))); | 
| LOG(out << "done.\n"); |