Chromium Code Reviews| Index: src/wasm/asm-wasm-builder.cc |
| diff --git a/src/wasm/asm-wasm-builder.cc b/src/wasm/asm-wasm-builder.cc |
| index 4c7410d7e004c612e809db7f3ccc06da5da3188e..df496dc1daab23ce26f31e1f3591bb3e55e3d2f6 100644 |
| --- a/src/wasm/asm-wasm-builder.cc |
| +++ b/src/wasm/asm-wasm-builder.cc |
| @@ -30,6 +30,7 @@ namespace wasm { |
| if (HasStackOverflow()) return; \ |
| } while (false) |
| +enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope }; |
| class AsmWasmBuilderImpl : public AstVisitor { |
| public: |
| @@ -43,9 +44,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| global_variables_(HashMap::PointersMatch, |
| ZoneHashMap::kDefaultHashMapCapacity, |
| ZoneAllocationPolicy(zone)), |
| - in_function_(false), |
| - is_set_op_(false), |
| - marking_exported(false), |
| + scope_(kModuleScope), |
| builder_(new (zone) WasmModuleBuilder(zone)), |
| current_function_builder_(nullptr), |
| literal_(literal), |
| @@ -55,7 +54,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| typer_(typer), |
| cache_(TypeCache::Get()), |
| breakable_blocks_(zone), |
| - block_size_(0), |
| init_function_index_(0), |
| next_table_index_(0), |
| function_tables_(HashMap::PointersMatch, |
| @@ -81,13 +79,13 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitVariableDeclaration(VariableDeclaration* decl) {} |
| void VisitFunctionDeclaration(FunctionDeclaration* decl) { |
| - DCHECK(!in_function_); |
| + DCHECK_EQ(kModuleScope, scope_); |
| DCHECK_NULL(current_function_builder_); |
| uint16_t index = LookupOrInsertFunction(decl->proxy()->var()); |
| current_function_builder_ = builder_->FunctionAt(index); |
| - in_function_ = true; |
| + scope_ = kFuncScope; |
| RECURSE(Visit(decl->fun())); |
| - in_function_ = false; |
| + scope_ = kModuleScope; |
| current_function_builder_ = nullptr; |
| local_variables_.Clear(); |
| } |
| @@ -115,12 +113,10 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| } |
| } |
| - if (in_function_) { |
| + if (scope_ == kFuncScope) { |
| BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, |
| - false, |
| - static_cast<byte>(stmt->statements()->length())); |
| + false); |
| RECURSE(VisitStatements(stmt->statements())); |
| - DCHECK(block_size_ >= 0); |
| } else { |
| RECURSE(VisitStatements(stmt->statements())); |
| } |
| @@ -128,25 +124,17 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| class BlockVisitor { |
| private: |
| - int prev_block_size_; |
| - uint32_t index_; |
| AsmWasmBuilderImpl* builder_; |
| public: |
| BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt, |
| - WasmOpcode opcode, bool is_loop, int initial_block_size) |
| + WasmOpcode opcode, bool is_loop) |
| : builder_(builder) { |
| builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop)); |
| builder_->current_function_builder_->Emit(opcode); |
| - index_ = |
| - builder_->current_function_builder_->EmitEditableVarIntImmediate(); |
| - prev_block_size_ = builder_->block_size_; |
| - builder_->block_size_ = initial_block_size; |
| } |
| ~BlockVisitor() { |
| - builder_->current_function_builder_->EditVarIntImmediate( |
| - index_, builder_->block_size_); |
| - builder_->block_size_ = prev_block_size_; |
| + builder_->current_function_builder_->Emit(kExprEnd); |
| builder_->breakable_blocks_.pop_back(); |
| } |
| }; |
| @@ -160,25 +148,21 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitEmptyParentheses(EmptyParentheses* paren) { UNREACHABLE(); } |
| void VisitIfStatement(IfStatement* stmt) { |
| - DCHECK(in_function_); |
| - if (stmt->HasElseStatement()) { |
| - current_function_builder_->Emit(kExprIfElse); |
| - } else { |
| - current_function_builder_->Emit(kExprIf); |
| - } |
| + DCHECK_EQ(kFuncScope, scope_); |
| RECURSE(Visit(stmt->condition())); |
| + current_function_builder_->Emit(kExprIf); |
| if (stmt->HasThenStatement()) { |
| RECURSE(Visit(stmt->then_statement())); |
| - } else { |
| - current_function_builder_->Emit(kExprNop); |
| } |
| if (stmt->HasElseStatement()) { |
| + current_function_builder_->Emit(kExprElse); |
| RECURSE(Visit(stmt->else_statement())); |
| } |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| void VisitContinueStatement(ContinueStatement* stmt) { |
| - DCHECK(in_function_); |
| + DCHECK_EQ(kFuncScope, scope_); |
| DCHECK_NOT_NULL(stmt->target()); |
| int i = static_cast<int>(breakable_blocks_.size()) - 1; |
| int block_distance = 0; |
| @@ -194,12 +178,12 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| } |
| DCHECK(i >= 0); |
| - current_function_builder_->EmitWithVarInt(kExprBr, block_distance); |
| current_function_builder_->Emit(kExprNop); |
| + current_function_builder_->EmitWithVarInt(kExprBr, block_distance); |
| } |
| void VisitBreakStatement(BreakStatement* stmt) { |
| - DCHECK(in_function_); |
| + DCHECK_EQ(kFuncScope, scope_); |
| DCHECK_NOT_NULL(stmt->target()); |
| int i = static_cast<int>(breakable_blocks_.size()) - 1; |
| int block_distance = 0; |
| @@ -217,57 +201,53 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| } |
| DCHECK(i >= 0); |
| - current_function_builder_->EmitWithVarInt(kExprBr, block_distance); |
| current_function_builder_->Emit(kExprNop); |
| + current_function_builder_->EmitWithVarInt(kExprBr, block_distance); |
| } |
| void VisitReturnStatement(ReturnStatement* stmt) { |
| - if (in_function_) { |
| + if (scope_ == kModuleScope) { |
| + scope_ = kExportScope; |
| + RECURSE(Visit(stmt->expression())); |
| + scope_ = kModuleScope; |
| + } else if (scope_ == kFuncScope) { |
| + RECURSE(Visit(stmt->expression())); |
| current_function_builder_->Emit(kExprReturn); |
| } else { |
| - marking_exported = true; |
| - } |
| - RECURSE(Visit(stmt->expression())); |
| - if (!in_function_) { |
| - marking_exported = false; |
| + UNREACHABLE(); |
| } |
| } |
| void VisitWithStatement(WithStatement* stmt) { UNREACHABLE(); } |
| - void SetLocalTo(uint16_t index, int value) { |
| + void SetLocalTo(uint16_t index, int32_t value) { |
| + current_function_builder_->EmitI32Const(value); |
| current_function_builder_->Emit(kExprSetLocal); |
| AddLeb128(index, true); |
| - // TODO(bradnelson): variable size |
| - byte code[] = {WASM_I32V(value)}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| - block_size_++; |
| } |
| void CompileCase(CaseClause* clause, uint16_t fall_through, |
| VariableProxy* tag) { |
| Literal* label = clause->label()->AsLiteral(); |
| DCHECK_NOT_NULL(label); |
| - block_size_++; |
| - current_function_builder_->Emit(kExprIf); |
| - current_function_builder_->Emit(kExprI32Ior); |
| - current_function_builder_->Emit(kExprI32Eq); |
| VisitVariableProxy(tag); |
| VisitLiteral(label); |
| + current_function_builder_->Emit(kExprI32Eq); |
| current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(fall_through, true); |
| - BlockVisitor visitor(this, nullptr, kExprBlock, false, 0); |
| + current_function_builder_->Emit(kExprI32Ior); |
| + current_function_builder_->Emit(kExprIf); |
| + BlockVisitor visitor(this, nullptr, kExprBlock, false); |
| SetLocalTo(fall_through, 1); |
| ZoneList<Statement*>* stmts = clause->statements(); |
| - block_size_ += stmts->length(); |
| RECURSE(VisitStatements(stmts)); |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| void VisitSwitchStatement(SwitchStatement* stmt) { |
| VariableProxy* tag = stmt->tag()->AsVariableProxy(); |
| DCHECK_NOT_NULL(tag); |
| - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false, |
| - 0); |
| + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false); |
| uint16_t fall_through = current_function_builder_->AddLocal(kAstI32); |
| SetLocalTo(fall_through, 0); |
| @@ -278,7 +258,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| CompileCase(clause, fall_through, tag); |
| } else { |
| ZoneList<Statement*>* stmts = clause->statements(); |
| - block_size_ += stmts->length(); |
| RECURSE(VisitStatements(stmts)); |
| } |
| } |
| @@ -287,53 +266,47 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitCaseClause(CaseClause* clause) { UNREACHABLE(); } |
| void VisitDoWhileStatement(DoWhileStatement* stmt) { |
| - DCHECK(in_function_); |
| - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true, |
| - 2); |
| + DCHECK_EQ(kFuncScope, scope_); |
| + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); |
| RECURSE(Visit(stmt->body())); |
| - current_function_builder_->Emit(kExprIf); |
| RECURSE(Visit(stmt->cond())); |
| - current_function_builder_->EmitWithVarInt(kExprBr, 0); |
| - current_function_builder_->Emit(kExprNop); |
| + current_function_builder_->Emit(kExprIf); |
| + current_function_builder_->EmitWithU8(kExprBr, 0); |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| void VisitWhileStatement(WhileStatement* stmt) { |
| - DCHECK(in_function_); |
| - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true, |
| - 1); |
| - current_function_builder_->Emit(kExprIf); |
| + DCHECK_EQ(kFuncScope, scope_); |
| + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); |
| RECURSE(Visit(stmt->cond())); |
| - current_function_builder_->EmitWithVarInt(kExprBr, 0); |
| + current_function_builder_->Emit(kExprIf); |
| RECURSE(Visit(stmt->body())); |
| + current_function_builder_->EmitWithU8(kExprBr, 0); |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| void VisitForStatement(ForStatement* stmt) { |
| - DCHECK(in_function_); |
| + DCHECK_EQ(kFuncScope, scope_); |
| if (stmt->init() != nullptr) { |
| - block_size_++; |
| RECURSE(Visit(stmt->init())); |
| } |
| - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true, |
| - 0); |
| + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); |
| if (stmt->cond() != nullptr) { |
| - block_size_++; |
| - current_function_builder_->Emit(kExprIf); |
| - current_function_builder_->Emit(kExprI32Eqz); |
| RECURSE(Visit(stmt->cond())); |
| - current_function_builder_->EmitWithVarInt(kExprBr, 1); |
| + current_function_builder_->Emit(kExprI32Eqz); |
| + current_function_builder_->Emit(kExprIf); |
| current_function_builder_->Emit(kExprNop); |
| + current_function_builder_->EmitWithU8(kExprBr, 1); |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| if (stmt->body() != nullptr) { |
| - block_size_++; |
| RECURSE(Visit(stmt->body())); |
| } |
| if (stmt->next() != nullptr) { |
| - block_size_++; |
| RECURSE(Visit(stmt->next())); |
| } |
| - block_size_++; |
| - current_function_builder_->EmitWithVarInt(kExprBr, 0); |
| current_function_builder_->Emit(kExprNop); |
| + current_function_builder_->EmitWithU8(kExprBr, 0); |
| } |
| void VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); } |
| @@ -348,7 +321,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitFunctionLiteral(FunctionLiteral* expr) { |
| Scope* scope = expr->scope(); |
| - if (in_function_) { |
| + if (scope_ == kFuncScope) { |
| if (expr->bounds().lower->IsFunction()) { |
| FunctionType* func_type = expr->bounds().lower->AsFunction(); |
| LocalType return_type = TypeFrom(func_type->Result()); |
| @@ -371,11 +344,13 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| void VisitConditional(Conditional* expr) { |
| - DCHECK(in_function_); |
| - current_function_builder_->Emit(kExprIfElse); |
| + DCHECK_EQ(kFuncScope, scope_); |
| RECURSE(Visit(expr->condition())); |
| + current_function_builder_->Emit(kExprIf); |
| RECURSE(Visit(expr->then_expression())); |
| + current_function_builder_->Emit(kExprElse); |
| RECURSE(Visit(expr->else_expression())); |
| + current_function_builder_->Emit(kExprEnd); |
| } |
| bool VisitStdlibConstant(Variable* var) { |
| @@ -431,45 +406,31 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| void VisitVariableProxy(VariableProxy* expr) { |
| - if (in_function_) { |
| + if (scope_ == kFuncScope || scope_ == kInitScope) { |
| Variable* var = expr->var(); |
| - if (is_set_op_) { |
| - if (var->IsContextSlot()) { |
| - current_function_builder_->Emit(kExprStoreGlobal); |
| - } else { |
| - current_function_builder_->Emit(kExprSetLocal); |
| - } |
| - is_set_op_ = false; |
| - } else { |
| - if (VisitStdlibConstant(var)) { |
| - return; |
| - } |
| - if (var->IsContextSlot()) { |
| - current_function_builder_->Emit(kExprLoadGlobal); |
| - } else { |
| - current_function_builder_->Emit(kExprGetLocal); |
| - } |
| + if (VisitStdlibConstant(var)) { |
| + return; |
| } |
| LocalType var_type = TypeOf(expr); |
| DCHECK_NE(kAstStmt, var_type); |
| if (var->IsContextSlot()) { |
| + current_function_builder_->Emit(kExprLoadGlobal); |
| AddLeb128(LookupOrInsertGlobal(var, var_type), false); |
| } else { |
| + current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(LookupOrInsertLocal(var, var_type), true); |
| } |
| } |
| } |
| void VisitLiteral(Literal* expr) { |
| - if (in_function_) { |
| + if (scope_ == kFuncScope || scope_ == kInitScope) { |
| if (expr->raw_value()->IsNumber()) { |
| LocalType type = TypeOf(expr); |
| switch (type) { |
| case kAstI32: { |
| - int val = static_cast<int>(expr->raw_value()->AsNumber()); |
| - // TODO(bradnelson): variable size |
| - byte code[] = {WASM_I32V(val)}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| + int32_t val = static_cast<int32_t>(expr->raw_value()->AsNumber()); |
| + current_function_builder_->EmitI32Const(val); |
| break; |
| } |
| case kAstF32: { |
| @@ -497,7 +458,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| ZoneList<ObjectLiteralProperty*>* props = expr->properties(); |
| for (int i = 0; i < props->length(); ++i) { |
| ObjectLiteralProperty* prop = props->at(i); |
| - DCHECK(marking_exported); |
| + DCHECK_EQ(kExportScope, scope_); |
| VariableProxy* expr = prop->value()->AsVariableProxy(); |
| DCHECK_NOT_NULL(expr); |
| Variable* var = expr->var(); |
| @@ -518,11 +479,11 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void LoadInitFunction() { |
| current_function_builder_ = builder_->FunctionAt(init_function_index_); |
| - in_function_ = true; |
| + scope_ = kInitScope; |
| } |
| void UnLoadInitFunction() { |
| - in_function_ = false; |
| + scope_ = kModuleScope; |
| current_function_builder_ = nullptr; |
| } |
| @@ -625,33 +586,98 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| }; |
| - void VisitAssignment(Assignment* expr) { |
| - bool in_init = false; |
| - if (!in_function_) { |
| - BinaryOperation* binop = expr->value()->AsBinaryOperation(); |
| - if (binop != nullptr) { |
| + void EmitAssignmentLhs(Expression* target, MachineType* mtype) { |
| + // Match the left hand side of the assignment. |
| + VariableProxy* target_var = target->AsVariableProxy(); |
| + if (target_var != nullptr) { |
| + // Left hand side is a local or a global variable, no code on LHS. |
| + return; |
| + } |
| + |
| + Property* target_prop = target->AsProperty(); |
| + if (target_prop != nullptr) { |
| + // Left hand side is a property access, i.e. the asm.js heap. |
| + VisitPropertyAndEmitIndex(target_prop, mtype); |
| + return; |
| + } |
| + |
| + if (target_var == nullptr && target_prop == nullptr) { |
|
bradnelson
2016/03/23 19:14:25
You return above, why guard this, as you return in
titzer
2016/03/23 19:53:05
Because paranoia :-)
bradn
2016/03/23 19:58:50
Seems like leaving the UNREACHABLE unguarded would
|
| + UNREACHABLE(); // invalid assignment. |
| + } |
| + } |
| + |
| + void EmitAssignmentRhs(Expression* target, Expression* value, bool* is_nop) { |
| + BinaryOperation* binop = value->AsBinaryOperation(); |
| + if (binop != nullptr) { |
| + if (scope_ == kInitScope) { |
| + // Handle foreign variables in the initialization scope. |
| Property* prop = binop->left()->AsProperty(); |
| - DCHECK_NOT_NULL(prop); |
| - LoadInitFunction(); |
| - is_set_op_ = true; |
| - RECURSE(Visit(expr->target())); |
| - DCHECK(!is_set_op_); |
| if (binop->op() == Token::MUL) { |
| DCHECK(binop->right()->IsLiteral()); |
| DCHECK_EQ(1.0, binop->right()->AsLiteral()->raw_value()->AsNumber()); |
| DCHECK(binop->right()->AsLiteral()->raw_value()->ContainsDot()); |
| VisitForeignVariable(true, prop); |
| + return; |
| } else if (binop->op() == Token::BIT_OR) { |
| DCHECK(binop->right()->IsLiteral()); |
| DCHECK_EQ(0.0, binop->right()->AsLiteral()->raw_value()->AsNumber()); |
| DCHECK(!binop->right()->AsLiteral()->raw_value()->ContainsDot()); |
| VisitForeignVariable(false, prop); |
| + return; |
| } else { |
| UNREACHABLE(); |
| } |
| - UnLoadInitFunction(); |
| - return; |
| } |
| + if (MatchBinaryOperation(binop) == kAsIs) { |
| + VariableProxy* target_var = target->AsVariableProxy(); |
| + VariableProxy* effective_value_var = GetLeft(binop)->AsVariableProxy(); |
| + if (target_var != nullptr && effective_value_var != nullptr && |
| + target_var->var() == effective_value_var->var()) { |
| + *is_nop = true; |
| + return; |
| + } |
| + } |
| + } |
| + RECURSE(Visit(value)); |
| + } |
| + |
| + void EmitAssignment(Assignment* expr, MachineType mtype) { |
| + // Match the left hand side of the assignment. |
| + VariableProxy* target_var = expr->target()->AsVariableProxy(); |
| + if (target_var != nullptr) { |
| + // Left hand side is a local or a global variable. |
| + Variable* var = target_var->var(); |
| + LocalType var_type = TypeOf(expr); |
| + DCHECK_NE(kAstStmt, var_type); |
| + if (var->IsContextSlot()) { |
| + current_function_builder_->Emit(kExprStoreGlobal); |
| + AddLeb128(LookupOrInsertGlobal(var, var_type), false); |
| + } else { |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(LookupOrInsertLocal(var, var_type), true); |
| + } |
| + } |
| + |
| + Property* target_prop = expr->target()->AsProperty(); |
| + if (target_prop != nullptr) { |
| + // Left hand side is a property access, i.e. the asm.js heap. |
| + if (TypeOf(expr->value()) == kAstF64 && expr->target()->IsProperty() && |
| + expr->target()->AsProperty()->obj()->bounds().lower->Is( |
| + cache_.kFloat32Array)) { |
| + current_function_builder_->Emit(kExprF32ConvertF64); |
| + } |
| + current_function_builder_->EmitWithU8U8( |
| + WasmOpcodes::LoadStoreOpcodeOf(mtype, true), 0, 0); |
| + } |
| + |
| + if (target_var == nullptr && target_prop == nullptr) { |
|
bradnelson
2016/03/23 19:14:25
Add returns instead of guard here?
titzer
2016/03/23 19:53:05
Don't want to silently forget to generate code.
|
| + UNREACHABLE(); // invalid assignment. |
| + } |
| + } |
| + |
| + void VisitAssignment(Assignment* expr) { |
| + bool as_init = false; |
| + if (scope_ == kModuleScope) { |
| Property* prop = expr->value()->AsProperty(); |
| if (prop != nullptr) { |
| VariableProxy* vp = prop->obj()->AsVariableProxy(); |
| @@ -681,32 +707,18 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| // No init code to emit for CallNew nodes. |
| return; |
| } |
| - in_init = true; |
| - LoadInitFunction(); |
| - } |
| - BinaryOperation* value_op = expr->value()->AsBinaryOperation(); |
| - if (value_op != nullptr && MatchBinaryOperation(value_op) == kAsIs) { |
| - VariableProxy* target_var = expr->target()->AsVariableProxy(); |
| - VariableProxy* effective_value_var = GetLeft(value_op)->AsVariableProxy(); |
| - if (target_var != nullptr && effective_value_var != nullptr && |
| - target_var->var() == effective_value_var->var()) { |
| - block_size_--; |
| - return; |
| - } |
| - } |
| - is_set_op_ = true; |
| - RECURSE(Visit(expr->target())); |
| - DCHECK(!is_set_op_); |
| - // Assignment to heapf32 from float64 converts. |
| - if (TypeOf(expr->value()) == kAstF64 && expr->target()->IsProperty() && |
| - expr->target()->AsProperty()->obj()->bounds().lower->Is( |
| - cache_.kFloat32Array)) { |
| - current_function_builder_->Emit(kExprF32ConvertF64); |
| + as_init = true; |
| } |
| - RECURSE(Visit(expr->value())); |
| - if (in_init) { |
| - UnLoadInitFunction(); |
| + |
| + if (as_init) LoadInitFunction(); |
| + MachineType mtype; |
| + bool is_nop = false; |
| + EmitAssignmentLhs(expr->target(), &mtype); |
| + EmitAssignmentRhs(expr->target(), expr->value(), &is_nop); |
| + if (!is_nop) { |
| + EmitAssignment(expr, mtype); |
| } |
| + if (as_init) UnLoadInitFunction(); |
| } |
| void VisitYield(Yield* expr) { UNREACHABLE(); } |
| @@ -745,9 +757,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| Handle<Object> nvalue = maybe_nvalue.ToHandleChecked(); |
| if (nvalue->IsNumber()) { |
| int32_t val = static_cast<int32_t>(nvalue->Number()); |
| - // TODO(bradnelson): variable size |
| - byte code[] = {WASM_I32V(val)}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| + current_function_builder_->EmitI32Const(val); |
| return; |
| } |
| } |
| @@ -763,46 +773,41 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| } |
| - void VisitProperty(Property* expr) { |
| + void VisitPropertyAndEmitIndex(Property* expr, MachineType* mtype) { |
| Expression* obj = expr->obj(); |
| DCHECK_EQ(obj->bounds().lower, obj->bounds().upper); |
| Type* type = obj->bounds().lower; |
| - MachineType mtype; |
| int size; |
| if (type->Is(cache_.kUint8Array)) { |
| - mtype = MachineType::Uint8(); |
| + *mtype = MachineType::Uint8(); |
| size = 1; |
| } else if (type->Is(cache_.kInt8Array)) { |
| - mtype = MachineType::Int8(); |
| + *mtype = MachineType::Int8(); |
| size = 1; |
| } else if (type->Is(cache_.kUint16Array)) { |
| - mtype = MachineType::Uint16(); |
| + *mtype = MachineType::Uint16(); |
| size = 2; |
| } else if (type->Is(cache_.kInt16Array)) { |
| - mtype = MachineType::Int16(); |
| + *mtype = MachineType::Int16(); |
| size = 2; |
| } else if (type->Is(cache_.kUint32Array)) { |
| - mtype = MachineType::Uint32(); |
| + *mtype = MachineType::Uint32(); |
| size = 4; |
| } else if (type->Is(cache_.kInt32Array)) { |
| - mtype = MachineType::Int32(); |
| + *mtype = MachineType::Int32(); |
| size = 4; |
| } else if (type->Is(cache_.kUint32Array)) { |
| - mtype = MachineType::Uint32(); |
| + *mtype = MachineType::Uint32(); |
| size = 4; |
| } else if (type->Is(cache_.kFloat32Array)) { |
| - mtype = MachineType::Float32(); |
| + *mtype = MachineType::Float32(); |
| size = 4; |
| } else if (type->Is(cache_.kFloat64Array)) { |
| - mtype = MachineType::Float64(); |
| + *mtype = MachineType::Float64(); |
| size = 8; |
| } else { |
| UNREACHABLE(); |
| } |
| - // TODO(titzer): use special asm-compatibility opcodes? |
| - current_function_builder_->EmitWithU8U8( |
| - WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_), 0, 0); |
| - is_set_op_ = false; |
| if (size == 1) { |
| // Allow more general expression in byte arrays than the spec |
| // strictly permits. |
| @@ -810,87 +815,103 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| // places that strictly should be HEAP8[HEAP32[..]>>0]. |
| RECURSE(Visit(expr->key())); |
| return; |
| - } else { |
| - Literal* value = expr->key()->AsLiteral(); |
| - if (value) { |
| - DCHECK(value->raw_value()->IsNumber()); |
| - DCHECK_EQ(kAstI32, TypeOf(value)); |
| - int val = static_cast<int>(value->raw_value()->AsNumber()); |
| - // TODO(bradnelson): variable size |
| - byte code[] = {WASM_I32V(val * size)}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| - return; |
| - } |
| - BinaryOperation* binop = expr->key()->AsBinaryOperation(); |
| - if (binop) { |
| - DCHECK_EQ(Token::SAR, binop->op()); |
| - DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber()); |
| - DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral())); |
| - DCHECK_EQ(size, |
| - 1 << static_cast<int>( |
| - binop->right()->AsLiteral()->raw_value()->AsNumber())); |
| - // Mask bottom bits to match asm.js behavior. |
| - current_function_builder_->Emit(kExprI32And); |
| - byte code[] = {WASM_I8(~(size - 1))}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| - RECURSE(Visit(binop->left())); |
| - return; |
| - } |
| + } |
| + |
| + Literal* value = expr->key()->AsLiteral(); |
| + if (value) { |
| + DCHECK(value->raw_value()->IsNumber()); |
| + DCHECK_EQ(kAstI32, TypeOf(value)); |
| + int32_t val = static_cast<int32_t>(value->raw_value()->AsNumber()); |
| + // TODO(titzer): handle overflow here. |
|
bradnelson
2016/03/23 19:14:25
Spec allows 0 - 2^32 -1 here. What did you mean?
titzer
2016/03/23 19:53:05
Right. If it is 2^31, e.g., and then we multiply b
bradn
2016/03/23 19:58:50
Ah, yes.
|
| + current_function_builder_->EmitI32Const(val * size); |
| + return; |
| + } |
| + BinaryOperation* binop = expr->key()->AsBinaryOperation(); |
| + if (binop) { |
| + DCHECK_EQ(Token::SAR, binop->op()); |
| + DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber()); |
| + DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral())); |
| + DCHECK_EQ(size, |
| + 1 << static_cast<int>( |
| + binop->right()->AsLiteral()->raw_value()->AsNumber())); |
| + // Mask bottom bits to match asm.js behavior. |
| + byte mask = static_cast<byte>(~(size - 1)); |
| + RECURSE(Visit(binop->left())); |
| + current_function_builder_->EmitWithU8(kExprI8Const, mask); |
| + current_function_builder_->Emit(kExprI32And); |
| + return; |
| } |
| UNREACHABLE(); |
| } |
| + void VisitProperty(Property* expr) { |
| + MachineType mtype; |
| + VisitPropertyAndEmitIndex(expr, &mtype); |
| + current_function_builder_->EmitWithU8U8( |
| + WasmOpcodes::LoadStoreOpcodeOf(mtype, false), 0, 0); |
| + } |
| + |
| bool VisitStdlibFunction(Call* call, VariableProxy* expr) { |
| Variable* var = expr->var(); |
| AsmTyper::StandardMember standard_object = |
| typer_->VariableAsStandardMember(var); |
| ZoneList<Expression*>* args = call->arguments(); |
| LocalType call_type = TypeOf(call); |
| + |
| switch (standard_object) { |
| case AsmTyper::kNone: { |
| return false; |
| } |
| case AsmTyper::kMathAcos: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Acos); |
| break; |
| } |
| case AsmTyper::kMathAsin: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Asin); |
| break; |
| } |
| case AsmTyper::kMathAtan: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Atan); |
| break; |
| } |
| case AsmTyper::kMathCos: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Cos); |
| break; |
| } |
| case AsmTyper::kMathSin: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Sin); |
| break; |
| } |
| case AsmTyper::kMathTan: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Tan); |
| break; |
| } |
| case AsmTyper::kMathExp: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Exp); |
| break; |
| } |
| case AsmTyper::kMathLog: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Log); |
| break; |
| } |
| case AsmTyper::kMathCeil: { |
| + VisitCallArgs(call); |
| if (call_type == kAstF32) { |
| current_function_builder_->Emit(kExprF32Ceil); |
| } else if (call_type == kAstF64) { |
| @@ -901,6 +922,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| case AsmTyper::kMathFloor: { |
| + VisitCallArgs(call); |
| if (call_type == kAstF32) { |
| current_function_builder_->Emit(kExprF32Floor); |
| } else if (call_type == kAstF64) { |
| @@ -911,6 +933,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| case AsmTyper::kMathSqrt: { |
| + VisitCallArgs(call); |
| if (call_type == kAstF32) { |
| current_function_builder_->Emit(kExprF32Sqrt); |
| } else if (call_type == kAstF64) { |
| @@ -921,19 +944,36 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| case AsmTyper::kMathAbs: { |
| - // TODO(bradnelson): Should this be cast to float? |
| if (call_type == kAstI32) { |
| - current_function_builder_->Emit(kExprIfElse); |
| - current_function_builder_->Emit(kExprI32LtS); |
| - Visit(args->at(0)); |
| + uint16_t tmp = current_function_builder_->AddLocal(kAstI32); |
| + |
| + // if set_local(tmp, x) < 0 |
| + Visit(call->arguments()->at(0)); |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(tmp, true); |
| byte code[] = {WASM_I8(0)}; |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| - current_function_builder_->Emit(kExprI32Sub); |
| + current_function_builder_->Emit(kExprI32LtS); |
| + current_function_builder_->Emit(kExprIf); |
| + |
| + // then (0 - tmp) |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| - Visit(args->at(0)); |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp, true); |
| + current_function_builder_->Emit(kExprI32Sub); |
| + |
| + // else tmp |
| + current_function_builder_->Emit(kExprElse); |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp, true); |
| + // end |
| + current_function_builder_->Emit(kExprEnd); |
| + |
| } else if (call_type == kAstF32) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF32Abs); |
| } else if (call_type == kAstF64) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF64Abs); |
| } else { |
| UNREACHABLE(); |
| @@ -943,13 +983,36 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| case AsmTyper::kMathMin: { |
| // TODO(bradnelson): Change wasm to match Math.min in asm.js mode. |
| if (call_type == kAstI32) { |
| - current_function_builder_->Emit(kExprIfElse); |
| + uint16_t tmp_x = current_function_builder_->AddLocal(kAstI32); |
| + uint16_t tmp_y = current_function_builder_->AddLocal(kAstI32); |
|
bradnelson
2016/03/23 19:14:25
Did we want to turn these into asm specific opcode
titzer
2016/03/23 19:53:05
It's probably about the same amount of work either
bradn
2016/03/23 19:58:50
K, although I still want to understand why tf call
|
| + |
| + // if set_local(tmp_x, x) < set_local(tmp_y, y) |
| + Visit(call->arguments()->at(0)); |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(tmp_x, true); |
| + |
| + Visit(call->arguments()->at(1)); |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(tmp_y, true); |
| + |
| current_function_builder_->Emit(kExprI32LeS); |
| - Visit(args->at(0)); |
| - Visit(args->at(1)); |
| + current_function_builder_->Emit(kExprIf); |
| + |
| + // then tmp_x |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp_x, true); |
| + |
| + // else tmp_y |
| + current_function_builder_->Emit(kExprElse); |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp_y, true); |
| + current_function_builder_->Emit(kExprEnd); |
| + |
| } else if (call_type == kAstF32) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF32Min); |
| } else if (call_type == kAstF64) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF64Min); |
| } else { |
| UNREACHABLE(); |
| @@ -959,13 +1022,36 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| case AsmTyper::kMathMax: { |
| // TODO(bradnelson): Change wasm to match Math.max in asm.js mode. |
| if (call_type == kAstI32) { |
| - current_function_builder_->Emit(kExprIfElse); |
| - current_function_builder_->Emit(kExprI32GtS); |
| - Visit(args->at(0)); |
| - Visit(args->at(1)); |
| + uint16_t tmp_x = current_function_builder_->AddLocal(kAstI32); |
| + uint16_t tmp_y = current_function_builder_->AddLocal(kAstI32); |
| + |
| + // if set_local(tmp_x, x) < set_local(tmp_y, y) |
| + Visit(call->arguments()->at(0)); |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(tmp_x, true); |
| + |
| + Visit(call->arguments()->at(1)); |
| + current_function_builder_->Emit(kExprSetLocal); |
| + AddLeb128(tmp_y, true); |
| + |
| + current_function_builder_->Emit(kExprI32LeS); |
| + current_function_builder_->Emit(kExprIf); |
| + |
| + // then tmp_y |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp_y, true); |
| + |
| + // else tmp_x |
| + current_function_builder_->Emit(kExprElse); |
| + current_function_builder_->Emit(kExprGetLocal); |
| + AddLeb128(tmp_x, true); |
| + current_function_builder_->Emit(kExprEnd); |
| + |
| } else if (call_type == kAstF32) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF32Max); |
| } else if (call_type == kAstF64) { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprF64Max); |
| } else { |
| UNREACHABLE(); |
| @@ -973,16 +1059,19 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| case AsmTyper::kMathAtan2: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Atan2); |
| break; |
| } |
| case AsmTyper::kMathPow: { |
| + VisitCallArgs(call); |
| DCHECK_EQ(kAstF64, call_type); |
| current_function_builder_->Emit(kExprF64Pow); |
| break; |
| } |
| case AsmTyper::kMathImul: { |
| + VisitCallArgs(call); |
| current_function_builder_->Emit(kExprI32Mul); |
| break; |
| } |
| @@ -990,6 +1079,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| DCHECK(args->length() == 1); |
| Literal* literal = args->at(0)->AsLiteral(); |
| if (literal != nullptr) { |
| + // constant fold Math.fround(#const); |
| if (literal->raw_value()->IsNumber()) { |
| float val = static_cast<float>(literal->raw_value()->AsNumber()); |
| byte code[] = {WASM_F32(val)}; |
| @@ -997,6 +1087,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| return true; |
| } |
| } |
| + VisitCallArgs(call); |
| switch (TypeIndexOf(args->at(0))) { |
| case kInt32: |
| case kFixnum: |
| @@ -1020,7 +1111,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| } |
| - VisitCallArgs(call); |
| return true; |
| } |
| @@ -1034,9 +1124,10 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitCall(Call* expr) { |
| Call::CallType call_type = expr->GetCallType(isolate_); |
| + std::vector<byte> call_code; |
| switch (call_type) { |
| case Call::OTHER_CALL: { |
| - DCHECK(in_function_); |
| + DCHECK_EQ(kFuncScope, scope_); |
| VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
| if (proxy != nullptr) { |
| if (VisitStdlibFunction(expr, proxy)) { |
| @@ -1062,32 +1153,35 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } else { |
| index = LookupOrInsertFunction(vp->var()); |
| } |
| - current_function_builder_->Emit(kExprCallFunction); |
| - std::vector<uint8_t> index_arr = UnsignedLEB128From(index); |
| - current_function_builder_->EmitCode( |
| - &index_arr[0], static_cast<uint32_t>(index_arr.size())); |
| + call_code.push_back(kExprCallFunction); |
| + // TODO(titzer): inefficient and clunky |
| + std::vector<byte> leb = UnsignedLEB128From(index); |
| + call_code.insert(call_code.end(), leb.begin(), leb.end()); |
| break; |
| } |
| case Call::KEYED_PROPERTY_CALL: { |
| - DCHECK(in_function_); |
| + DCHECK_EQ(kFuncScope, scope_); |
| Property* p = expr->expression()->AsProperty(); |
| DCHECK_NOT_NULL(p); |
| VariableProxy* var = p->obj()->AsVariableProxy(); |
| DCHECK_NOT_NULL(var); |
| FunctionTableIndices* indices = LookupFunctionTable(var->var()); |
| - current_function_builder_->EmitWithVarInt(kExprCallIndirect, |
| - indices->signature_index); |
| - current_function_builder_->Emit(kExprI32Add); |
| - // TODO(bradnelson): variable size |
| - byte code[] = {WASM_I32V(indices->start_index)}; |
| - current_function_builder_->EmitCode(code, sizeof(code)); |
| RECURSE(Visit(p->key())); |
| + current_function_builder_->EmitI32Const(indices->start_index); |
| + current_function_builder_->Emit(kExprI32Add); |
| + call_code.push_back(kExprCallIndirect); |
| + // TODO(titzer): inefficient and clunky |
| + std::vector<byte> leb = UnsignedLEB128From(indices->signature_index); |
| + call_code.insert(call_code.end(), leb.begin(), leb.end()); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| VisitCallArgs(expr); |
| + |
| + current_function_builder_->EmitCode( |
| + &call_code[0], static_cast<uint32_t>(call_code.size())); |
| } |
| void VisitCallNew(CallNew* expr) { UNREACHABLE(); } |
| @@ -1095,6 +1189,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitCallRuntime(CallRuntime* expr) { UNREACHABLE(); } |
| void VisitUnaryOperation(UnaryOperation* expr) { |
| + RECURSE(Visit(expr->expression())); |
| switch (expr->op()) { |
| case Token::NOT: { |
| DCHECK_EQ(kAstI32, TypeOf(expr->expression())); |
| @@ -1104,7 +1199,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| default: |
| UNREACHABLE(); |
| } |
| - RECURSE(Visit(expr->expression())); |
| } |
| void VisitCountOperation(CountOperation* expr) { UNREACHABLE(); } |
| @@ -1250,6 +1344,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| void VisitBinaryOperation(BinaryOperation* expr) { |
| ConvertOperation convertOperation = MatchBinaryOperation(expr); |
| if (convertOperation == kToDouble) { |
| + RECURSE(Visit(expr->left())); |
| TypeIndex type = TypeIndexOf(expr->left()); |
| if (type == kInt32 || type == kFixnum) { |
| current_function_builder_->Emit(kExprF64SConvertI32); |
| @@ -1260,8 +1355,8 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } else { |
| UNREACHABLE(); |
| } |
| - RECURSE(Visit(expr->left())); |
| } else if (convertOperation == kToInt) { |
| + RECURSE(Visit(GetLeft(expr))); |
| TypeIndex type = TypeIndexOf(GetLeft(expr)); |
| if (type == kFloat32) { |
| current_function_builder_->Emit(kExprI32SConvertF32); |
| @@ -1270,10 +1365,20 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } else { |
| UNREACHABLE(); |
| } |
| - RECURSE(Visit(GetLeft(expr))); |
| } else if (convertOperation == kAsIs) { |
| RECURSE(Visit(GetLeft(expr))); |
| } else { |
| + if (expr->op() == Token::COMMA) { |
| + current_function_builder_->Emit(kExprBlock); |
| + } |
| + |
| + RECURSE(Visit(expr->left())); |
| + RECURSE(Visit(expr->right())); |
| + |
| + if (expr->op() == Token::COMMA) { |
| + current_function_builder_->Emit(kExprEnd); |
|
bradnelson
2016/03/23 19:14:25
return here vs the break down below, might be more
|
| + } |
| + |
| switch (expr->op()) { |
| BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true); |
| BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true); |
| @@ -1300,14 +1405,11 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| break; |
| } |
| case Token::COMMA: { |
| - current_function_builder_->EmitWithVarInt(kExprBlock, 2); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| - RECURSE(Visit(expr->left())); |
| - RECURSE(Visit(expr->right())); |
| } |
| } |
| @@ -1325,6 +1427,8 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| } |
| void VisitCompareOperation(CompareOperation* expr) { |
| + RECURSE(Visit(expr->left())); |
| + RECURSE(Visit(expr->right())); |
| switch (expr->op()) { |
| BINOP_CASE(Token::EQ, Eq, NON_SIGNED_BINOP, false); |
| BINOP_CASE(Token::LT, Lt, SIGNED_BINOP, false); |
| @@ -1334,8 +1438,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| default: |
| UNREACHABLE(); |
| } |
| - RECURSE(Visit(expr->left())); |
| - RECURSE(Visit(expr->right())); |
| } |
| #undef BINOP_CASE |
| @@ -1497,9 +1599,7 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| ZoneHashMap local_variables_; |
| ZoneHashMap functions_; |
| ZoneHashMap global_variables_; |
| - bool in_function_; |
| - bool is_set_op_; |
| - bool marking_exported; |
| + AsmScope scope_; |
| WasmModuleBuilder* builder_; |
| WasmFunctionBuilder* current_function_builder_; |
| FunctionLiteral* literal_; |
| @@ -1509,7 +1609,6 @@ class AsmWasmBuilderImpl : public AstVisitor { |
| AsmTyper* typer_; |
| TypeCache const& cache_; |
| ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_; |
| - int block_size_; |
| uint16_t init_function_index_; |
| uint32_t next_table_index_; |
| ZoneHashMap function_tables_; |