Index: src/wasm/asm-wasm-builder.cc |
diff --git a/src/wasm/asm-wasm-builder.cc b/src/wasm/asm-wasm-builder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d7924926fc5239853785f3d7a51f0d535b42b6a4 |
--- /dev/null |
+++ b/src/wasm/asm-wasm-builder.cc |
@@ -0,0 +1,938 @@ |
+// 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) { |
+ 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 (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(); |
+ } else { |
+ RECURSE(VisitStatements(stmt->statements())); |
+ } |
+ } |
+ |
+ 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 VisitAssignment(Assignment* expr) { |
+ if (in_function_) { |
+ 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())); |
+ } |
+ } |
+ |
+ 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_; |
+ |
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(AsmWasmBuilderImpl); |
+}; |
+ |
+AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone, |
+ FunctionLiteral* literal) |
+ : isolate_(isolate), zone_(zone), literal_(literal) {} |
+ |
+// TODO(aseemgarg): 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 |