Commit 28392ab1 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Master CL for Binary 0xC changes.

[0xC] Convert to stack machine semantics.
[0xC] Use section codes instead of names.
[0xC] Add elements section decoding.
[0xC] Decoding of globals section.
[0xC] Decoding of memory section.
[0xC] Decoding of imports section.
[0xC] Decoding of exports section.
[0xC] Decoding of data section.
[0xC] Remove CallImport bytecode.
[0xC] Function bodies have an implicit block.
[0xC] Remove the bottom label from loops.
[0xC] Add signatures to blocks.
[0xC] Remove arities from branches.
Add tests for init expression decoding.
Rework compilation of import wrappers and how they are patched.
Rework function indices in debugging.
Fix ASM->WASM builder for stack machine.
Reorganize asm.js foreign functions due to import indices change.

R=ahaas@chromium.org,rossberg@chromium.org,bradnelson@chromium.org
BUG=chromium:575167
LOG=Y

Committed: https://crrev.com/76eb976a67273b8c03c744f64ad850b0432554b9
Review-Url: https://codereview.chromium.org/2345593003
Cr-Original-Commit-Position: refs/heads/master@{#39678}
Cr-Commit-Position: refs/heads/master@{#39795}
parent fcf1bac9
......@@ -207,7 +207,8 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
i::MaybeHandle<i::JSObject> maybe_module_object =
i::wasm::WasmModule::Instantiate(isolate, module, foreign, memory);
i::wasm::WasmModule::Instantiate(isolate, &thrower, module, foreign,
memory);
if (maybe_module_object.is_null()) {
return MaybeHandle<Object>();
}
......
......@@ -128,6 +128,7 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
script_(script),
root_(root),
forward_definitions_(zone),
ffi_use_signatures_(zone),
stdlib_types_(zone),
stdlib_math_types_(zone),
module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)),
......@@ -329,8 +330,8 @@ AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
return i->second;
}
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) {
ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const {
const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
ZoneHashMap::Entry* entry =
scope->Lookup(variable, ComputePointerHash(variable));
if (entry == nullptr && in_function_) {
......@@ -423,6 +424,8 @@ AsmType* AsmTyper::TypeOf(AstNode* node) const {
return AsmType::None();
}
AsmType* AsmTyper::TypeOf(Variable* v) const { return Lookup(v)->type(); }
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
auto* var_info = Lookup(var);
if (var_info == nullptr) {
......@@ -2306,10 +2309,21 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
FAIL(call, "Calling something that's not a function.");
}
if (callee_type->AsFFIType() != nullptr &&
return_type == AsmType::Float()) {
if (callee_type->AsFFIType() != nullptr) {
if (return_type == AsmType::Float()) {
FAIL(call, "Foreign functions can't return float.");
}
// Record FFI use signature, since the asm->wasm translator must know
// all uses up-front.
ffi_use_signatures_.emplace_back(
FFIUseSignature(call_var_proxy->var(), zone_));
FFIUseSignature* sig = &ffi_use_signatures_.back();
sig->return_type_ = return_type;
sig->arg_types_.reserve(args.size());
for (size_t i = 0; i < args.size(); ++i) {
sig->arg_types_.emplace_back(args[i]);
}
}
if (!callee_type->CanBeInvokedWith(return_type, args)) {
FAIL(call, "Function invocation does not match function type.");
......
......@@ -73,12 +73,26 @@ class AsmTyper final {
const char* error_message() const { return error_message_; }
AsmType* TypeOf(AstNode* node) const;
AsmType* TypeOf(Variable* v) const;
StandardMember VariableAsStandardMember(Variable* var);
typedef std::unordered_set<StandardMember, std::hash<int> > StdlibSet;
StdlibSet StdlibUses() const { return stdlib_uses_; }
// Each FFI import has a usage-site signature associated with it.
struct FFIUseSignature {
Variable* var;
ZoneVector<AsmType*> arg_types_;
AsmType* return_type_;
FFIUseSignature(Variable* v, Zone* zone)
: var(v), arg_types_(zone), return_type_(nullptr) {}
};
const ZoneVector<FFIUseSignature>& FFIUseSignatures() {
return ffi_use_signatures_;
}
private:
friend class v8::internal::wasm::AsmTyperHarnessBuilder;
......@@ -192,7 +206,7 @@ class AsmTyper final {
// Lookup(Delta, Gamma, x)
//
// Delta is the global_scope_ member, and Gamma, local_scope_.
VariableInfo* Lookup(Variable* variable);
VariableInfo* Lookup(Variable* variable) const;
// All of the ValidateXXX methods below return AsmType::None() in case of
// validation failure.
......@@ -306,6 +320,7 @@ class AsmTyper final {
AsmType* return_type_ = nullptr;
ZoneVector<VariableInfo*> forward_definitions_;
ZoneVector<FFIUseSignature> ffi_use_signatures_;
ObjectTypeMap stdlib_types_;
ObjectTypeMap stdlib_math_types_;
......
This diff is collapsed.
......@@ -778,6 +778,18 @@ void Int64Lowering::LowerNode(Node* node) {
}
break;
}
case IrOpcode::kProjection: {
Node* call = node->InputAt(0);
DCHECK_EQ(IrOpcode::kCall, call->opcode());
CallDescriptor* descriptor =
const_cast<CallDescriptor*>(CallDescriptorOf(call->op()));
for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
if (descriptor->GetReturnType(i) == MachineType::Int64()) {
UNREACHABLE(); // TODO(titzer): implement multiple i64 returns.
}
}
break;
}
case IrOpcode::kWord64ReverseBytes: {
Node* input = node->InputAt(0);
ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(),
......
......@@ -189,7 +189,14 @@ class WasmTrapHelper : public ZoneObject {
Node* GetTrapValue(wasm::FunctionSig* sig) {
if (sig->return_count() > 0) {
switch (sig->GetReturn()) {
return GetTrapValue(sig->GetReturn());
} else {
return jsgraph()->Int32Constant(0xdeadbeef);
}
}
Node* GetTrapValue(wasm::LocalType type) {
switch (type) {
case wasm::kAstI32:
return jsgraph()->Int32Constant(0xdeadbeef);
case wasm::kAstI64:
......@@ -197,16 +204,12 @@ class WasmTrapHelper : public ZoneObject {
case wasm::kAstF32:
return jsgraph()->Float32Constant(bit_cast<float>(0xdeadbeef));
case wasm::kAstF64:
return jsgraph()->Float64Constant(
bit_cast<double>(0xdeadbeefdeadbeef));
return jsgraph()->Float64Constant(bit_cast<double>(0xdeadbeefdeadbeef));
break;
default:
UNREACHABLE();
return nullptr;
}
} else {
return jsgraph()->Int32Constant(0xdeadbeef);
}
}
private:
......@@ -993,16 +996,11 @@ Node* WasmGraphBuilder::Return(unsigned count, Node** vals) {
DCHECK_NOT_NULL(*control_);
DCHECK_NOT_NULL(*effect_);
if (count == 0) {
// Handle a return of void.
vals[0] = jsgraph()->Int32Constant(0);
count = 1;
}
Node** buf = Realloc(vals, count, count + 2);
buf[count] = *effect_;
buf[count + 1] = *control_;
Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals);
Node* ret =
graph()->NewNode(jsgraph()->common()->Return(count), count + 2, vals);
MergeControlToEnd(jsgraph(), ret);
return ret;
......@@ -1994,7 +1992,7 @@ Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) {
return call;
}
Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position) {
const size_t params = sig->parameter_count();
const size_t extra = 2; // effect and control inputs.
......@@ -2014,32 +2012,37 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
SetSourcePosition(call, position);
*effect_ = call;
return call;
}
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = HeapConstant(module_->GetCodeOrPlaceholder(index));
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
size_t ret_count = sig->return_count();
if (ret_count == 0) return nullptr; // No return value.
return BuildWasmCall(sig, args, position);
Node** rets = Buffer(ret_count);
if (ret_count == 1) {
// Only a single return value.
rets[0] = call;
} else {
// Create projections for all return values.
for (size_t i = 0; i < ret_count; i++) {
rets[i] = graph()->NewNode(jsgraph()->common()->Projection(i), call,
graph()->start());
}
}
return rets;
}
Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args,
Node** WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = HeapConstant(module_->GetImportCode(index));
wasm::FunctionSig* sig = module_->GetImportSignature(index);
Handle<Code> code = module_->GetFunctionCode(index);
DCHECK(!code.is_null());
args[0] = HeapConstant(code);
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args, position);
}
Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
DCHECK_NOT_NULL(args[0]);
DCHECK(module_ && module_->instance);
......@@ -2054,6 +2057,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
// Bounds check the index.
uint32_t table_size =
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0;
wasm::FunctionSig* sig = module_->GetSignature(index);
if (table_size > 0) {
// Bounds check against the table size.
Node* size = Uint32Constant(table_size);
......@@ -2062,7 +2066,11 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
} else {
// No function table. Generate a trap and return a constant.
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
return trap_->GetTrapValue(module_->GetSignature(index));
Node** rets = Buffer(sig->return_count());
for (size_t i = 0; i < sig->return_count(); i++) {
rets[i] = trap_->GetTrapValue(sig->GetReturn(i));
}
return rets;
}
Node* table = FunctionTable(0);
......@@ -2096,7 +2104,6 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
*effect_, *control_);
args[0] = load_code;
wasm::FunctionSig* sig = module_->GetSignature(index);
return BuildWasmCall(sig, args, position);
}
......@@ -2693,6 +2700,11 @@ Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
}
}
Node* WasmGraphBuilder::CurrentMemoryPages() {
return graph()->NewNode(jsgraph()->machine()->Word32Shr(), MemSize(0),
jsgraph()->Int32Constant(16));
}
Node* WasmGraphBuilder::MemSize(uint32_t offset) {
DCHECK(module_ && module_->instance);
uint32_t size = static_cast<uint32_t>(module_->instance->mem_size);
......
......@@ -153,12 +153,11 @@ class WasmGraphBuilder {
Node* ReturnVoid();
Node* Unreachable(wasm::WasmCodePosition position);
Node* CallDirect(uint32_t index, Node** args,
Node** CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node* CallImport(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node* CallIndirect(uint32_t index, Node** args,
Node** CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig);
......@@ -170,7 +169,7 @@ class WasmGraphBuilder {
//-----------------------------------------------------------------------
// Operations that concern the linear memory.
//-----------------------------------------------------------------------
Node* MemSize(uint32_t offset);
Node* CurrentMemoryPages();
Node* GetGlobal(uint32_t index);
Node* SetGlobal(uint32_t index, Node* val);
Node* LoadMem(wasm::LocalType type, MachineType memtype, Node* index,
......@@ -229,6 +228,7 @@ class WasmGraphBuilder {
Graph* graph();
Node* String(const char* string);
Node* MemSize(uint32_t offset);
Node* MemBuffer(uint32_t offset);
void BoundsCheckMem(MachineType memtype, Node* index, uint32_t offset,
wasm::WasmCodePosition position);
......@@ -240,7 +240,7 @@ class WasmGraphBuilder {
Node* MaskShiftCount64(Node* node);
Node* BuildCCall(MachineSignature* sig, Node** args);
Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node** BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position);
Node* BuildF32CopySign(Node* left, Node* right);
......
......@@ -532,6 +532,8 @@ DEFINE_BOOL(wasm_simd_prototype, false,
"enable prototype simd opcodes for wasm")
DEFINE_BOOL(wasm_eh_prototype, false,
"enable prototype exception handling opcodes for wasm")
DEFINE_BOOL(wasm_mv_prototype, false,
"enable prototype multi-value support for wasm")
DEFINE_BOOL(wasm_trap_handler, false,
"use signal handlers to catch out of bounds memory access in wasm"
......
This diff is collapsed.
......@@ -22,6 +22,7 @@ class WasmGraphBuilder;
namespace wasm {
const uint32_t kMaxNumWasmLocals = 8000000;
struct WasmGlobal;
// Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand {
......@@ -81,39 +82,111 @@ struct ImmF64Operand {
struct GlobalIndexOperand {
uint32_t index;
LocalType type;
const WasmGlobal* global;
unsigned length;
inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "global index");
global = nullptr;
type = kAstStmt;
}
};
struct BlockTypeOperand {
uint32_t arity;
const byte* types; // pointer to encoded types for the block.
unsigned length;
inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
uint8_t val = decoder->checked_read_u8(pc, 1, "block type");
LocalType type = kAstStmt;
length = 1;
arity = 0;
types = nullptr;
if (decode_local_type(val, &type)) {
arity = type == kAstStmt ? 0 : 1;
types = pc + 1;
} else {
// Handle multi-value blocks.
if (!FLAG_wasm_mv_prototype) {
decoder->error(pc, pc + 1, "invalid block arity > 1");
return;
}
if (val != kMultivalBlock) {
decoder->error(pc, pc + 1, "invalid block type");
return;
}
// Decode and check the types vector of the block.
unsigned len = 0;
uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity");
// {count} is encoded as {arity-2}, so that a {0} count here corresponds
// to a block with 2 values. This makes invalid/redundant encodings
// impossible.
arity = count + 2;
length = 1 + len + arity;
types = pc + 1 + 1 + len;
for (uint32_t i = 0; i < arity; i++) {
uint32_t offset = 1 + 1 + len + i;
val = decoder->checked_read_u8(pc, offset, "block type");
decode_local_type(val, &type);
if (type == kAstStmt) {
decoder->error(pc, pc + offset, "invalid block type");
return;
}
}
}
}
// Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or {kMultivalBlock}.
bool decode_local_type(uint8_t val, LocalType* result) {
switch (static_cast<LocalTypeCode>(val)) {
case kLocalVoid:
*result = kAstStmt;
return true;
case kLocalI32:
*result = kAstI32;
return true;
case kLocalI64:
*result = kAstI64;
return true;
case kLocalF32:
*result = kAstF32;
return true;
case kLocalF64:
*result = kAstF64;
return true;
default:
*result = kAstStmt;
return false;
}
}
LocalType read_entry(unsigned index) {
DCHECK_LT(index, arity);
LocalType result;
CHECK(decode_local_type(types[index], &result));
return result;
}
};
struct Control;
struct BreakDepthOperand {
uint32_t arity;
uint32_t depth;
Control* target;
unsigned length;
inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
depth = decoder->checked_read_u32v(pc, 1 + len1, &len2, "break depth");
length = len1 + len2;
depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
target = nullptr;
}
};
struct CallIndirectOperand {
uint32_t arity;
uint32_t index;
FunctionSig* sig;
unsigned length;
inline CallIndirectOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "signature index");
length = len1 + len2;
sig = nullptr;
......@@ -121,59 +194,32 @@ struct CallIndirectOperand {
};
struct CallFunctionOperand {
uint32_t arity;
uint32_t index;
FunctionSig* sig;
unsigned length;
inline CallFunctionOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index");
length = len1 + len2;
sig = nullptr;
}
};
struct CallImportOperand {
uint32_t arity;
uint32_t index;
FunctionSig* sig;
unsigned length;
inline CallImportOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "import index");
length = len1 + len2;
sig = nullptr;
}
};
struct BranchTableOperand {
uint32_t arity;
uint32_t table_count;
const byte* start;
const byte* table;
unsigned length;
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode"));
start = pc + 1;
unsigned len1 = 0;
unsigned len2 = 0;
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
table_count =
decoder->checked_read_u32v(pc, 1 + len1, &len2, "table count");
table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count");
if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 ||
len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
decoder->error(pc, "branch table size overflow");
}
length = len1 + len2 + (table_count + 1) * sizeof(uint32_t);
uint32_t table_start = 1 + len1 + len2;
if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
"expected <table entries>")) {
table = pc + table_start;
} else {
table = nullptr;
}
table = pc + 1 + len1;
}
inline uint32_t read_entry(Decoder* decoder, unsigned i) {
DCHECK(i <= table_count);
......@@ -181,6 +227,43 @@ struct BranchTableOperand {
}
};
// A helper to iterate over a branch table.
class BranchTableIterator {
public:
unsigned cur_index() { return index_; }
bool has_next() { return index_ <= table_count_; }
uint32_t next() {
DCHECK(has_next());
index_++;
unsigned length = 0;
uint32_t result =
decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry");
pc_ += length;
return result;
}
// length, including the length of the {BranchTableOperand}, but not the
// opcode.
unsigned length() {
while (has_next()) next();
return static_cast<unsigned>(pc_ - start_);
}
const byte* pc() { return pc_; }
BranchTableIterator(Decoder* decoder, BranchTableOperand& operand)
: decoder_(decoder),
start_(operand.start),
pc_(operand.table),
index_(0),
table_count_(operand.table_count) {}
private:
Decoder* decoder_;
const byte* start_;
const byte* pc_;
uint32_t index_; // the current index.
uint32_t table_count_; // the count of entries, not including default.
};
struct MemoryAccessOperand {
uint32_t alignment;
uint32_t offset;
......@@ -203,15 +286,6 @@ struct MemoryAccessOperand {
}
};
struct ReturnArityOperand {
uint32_t arity;
unsigned length;
inline ReturnArityOperand(Decoder* decoder, const byte* pc) {
arity = decoder->checked_read_u32v(pc, 1, &length, "return count");
}
};
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface.
......@@ -285,9 +359,6 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
// Computes the length of the opcode at the given address.
unsigned OpcodeLength(const byte* pc, const byte* end);
// Computes the arity (number of sub-nodes) of the opcode at the given address.
unsigned OpcodeArity(const byte* pc, const byte* end);
// A simple forward iterator for bytecodes.
class BytecodeIterator : public Decoder {
public:
......
......@@ -208,6 +208,19 @@ class Decoder {
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(int size) {
TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), "skip",
size);
if (checkAvailable(size)) {
pc_ += size;
} else {
pc_ = limit_;
}
}
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(uint32_t size, const char* name = "skip") {
TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), name,
size);
if (checkAvailable(size)) {
pc_ += size;
} else {
......
This diff is collapsed.
......@@ -121,19 +121,22 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void Emit(WasmOpcode opcode);
void EmitGetLocal(uint32_t index);
void EmitSetLocal(uint32_t index);
void EmitTeeLocal(uint32_t index);
void EmitI32Const(int32_t val);
void EmitWithU8(WasmOpcode opcode, const byte immediate);
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
void SetExported();
void SetName(const char* name, int name_length);
bool exported() { return exported_; }
// Writing methods.
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
void WriteExport(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const;
bool exported() { return exported_; }
uint32_t func_index() { return func_index_; }
FunctionSig* signature();
private:
explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
friend class WasmModuleBuilder;
......@@ -142,6 +145,7 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
LocalDeclEncoder locals_;
uint32_t signature_index_;
bool exported_;
uint32_t func_index_;
ZoneVector<uint8_t> body_;
ZoneVector<char> name_;
ZoneVector<uint32_t> i32_temps_;
......@@ -212,14 +216,17 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
explicit WasmModuleBuilder(Zone* zone);
// Building methods.
uint32_t AddFunction();
uint32_t AddGlobal(LocalType type, bool exported);
WasmFunctionBuilder* FunctionAt(size_t index);
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
void SetImportName(uint32_t index, const char* name, int name_length) {
imports_[index].name = name;
imports_[index].name_length = name_length;
}
WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true);
void AddDataSegment(WasmDataSegmentEncoder* data);
uint32_t AddSignature(FunctionSig* sig);
void AddIndirectFunction(uint32_t index);
void MarkStartFunction(uint32_t index);
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
void MarkStartFunction(WasmFunctionBuilder* builder);
// Writing methods.
void WriteTo(ZoneBuffer& buffer) const;
......@@ -231,18 +238,25 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
Zone* zone() { return zone_; }
FunctionSig* GetSignature(uint32_t index) { return signatures_[index]; }
private:
friend class WasmFunctionBuilder;
Zone* zone_;
ZoneVector<FunctionSig*> signatures_;
ZoneVector<WasmFunctionImport> imports_;
ZoneVector<WasmFunctionBuilder*> functions_;
ZoneVector<WasmDataSegmentEncoder*> data_segments_;
ZoneVector<uint32_t> indirect_functions_;
ZoneVector<std::pair<LocalType, bool>> globals_;
ZoneVector<std::tuple<LocalType, bool, bool>> globals_;
SignatureMap signature_map_;
int start_function_index_;
};
inline FunctionSig* WasmFunctionBuilder::signature() {
return builder_->signatures_[signature_index_];
}
} // namespace wasm
} // namespace internal
} // namespace v8
......
This diff is collapsed.
......@@ -32,8 +32,11 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* env,
// Extracts the function offset table from the wasm module bytes.
// Returns a vector with <offset, length> entries, or failure if the wasm bytes
// are detected as invalid. Note that this validation is not complete.
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_end);
FunctionOffsetsResult DecodeWasmFunctionOffsets(
const byte* module_start, const byte* module_end,
uint32_t num_imported_functions);
WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
} // namespace wasm
} // namespace internal
......
......@@ -32,11 +32,15 @@ ByteArray *GetOrCreateFunctionOffsetTable(Handle<WasmDebugInfo> debug_info) {
FunctionOffsetsResult function_offsets;
{
DisallowHeapAllocation no_gc;
Handle<JSObject> wasm_object(debug_info->wasm_object(), isolate);
uint32_t num_imported_functions =
wasm::GetNumImportedFunctions(wasm_object);
SeqOneByteString *wasm_bytes =
wasm::GetWasmBytes(debug_info->wasm_object());
const byte *bytes_start = wasm_bytes->GetChars();
const byte *bytes_end = bytes_start + wasm_bytes->length();
function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end);
function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end,
num_imported_functions);
}
DCHECK(function_offsets.ok());
size_t array_size = 2 * kIntSize * function_offsets.val.size();
......
This diff is collapsed.
......@@ -28,15 +28,7 @@ typedef uint32_t spdiff_t;
const pc_t kInvalidPc = 0x80000000;
// Visible for testing. A {ControlTransfer} helps the interpreter figure out
// the target program counter and stack manipulations for a branch.
struct ControlTransfer {
enum StackAction { kNoAction, kPopAndRepush, kPushVoid };
pcdiff_t pcdiff; // adjustment to the program counter (positive or negative).
spdiff_t spdiff; // number of elements to pop off the stack.
StackAction action; // action to perform on the stack.
};
typedef ZoneMap<pc_t, ControlTransfer> ControlTransferMap;
typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap;
// Macro for defining union members.
#define FOREACH_UNION_MEMBER(V) \
......@@ -132,7 +124,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
virtual int GetFrameCount() = 0;
virtual const WasmFrame* GetFrame(int index) = 0;
virtual WasmFrame* GetMutableFrame(int index) = 0;
virtual WasmVal GetReturnValue() = 0;
virtual WasmVal GetReturnValue(int index = 0) = 0;
// Thread-specific breakpoints.
bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled);
......@@ -189,9 +181,8 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
bool SetFunctionCodeForTesting(const WasmFunction* function,
const byte* start, const byte* end);
// Computes the control targets for the given bytecode as {pc offset, sp
// offset}
// pairs. Used internally in the interpreter, but exposed for testing.
// Computes the control transfers for the given bytecode. Used internally in
// the interpreter, but exposed for testing.
static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone,
const byte* start,
const byte* end);
......
......@@ -160,7 +160,7 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
}
object = i::wasm::WasmModule::Instantiate(
isolate, module_object.ToHandleChecked(), ffi, memory);
isolate, thrower, module_object.ToHandleChecked(), ffi, memory);
if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
}
......@@ -296,10 +296,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
i::MaybeHandle<i::JSObject> instance =
i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory);
i::MaybeHandle<i::JSObject> instance = i::wasm::WasmModule::Instantiate(
i_isolate, &thrower, module_obj, ffi, memory);
if (instance.is_null()) {
thrower.Error("Could not instantiate module");
if (!thrower.error()) thrower.Error("Could not instantiate module");
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
......@@ -384,10 +384,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> table_cons(
i::Handle<i::JSFunction> table_ctor(
i_isolate->native_context()->wasm_table_constructor());
i::Handle<i::JSObject> table_obj =
i_isolate->factory()->NewJSObject(table_cons);
i_isolate->factory()->NewJSObject(table_ctor);
i::Handle<i::FixedArray> fixed_array =
i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value();
......@@ -402,6 +402,7 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(table_obj));
}
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
......@@ -435,23 +436,14 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> memory_cons(
i_isolate->native_context()->wasm_memory_constructor());
i::Handle<i::JSObject> memory_obj =
i_isolate->factory()->NewJSObject(memory_cons);
i::Handle<i::JSArrayBuffer> buffer =
i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared);
size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
static_cast<size_t>(initial);
i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size);
memory_obj->SetInternalField(0, *buffer);
memory_obj->SetInternalField(
1, has_maximum.FromJust()
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> memory_sym(
i_isolate->native_context()->wasm_memory_sym());
i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check();
i::Handle<i::JSObject> memory_obj = i::WasmJs::CreateWasmMemoryObject(
i_isolate, buffer, has_maximum.FromJust(), maximum);
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(memory_obj));
}
......@@ -491,6 +483,24 @@ void WebAssemblyMemoryGetBuffer(
}
} // namespace
i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
i::Isolate* i_isolate, i::Handle<i::JSArrayBuffer> buffer, bool has_maximum,
int maximum) {
i::Handle<i::JSFunction> memory_ctor(
i_isolate->native_context()->wasm_memory_constructor());
i::Handle<i::JSObject> memory_obj =
i_isolate->factory()->NewJSObject(memory_ctor);
memory_obj->SetInternalField(0, *buffer);
memory_obj->SetInternalField(
1, has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> memory_sym(
i_isolate->native_context()->wasm_memory_sym());
i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check();
return memory_obj;
}
// TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
......
......@@ -23,6 +23,10 @@ class WasmJs {
static void InstallWasmConstructors(Isolate* isolate,
Handle<JSGlobalObject> global,
Handle<Context> context);
static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
bool has_maximum, int maximum);
};
} // namespace internal
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -22,6 +22,9 @@ enum LocalTypeCode {
kLocalS128 = 5
};
// Type code for multi-value block types.
static const uint8_t kMultivalBlock = 0x41;
// We reuse the internal machine type to represent WebAssembly AST types.
// A typedef improves readability without adding a whole new type system.
typedef MachineRepresentation LocalType;
......@@ -44,7 +47,7 @@ const WasmCodePosition kNoCodePosition = -1;
// Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \
V(Nop, 0x00, _) \
V(Unreachable, 0x00, _) \
V(Block, 0x01, _) \
V(Loop, 0x02, _) \
V(If, 0x03, _) \
......@@ -54,7 +57,7 @@ const WasmCodePosition kNoCodePosition = -1;
V(BrIf, 0x07, _) \
V(BrTable, 0x08, _) \
V(Return, 0x09, _) \
V(Unreachable, 0x0a, _) \
V(Nop, 0x0a, _) \
V(Throw, 0xfa, _) \
V(Try, 0xfb, _) \
V(Catch, 0xfe, _) \
......@@ -68,9 +71,10 @@ const WasmCodePosition kNoCodePosition = -1;
V(F32Const, 0x13, _) \
V(GetLocal, 0x14, _) \
V(SetLocal, 0x15, _) \
V(TeeLocal, 0x19, _) \
V(Drop, 0x0b, _) \
V(CallFunction, 0x16, _) \
V(CallIndirect, 0x17, _) \
V(CallImport, 0x18, _) \
V(I8Const, 0xcb, _) \
V(GetGlobal, 0xbb, _) \
V(SetGlobal, 0xbc, _)
......@@ -497,6 +501,8 @@ class WasmOpcodes {
return 1 << ElementSizeLog2Of(type.representation());
}
static byte MemSize(LocalType type) { return 1 << ElementSizeLog2Of(type); }
static LocalTypeCode LocalTypeCodeFor(LocalType type) {
switch (type) {
case kAstI32:
......
......@@ -22,19 +22,7 @@ namespace wasm {
// Error codes for programmatic checking of the decoder's verification.
enum ErrorCode {
kSuccess,
kError, // TODO(titzer): remove me
kOutOfMemory, // decoder ran out of memory
kEndOfCode, // end of code reached prematurely
kInvalidOpcode, // found invalid opcode
kUnreachableCode, // found unreachable code
kImproperContinue, // improperly nested continue
kImproperBreak, // improperly nested break
kReturnCount, // return count mismatch
kTypeError, // type mismatch
kInvalidLocalIndex, // invalid local
kInvalidGlobalIndex, // invalid global
kInvalidFunctionIndex, // invalid function
kInvalidMemType // invalid memory type
kError, // TODO(titzer): introduce real error codes
};
// The overall result of decoding a function or a module.
......
This diff is collapsed.
......@@ -152,7 +152,7 @@ TEST(Run_CallJS_Add_jswrapped) {
WasmFunctionCompiler t(sigs.i_i(), &module);
uint32_t js_index =
module.AddJsFunction(sigs.i_i(), "(function(a) { return a + 99; })");
BUILD(t, WASM_CALL_FUNCTION1(js_index, WASM_GET_LOCAL(0)));
BUILD(t, WASM_CALL_FUNCTION(js_index, WASM_GET_LOCAL(0)));
Handle<JSFunction> jsfunc = module.WrapCode(t.CompileAndAdd());
......@@ -182,8 +182,7 @@ void RunJSSelectTest(int which) {
ADD_CODE(code, WASM_F64(inputs.arg_d(i)));
}
ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params),
static_cast<byte>(js_index));
ADD_CODE(code, kExprCallFunction, static_cast<byte>(js_index));
size_t end = code.size();
code.push_back(0);
......@@ -420,7 +419,7 @@ void RunJSSelectAlignTest(int num_args, int num_params) {
ADD_CODE(code, WASM_GET_LOCAL(i));
}
ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params), 0);
ADD_CODE(code, kExprCallFunction, 0);
size_t end = code.size();
code.push_back(0);
......
This diff is collapsed.
This diff is collapsed.
......@@ -41,9 +41,9 @@ void testFunctionNameTable(Vector<Vector<const char>> names) {
name.start() + name.length());
// Make every second function name null-terminated.
if (func_index % 2) all_names.push_back('\0');
module.functions.push_back({nullptr, 0, 0,
static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0});
module.functions.push_back(
{nullptr, 0, 0, static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0, false, false});
++func_index;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment