blob: e41f13af725c4c19a1b4e2cae41991a09d494096 [file] [log] [blame]
// 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