| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/v8.h" |
| |
| #include "src/wasm/asm-wasm-builder.h" |
| #include "src/wasm/wasm-macro-gen.h" |
| #include "src/wasm/wasm-opcodes.h" |
| |
| #include "src/ast/ast.h" |
| #include "src/ast/scopes.h" |
| #include "src/codegen.h" |
| #include "src/type-cache.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| #define RECURSE(call) \ |
| do { \ |
| DCHECK(!HasStackOverflow()); \ |
| call; \ |
| if (HasStackOverflow()) return; \ |
| } while (false) |
| |
| |
| class AsmWasmBuilderImpl : public AstVisitor { |
| public: |
| AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal) |
| : local_variables_(HashMap::PointersMatch, |
| ZoneHashMap::kDefaultHashMapCapacity, |
| ZoneAllocationPolicy(zone)), |
| functions_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, |
| ZoneAllocationPolicy(zone)), |
| global_variables_(HashMap::PointersMatch, |
| ZoneHashMap::kDefaultHashMapCapacity, |
| ZoneAllocationPolicy(zone)), |
| in_function_(false), |
| is_set_op_(false), |
| marking_exported(false), |
| builder_(new (zone) WasmModuleBuilder(zone)), |
| current_function_builder_(NULL), |
| literal_(literal), |
| isolate_(isolate), |
| zone_(zone), |
| cache_(TypeCache::Get()), |
| breakable_blocks_(zone), |
| block_size_(0), |
| init_function_initialized(false), |
| init_function_index(0) { |
| InitializeAstVisitor(isolate); |
| } |
| |
| void Compile() { RECURSE(VisitFunctionLiteral(literal_)); } |
| |
| void VisitVariableDeclaration(VariableDeclaration* decl) {} |
| |
| void VisitFunctionDeclaration(FunctionDeclaration* decl) { |
| DCHECK(!in_function_); |
| DCHECK(current_function_builder_ == NULL); |
| uint16_t index = LookupOrInsertFunction(decl->proxy()->var()); |
| current_function_builder_ = builder_->FunctionAt(index); |
| in_function_ = true; |
| RECURSE(Visit(decl->fun())); |
| in_function_ = false; |
| current_function_builder_ = NULL; |
| local_variables_.Clear(); |
| } |
| |
| void VisitImportDeclaration(ImportDeclaration* decl) {} |
| |
| void VisitExportDeclaration(ExportDeclaration* decl) {} |
| |
| void VisitStatements(ZoneList<Statement*>* stmts) { |
| for (int i = 0; i < stmts->length(); ++i) { |
| Statement* stmt = stmts->at(i); |
| RECURSE(Visit(stmt)); |
| if (stmt->IsJump()) break; |
| } |
| } |
| |
| void VisitBlock(Block* stmt) { |
| if (stmt->statements()->length() == 1) { |
| ExpressionStatement* expr = |
| stmt->statements()->at(0)->AsExpressionStatement(); |
| if (expr != NULL) { |
| if (expr->expression()->IsAssignment()) { |
| RECURSE(VisitExpressionStatement(expr)); |
| return; |
| } |
| } |
| } |
| DCHECK(in_function_); |
| breakable_blocks_.push_back( |
| std::make_pair(stmt->AsBreakableStatement(), false)); |
| current_function_builder_->Emit(kExprBlock); |
| uint32_t index = current_function_builder_->EmitEditableImmediate(0); |
| int prev_block_size = block_size_; |
| block_size_ = static_cast<byte>(stmt->statements()->length()); |
| RECURSE(VisitStatements(stmt->statements())); |
| DCHECK(block_size_ >= 0); |
| current_function_builder_->EditImmediate(index, block_size_); |
| block_size_ = prev_block_size; |
| breakable_blocks_.pop_back(); |
| } |
| |
| void VisitExpressionStatement(ExpressionStatement* stmt) { |
| RECURSE(Visit(stmt->expression())); |
| } |
| |
| void VisitEmptyStatement(EmptyStatement* stmt) {} |
| |
| 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); |
| } |
| RECURSE(Visit(stmt->condition())); |
| if (stmt->HasThenStatement()) { |
| RECURSE(Visit(stmt->then_statement())); |
| } else { |
| current_function_builder_->Emit(kExprNop); |
| } |
| if (stmt->HasElseStatement()) { |
| RECURSE(Visit(stmt->else_statement())); |
| } |
| } |
| |
| void VisitContinueStatement(ContinueStatement* stmt) { |
| DCHECK(in_function_); |
| int i = static_cast<int>(breakable_blocks_.size()) - 1; |
| int block_distance = 0; |
| for (; i >= 0; i--) { |
| auto elem = breakable_blocks_.at(i); |
| if (elem.first == stmt->target()) { |
| DCHECK(elem.second); |
| break; |
| } else if (elem.second) { |
| block_distance += 2; |
| } else { |
| block_distance += 1; |
| } |
| } |
| DCHECK(i >= 0); |
| current_function_builder_->EmitWithU8(kExprBr, block_distance); |
| current_function_builder_->Emit(kExprNop); |
| } |
| |
| void VisitBreakStatement(BreakStatement* stmt) { |
| DCHECK(in_function_); |
| int i = static_cast<int>(breakable_blocks_.size()) - 1; |
| int block_distance = 0; |
| for (; i >= 0; i--) { |
| auto elem = breakable_blocks_.at(i); |
| if (elem.first == stmt->target()) { |
| if (elem.second) { |
| block_distance++; |
| } |
| break; |
| } else if (elem.second) { |
| block_distance += 2; |
| } else { |
| block_distance += 1; |
| } |
| } |
| DCHECK(i >= 0); |
| current_function_builder_->EmitWithU8(kExprBr, block_distance); |
| current_function_builder_->Emit(kExprNop); |
| } |
| |
| void VisitReturnStatement(ReturnStatement* stmt) { |
| if (in_function_) { |
| current_function_builder_->Emit(kExprReturn); |
| } else { |
| marking_exported = true; |
| } |
| RECURSE(Visit(stmt->expression())); |
| if (!in_function_) { |
| marking_exported = false; |
| } |
| } |
| |
| void VisitWithStatement(WithStatement* stmt) { |
| RECURSE(stmt->expression()); |
| RECURSE(stmt->statement()); |
| } |
| |
| void VisitSwitchStatement(SwitchStatement* stmt) { |
| RECURSE(Visit(stmt->tag())); |
| |
| ZoneList<CaseClause*>* clauses = stmt->cases(); |
| |
| for (int i = 0; i < clauses->length(); ++i) { |
| CaseClause* clause = clauses->at(i); |
| if (!clause->is_default()) { |
| Expression* label = clause->label(); |
| RECURSE(Visit(label)); |
| } |
| ZoneList<Statement*>* stmts = clause->statements(); |
| RECURSE(VisitStatements(stmts)); |
| } |
| } |
| |
| void VisitCaseClause(CaseClause* clause) { UNREACHABLE(); } |
| |
| void VisitDoWhileStatement(DoWhileStatement* stmt) { |
| RECURSE(Visit(stmt->body())); |
| RECURSE(Visit(stmt->cond())); |
| } |
| |
| void VisitWhileStatement(WhileStatement* stmt) { |
| DCHECK(in_function_); |
| current_function_builder_->EmitWithU8(kExprLoop, 1); |
| breakable_blocks_.push_back( |
| std::make_pair(stmt->AsBreakableStatement(), true)); |
| current_function_builder_->Emit(kExprIf); |
| RECURSE(Visit(stmt->cond())); |
| current_function_builder_->EmitWithU8(kExprBr, 0); |
| RECURSE(Visit(stmt->body())); |
| breakable_blocks_.pop_back(); |
| } |
| |
| void VisitForStatement(ForStatement* stmt) { |
| if (stmt->init() != NULL) { |
| RECURSE(Visit(stmt->init())); |
| } |
| if (stmt->cond() != NULL) { |
| RECURSE(Visit(stmt->cond())); |
| } |
| if (stmt->next() != NULL) { |
| RECURSE(Visit(stmt->next())); |
| } |
| RECURSE(Visit(stmt->body())); |
| } |
| |
| void VisitForInStatement(ForInStatement* stmt) { |
| RECURSE(Visit(stmt->enumerable())); |
| RECURSE(Visit(stmt->body())); |
| } |
| |
| void VisitForOfStatement(ForOfStatement* stmt) { |
| RECURSE(Visit(stmt->iterable())); |
| RECURSE(Visit(stmt->body())); |
| } |
| |
| void VisitTryCatchStatement(TryCatchStatement* stmt) { |
| RECURSE(Visit(stmt->try_block())); |
| RECURSE(Visit(stmt->catch_block())); |
| } |
| |
| void VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
| RECURSE(Visit(stmt->try_block())); |
| RECURSE(Visit(stmt->finally_block())); |
| } |
| |
| void VisitDebuggerStatement(DebuggerStatement* stmt) {} |
| |
| void VisitFunctionLiteral(FunctionLiteral* expr) { |
| Scope* scope = expr->scope(); |
| if (in_function_) { |
| if (expr->bounds().lower->IsFunction()) { |
| Type::FunctionType* func_type = expr->bounds().lower->AsFunction(); |
| LocalType return_type = TypeFrom(func_type->Result()); |
| current_function_builder_->ReturnType(return_type); |
| for (int i = 0; i < expr->parameter_count(); i++) { |
| LocalType type = TypeFrom(func_type->Parameter(i)); |
| DCHECK(type != kAstStmt); |
| LookupOrInsertLocal(scope->parameter(i), type); |
| } |
| } else { |
| UNREACHABLE(); |
| } |
| } |
| RECURSE(VisitDeclarations(scope->declarations())); |
| RECURSE(VisitStatements(expr->body())); |
| } |
| |
| void VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {} |
| |
| void VisitConditional(Conditional* expr) { |
| RECURSE(Visit(expr->condition())); |
| RECURSE(Visit(expr->then_expression())); |
| RECURSE(Visit(expr->else_expression())); |
| } |
| |
| void VisitVariableProxy(VariableProxy* expr) { |
| if (in_function_) { |
| Variable* var = expr->var(); |
| if (var->is_function()) { |
| DCHECK(!is_set_op_); |
| std::vector<uint8_t> index = |
| UnsignedLEB128From(LookupOrInsertFunction(var)); |
| current_function_builder_->EmitCode( |
| index.data(), static_cast<uint32_t>(index.size())); |
| } else { |
| if (is_set_op_) { |
| if (var->IsContextSlot()) { |
| current_function_builder_->Emit(kExprStoreGlobal); |
| } else { |
| current_function_builder_->Emit(kExprSetLocal); |
| } |
| is_set_op_ = false; |
| } else { |
| if (var->IsContextSlot()) { |
| current_function_builder_->Emit(kExprLoadGlobal); |
| } else { |
| current_function_builder_->Emit(kExprGetLocal); |
| } |
| } |
| LocalType var_type = TypeOf(expr); |
| DCHECK(var_type != kAstStmt); |
| if (var->IsContextSlot()) { |
| AddLeb128(LookupOrInsertGlobal(var, var_type), false); |
| } else { |
| AddLeb128(LookupOrInsertLocal(var, var_type), true); |
| } |
| } |
| } else if (marking_exported) { |
| Variable* var = expr->var(); |
| if (var->is_function()) { |
| uint16_t index = LookupOrInsertFunction(var); |
| builder_->FunctionAt(index)->Exported(1); |
| } |
| } |
| } |
| |
| void VisitLiteral(Literal* expr) { |
| if (in_function_) { |
| if (expr->raw_value()->IsNumber()) { |
| LocalType type = TypeOf(expr); |
| switch (type) { |
| case kAstI32: { |
| int val = static_cast<int>(expr->raw_value()->AsNumber()); |
| byte code[] = {WASM_I32(val)}; |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| break; |
| } |
| case kAstF32: { |
| float val = static_cast<float>(expr->raw_value()->AsNumber()); |
| byte code[] = {WASM_F32(val)}; |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| break; |
| } |
| case kAstF64: { |
| double val = static_cast<double>(expr->raw_value()->AsNumber()); |
| byte code[] = {WASM_F64(val)}; |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| } |
| } |
| |
| void VisitRegExpLiteral(RegExpLiteral* expr) {} |
| |
| void VisitObjectLiteral(ObjectLiteral* expr) { |
| ZoneList<ObjectLiteralProperty*>* props = expr->properties(); |
| for (int i = 0; i < props->length(); ++i) { |
| ObjectLiteralProperty* prop = props->at(i); |
| RECURSE(Visit(prop->value())); |
| } |
| } |
| |
| void VisitArrayLiteral(ArrayLiteral* expr) { |
| ZoneList<Expression*>* values = expr->values(); |
| for (int i = 0; i < values->length(); ++i) { |
| Expression* value = values->at(i); |
| RECURSE(Visit(value)); |
| } |
| } |
| |
| void LoadInitFunction() { |
| if (!init_function_initialized) { |
| init_function_initialized = true; |
| unsigned char init[] = "__init__"; |
| init_function_index = builder_->AddFunction(init, 8); |
| current_function_builder_ = builder_->FunctionAt(init_function_index); |
| current_function_builder_->ReturnType(kAstStmt); |
| current_function_builder_->Exported(1); |
| in_function_ = true; |
| } else { |
| current_function_builder_ = builder_->FunctionAt(init_function_index); |
| in_function_ = true; |
| } |
| } |
| |
| void UnLoadInitFunction() { |
| in_function_ = false; |
| current_function_builder_ = NULL; |
| } |
| |
| void VisitAssignment(Assignment* expr) { |
| bool in_init = false; |
| if (!in_function_) { |
| // TODO (bradnelson) : get rid of this |
| if (TypeOf(expr->value()) == kAstStmt) { |
| return; |
| } |
| in_init = true; |
| LoadInitFunction(); |
| } |
| BinaryOperation* value_op = expr->value()->AsBinaryOperation(); |
| if (value_op != NULL && MatchBinaryOperation(value_op) == kAsIs) { |
| VariableProxy* target_var = expr->target()->AsVariableProxy(); |
| VariableProxy* effective_value_var = |
| GetLeft(value_op)->AsVariableProxy(); |
| // TODO (aseemgarg): simplify block_size_ or replace with a kNop |
| if (target_var != NULL && effective_value_var != NULL && |
| target_var->var() == effective_value_var->var()) { |
| block_size_--; |
| return; |
| } |
| } |
| is_set_op_ = true; |
| RECURSE(Visit(expr->target())); |
| DCHECK(!is_set_op_); |
| RECURSE(Visit(expr->value())); |
| if (in_init) { |
| UnLoadInitFunction(); |
| } |
| } |
| |
| void VisitYield(Yield* expr) { |
| RECURSE(Visit(expr->generator_object())); |
| RECURSE(Visit(expr->expression())); |
| } |
| |
| void VisitThrow(Throw* expr) { RECURSE(Visit(expr->exception())); } |
| |
| void VisitProperty(Property* expr) { |
| Expression* obj = expr->obj(); |
| DCHECK(obj->bounds().lower == obj->bounds().upper); |
| TypeImpl<ZoneTypeConfig>* type = obj->bounds().lower; |
| MachineType mtype; |
| int size; |
| if (type->Is(cache_.kUint8Array)) { |
| mtype = MachineType::Uint8(); |
| size = 1; |
| } else if (type->Is(cache_.kInt8Array)) { |
| mtype = MachineType::Int8(); |
| size = 1; |
| } else if (type->Is(cache_.kUint16Array)) { |
| mtype = MachineType::Uint16(); |
| size = 2; |
| } else if (type->Is(cache_.kInt16Array)) { |
| mtype = MachineType::Int16(); |
| size = 2; |
| } else if (type->Is(cache_.kUint32Array)) { |
| mtype = MachineType::Uint32(); |
| size = 4; |
| } else if (type->Is(cache_.kInt32Array)) { |
| mtype = MachineType::Int32(); |
| size = 4; |
| } else if (type->Is(cache_.kUint32Array)) { |
| mtype = MachineType::Uint32(); |
| size = 4; |
| } else if (type->Is(cache_.kFloat32Array)) { |
| mtype = MachineType::Float32(); |
| size = 4; |
| } else if (type->Is(cache_.kFloat64Array)) { |
| mtype = MachineType::Float64(); |
| size = 8; |
| } else { |
| UNREACHABLE(); |
| } |
| current_function_builder_->EmitWithU8( |
| WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_), |
| WasmOpcodes::LoadStoreAccessOf(false)); |
| is_set_op_ = false; |
| Literal* value = expr->key()->AsLiteral(); |
| if (value) { |
| DCHECK(value->raw_value()->IsNumber()); |
| DCHECK(kAstI32 == TypeOf(value)); |
| int val = static_cast<int>(value->raw_value()->AsNumber()); |
| byte code[] = {WASM_I32(val * size)}; |
| current_function_builder_->EmitCode(code, sizeof(code)); |
| return; |
| } |
| BinaryOperation* binop = expr->key()->AsBinaryOperation(); |
| if (binop) { |
| DCHECK(Token::SAR == binop->op()); |
| DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber()); |
| DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral())); |
| DCHECK(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; |
| } |
| UNREACHABLE(); |
| } |
| |
| void VisitCall(Call* expr) { |
| Call::CallType call_type = expr->GetCallType(isolate_); |
| switch (call_type) { |
| case Call::OTHER_CALL: { |
| DCHECK(in_function_); |
| current_function_builder_->Emit(kExprCallFunction); |
| RECURSE(Visit(expr->expression())); |
| ZoneList<Expression*>* args = expr->arguments(); |
| for (int i = 0; i < args->length(); ++i) { |
| Expression* arg = args->at(i); |
| RECURSE(Visit(arg)); |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void VisitCallNew(CallNew* expr) { UNREACHABLE(); } |
| |
| void VisitCallRuntime(CallRuntime* expr) { UNREACHABLE(); } |
| |
| void VisitUnaryOperation(UnaryOperation* expr) { |
| switch (expr->op()) { |
| case Token::NOT: { |
| DCHECK(TypeOf(expr->expression()) == kAstI32); |
| current_function_builder_->Emit(kExprBoolNot); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| RECURSE(Visit(expr->expression())); |
| } |
| |
| void VisitCountOperation(CountOperation* expr) { |
| RECURSE(Visit(expr->expression())); |
| } |
| |
| bool MatchIntBinaryOperation(BinaryOperation* expr, Token::Value op, |
| int32_t val) { |
| DCHECK(expr->right() != NULL); |
| if (expr->op() == op && expr->right()->IsLiteral() && |
| TypeOf(expr) == kAstI32) { |
| Literal* right = expr->right()->AsLiteral(); |
| DCHECK(right->raw_value()->IsNumber()); |
| if (static_cast<int32_t>(right->raw_value()->AsNumber()) == val) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool MatchDoubleBinaryOperation(BinaryOperation* expr, Token::Value op, |
| double val) { |
| DCHECK(expr->right() != NULL); |
| if (expr->op() == op && expr->right()->IsLiteral() && |
| TypeOf(expr) == kAstF64) { |
| Literal* right = expr->right()->AsLiteral(); |
| DCHECK(right->raw_value()->IsNumber()); |
| if (right->raw_value()->AsNumber() == val) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| enum ConvertOperation { kNone, kAsIs, kToInt, kToDouble }; |
| |
| ConvertOperation MatchOr(BinaryOperation* expr) { |
| if (MatchIntBinaryOperation(expr, Token::BIT_OR, 0)) { |
| DCHECK(TypeOf(expr->left()) == kAstI32); |
| DCHECK(TypeOf(expr->right()) == kAstI32); |
| return kAsIs; |
| } else { |
| return kNone; |
| } |
| } |
| |
| ConvertOperation MatchShr(BinaryOperation* expr) { |
| if (MatchIntBinaryOperation(expr, Token::SHR, 0)) { |
| DCHECK(TypeOf(expr->left()) == kAstI32); |
| DCHECK(TypeOf(expr->right()) == kAstI32); |
| return kAsIs; |
| } else { |
| return kNone; |
| } |
| } |
| |
| ConvertOperation MatchXor(BinaryOperation* expr) { |
| if (MatchIntBinaryOperation(expr, Token::BIT_XOR, 0xffffffff)) { |
| DCHECK(TypeOf(expr->left()) == kAstI32); |
| DCHECK(TypeOf(expr->right()) == kAstI32); |
| BinaryOperation* op = expr->left()->AsBinaryOperation(); |
| if (op != NULL) { |
| if (MatchIntBinaryOperation(op, Token::BIT_XOR, 0xffffffff)) { |
| DCHECK(TypeOf(op->right()) == kAstI32); |
| if (TypeOf(op->left()) != kAstI32) { |
| return kToInt; |
| } else { |
| return kAsIs; |
| } |
| } |
| } |
| } |
| return kNone; |
| } |
| |
| ConvertOperation MatchMul(BinaryOperation* expr) { |
| if (MatchDoubleBinaryOperation(expr, Token::MUL, 1.0)) { |
| DCHECK(TypeOf(expr->right()) == kAstF64); |
| if (TypeOf(expr->left()) != kAstF64) { |
| return kToDouble; |
| } else { |
| return kAsIs; |
| } |
| } else { |
| return kNone; |
| } |
| } |
| |
| ConvertOperation MatchBinaryOperation(BinaryOperation* expr) { |
| switch (expr->op()) { |
| case Token::BIT_OR: |
| return MatchOr(expr); |
| case Token::SHR: |
| return MatchShr(expr); |
| case Token::BIT_XOR: |
| return MatchXor(expr); |
| case Token::MUL: |
| return MatchMul(expr); |
| default: |
| return kNone; |
| } |
| } |
| |
| #define NON_SIGNED_BINOP(op) \ |
| static WasmOpcode opcodes[] = { \ |
| kExprI32##op, \ |
| kExprI32##op, \ |
| kExprF32##op, \ |
| kExprF64##op \ |
| } |
| |
| #define SIGNED_BINOP(op) \ |
| static WasmOpcode opcodes[] = { \ |
| kExprI32##op##S, \ |
| kExprI32##op##U, \ |
| kExprF32##op, \ |
| kExprF64##op \ |
| } |
| |
| #define NON_SIGNED_INT_BINOP(op) \ |
| static WasmOpcode opcodes[] = { kExprI32##op, kExprI32##op } |
| |
| #define BINOP_CASE(token, op, V, ignore_sign) \ |
| case token: { \ |
| V(op); \ |
| int type = TypeIndexOf(expr->left(), expr->right(), ignore_sign); \ |
| current_function_builder_->Emit(opcodes[type]); \ |
| break; \ |
| } |
| |
| Expression* GetLeft(BinaryOperation* expr) { |
| if (expr->op() == Token::BIT_XOR) { |
| return expr->left()->AsBinaryOperation()->left(); |
| } else { |
| return expr->left(); |
| } |
| } |
| |
| void VisitBinaryOperation(BinaryOperation* expr) { |
| ConvertOperation convertOperation = MatchBinaryOperation(expr); |
| if (convertOperation == kToDouble) { |
| TypeIndex type = TypeIndexOf(expr->left()); |
| if (type == kInt32 || type == kFixnum) { |
| current_function_builder_->Emit(kExprF64SConvertI32); |
| } else if (type == kUint32) { |
| current_function_builder_->Emit(kExprF64UConvertI32); |
| } else if (type == kFloat32) { |
| current_function_builder_->Emit(kExprF64ConvertF32); |
| } else { |
| UNREACHABLE(); |
| } |
| RECURSE(Visit(expr->left())); |
| } else if (convertOperation == kToInt) { |
| TypeIndex type = TypeIndexOf(GetLeft(expr)); |
| if (type == kFloat32) { |
| current_function_builder_->Emit(kExprI32SConvertF32); |
| } else if (type == kFloat64) { |
| current_function_builder_->Emit(kExprI32SConvertF64); |
| } else { |
| UNREACHABLE(); |
| } |
| RECURSE(Visit(GetLeft(expr))); |
| } else if (convertOperation == kAsIs) { |
| RECURSE(Visit(GetLeft(expr))); |
| } else { |
| switch (expr->op()) { |
| BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true); |
| BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true); |
| BINOP_CASE(Token::MUL, Mul, NON_SIGNED_BINOP, true); |
| BINOP_CASE(Token::DIV, Div, SIGNED_BINOP, false); |
| BINOP_CASE(Token::BIT_OR, Ior, NON_SIGNED_INT_BINOP, true); |
| BINOP_CASE(Token::BIT_XOR, Xor, NON_SIGNED_INT_BINOP, true); |
| BINOP_CASE(Token::SHL, Shl, NON_SIGNED_INT_BINOP, true); |
| BINOP_CASE(Token::SAR, ShrS, NON_SIGNED_INT_BINOP, true); |
| BINOP_CASE(Token::SHR, ShrU, NON_SIGNED_INT_BINOP, true); |
| case Token::MOD: { |
| TypeIndex type = TypeIndexOf(expr->left(), expr->right(), false); |
| if (type == kInt32) { |
| current_function_builder_->Emit(kExprI32RemS); |
| } else if (type == kUint32) { |
| current_function_builder_->Emit(kExprI32RemU); |
| } else if (type == kFloat64) { |
| ModF64(expr); |
| return; |
| } else { |
| UNREACHABLE(); |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| RECURSE(Visit(expr->left())); |
| RECURSE(Visit(expr->right())); |
| } |
| } |
| |
| void ModF64(BinaryOperation* expr) { |
| current_function_builder_->EmitWithU8(kExprBlock, 3); |
| uint16_t index_0 = current_function_builder_->AddLocal(kAstF64); |
| uint16_t index_1 = current_function_builder_->AddLocal(kAstF64); |
| current_function_builder_->Emit(kExprSetLocal); |
| AddLeb128(index_0, true); |
| RECURSE(Visit(expr->left())); |
| current_function_builder_->Emit(kExprSetLocal); |
| AddLeb128(index_1, true); |
| RECURSE(Visit(expr->right())); |
| current_function_builder_->Emit(kExprF64Sub); |
| current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(index_0, true); |
| current_function_builder_->Emit(kExprF64Mul); |
| current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(index_1, true); |
| // Use trunc instead of two casts |
| current_function_builder_->Emit(kExprF64SConvertI32); |
| current_function_builder_->Emit(kExprI32SConvertF64); |
| current_function_builder_->Emit(kExprF64Div); |
| current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(index_0, true); |
| current_function_builder_->Emit(kExprGetLocal); |
| AddLeb128(index_1, true); |
| } |
| |
| void AddLeb128(uint32_t index, bool is_local) { |
| std::vector<uint8_t> index_vec = UnsignedLEB128From(index); |
| if (is_local) { |
| uint32_t pos_of_index[1] = {0}; |
| current_function_builder_->EmitCode( |
| index_vec.data(), static_cast<uint32_t>(index_vec.size()), |
| pos_of_index, 1); |
| } else { |
| current_function_builder_->EmitCode( |
| index_vec.data(), static_cast<uint32_t>(index_vec.size())); |
| } |
| } |
| |
| void VisitCompareOperation(CompareOperation* expr) { |
| switch (expr->op()) { |
| BINOP_CASE(Token::EQ, Eq, NON_SIGNED_BINOP, false); |
| BINOP_CASE(Token::LT, Lt, SIGNED_BINOP, false); |
| BINOP_CASE(Token::LTE, Le, SIGNED_BINOP, false); |
| BINOP_CASE(Token::GT, Gt, SIGNED_BINOP, false); |
| BINOP_CASE(Token::GTE, Ge, SIGNED_BINOP, false); |
| default: |
| UNREACHABLE(); |
| } |
| RECURSE(Visit(expr->left())); |
| RECURSE(Visit(expr->right())); |
| } |
| |
| #undef BINOP_CASE |
| #undef NON_SIGNED_INT_BINOP |
| #undef SIGNED_BINOP |
| #undef NON_SIGNED_BINOP |
| |
| enum TypeIndex { |
| kInt32 = 0, |
| kUint32 = 1, |
| kFloat32 = 2, |
| kFloat64 = 3, |
| kFixnum = 4 |
| }; |
| |
| TypeIndex TypeIndexOf(Expression* left, Expression* right, bool ignore_sign) { |
| TypeIndex left_index = TypeIndexOf(left); |
| TypeIndex right_index = TypeIndexOf(right); |
| if (left_index == kFixnum) { |
| left_index = right_index; |
| } |
| if (right_index == kFixnum) { |
| right_index = left_index; |
| } |
| if (left_index == kFixnum && right_index == kFixnum) { |
| left_index = kInt32; |
| right_index = kInt32; |
| } |
| DCHECK((left_index == right_index) || |
| (ignore_sign && (left_index <= 1) && (right_index <= 1))); |
| return left_index; |
| } |
| |
| TypeIndex TypeIndexOf(Expression* expr) { |
| DCHECK(expr->bounds().lower == expr->bounds().upper); |
| TypeImpl<ZoneTypeConfig>* type = expr->bounds().lower; |
| if (type->Is(cache_.kAsmFixnum)) { |
| return kFixnum; |
| } else if (type->Is(cache_.kAsmSigned)) { |
| return kInt32; |
| } else if (type->Is(cache_.kAsmUnsigned)) { |
| return kUint32; |
| } else if (type->Is(cache_.kAsmInt)) { |
| return kInt32; |
| } else if (type->Is(cache_.kAsmFloat)) { |
| return kFloat32; |
| } else if (type->Is(cache_.kAsmDouble)) { |
| return kFloat64; |
| } else { |
| UNREACHABLE(); |
| } |
| } |
| |
| #undef CASE |
| #undef NON_SIGNED_INT |
| #undef SIGNED |
| #undef NON_SIGNED |
| |
| void VisitThisFunction(ThisFunction* expr) {} |
| |
| void VisitDeclarations(ZoneList<Declaration*>* decls) { |
| for (int i = 0; i < decls->length(); ++i) { |
| Declaration* decl = decls->at(i); |
| RECURSE(Visit(decl)); |
| } |
| } |
| |
| void VisitClassLiteral(ClassLiteral* expr) {} |
| |
| void VisitSpread(Spread* expr) {} |
| |
| void VisitSuperPropertyReference(SuperPropertyReference* expr) {} |
| |
| void VisitSuperCallReference(SuperCallReference* expr) {} |
| |
| void VisitSloppyBlockFunctionStatement(SloppyBlockFunctionStatement* expr) {} |
| |
| void VisitDoExpression(DoExpression* expr) {} |
| |
| void VisitRewritableAssignmentExpression( |
| RewritableAssignmentExpression* expr) {} |
| |
| struct IndexContainer : public ZoneObject { |
| uint16_t index; |
| }; |
| |
| uint16_t LookupOrInsertLocal(Variable* v, LocalType type) { |
| DCHECK(current_function_builder_ != NULL); |
| ZoneHashMap::Entry* entry = |
| local_variables_.Lookup(v, ComputePointerHash(v)); |
| if (entry == NULL) { |
| uint16_t index; |
| if (v->IsParameter()) { |
| index = current_function_builder_->AddParam(type); |
| } else { |
| index = current_function_builder_->AddLocal(type); |
| } |
| IndexContainer* container = new (zone()) IndexContainer(); |
| container->index = index; |
| entry = local_variables_.LookupOrInsert(v, ComputePointerHash(v), |
| ZoneAllocationPolicy(zone())); |
| entry->value = container; |
| } |
| return (reinterpret_cast<IndexContainer*>(entry->value))->index; |
| } |
| |
| uint16_t LookupOrInsertGlobal(Variable* v, LocalType type) { |
| ZoneHashMap::Entry* entry = |
| global_variables_.Lookup(v, ComputePointerHash(v)); |
| if (entry == NULL) { |
| uint16_t index = |
| builder_->AddGlobal(WasmOpcodes::MachineTypeFor(type), 0); |
| IndexContainer* container = new (zone()) IndexContainer(); |
| container->index = index; |
| entry = global_variables_.LookupOrInsert(v, ComputePointerHash(v), |
| ZoneAllocationPolicy(zone())); |
| entry->value = container; |
| } |
| return (reinterpret_cast<IndexContainer*>(entry->value))->index; |
| } |
| |
| uint16_t LookupOrInsertFunction(Variable* v) { |
| DCHECK(builder_ != NULL); |
| ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v)); |
| if (entry == NULL) { |
| uint16_t index = builder_->AddFunction(v->raw_name()->raw_data(), |
| v->raw_name()->length()); |
| IndexContainer* container = new (zone()) IndexContainer(); |
| container->index = index; |
| entry = functions_.LookupOrInsert(v, ComputePointerHash(v), |
| ZoneAllocationPolicy(zone())); |
| entry->value = container; |
| } |
| return (reinterpret_cast<IndexContainer*>(entry->value))->index; |
| } |
| |
| LocalType TypeOf(Expression* expr) { |
| DCHECK(expr->bounds().lower == expr->bounds().upper); |
| return TypeFrom(expr->bounds().lower); |
| } |
| |
| LocalType TypeFrom(TypeImpl<ZoneTypeConfig>* type) { |
| if (type->Is(cache_.kAsmInt)) { |
| return kAstI32; |
| } else if (type->Is(cache_.kAsmFloat)) { |
| return kAstF32; |
| } else if (type->Is(cache_.kAsmDouble)) { |
| return kAstF64; |
| } else { |
| return kAstStmt; |
| } |
| } |
| |
| Zone* zone() { return zone_; } |
| |
| ZoneHashMap local_variables_; |
| ZoneHashMap functions_; |
| ZoneHashMap global_variables_; |
| bool in_function_; |
| bool is_set_op_; |
| bool marking_exported; |
| WasmModuleBuilder* builder_; |
| WasmFunctionBuilder* current_function_builder_; |
| FunctionLiteral* literal_; |
| Isolate* isolate_; |
| Zone* zone_; |
| TypeCache const& cache_; |
| ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_; |
| int block_size_; |
| bool init_function_initialized; |
| uint16_t init_function_index; |
| |
| DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); |
| DISALLOW_COPY_AND_ASSIGN(AsmWasmBuilderImpl); |
| }; |
| |
| AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone, |
| FunctionLiteral* literal) |
| : isolate_(isolate), zone_(zone), literal_(literal) {} |
| |
| /*TODO: probably should take zone (to write wasm to) as input so that zone in |
| constructor may be thrown away once wasm module is written */ |
| WasmModuleIndex* AsmWasmBuilder::Run() { |
| AsmWasmBuilderImpl impl(isolate_, zone_, literal_); |
| impl.Compile(); |
| WasmModuleWriter* writer = impl.builder_->Build(zone_); |
| return writer->WriteTo(zone_); |
| } |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |