[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}
This commit is contained in:
titzer 2016-03-07 13:04:07 -08:00 committed by Commit bot
parent 826f67bec3
commit 835c5e6beb
21 changed files with 981 additions and 933 deletions

View File

@ -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) {

View File

@ -52,7 +52,6 @@ struct Production {
Tree* last() const { return index > 0 ? tree->children[index - 1] : nullptr; }
};
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment
@ -74,14 +73,12 @@ struct SsaEnv {
}
};
// An entry in the stack of blocks during decoding.
struct Block {
SsaEnv* ssa_env; // SSA renaming environment.
int stack_depth; // production stack depth.
};
// An entry in the stack of ifs during decoding.
struct IfEnv {
SsaEnv* false_env;
@ -89,27 +86,27 @@ struct IfEnv {
SsaEnv** case_envs;
};
// Macros that build nodes only if there is a graph and the current SSA
// environment is reachable from start. This avoids problems with malformed
// TF graphs when decoding inputs that have unreachable code.
#define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr)
#define BUILD0(func) (build() ? builder_->func() : nullptr)
// Generic Wasm bytecode decoder with utilities for decoding operands,
// lengths, etc.
class WasmDecoder : public Decoder {
public:
WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {}
WasmDecoder(FunctionEnv* env, const byte* start, const byte* end)
: Decoder(start, end), function_env_(env) {}
FunctionEnv* function_env_;
void Reset(FunctionEnv* function_env, const byte* start, const byte* end) {
Decoder::Reset(start, end);
function_env_ = function_env;
}
WasmDecoder(ModuleEnv* module, FunctionSig* sig, const byte* start,
const byte* end)
: Decoder(start, end),
module_(module),
sig_(sig),
total_locals_(0),
local_types_(nullptr) {}
ModuleEnv* module_;
FunctionSig* sig_;
size_t total_locals_;
ZoneVector<LocalType>* local_types_;
byte ByteOperand(const byte* pc, const char* msg = "missing 1-byte operand") {
if ((pc + sizeof(byte)) >= limit_) {
@ -136,8 +133,12 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, LocalIndexOperand& operand) {
if (operand.index < function_env_->total_locals) {
operand.type = function_env_->GetLocalType(operand.index);
if (operand.index < total_locals_) {
if (local_types_) {
operand.type = local_types_->at(operand.index);
} else {
operand.type = kAstStmt;
}
return true;
}
error(pc, pc + 1, "invalid local index");
@ -145,7 +146,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, GlobalIndexOperand& operand) {
ModuleEnv* m = function_env_->module;
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->globals.size()) {
operand.machine_type = m->module->globals[operand.index].type;
operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type);
@ -156,7 +157,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, FunctionIndexOperand& operand) {
ModuleEnv* m = function_env_->module;
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->functions.size()) {
operand.sig = m->module->functions[operand.index].sig;
return true;
@ -166,7 +167,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, SignatureIndexOperand& operand) {
ModuleEnv* m = function_env_->module;
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->signatures.size()) {
operand.sig = m->module->signatures[operand.index];
return true;
@ -176,7 +177,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, ImportIndexOperand& operand) {
ModuleEnv* m = function_env_->module;
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->import_table.size()) {
operand.sig = m->module->import_table[operand.index].sig;
return true;
@ -250,23 +251,20 @@ class WasmDecoder : public Decoder {
case kExprCallFunction: {
FunctionIndexOperand operand(this, pc);
return static_cast<int>(
function_env_->module->GetFunctionSignature(operand.index)
->parameter_count());
module_->GetFunctionSignature(operand.index)->parameter_count());
}
case kExprCallIndirect: {
SignatureIndexOperand operand(this, pc);
return 1 + static_cast<int>(
function_env_->module->GetSignature(operand.index)
->parameter_count());
module_->GetSignature(operand.index)->parameter_count());
}
case kExprCallImport: {
ImportIndexOperand operand(this, pc);
return static_cast<int>(
function_env_->module->GetImportSignature(operand.index)
->parameter_count());
module_->GetImportSignature(operand.index)->parameter_count());
}
case kExprReturn: {
return static_cast<int>(function_env_->sig->return_count());
return static_cast<int>(sig_->return_count());
}
case kExprBrTable: {
return 1;
@ -282,9 +280,11 @@ class WasmDecoder : public Decoder {
FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE)
#undef DECLARE_OPCODE_CASE
case kExprDeclLocals:
default:
UNREACHABLE();
return 0;
}
UNREACHABLE();
return 0;
}
int OpcodeLength(const byte* pc) {
@ -353,35 +353,33 @@ class WasmDecoder : public Decoder {
// A shift-reduce-parser strategy for decoding Wasm code that uses an explicit
// shift-reduce strategy with multiple internal stacks.
class LR_WasmDecoder : public WasmDecoder {
class SR_WasmDecoder : public WasmDecoder {
public:
LR_WasmDecoder(Zone* zone, TFBuilder* builder)
: zone_(zone),
SR_WasmDecoder(Zone* zone, TFBuilder* builder, FunctionBody& body)
: WasmDecoder(body.module, body.sig, body.start, body.end),
zone_(zone),
builder_(builder),
base_(body.base),
local_type_vec_(zone),
trees_(zone),
stack_(zone),
blocks_(zone),
ifs_(zone) {}
ifs_(zone) {
local_types_ = &local_type_vec_;
}
TreeResult Decode(FunctionEnv* function_env, const byte* base, const byte* pc,
const byte* end) {
TreeResult Decode() {
base::ElapsedTimer decode_timer;
if (FLAG_trace_wasm_decode_time) {
decode_timer.Start();
}
trees_.clear();
stack_.clear();
blocks_.clear();
ifs_.clear();
if (end < pc) {
error(pc, "function body end < start");
if (end_ < pc_) {
error(pc_, "function body end < start");
return result_;
}
base_ = base;
Reset(function_env, pc, end);
DecodeLocalDecls();
InitSsaEnv();
DecodeFunctionBody();
@ -389,12 +387,12 @@ class LR_WasmDecoder : public WasmDecoder {
if (ok()) {
if (ssa_env_->go()) {
if (stack_.size() > 0) {
error(stack_.back().pc(), end, "fell off end of code");
error(stack_.back().pc(), end_, "fell off end of code");
}
AddImplicitReturnAtEnd();
}
if (trees_.size() == 0) {
if (function_env_->sig->return_count() > 0) {
if (sig_->return_count() > 0) {
error(start_, "no trees created");
}
} else {
@ -404,7 +402,8 @@ class LR_WasmDecoder : public WasmDecoder {
if (ok()) {
if (FLAG_trace_wasm_ast) {
PrintAst(function_env, pc, end);
FunctionBody body = {module_, sig_, base_, start_, end_};
PrintAst(body);
}
if (FLAG_trace_wasm_decode_time) {
double ms = decode_timer.Elapsed().InMillisecondsF();
@ -420,6 +419,28 @@ class LR_WasmDecoder : public WasmDecoder {
return toResult(tree);
}
std::vector<LocalType>* DecodeLocalDeclsForTesting() {
DecodeLocalDecls();
if (failed()) return nullptr;
auto result = new std::vector<LocalType>();
result->reserve(local_type_vec_.size());
for (size_t i = 0; i < local_type_vec_.size(); i++) {
result->push_back(local_type_vec_[i]);
}
return result;
}
BitVector* AnalyzeLoopAssignmentForTesting(const byte* pc,
size_t num_locals) {
total_locals_ = num_locals;
local_type_vec_.reserve(num_locals);
if (num_locals > local_type_vec_.size()) {
local_type_vec_.insert(local_type_vec_.end(),
num_locals - local_type_vec_.size(), kAstI32);
}
return AnalyzeLoopAssignment(pc);
}
private:
static const size_t kErrorMsgSize = 128;
@ -430,6 +451,7 @@ class LR_WasmDecoder : public WasmDecoder {
SsaEnv* ssa_env_;
ZoneVector<LocalType> local_type_vec_;
ZoneVector<Tree*> trees_;
ZoneVector<Production> stack_;
ZoneVector<Block> blocks_;
@ -438,8 +460,6 @@ class LR_WasmDecoder : public WasmDecoder {
inline bool build() { return builder_ && ssa_env_->go(); }
void InitSsaEnv() {
FunctionSig* sig = function_env_->sig;
int param_count = static_cast<int>(sig->parameter_count());
TFNode* start = nullptr;
SsaEnv* ssa_env = reinterpret_cast<SsaEnv*>(zone_->New(sizeof(SsaEnv)));
size_t size = sizeof(TFNode*) * EnvironmentCount();
@ -447,50 +467,46 @@ class LR_WasmDecoder : public WasmDecoder {
ssa_env->locals =
size > 0 ? reinterpret_cast<TFNode**>(zone_->New(size)) : nullptr;
int pos = 0;
if (builder_) {
start = builder_->Start(param_count + 1);
// Initialize parameters.
for (int i = 0; i < param_count; i++) {
ssa_env->locals[pos++] = builder_->Param(i, sig->GetParam(i));
start = builder_->Start(static_cast<int>(sig_->parameter_count() + 1));
// Initialize local variables.
uint32_t index = 0;
while (index < sig_->parameter_count()) {
ssa_env->locals[index] = builder_->Param(index, local_type_vec_[index]);
index++;
}
// Initialize int32 locals.
if (function_env_->local_i32_count > 0) {
TFNode* zero = builder_->Int32Constant(0);
for (uint32_t i = 0; i < function_env_->local_i32_count; i++) {
ssa_env->locals[pos++] = zero;
while (index < local_type_vec_.size()) {
LocalType type = local_type_vec_[index];
TFNode* node = DefaultValue(type);
while (index < local_type_vec_.size() &&
local_type_vec_[index] == type) {
// Do a whole run of like-typed locals at a time.
ssa_env->locals[index++] = node;
}
}
// Initialize int64 locals.
if (function_env_->local_i64_count > 0) {
TFNode* zero = builder_->Int64Constant(0);
for (uint32_t i = 0; i < function_env_->local_i64_count; i++) {
ssa_env->locals[pos++] = zero;
}
}
// Initialize float32 locals.
if (function_env_->local_f32_count > 0) {
TFNode* zero = builder_->Float32Constant(0);
for (uint32_t i = 0; i < function_env_->local_f32_count; i++) {
ssa_env->locals[pos++] = zero;
}
}
// Initialize float64 locals.
if (function_env_->local_f64_count > 0) {
TFNode* zero = builder_->Float64Constant(0);
for (uint32_t i = 0; i < function_env_->local_f64_count; i++) {
ssa_env->locals[pos++] = zero;
}
}
DCHECK_EQ(function_env_->total_locals, pos);
DCHECK_EQ(EnvironmentCount(), pos);
builder_->set_module(function_env_->module);
builder_->set_module(module_);
}
ssa_env->control = start;
ssa_env->effect = start;
SetEnv("initial", ssa_env);
}
TFNode* DefaultValue(LocalType type) {
switch (type) {
case kAstI32:
return builder_->Int32Constant(0);
case kAstI64:
return builder_->Int64Constant(0);
case kAstF32:
return builder_->Float32Constant(0);
case kAstF64:
return builder_->Float64Constant(0);
default:
UNREACHABLE();
return nullptr;
}
}
void Leaf(LocalType type, TFNode* node = nullptr) {
size_t size = sizeof(Tree);
Tree* tree = reinterpret_cast<Tree*>(zone_->New(size));
@ -549,6 +565,45 @@ class LR_WasmDecoder : public WasmDecoder {
return bytes;
}
// Decodes the locals declarations, if any, populating {local_type_vec_}.
void DecodeLocalDecls() {
DCHECK_EQ(0, local_type_vec_.size());
// Initialize {local_type_vec} from signature.
if (sig_) {
local_type_vec_.reserve(sig_->parameter_count());
for (size_t i = 0; i < sig_->parameter_count(); i++) {
local_type_vec_.push_back(sig_->GetParam(i));
}
}
// Decode local declarations, if any.
int length;
uint32_t entries = consume_u32v(&length, "local decls count");
while (entries-- > 0 && pc_ < limit_) {
uint32_t count = consume_u32v(&length, "local count");
byte code = consume_u8("local type");
LocalType type;
switch (code) {
case kLocalI32:
type = kAstI32;
break;
case kLocalI64:
type = kAstI64;
break;
case kLocalF32:
type = kAstF32;
break;
case kLocalF64:
type = kAstF64;
break;
default:
error(pc_ - 1, "invalid local type");
return;
}
local_type_vec_.insert(local_type_vec_.end(), count, type);
}
total_locals_ = local_type_vec_.size();
}
// Decodes the body of a function, producing reduced trees into {result}.
void DecodeFunctionBody() {
TRACE("wasm-decode %p...%p (%d bytes) %s\n",
@ -652,7 +707,7 @@ class LR_WasmDecoder : public WasmDecoder {
break;
}
case kExprReturn: {
int count = static_cast<int>(function_env_->sig->return_count());
int count = static_cast<int>(sig_->return_count());
if (count == 0) {
BUILD(Return, 0, builder_->Buffer(0));
ssa_env_->Kill();
@ -809,6 +864,7 @@ class LR_WasmDecoder : public WasmDecoder {
len = 1 + operand.length;
break;
}
case kExprDeclLocals:
default:
error("Invalid opcode");
return;
@ -841,7 +897,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
void AddImplicitReturnAtEnd() {
int retcount = static_cast<int>(function_env_->sig->return_count());
int retcount = static_cast<int>(sig_->return_count());
if (retcount == 0) {
BUILD0(ReturnVoid);
return;
@ -860,7 +916,7 @@ class LR_WasmDecoder : public WasmDecoder {
for (int index = 0; index < retcount; index++) {
Tree* tree = trees_[trees_.size() - 1 - index];
if (buffer) buffer[index] = tree->node;
LocalType expected = function_env_->sig->GetReturn(index);
LocalType expected = sig_->GetReturn(index);
if (tree->type != expected) {
error(limit_, tree->pc,
"ImplicitReturn[%d] expected type %s, found %s of type %s", index,
@ -1066,7 +1122,7 @@ class LR_WasmDecoder : public WasmDecoder {
break;
}
case kExprReturn: {
TypeCheckLast(p, function_env_->sig->GetReturn(p->index - 1));
TypeCheckLast(p, sig_->GetReturn(p->index - 1));
if (p->done()) {
if (build()) {
int count = p->tree->count;
@ -1346,8 +1402,7 @@ class LR_WasmDecoder : public WasmDecoder {
TFNode* b = from->locals[i];
if (a != b) {
TFNode* vals[] = {a, b};
to->locals[i] =
builder_->Phi(function_env_->GetLocalType(i), 2, vals, merge);
to->locals[i] = builder_->Phi(local_type_vec_[i], 2, vals, merge);
}
}
break;
@ -1382,8 +1437,8 @@ class LR_WasmDecoder : public WasmDecoder {
vals[j] = tnode;
}
vals[count - 1] = fnode;
to->locals[i] = builder_->Phi(function_env_->GetLocalType(i), count,
vals, merge);
to->locals[i] =
builder_->Phi(local_type_vec_[i], count, vals, merge);
}
}
break;
@ -1426,8 +1481,8 @@ class LR_WasmDecoder : public WasmDecoder {
env->effect = builder_->EffectPhi(1, &env->effect, env->control);
builder_->Terminate(env->effect, env->control);
for (int i = EnvironmentCount() - 1; i >= 0; i--) {
env->locals[i] = builder_->Phi(function_env_->GetLocalType(i), 1,
&env->locals[i], env->control);
env->locals[i] = builder_->Phi(local_type_vec_[i], 1, &env->locals[i],
env->control);
}
}
}
@ -1481,7 +1536,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
int EnvironmentCount() {
if (builder_) return static_cast<int>(function_env_->GetLocalCount());
if (builder_) return static_cast<int>(local_type_vec_.size());
return 0; // if we aren't building a graph, don't bother with SSA renaming.
}
@ -1517,23 +1572,68 @@ class LR_WasmDecoder : public WasmDecoder {
PrintProduction(depth + 1);
}
#endif
BitVector* AnalyzeLoopAssignment(const byte* pc) {
if (pc >= limit_) return nullptr;
if (*pc != kExprLoop) return nullptr;
BitVector* assigned =
new (zone_) BitVector(static_cast<int>(total_locals_), zone_);
// Keep a stack to model the nesting of expressions.
std::vector<int> arity_stack;
arity_stack.push_back(OpcodeArity(pc));
pc += OpcodeLength(pc);
// Iteratively process all AST nodes nested inside the loop.
while (pc < limit_) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
int arity = 0;
int length = 1;
if (opcode == kExprSetLocal) {
LocalIndexOperand operand(this, pc);
if (assigned->length() > 0 &&
static_cast<int>(operand.index) < assigned->length()) {
// Unverified code might have an out-of-bounds index.
assigned->Add(operand.index);
}
arity = 1;
length = 1 + operand.length;
} else {
arity = OpcodeArity(pc);
length = OpcodeLength(pc);
}
pc += length;
arity_stack.push_back(arity);
while (arity_stack.back() == 0) {
arity_stack.pop_back();
if (arity_stack.empty()) return assigned; // reached end of loop
arity_stack.back()--;
}
}
return assigned;
}
};
TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
const byte* end) {
std::vector<LocalType>* DecodeLocalDeclsForTesting(const byte* start,
const byte* end) {
Zone zone;
LR_WasmDecoder decoder(&zone, nullptr);
TreeResult result = decoder.Decode(env, base, start, end);
FunctionBody body = {nullptr, nullptr, nullptr, start, end};
SR_WasmDecoder decoder(&zone, nullptr, body);
return decoder.DecodeLocalDeclsForTesting();
}
TreeResult VerifyWasmCode(FunctionBody& body) {
Zone zone;
SR_WasmDecoder decoder(&zone, nullptr, body);
TreeResult result = decoder.Decode();
return result;
}
TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
const byte* start, const byte* end) {
TreeResult BuildTFGraph(TFBuilder* builder, FunctionBody& body) {
Zone zone;
LR_WasmDecoder decoder(&zone, builder);
TreeResult result = decoder.Decode(env, base, start, end);
SR_WasmDecoder decoder(&zone, builder, body);
TreeResult result = decoder.Decode();
return result;
}
@ -1565,20 +1665,21 @@ ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte* pc,
}
int OpcodeLength(const byte* pc, const byte* end) {
WasmDecoder decoder(nullptr, pc, end);
WasmDecoder decoder(nullptr, nullptr, pc, end);
return decoder.OpcodeLength(pc);
}
int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) {
WasmDecoder decoder(env, pc, end);
int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
const byte* end) {
WasmDecoder decoder(module, sig, pc, end);
return decoder.OpcodeArity(pc);
}
void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
WasmDecoder decoder(env, start, end);
const byte* pc = start;
void PrintAst(FunctionBody& body) {
WasmDecoder decoder(body.module, body.sig, body.start, body.end);
const byte* pc = body.start;
std::vector<int> arity_stack;
while (pc < end) {
while (pc < body.end) {
int arity = decoder.OpcodeArity(pc);
size_t length = decoder.OpcodeLength(pc);
@ -1605,65 +1706,11 @@ void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
}
}
// Analyzes loop bodies for static assignments to locals, which helps in
// reducing the number of phis introduced at loop headers.
class LoopAssignmentAnalyzer : public WasmDecoder {
public:
LoopAssignmentAnalyzer(Zone* zone, FunctionEnv* function_env) : zone_(zone) {
function_env_ = function_env;
}
BitVector* Analyze(const byte* pc, const byte* limit) {
Decoder::Reset(pc, limit);
if (pc_ >= limit_) return nullptr;
if (*pc_ != kExprLoop) return nullptr;
BitVector* assigned =
new (zone_) BitVector(function_env_->total_locals, zone_);
// Keep a stack to model the nesting of expressions.
std::vector<int> arity_stack;
arity_stack.push_back(OpcodeArity(pc_));
pc_ += OpcodeLength(pc_);
// Iteratively process all AST nodes nested inside the loop.
while (pc_ < limit_) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
int arity = 0;
int length = 1;
if (opcode == kExprSetLocal) {
LocalIndexOperand operand(this, pc_);
if (assigned->length() > 0 &&
static_cast<int>(operand.index) < assigned->length()) {
// Unverified code might have an out-of-bounds index.
assigned->Add(operand.index);
}
arity = 1;
length = 1 + operand.length;
} else {
arity = OpcodeArity(pc_);
length = OpcodeLength(pc_);
}
pc_ += length;
arity_stack.push_back(arity);
while (arity_stack.back() == 0) {
arity_stack.pop_back();
if (arity_stack.empty()) return assigned; // reached end of loop
arity_stack.back()--;
}
}
return assigned;
}
private:
Zone* zone_;
};
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end) {
LoopAssignmentAnalyzer analyzer(zone, env);
return analyzer.Analyze(start, end);
FunctionBody body = {nullptr, nullptr, nullptr, start, end};
SR_WasmDecoder decoder(zone, nullptr, body);
return decoder.AnalyzeLoopAssignmentForTesting(start, num_locals);
}
} // namespace wasm

View File

@ -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);
TreeResult VerifyWasmCode(FunctionBody& body);
TreeResult BuildTFGraph(TFBuilder* builder, FunctionBody& body);
void PrintAst(FunctionBody& body);
void PrintAst(FunctionEnv* env, const byte* start, const byte* end);
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

View File

@ -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_; }

View File

@ -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();

View File

@ -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; }
};

View File

@ -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;

View File

@ -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_

View File

@ -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) \

View File

@ -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(

View File

@ -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, // --

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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])

View File

@ -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
);

View File

@ -48,6 +48,8 @@ function bytesWithHeader() {
return buffer;
}
var kDeclNoLocals = 0;
// Section declaration constants
var kDeclMemory = 0x00;
var kDeclSignatures = 0x01;

View File

@ -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++;
}

File diff suppressed because it is too large Load Diff

View File

@ -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)));
}

View File

@ -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(

View File

@ -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);
}