Commit 835c5e6b authored by titzer's avatar titzer Committed by Commit bot

[wasm] Rework encoding of local declarations.

Local declarations were previously encoded as an optional set of
4 uint16 values as part of the function declaration. This CL
implements the current design of moving these declarations to
a list of pairs of (type, count) that is part of the body.

R=bradnelson@chromium.org,binji@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1763433002

Cr-Commit-Position: refs/heads/master@{#34564}
parent 826f67be
......@@ -2409,15 +2409,6 @@ Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
<< wasm::WasmFunctionName(&function, module_env) << std::endl;
os << std::endl;
}
// Initialize the function environment for decoding.
wasm::FunctionEnv env;
env.module = module_env;
env.sig = function.sig;
env.local_i32_count = function.local_i32_count;
env.local_i64_count = function.local_i64_count;
env.local_f32_count = function.local_f32_count;
env.local_f64_count = function.local_f64_count;
env.SumLocals();
// Create a TF graph during decoding.
Zone zone;
......@@ -2428,11 +2419,11 @@ Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
InstructionSelector::SupportedMachineOperatorFlags());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
WasmGraphBuilder builder(&zone, &jsgraph, function.sig);
wasm::TreeResult result = wasm::BuildTFGraph(
&builder, &env, // --
module_env->module->module_start, // --
module_env->module->module_start + function.code_start_offset, // --
module_env->module->module_start + function.code_end_offset); // --
wasm::FunctionBody body = {
module_env, function.sig, module_env->module->module_start,
module_env->module->module_start + function.code_start_offset,
module_env->module->module_start + function.code_end_offset};
wasm::TreeResult result = wasm::BuildTFGraph(&builder, body);
if (result.failed()) {
if (FLAG_trace_wasm_compiler) {
......
This diff is collapsed.
......@@ -183,61 +183,13 @@ struct MemoryAccessOperand {
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface.
// Interface the function environment during decoding, include the signature
// and number of locals.
struct FunctionEnv {
ModuleEnv* module; // module environment
FunctionSig* sig; // signature of this function
uint32_t local_i32_count; // number of int32 locals
uint32_t local_i64_count; // number of int64 locals
uint32_t local_f32_count; // number of float32 locals
uint32_t local_f64_count; // number of float64 locals
uint32_t total_locals; // sum of parameters and all locals
uint32_t GetLocalCount() { return total_locals; }
LocalType GetLocalType(uint32_t index) {
if (index < static_cast<uint32_t>(sig->parameter_count())) {
return sig->GetParam(index);
}
index -= static_cast<uint32_t>(sig->parameter_count());
if (index < local_i32_count) return kAstI32;
index -= local_i32_count;
if (index < local_i64_count) return kAstI64;
index -= local_i64_count;
if (index < local_f32_count) return kAstF32;
index -= local_f32_count;
if (index < local_f64_count) return kAstF64;
return kAstStmt;
}
void AddLocals(LocalType type, uint32_t count) {
switch (type) {
case kAstI32:
local_i32_count += count;
break;
case kAstI64:
local_i64_count += count;
break;
case kAstF32:
local_f32_count += count;
break;
case kAstF64:
local_f64_count += count;
break;
default:
UNREACHABLE();
}
total_locals += count;
DCHECK_EQ(total_locals,
(sig->parameter_count() + local_i32_count + local_i64_count +
local_f32_count + local_f64_count));
}
void SumLocals() {
total_locals = static_cast<uint32_t>(sig->parameter_count()) +
local_i32_count + local_i64_count + local_f32_count +
local_f64_count;
}
// All of the various data structures necessary to decode a function body.
struct FunctionBody {
ModuleEnv* module; // module environment
FunctionSig* sig; // function signature
const byte* base; // base of the module bytes, for error reporting
const byte* start; // start of the function body
const byte* end; // end of the function body
};
struct Tree;
......@@ -245,21 +197,21 @@ typedef Result<Tree*> TreeResult;
std::ostream& operator<<(std::ostream& os, const Tree& tree);
TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
const byte* end);
TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
const byte* start, const byte* end);
void PrintAst(FunctionEnv* env, const byte* start, const byte* end);
TreeResult VerifyWasmCode(FunctionBody& body);
TreeResult BuildTFGraph(TFBuilder* builder, FunctionBody& body);
void PrintAst(FunctionBody& body);
inline TreeResult VerifyWasmCode(FunctionEnv* env, const byte* start,
const byte* end) {
return VerifyWasmCode(env, nullptr, start, end);
inline TreeResult VerifyWasmCode(ModuleEnv* module, FunctionSig* sig,
const byte* start, const byte* end) {
FunctionBody body = {module, sig, nullptr, start, end};
return VerifyWasmCode(body);
}
inline TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env,
const byte* start, const byte* end) {
return BuildTFGraph(builder, env, nullptr, start, end);
inline TreeResult BuildTFGraph(TFBuilder* builder, ModuleEnv* module,
FunctionSig* sig, const byte* start,
const byte* end) {
FunctionBody body = {module, sig, nullptr, start, end};
return BuildTFGraph(builder, body);
}
enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 };
......@@ -267,14 +219,17 @@ enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 };
ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte*, const byte*,
int*, uint32_t*);
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
std::vector<LocalType>* DecodeLocalDeclsForTesting(const byte* start,
const byte* end);
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end);
// Computes the length of the opcode at the given address.
int OpcodeLength(const byte* pc, const byte* end);
// Computes the arity (number of sub-nodes) of the opcode at the given address.
int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end);
int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
const byte* end);
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -327,6 +327,7 @@ class Decoder {
bool ok() const { return error_pc_ == nullptr; }
bool failed() const { return error_pc_ != nullptr; }
bool more() const { return pc_ < limit_; }
const byte* start() { return start_; }
const byte* pc() { return pc_; }
......
......@@ -10,6 +10,7 @@
#include "src/wasm/ast-decoder.h"
#include "src/wasm/encoder.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
......@@ -250,7 +251,6 @@ WasmFunctionEncoder::WasmFunctionEncoder(Zone* zone, LocalType return_type,
uint32_t WasmFunctionEncoder::HeaderSize() const {
uint32_t size = 3;
if (HasLocals()) size += 8;
if (!external_) size += 2;
if (HasName()) size += 4;
return size;
......@@ -258,7 +258,15 @@ uint32_t WasmFunctionEncoder::HeaderSize() const {
uint32_t WasmFunctionEncoder::BodySize(void) const {
return external_ ? 0 : static_cast<uint32_t>(body_.size());
// TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
LocalDeclEncoder local_decl;
local_decl.AddLocals(local_i32_count_, kAstI32);
local_decl.AddLocals(local_i64_count_, kAstI64);
local_decl.AddLocals(local_f32_count_, kAstF32);
local_decl.AddLocals(local_f64_count_, kAstF64);
return external_ ? 0
: static_cast<uint32_t>(body_.size() + local_decl.Size());
}
......@@ -271,7 +279,6 @@ void WasmFunctionEncoder::Serialize(byte* buffer, byte** header,
byte** body) const {
uint8_t decl_bits = (exported_ ? kDeclFunctionExport : 0) |
(external_ ? kDeclFunctionImport : 0) |
(HasLocals() ? kDeclFunctionLocals : 0) |
(HasName() ? kDeclFunctionName : 0);
EmitUint8(header, decl_bits);
......@@ -284,15 +291,17 @@ void WasmFunctionEncoder::Serialize(byte* buffer, byte** header,
(*body) += name_.size();
}
if (HasLocals()) {
EmitUint16(header, local_i32_count_);
EmitUint16(header, local_i64_count_);
EmitUint16(header, local_f32_count_);
EmitUint16(header, local_f64_count_);
}
if (!external_) {
EmitUint16(header, static_cast<uint16_t>(body_.size()));
// TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
LocalDeclEncoder local_decl;
local_decl.AddLocals(local_i32_count_, kAstI32);
local_decl.AddLocals(local_i64_count_, kAstI64);
local_decl.AddLocals(local_f32_count_, kAstF32);
local_decl.AddLocals(local_f64_count_, kAstF64);
EmitUint16(header, static_cast<uint16_t>(body_.size() + local_decl.Size()));
(*header) += local_decl.Emit(*header);
if (body_.size() > 0) {
std::memcpy(*header, &body_[0], body_.size());
(*header) += body_.size();
......
......@@ -42,11 +42,6 @@ class WasmFunctionEncoder : public ZoneObject {
ZoneVector<uint8_t> body_;
ZoneVector<char> name_;
bool HasLocals() const {
return (local_i32_count_ + local_i64_count_ + local_f32_count_ +
local_f64_count_) > 0;
}
bool HasName() const { return (exported_ || external_) && name_.size() > 0; }
};
......
......@@ -338,14 +338,10 @@ class ModuleDecoder : public Decoder {
FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
WasmFunction* function) {
pc_ = start_;
function->sig = consume_sig(); // read signature
function->sig = consume_sig(); // read signature
function->name_offset = 0; // ---- name
function->code_start_offset = off(pc_ + 8); // ---- code start
function->code_start_offset = off(pc_); // ---- code start
function->code_end_offset = off(limit_); // ---- code end
function->local_i32_count = consume_u16(); // read u16
function->local_i64_count = consume_u16(); // read u16
function->local_f32_count = consume_u16(); // read u16
function->local_f64_count = consume_u16(); // read u16
function->exported = false; // ---- exported
function->external = false; // ---- external
......@@ -473,18 +469,10 @@ class ModuleDecoder : public Decoder {
<< std::endl;
os << std::endl;
}
FunctionEnv fenv;
fenv.module = menv;
fenv.sig = function->sig;
fenv.local_i32_count = function->local_i32_count;
fenv.local_i64_count = function->local_i64_count;
fenv.local_f32_count = function->local_f32_count;
fenv.local_f64_count = function->local_f64_count;
fenv.SumLocals();
TreeResult result =
VerifyWasmCode(&fenv, start_, start_ + function->code_start_offset,
start_ + function->code_end_offset);
FunctionBody body = {menv, function->sig, start_,
start_ + function->code_start_offset,
start_ + function->code_end_offset};
TreeResult result = VerifyWasmCode(body);
if (result.failed()) {
// Wrap the error message from the function decoder.
std::ostringstream str;
......
......@@ -7,6 +7,43 @@
#include "src/wasm/wasm-opcodes.h"
#define U32_LE(v) \
static_cast<byte>(v), static_cast<byte>((v) >> 8), \
static_cast<byte>((v) >> 16), static_cast<byte>((v) >> 24)
#define U16_LE(v) static_cast<byte>(v), static_cast<byte>((v) >> 8)
#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion)
#define SIG_INDEX(v) U16_LE(v)
#define FUNC_INDEX(v) U16_LE(v)
#define NAME_OFFSET(v) U32_LE(v)
#define BR_TARGET(v) U16_LE(v)
#define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1)
#define MASK_21 ((1 << 21) - 1)
#define MASK_28 ((1 << 28) - 1)
#define U32V_1(x) static_cast<byte>((x)&MASK_7)
#define U32V_2(x) \
static_cast<byte>(((x)&MASK_7) | 0x80), static_cast<byte>(((x) >> 7) & MASK_7)
#define U32V_3(x) \
static_cast<byte>((((x)) & MASK_7) | 0x80), \
static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
static_cast<byte>(((x) >> 14) & MASK_7)
#define U32V_4(x) \
static_cast<byte>(((x)&MASK_7) | 0x80), \
static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
static_cast<byte>(((x) >> 21) & MASK_7)
#define U32V_5(x) \
static_cast<byte>(((x)&MASK_7) | 0x80), \
static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
static_cast<byte>((((x) >> 21) & MASK_7) | 0x80), \
static_cast<byte>((((x) >> 28) & MASK_7))
// Convenience macros for building Wasm bytecode directly into a byte array.
//------------------------------------------------------------------------------
......@@ -57,6 +94,8 @@
#define I64V_IN_RANGE(value, length) \
((value) >= I64V_MIN(length) && (value) <= I64V_MAX(length))
#define WASM_NO_LOCALS 0
namespace v8 {
namespace internal {
namespace wasm {
......@@ -73,6 +112,83 @@ inline void CheckI64v(int64_t value, int length) {
DCHECK(length == 1 || !I64V_IN_RANGE(value, length - 1));
}
// A helper for encoding local declarations prepended to the body of a
// function.
class LocalDeclEncoder {
public:
// Prepend local declarations by creating a new buffer and copying data
// over. The new buffer must be delete[]'d by the caller.
void Prepend(const byte** start, const byte** end) const {
size_t size = (*end - *start);
byte* buffer = new byte[Size() + size];
size_t pos = Emit(buffer);
memcpy(buffer + pos, *start, size);
pos += size;
*start = buffer;
*end = buffer + pos;
}
size_t Emit(byte* buffer) const {
size_t pos = 0;
pos = WriteUint32v(buffer, pos, static_cast<uint32_t>(local_decls.size()));
for (size_t i = 0; i < local_decls.size(); i++) {
pos = WriteUint32v(buffer, pos, local_decls[i].first);
buffer[pos++] = WasmOpcodes::LocalTypeCodeFor(local_decls[i].second);
}
DCHECK_EQ(Size(), pos);
return pos;
}
// Add locals declarations to this helper. Return the index of the newly added
// local(s), with an optional adjustment for the parameters.
uint32_t AddLocals(uint32_t count, LocalType type,
FunctionSig* sig = nullptr) {
if (count == 0) {
return static_cast<uint32_t>((sig ? sig->parameter_count() : 0) +
local_decls.size());
}
size_t pos = local_decls.size();
if (local_decls.size() > 0 && local_decls.back().second == type) {
count += local_decls.back().first;
local_decls.pop_back();
}
local_decls.push_back(std::pair<uint32_t, LocalType>(count, type));
return static_cast<uint32_t>(pos + (sig ? sig->parameter_count() : 0));
}
size_t Size() const {
size_t size = SizeofUint32v(static_cast<uint32_t>(local_decls.size()));
for (auto p : local_decls) size += 1 + SizeofUint32v(p.first);
return size;
}
private:
std::vector<std::pair<uint32_t, LocalType>> local_decls;
size_t SizeofUint32v(uint32_t val) const {
size_t size = 1;
while (true) {
byte b = val & MASK_7;
if (b == val) return size;
size++;
val = val >> 7;
}
}
// TODO(titzer): lift encoding of u32v to a common place.
size_t WriteUint32v(byte* buffer, size_t pos, uint32_t val) const {
while (true) {
byte b = val & MASK_7;
if (b == val) {
buffer[pos++] = b;
break;
}
buffer[pos++] = 0x80 | b;
val = val >> 7;
}
return pos;
}
};
} // namespace wasm
} // namespace internal
} // namespace v8
......@@ -390,41 +506,4 @@ inline void CheckI64v(int64_t value, int length) {
#define WASM_I32_REINTERPRET_F32(x) kExprI32ReinterpretF32, x
#define WASM_I64_REINTERPRET_F64(x) kExprI64ReinterpretF64, x
#define U32_LE(v) \
static_cast<byte>(v), static_cast<byte>((v) >> 8), \
static_cast<byte>((v) >> 16), static_cast<byte>((v) >> 24)
#define U16_LE(v) static_cast<byte>(v), static_cast<byte>((v) >> 8)
#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion)
#define SIG_INDEX(v) U16_LE(v)
#define FUNC_INDEX(v) U16_LE(v)
#define NAME_OFFSET(v) U32_LE(v)
#define BR_TARGET(v) U16_LE(v)
#define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1)
#define MASK_21 ((1 << 21) - 1)
#define MASK_28 ((1 << 28) - 1)
#define U32V_1(x) static_cast<byte>(x & MASK_7)
#define U32V_2(x) \
static_cast<byte>((x & MASK_7) | 0x80), static_cast<byte>((x >> 7) & MASK_7)
#define U32V_3(x) \
static_cast<byte>((x & MASK_7) | 0x80), \
static_cast<byte>(((x >> 7) & MASK_7) | 0x80), \
static_cast<byte>((x >> 14) & MASK_7)
#define U32V_4(x) \
static_cast<byte>((x & MASK_7) | 0x80), \
static_cast<byte>(((x >> 7) & MASK_7) | 0x80), \
static_cast<byte>(((x >> 14) & MASK_7) | 0x80), \
static_cast<byte>((x >> 21) & MASK_7)
#define U32V_5(x) \
static_cast<byte>((x & MASK_7) | 0x80), \
static_cast<byte>(((x >> 7) & MASK_7) | 0x80), \
static_cast<byte>(((x >> 14) & MASK_7) | 0x80), \
static_cast<byte>(((x >> 21) & MASK_7) | 0x80), \
static_cast<byte>(((x >> 28) & MASK_7))
#endif // V8_WASM_MACRO_GEN_H_
......@@ -97,7 +97,8 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(StoreGlobal, 0x11, _) \
V(CallFunction, 0x12, _) \
V(CallIndirect, 0x13, _) \
V(CallImport, 0x1F, _)
V(CallImport, 0x1F, _) \
V(DeclLocals, 0x1E, _)
// Load memory expressions.
#define FOREACH_LOAD_MEM_OPCODE(V) \
......
......@@ -206,8 +206,7 @@ TEST(Run_WasmCallI64Parameter) {
uint32_t index = t.CompileAndAdd();
// Build the calling function.
WasmRunner<int32_t> r;
r.env()->module = &module;
WasmRunner<int32_t> r(&module);
BUILD(
r,
WASM_I32_CONVERT_I64(WASM_CALL_FUNCTION(
......
......@@ -42,14 +42,16 @@ TEST(Run_WasmModule_CallAdd_rev) {
2, kLocalI32, kLocalI32, kLocalI32, // int,int -> int
// func#0 (main) ----------------------------------
kDeclFunctions, 2, kDeclFunctionExport, 0, 0, // sig index
6, 0, // body size
7, 0, // body size
0, // locals
kExprCallFunction, 1, // --
kExprI8Const, 77, // --
kExprI8Const, 22, // --
// func#1 -----------------------------------------
0, // no name, not exported
1, 0, // sig index
5, 0, // body size
6, 0, // body size
0, // locals
kExprI32Add, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
......
......@@ -973,7 +973,8 @@ TEST(Run_Wasm_Select_strict1) {
TEST(Run_Wasm_Select_strict2) {
WasmRunner<int32_t> r(MachineType::Int32());
r.env()->AddLocals(kAstI32, 2);
r.AllocateLocal(kAstI32);
r.AllocateLocal(kAstI32);
// select(b=5, c=6, a)
BUILD(r, WASM_SELECT(WASM_SET_LOCAL(1, WASM_I8(5)),
WASM_SET_LOCAL(2, WASM_I8(6)), WASM_GET_LOCAL(0)));
......@@ -985,7 +986,8 @@ TEST(Run_Wasm_Select_strict2) {
TEST(Run_Wasm_Select_strict3) {
WasmRunner<int32_t> r(MachineType::Int32());
r.env()->AddLocals(kAstI32, 2);
r.AllocateLocal(kAstI32);
r.AllocateLocal(kAstI32);
// select(b=5, c=6, a=b)
BUILD(r, WASM_SELECT(WASM_SET_LOCAL(1, WASM_I8(5)),
WASM_SET_LOCAL(2, WASM_I8(6)),
......@@ -1232,8 +1234,7 @@ TEST(Run_Wasm_VoidReturn1) {
const int32_t kExpected = -414444;
// Build the calling function.
WasmRunner<int32_t> r;
r.env()->module = &module;
WasmRunner<int32_t> r(&module);
BUILD(r, B2(WASM_CALL_FUNCTION0(index), WASM_I32V_3(kExpected)));
int32_t result = r.Call();
......@@ -1252,8 +1253,7 @@ TEST(Run_Wasm_VoidReturn2) {
const int32_t kExpected = -414444;
// Build the calling function.
WasmRunner<int32_t> r;
r.env()->module = &module;
WasmRunner<int32_t> r(&module);
BUILD(r, B2(WASM_CALL_FUNCTION0(index), WASM_I32V_3(kExpected)));
int32_t result = r.Call();
......@@ -2006,18 +2006,19 @@ static void TestBuildGraphForSimpleExpression(WasmOpcode opcode) {
MachineOperatorBuilder::kAllOptionalOps);
Graph graph(&zone);
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
FunctionEnv env;
FunctionSig* sig = WasmOpcodes::Signature(opcode);
init_env(&env, sig);
if (sig->parameter_count() == 1) {
byte code[] = {static_cast<byte>(opcode), kExprGetLocal, 0};
TestBuildingGraph(&zone, &jsgraph, &env, code, code + arraysize(code));
byte code[] = {WASM_NO_LOCALS, static_cast<byte>(opcode), kExprGetLocal, 0};
TestBuildingGraph(&zone, &jsgraph, nullptr, sig, code,
code + arraysize(code));
} else {
CHECK_EQ(2, sig->parameter_count());
byte code[] = {static_cast<byte>(opcode), kExprGetLocal, 0, kExprGetLocal,
1};
TestBuildingGraph(&zone, &jsgraph, &env, code, code + arraysize(code));
byte code[] = {WASM_NO_LOCALS, static_cast<byte>(opcode),
kExprGetLocal, 0,
kExprGetLocal, 1};
TestBuildingGraph(&zone, &jsgraph, nullptr, sig, code,
code + arraysize(code));
}
}
......
......@@ -63,16 +63,6 @@ using namespace v8::internal;
using namespace v8::internal::compiler;
using namespace v8::internal::wasm;
inline void init_env(FunctionEnv* env, FunctionSig* sig) {
env->module = nullptr;
env->sig = sig;
env->local_i32_count = 0;
env->local_i64_count = 0;
env->local_f32_count = 0;
env->local_f64_count = 0;
env->SumLocals();
}
const uint32_t kMaxGlobalsSize = 128;
// A helper for module environments that adds the ability to allocate memory
......@@ -227,11 +217,11 @@ class TestingModule : public ModuleEnv {
}
};
inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, FunctionEnv* env,
const byte* start, const byte* end) {
compiler::WasmGraphBuilder builder(zone, jsgraph, env->sig);
TreeResult result = BuildTFGraph(&builder, env, start, end);
inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
FunctionSig* sig, const byte* start,
const byte* end) {
compiler::WasmGraphBuilder builder(zone, jsgraph, sig);
TreeResult result = BuildTFGraph(&builder, module, sig, start, end);
if (result.failed()) {
ptrdiff_t pc = result.error_pc - result.start;
ptrdiff_t pt = result.error_pt - result.start;
......@@ -399,10 +389,9 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
: GraphAndBuilders(main_zone()),
jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
nullptr, this->machine()),
sig(sig),
descriptor_(nullptr),
testing_module_(module) {
init_env(&env, sig);
env.module = module;
if (module) {
// Get a new function from the testing module.
function_ = nullptr;
......@@ -420,12 +409,13 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
}
JSGraph jsgraph;
FunctionEnv env;
FunctionSig* sig;
// The call descriptor is initialized when the function is compiled.
CallDescriptor* descriptor_;
TestingModule* testing_module_;
WasmFunction* function_;
int function_index_;
LocalDeclEncoder local_decls;
Isolate* isolate() { return main_isolate(); }
Graph* graph() const { return main_graph_; }
......@@ -434,28 +424,23 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
MachineOperatorBuilder* machine() { return &main_machine_; }
void InitializeDescriptor() {
if (descriptor_ == nullptr) {
descriptor_ = env.module->GetWasmCallDescriptor(main_zone(), env.sig);
descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
}
}
CallDescriptor* descriptor() { return descriptor_; }
void Build(const byte* start, const byte* end) {
// Transfer local counts before compiling.
function()->local_i32_count = env.local_i32_count;
function()->local_i64_count = env.local_i64_count;
function()->local_f32_count = env.local_f32_count;
function()->local_f64_count = env.local_f64_count;
// Build the TurboFan graph.
TestBuildingGraph(main_zone(), &jsgraph, &env, start, end);
local_decls.Prepend(&start, &end);
TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig, start, end);
delete[] start;
}
byte AllocateLocal(LocalType type) {
int result = static_cast<int>(env.total_locals);
env.AddLocals(type, 1);
byte b = static_cast<byte>(result);
CHECK_EQ(result, b);
return b;
uint32_t index = local_decls.AddLocals(1, type, sig);
byte result = static_cast<byte>(index);
DCHECK_EQ(index, result);
return result;
}
// TODO(titzer): remove me.
......@@ -542,8 +527,6 @@ class WasmRunner {
wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3);
}
FunctionEnv* env() { return &compiler_.env; }
// Builds a graph from the given Wasm code and generates the machine
// code and call wrapper for that graph. This method must not be called
// more than once.
......@@ -593,13 +576,7 @@ class WasmRunner {
return return_value;
}
byte AllocateLocal(LocalType type) {
int result = static_cast<int>(env()->total_locals);
env()->AddLocals(type, 1);
byte b = static_cast<byte>(result);
CHECK_EQ(result, b);
return b;
}
byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
protected:
Zone zone;
......
......@@ -47,6 +47,29 @@ var debug = false;
assertEquals(27777, instance.exports.main(27777));
})();
(function LocalsTest2() {
// TODO(titzer): i64 only works on 64-bit platforms.
var types = [
{locals: {i32_count: 1}, type: kAstI32},
// {locals: {i64_count: 1}, type: kAstI64},
{locals: {f32_count: 1}, type: kAstF32},
{locals: {f64_count: 1}, type: kAstF64},
];
for (p of types) {
var module = new WasmModuleBuilder();
module.addFunction(undefined, [p.type, p.type])
.addLocals(p.locals)
.addBody([kExprSetLocal, 1, kExprGetLocal, 0])
.exportAs("main");
var buffer = module.toBuffer(debug);
var instance = _WASMEXP_.instantiateModule(buffer);
assertEquals(19, instance.exports.main(19));
assertEquals(27777, instance.exports.main(27777));
}
})();
(function CallTest() {
var module = new WasmModuleBuilder();
module.addFunction("add", [kAstI32, kAstI32, kAstI32])
......
......@@ -9,10 +9,7 @@ load("test/mjsunit/wasm/wasm-constants.js");
try {
var data = bytes(
0, kAstStmt, // signature
3, 0, // local int32 count
4, 0, // local int64 count
5, 0, // local float32 count
6, 0, // local float64 count
kDeclNoLocals, // --
kExprNop // body
);
......@@ -27,10 +24,7 @@ var threw = false;
try {
var data = bytes(
0, kAstI32, // signature
2, 0, // local int32 count
3, 0, // local int64 count
4, 0, // local float32 count
5, 0, // local float64 count
kDeclNoLocals, // --
kExprBlock, 2, kExprNop, kExprNop // body
);
......
......@@ -48,6 +48,8 @@ function bytesWithHeader() {
return buffer;
}
var kDeclNoLocals = 0;
// Section declaration constants
var kDeclMemory = 0x00;
var kDeclSignatures = 0x01;
......
......@@ -190,25 +190,46 @@ WasmModuleBuilder.prototype.toArray = function(debug) {
var hasName = func.name != undefined && func.name.length > 0;
names = names || hasName;
if (hasName) flags |= kDeclFunctionName;
if (func.locals != undefined) flags |= kDeclFunctionLocals;
exports += func.exports.length;
emit_u8(bytes, flags);
emit_u16(bytes, func.sig_index);
if (hasName) {
emit_string(bytes, func.name);
if (hasName) emit_string(bytes, func.name);
// Function body length will be patched later.
var length_pos = bytes.length;
emit_u16(bytes, 0);
var local_decls = [];
var l = func.locals;
if (l != undefined) {
var local_decls_count = 0;
if (l.i32_count > 0) {
local_decls.push({count: l.i32_count, type: kAstI32});
}
if (l.i64_count > 0) {
local_decls.push({count: l.i64_count, type: kAstI64});
}
if (l.f32_count > 0) {
local_decls.push({count: l.f32_count, type: kAstF32});
}
if (l.f64_count > 0) {
local_decls.push({count: l.f64_count, type: kAstF64});
}
}
if (func.locals != undefined) {
emit_u16(bytes, func.locals.i32_count);
emit_u16(bytes, func.locals.i64_count);
emit_u16(bytes, func.locals.f32_count);
emit_u16(bytes, func.locals.f64_count);
emit_u8(bytes, local_decls.length);
for (decl of local_decls) {
emit_varint(bytes, decl.count);
emit_u8(bytes, decl.type);
}
emit_u16(bytes, func.body.length);
for (var i = 0; i < func.body.length; i++) {
emit_u8(bytes, func.body[i]);
}
var length = bytes.length - length_pos - 2;
bytes[length_pos] = length & 0xff;
bytes[length_pos + 1] = (length >> 8) & 0xff;
index++;
}
......
This diff is collapsed.
......@@ -86,9 +86,6 @@ TEST_F(EncoderTest, Function_Builder_Variable_Indexing) {
byte* header = buffer;
byte* body = buffer + f->HeaderSize();
f->Serialize(buffer, &header, &body);
for (size_t i = 0; i < 7; i++) {
CHECK_EQ(i, static_cast<size_t>(*(buffer + 2 * i + f->HeaderSize() + 1)));
}
}
......@@ -109,15 +106,6 @@ TEST_F(EncoderTest, Function_Builder_Indexing_Variable_Width) {
byte* body = buffer + f->HeaderSize();
f->Serialize(buffer, &header, &body);
body = buffer + f->HeaderSize();
for (size_t i = 0; i < 127; i++) {
CHECK_EQ(kExprGetLocal, static_cast<size_t>(*(body + 2 * i)));
CHECK_EQ(i + 1, static_cast<size_t>(*(body + 2 * i + 1)));
}
CHECK_EQ(kExprGetLocal, static_cast<size_t>(*(body + 2 * 127)));
CHECK_EQ(0x80, static_cast<size_t>(*(body + 2 * 127 + 1)));
CHECK_EQ(0x01, static_cast<size_t>(*(body + 2 * 127 + 2)));
CHECK_EQ(kExprGetLocal, static_cast<size_t>(*(body + 2 * 127 + 3)));
CHECK_EQ(0x00, static_cast<size_t>(*(body + 2 * 127 + 4)));
}
......
......@@ -23,25 +23,12 @@ namespace wasm {
class WasmLoopAssignmentAnalyzerTest : public TestWithZone {
public:
WasmLoopAssignmentAnalyzerTest() : TestWithZone(), sigs() {
init_env(&env, sigs.v_v());
}
WasmLoopAssignmentAnalyzerTest() : num_locals(0) {}
TestSignatures sigs;
FunctionEnv env;
static void init_env(FunctionEnv* env, FunctionSig* sig) {
env->module = nullptr;
env->sig = sig;
env->local_i32_count = 0;
env->local_i64_count = 0;
env->local_f32_count = 0;
env->local_f64_count = 0;
env->SumLocals();
}
uint32_t num_locals;
BitVector* Analyze(const byte* start, const byte* end) {
return AnalyzeLoopAssignmentForTesting(zone(), &env, start, end);
return AnalyzeLoopAssignmentForTesting(zone(), num_locals, start, end);
}
};
......@@ -60,13 +47,13 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) {
for (int j = 0; j < assigned->length(); j++) {
CHECK_EQ(false, assigned->Contains(j));
}
env.AddLocals(kAstI32, 1);
num_locals++;
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, One) {
env.AddLocals(kAstI32, 5);
num_locals = 5;
for (int i = 0; i < 5; i++) {
byte code[] = {WASM_LOOP(1, WASM_SET_ZERO(i))};
BitVector* assigned = Analyze(code, code + arraysize(code));
......@@ -78,7 +65,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, One) {
TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
env.AddLocals(kAstI32, 5);
num_locals = 5;
for (int i = 0; i < 5; i++) {
byte code[] = {WASM_LOOP(1, WASM_SET_ZERO(i)), WASM_SET_ZERO(1)};
BitVector* assigned = Analyze(code, code + arraysize(code));
......@@ -90,7 +77,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
TEST_F(WasmLoopAssignmentAnalyzerTest, Two) {
env.AddLocals(kAstI32, 5);
num_locals = 5;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
byte code[] = {WASM_LOOP(2, WASM_SET_ZERO(i), WASM_SET_ZERO(j))};
......@@ -105,7 +92,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Two) {
TEST_F(WasmLoopAssignmentAnalyzerTest, NestedIf) {
env.AddLocals(kAstI32, 5);
num_locals = 5;
for (int i = 0; i < 5; i++) {
byte code[] = {WASM_LOOP(
1, WASM_IF_ELSE(WASM_SET_ZERO(0), WASM_SET_ZERO(i), WASM_SET_ZERO(1)))};
......@@ -126,7 +113,7 @@ static byte LEBByte(uint32_t val, byte which) {
TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
env.AddLocals(kAstI32, 65000);
num_locals = 65000;
for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) {
byte code[] = {kExprLoop,
1,
......@@ -148,7 +135,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
TEST_F(WasmLoopAssignmentAnalyzerTest, Break) {
env.AddLocals(kAstI32, 3);
num_locals = 3;
byte code[] = {
WASM_LOOP(1, WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_SET_ZERO(1)))),
WASM_SET_ZERO(0)};
......@@ -162,7 +149,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Break) {
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop1) {
env.AddLocals(kAstI32, 5);
num_locals = 5;
byte code[] = {
WASM_LOOP(1, WASM_IF(WASM_GET_LOCAL(0),
WASM_BRV(0, WASM_SET_LOCAL(
......@@ -179,9 +166,8 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop1) {
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
env.AddLocals(kAstI32, 3);
num_locals = 6;
const byte kIter = 0;
env.AddLocals(kAstF32, 3);
const byte kSum = 3;
byte code[] = {WASM_BLOCK(
......
......@@ -903,10 +903,11 @@ class WasmFunctionVerifyTest : public TestWithZone {};
TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
static const byte data[] = {
0, kLocalVoid, // signature
3, 0, // local int32 count
4, 0, // local int64 count
5, 0, // local float32 count
6, 0, // local float64 count
4, // locals
3, kLocalI32, // --
4, kLocalI64, // --
5, kLocalF32, // --
6, kLocalF64, // --
kExprNop // body
};
......@@ -919,12 +920,9 @@ TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
EXPECT_EQ(0, function->sig->parameter_count());
EXPECT_EQ(0, function->sig->return_count());
EXPECT_EQ(0, function->name_offset);
EXPECT_EQ(arraysize(data) - 1, function->code_start_offset);
EXPECT_EQ(2, function->code_start_offset);
EXPECT_EQ(arraysize(data), function->code_end_offset);
EXPECT_EQ(3, function->local_i32_count);
EXPECT_EQ(4, function->local_i64_count);
EXPECT_EQ(5, function->local_f32_count);
EXPECT_EQ(6, function->local_f64_count);
// TODO(titzer): verify encoding of local declarations
EXPECT_FALSE(function->external);
EXPECT_FALSE(function->exported);
}
......
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