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