[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:
parent
826f67bec3
commit
835c5e6beb
@ -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) {
|
||||
|
@ -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,10 +280,12 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
int OpcodeLength(const byte* pc) {
|
||||
switch (static_cast<WasmOpcode>(*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,
|
||||
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
|
||||
|
@ -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 {
|
||||
// All of the various data structures necessary to decode a function body.
|
||||
struct FunctionBody {
|
||||
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;
|
||||
}
|
||||
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
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
|
@ -340,12 +340,8 @@ class ModuleDecoder : public Decoder {
|
||||
pc_ = start_;
|
||||
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 (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);
|
||||
if (l.i64_count > 0) {
|
||||
local_decls.push({count: l.i64_count, type: kAstI64});
|
||||
}
|
||||
emit_u16(bytes, func.body.length);
|
||||
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});
|
||||
}
|
||||
}
|
||||
emit_u8(bytes, local_decls.length);
|
||||
for (decl of local_decls) {
|
||||
emit_varint(bytes, decl.count);
|
||||
emit_u8(bytes, decl.type);
|
||||
}
|
||||
|
||||
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
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user