| Index: src/WasmTranslator.cpp
|
| diff --git a/src/WasmTranslator.cpp b/src/WasmTranslator.cpp
|
| index b42ad7761442d9adee2475aaa18f9eefa8cb1f0a..156dcee50b07e4fe12f18562b22bade9c694e6de 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,19 @@ 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 {
|
| +// 64KB
|
| +const uint32_t WASM_PAGE_SIZE = 64 << 10;
|
|
|
| -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 +88,57 @@ Ice::Type toIceType(wasm::LocalType 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
|
| @@ -132,7 +200,11 @@ public:
|
|
|
| Ostream &operator<<(Ostream &Out, const OperandNode &Op) {
|
| if (Op.isOperand()) {
|
| - Out << "(Operand*)" << Op.toOperand();
|
| + const auto *Oper = Op.toOperand();
|
| + Out << "(Operand*)" << Oper;
|
| + if (Oper) {
|
| + Out << "::" << Oper->getType();
|
| + }
|
| } else if (Op.isCfgNode()) {
|
| Out << "(CfgNode*)" << Op.toCfgNode();
|
| } else {
|
| @@ -141,7 +213,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 +229,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 +254,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) {
|
| @@ -181,14 +263,14 @@ public:
|
| }
|
|
|
| Node Error() { llvm::report_fatal_error("Error"); }
|
| - Node Start(unsigned Params) {
|
| + Node Start(uint32_t 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);
|
| }
|
| - Node Param(unsigned Index, wasm::LocalType Type) {
|
| + Node Param(uint32_t Index, wasm::LocalType Type) {
|
| LOG(out << "Param(" << Index << ") = ");
|
| auto *Arg = makeVariable(toIceType(Type));
|
| assert(Index == NextArg);
|
| @@ -208,31 +290,31 @@ public:
|
| LOG(out << "Terminate(" << Effect << ", " << Control << ")"
|
| << "\n");
|
| }
|
| - Node Merge(unsigned Count, Node *Controls) {
|
| + Node Merge(uint32_t Count, Node *Controls) {
|
| LOG(out << "Merge(" << Count);
|
| - for (unsigned i = 0; i < Count; ++i) {
|
| + for (uint32_t i = 0; i < Count; ++i) {
|
| LOG(out << ", " << Controls[i]);
|
| }
|
| LOG(out << ") = ");
|
|
|
| auto *MergedNode = Func->makeNode();
|
|
|
| - for (unsigned i = 0; i < Count; ++i) {
|
| + 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 Type, unsigned Count, Node *Vals, Node Control) {
|
| + Node Phi(wasm::LocalType, uint32_t Count, Node *Vals, Node Control) {
|
| 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 << ") = ");
|
|
|
| const auto &InEdges = Control.toCfgNode()->getInEdges();
|
| - assert(Count == InEdges.size());
|
| + //assert(Count == InEdges.size());
|
|
|
| assert(Count > 0);
|
|
|
| @@ -243,20 +325,21 @@ 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]);
|
| }
|
| setDefiningInst(Dest, Phi);
|
| - Control.toCfgNode()->appendInst(Phi);
|
| + constexpr bool AllowPhisAnywhere = true;
|
| + Control.toCfgNode()->appendInst(Phi, AllowPhisAnywhere);
|
| LOG(out << Node(Dest) << "\n");
|
| return OperandNode(Dest);
|
| }
|
| - Node EffectPhi(unsigned Count, Node *Effects, Node Control) {
|
| + Node EffectPhi(uint32_t Count, Node *Effects, Node Control) {
|
| // TODO(eholk): this function is almost certainly wrong.
|
| LOG(out << "EffectPhi(" << Count << ", " << Control << "):\n");
|
| - for (unsigned i = 0; i < Count; ++i) {
|
| + for (uint32_t i = 0; i < Count; ++i) {
|
| LOG(out << " " << Effects[i] << "\n");
|
| }
|
| return OperandNode(nullptr);
|
| @@ -294,13 +377,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 +409,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 +429,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 +464,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::Zext, 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::Zext, 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::Zext, 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::Zext, 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::Zext, 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::Zext, Dest, TmpDest));
|
| + break;
|
| + }
|
| case kExprI32LtU:
|
| - case kExprI64LtU:
|
| + case kExprI64LtU: {
|
| + auto *TmpDest = makeVariable(IceType_i1);
|
| Control()->appendInst(
|
| - InstIcmp::create(Func, InstIcmp::Ult, Dest, Left, Right));
|
| + InstIcmp::create(Func, InstIcmp::Ult, TmpDest, Left, Right));
|
| + Control()->appendInst(
|
| + InstCast::create(Func, InstCast::Zext, 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::Zext, 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::Zext, 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::Zext, Dest, TmpDest));
|
| + break;
|
| + }
|
| + case kExprF32Ne:
|
| + case kExprF64Ne: {
|
| + auto *TmpDest = makeVariable(IceType_i1);
|
| + Control()->appendInst(
|
| + InstFcmp::create(Func, InstFcmp::Une, TmpDest, Left, Right));
|
| Control()->appendInst(
|
| - InstIcmp::create(Func, InstIcmp::Ugt, Dest, Left, Right));
|
| + 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));
|
| + 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.");
|
| @@ -395,6 +583,22 @@ 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::Zext, Dest, Tmp));
|
| + break;
|
| + }
|
| + case kExprI64Eqz: {
|
| + Dest = makeVariable(IceType_i32);
|
| + auto *Tmp = makeVariable(IceType_i1);
|
| + Control()->appendInst(InstIcmp::create(Func, InstIcmp::Eq, Tmp, Input,
|
| + Ctx->getConstantInt64(0)));
|
| + Control()->appendInst(InstCast::create(Func, InstCast::Zext, Dest, Tmp));
|
| + break;
|
| + }
|
| case kExprF32Neg: {
|
| Dest = makeVariable(IceType_f32);
|
| Control()->appendInst(InstArithmetic::create(
|
| @@ -412,6 +616,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.");
|
| @@ -420,14 +639,14 @@ public:
|
| LOG(out << Dest << "\n");
|
| return OperandNode(Dest);
|
| }
|
| - unsigned InputCount(CfgNode *Node) const { return Node->getInEdges().size(); }
|
| + 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 (auto *Inst = getDefiningInst(Phi)) {
|
| + if (getDefiningInst(Phi)) {
|
| LOG(out << " ...has defining instruction"
|
| << "\n");
|
| LOG(out << getDefNode(Phi) << "\n");
|
| @@ -444,6 +663,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,13 +683,51 @@ public:
|
| LOG(out << *TrueNode << ", " << *FalseNode << ")"
|
| << "\n");
|
|
|
| - Ctrl->appendInst(InstBr::create(Func, Cond, *TrueNode, *FalseNode));
|
| + auto *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);
|
| }
|
| - 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"); }
|
| - Node Return(unsigned Count, Node *Vals) {
|
| + 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)
|
| @@ -511,20 +769,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 +803,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 +821,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 +832,155 @@ 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(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) {
|
| + SizeT MemSize = Module->module->min_mem_pages * WASM_PAGE_SIZE;
|
| + SizeT MemMask = MemSize - 1;
|
| +
|
| + 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();
|
| + RealOffset &= MemMask;
|
| + Base = Ctx->getConstantInt32(RealOffset);
|
| + ConstZeroBase = (0 == RealOffset);
|
| + } else 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;
|
| + }
|
| +
|
| + if (!llvm::dyn_cast<ConstantInteger32>(Base)) {
|
| + auto ClampedAddr = makeVariable(IceType_i32);
|
| + Control()->appendInst(
|
| + InstArithmetic::create(Func, InstArithmetic::And, ClampedAddr, Base,
|
| + Ctx->getConstantInt32(MemSize - 1)));
|
| + Base = ClampedAddr;
|
| + }
|
| +
|
| + Ice::Operand *RealAddr = nullptr;
|
| + auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY"));
|
| + if (!ConstZeroBase) {
|
| + auto RealAddrV = Func->makeVariable(IceType_i32);
|
| + Control()->appendInst(InstArithmetic::create(
|
| + Func, InstArithmetic::Add, RealAddrV, Base, MemBase));
|
| +
|
| + RealAddr = RealAddrV;
|
| + } else {
|
| + RealAddr = 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::report_fatal_error("Unsupported type for memory load");
|
| + }
|
| } else {
|
| Result = LoadResult;
|
| }
|
| @@ -615,13 +992,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 +1006,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 +1077,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 +1130,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 +1140,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 +1197,73 @@ 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 * WASM_PAGE_SIZE;
|
| + if (WritePtr < DataSize) {
|
| + WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create(
|
| + Globals.get(), DataSize - WritePtr));
|
| + }
|
| +
|
| + 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));
|
| + const auto FnName = getFunctionName(Module, Fn.func_index);
|
| +
|
| + 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");
|
|
|