Subzero. WASM. Additional progress.
This change includes a number of improvements since the last WASM
CL. It compiles against a newer version of V8 that matches the current
WASM binary format. Many more WASM instructions are supported, as well
as global variable initializers.
There is also the beginning of a runtime library that implements some
system calls required by the WASM C library. The c2wasm-exe.sh script
can be used to compile a C program to a .wasm module, which is then
compiled by Subzero into a native executable.
This change includes a new Breakpoint instruction, which inserts an
unconditional breakpoint into the executable. This has been helpful in
debugging code generation for some WASM instructions. The Breakpoint
instruction is only completely implemented on X86.
BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4369
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/1876413002 .
diff --git a/src/WasmTranslator.cpp b/src/WasmTranslator.cpp
index b42ad77..6c1ed24 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::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,57 @@
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 +198,11 @@
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 +211,7 @@
return Out;
}
-constexpr bool isComparison(wasm::WasmOpcode Opcode) {
+bool isComparison(wasm::WasmOpcode Opcode) {
switch (Opcode) {
case kExprI32Ne:
case kExprI64Ne:
@@ -157,6 +227,16 @@
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 +252,7 @@
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 +261,14 @@
}
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,25 +288,25 @@
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 << ") = ");
@@ -243,7 +323,7 @@
// 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]);
@@ -253,10 +333,10 @@
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 +374,18 @@
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 +406,11 @@
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 +426,28 @@
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 +461,112 @@
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, Dest, Left, Right));
+ InstIcmp::create(Func, InstIcmp::Ne, TmpDest, Left, Right));
+ Control()->appendInst(
+ 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, Dest, Left, Right));
+ InstIcmp::create(Func, InstIcmp::Eq, TmpDest, Left, Right));
+ Control()->appendInst(
+ 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, Dest, Left, Right));
+ InstIcmp::create(Func, InstIcmp::Ult, TmpDest, Left, Right));
+ Control()->appendInst(
+ 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, Dest, Left, Right));
+ InstIcmp::create(Func, InstIcmp::Sge, TmpDest, Left, Right));
+ Control()->appendInst(
+ 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, Dest, Left, Right));
+ InstIcmp::create(Func, InstIcmp::Sgt, TmpDest, Left, Right));
+ Control()->appendInst(
+ 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, Dest, Left, Right));
+ 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(
+ 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 +580,14 @@
<< ") = ");
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 +605,21 @@
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 +628,14 @@
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 +652,7 @@
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 +672,50 @@
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"); }
- 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 +757,20 @@
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 +791,17 @@
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 +809,10 @@
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 +820,140 @@
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) { llvm::report_fatal_error("Invert"); }
- Node FunctionTable() { llvm::report_fatal_error("FunctionTable"); }
+ Node Invert(Node Node) {
+ (void)Node;
+ llvm::report_fatal_error("Invert");
+ }
//-----------------------------------------------------------------------
// 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::report_fatal_error("Unsupported type for memory load");
+ }
} else {
Result = LoadResult;
}
@@ -615,13 +965,7 @@
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 +979,11 @@
}
// 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 +1050,33 @@
}
};
-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 +1103,8 @@
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 +1113,56 @@
<< "\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 +1170,76 @@
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 << "...");
+ const auto FnName = getFunctionName(Module, Fn.func_index);
- 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();
+ LOG(out << " " << Fn.func_index << ": " << FnName << "...");
- auto Func = translateFunction(&Zone, &Fenv, Buffer.get(),
- Buffer.get() + Fn.code_start_offset,
- Buffer.get() + Fn.code_end_offset);
- Func->setFunctionName(Ctx->getGlobalString(NewName));
+ 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");