Commit 821c603e authored by bradnelson's avatar bradnelson Committed by Commit bot

Adding support for asm.js foreign globals.

Since wasm has no direct notion of foreign globals,
pass the ffi object on to the AsmWasmBuilder
so that foreign globals can be extracted at module
instantiation time.

BUG= https://code.google.com/p/v8/issues/detail?id=4203
TEST=mjsunit/asm-wasm
R=titzer@chromium.org,aseemgarg@chromium.org
LOG=N

Review URL: https://codereview.chromium.org/1677373002

Cr-Commit-Position: refs/heads/master@{#33956}
parent 54404c47
......@@ -27,7 +27,8 @@ namespace wasm {
class AsmWasmBuilderImpl : public AstVisitor {
public:
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal)
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal,
Handle<Object> foreign)
: local_variables_(HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
......@@ -44,6 +45,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
literal_(literal),
isolate_(isolate),
zone_(zone),
foreign_(foreign),
cache_(TypeCache::Get()),
breakable_blocks_(zone),
block_size_(0),
......@@ -559,6 +561,30 @@ class AsmWasmBuilderImpl : public AstVisitor {
void VisitAssignment(Assignment* expr) {
bool in_init = false;
if (!in_function_) {
BinaryOperation* binop = expr->value()->AsBinaryOperation();
if (binop != nullptr) {
Property* prop = binop->left()->AsProperty();
DCHECK(prop != nullptr);
LoadInitFunction();
is_set_op_ = true;
RECURSE(Visit(expr->target()));
DCHECK(!is_set_op_);
if (binop->op() == Token::MUL) {
DCHECK(binop->right()->IsLiteral());
DCHECK(binop->right()->AsLiteral()->raw_value()->AsNumber() == 1.0);
DCHECK(binop->right()->AsLiteral()->raw_value()->ContainsDot());
VisitForeignVariable(true, prop);
} else if (binop->op() == Token::BIT_OR) {
DCHECK(binop->right()->IsLiteral());
DCHECK(binop->right()->AsLiteral()->raw_value()->AsNumber() == 0.0);
DCHECK(!binop->right()->AsLiteral()->raw_value()->ContainsDot());
VisitForeignVariable(false, prop);
} else {
UNREACHABLE();
}
UnLoadInitFunction();
return;
}
// TODO(bradnelson): Get rid of this.
if (TypeOf(expr->value()) == kAstStmt) {
Property* prop = expr->value()->AsProperty();
......@@ -610,6 +636,55 @@ class AsmWasmBuilderImpl : public AstVisitor {
void VisitThrow(Throw* expr) { UNREACHABLE(); }
void VisitForeignVariable(bool is_float, Property* expr) {
DCHECK(expr->obj()->AsVariableProxy());
DCHECK(expr->obj()->AsVariableProxy()->var()->location() ==
VariableLocation::PARAMETER);
DCHECK(expr->obj()->AsVariableProxy()->var()->index() == 1);
Literal* key_literal = expr->key()->AsLiteral();
DCHECK(key_literal != nullptr);
if (!key_literal->value().is_null() && !foreign_.is_null() &&
foreign_->IsObject()) {
Handle<Name> name =
i::Object::ToName(isolate_, key_literal->value()).ToHandleChecked();
MaybeHandle<Object> maybe_value = i::Object::GetProperty(foreign_, name);
if (!maybe_value.is_null()) {
Handle<Object> value = maybe_value.ToHandleChecked();
if (is_float) {
MaybeHandle<Object> maybe_nvalue = i::Object::ToNumber(value);
if (!maybe_nvalue.is_null()) {
Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
if (nvalue->IsNumber()) {
double val = nvalue->Number();
byte code[] = {WASM_F64(val)};
current_function_builder_->EmitCode(code, sizeof(code));
return;
}
}
} else {
MaybeHandle<Object> maybe_nvalue =
i::Object::ToInt32(isolate_, value);
if (!maybe_nvalue.is_null()) {
Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
if (nvalue->IsNumber()) {
int32_t val = static_cast<int32_t>(nvalue->Number());
byte code[] = {WASM_I32(val)};
current_function_builder_->EmitCode(code, sizeof(code));
return;
}
}
}
}
}
if (is_float) {
byte code[] = {WASM_F64(std::numeric_limits<double>::quiet_NaN())};
current_function_builder_->EmitCode(code, sizeof(code));
} else {
byte code[] = {WASM_I32(0)};
current_function_builder_->EmitCode(code, sizeof(code));
}
}
void VisitProperty(Property* expr) {
Expression* obj = expr->obj();
DCHECK(obj->bounds().lower == obj->bounds().upper);
......@@ -1173,6 +1248,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
FunctionLiteral* literal_;
Isolate* isolate_;
Zone* zone_;
Handle<Object> foreign_;
TypeCache const& cache_;
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
int block_size_;
......@@ -1188,13 +1264,13 @@ class AsmWasmBuilderImpl : public AstVisitor {
};
AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
FunctionLiteral* literal)
: isolate_(isolate), zone_(zone), literal_(literal) {}
FunctionLiteral* literal, Handle<Object> foreign)
: isolate_(isolate), zone_(zone), literal_(literal), foreign_(foreign) {}
// 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_);
AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_);
impl.Compile();
WasmModuleWriter* writer = impl.builder_->Build(zone_);
return writer->WriteTo(zone_);
......
......@@ -6,6 +6,7 @@
#define V8_WASM_ASM_WASM_BUILDER_H_
#include "src/allocation.h"
#include "src/objects.h"
#include "src/wasm/encoder.h"
#include "src/zone.h"
......@@ -18,13 +19,15 @@ namespace wasm {
class AsmWasmBuilder {
public:
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root);
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
Handle<Object> foreign);
WasmModuleIndex* Run();
private:
Isolate* isolate_;
Zone* zone_;
FunctionLiteral* literal_;
Handle<Object> foreign_;
};
} // namespace wasm
} // namespace internal
......
......@@ -99,9 +99,8 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (result.val) delete result.val;
}
v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info,
ErrorThrower* thrower) {
v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) {
info->set_global();
info->set_lazy(false);
info->set_allow_lazy_parsing(false);
......@@ -125,7 +124,7 @@ v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info,
}
auto module = v8::internal::wasm::AsmWasmBuilder(
info->isolate(), info->zone(), info->literal())
info->isolate(), info->zone(), info->literal(), foreign)
.Run();
return module;
}
......@@ -189,7 +188,13 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
i::ParseInfo info(&zone, script);
auto module = TranslateAsmModule(&info, &thrower);
i::Handle<i::Object> foreign;
if (args.Length() > 1 && args[1]->IsObject()) {
Local<Object> local_foreign = Local<Object>::Cast(args[1]);
foreign = v8::Utils::OpenHandle(*local_foreign);
}
auto module = TranslateAsmModule(&info, foreign, &thrower);
if (module == nullptr) {
return;
}
......
......@@ -1080,3 +1080,93 @@ function TestForeignFunctionMultipleUse() {
}
TestForeignFunctionMultipleUse();
function TestForeignVariables() {
function AsmModule(stdlib, foreign, buffer) {
"use asm";
var i1 = foreign.foo | 0;
var f1 = +foreign.bar;
var i2 = foreign.baz | 0;
var f2 = +foreign.baz;
function geti1() {
return i1|0;
}
function getf1() {
return +f1;
}
function geti2() {
return i2|0;
}
function getf2() {
return +f2;
}
return {geti1:geti1, getf1:getf1, geti2:geti2, getf2:getf2};
}
function TestCase(env, i1, f1, i2, f2) {
var module = _WASMEXP_.instantiateModuleFromAsm(
AsmModule.toString(), env);
module.__init__();
assertEquals(i1, module.geti1());
assertEquals(f1, module.getf1());
assertEquals(i2, module.geti2());
assertEquals(f2, module.getf2());
}
// Check normal operation.
TestCase({foo: 123, bar: 234.5, baz: 345.7}, 123, 234.5, 345, 345.7);
// Check partial operation.
TestCase({baz: 345.7}, 0, NaN, 345, 345.7);
// Check that undefined values are converted to proper defaults.
TestCase({qux: 999}, 0, NaN, 0, NaN);
// Check that an undefined ffi is ok.
TestCase(undefined, 0, NaN, 0, NaN);
// Check that true values are converted properly.
TestCase({foo: true, bar: true, baz: true}, 1, 1.0, 1, 1.0);
// Check that false values are converted properly.
TestCase({foo: false, bar: false, baz: false}, 0, 0, 0, 0);
// Check that null values are converted properly.
TestCase({foo: null, bar: null, baz: null}, 0, 0, 0, 0);
// Check that string values are converted properly.
TestCase({foo: 'hi', bar: 'there', baz: 'dude'}, 0, NaN, 0, NaN);
TestCase({foo: '0xff', bar: '234', baz: '456.1'}, 255, 234, 456, 456.1, 456);
// Check that Date values are converted properly.
TestCase({foo: new Date(123), bar: new Date(456),
baz: new Date(789)}, 123, 456, 789, 789);
// Check that list values are converted properly.
TestCase({foo: [], bar: [], baz: []}, 0, 0, 0, 0);
// Check that object values are converted properly.
TestCase({foo: {}, bar: {}, baz: {}}, 0, NaN, 0, NaN);
// Check that getter object values are converted properly.
var o = {
get foo() {
return 123.4;
}
};
TestCase({foo: o.foo, bar: o.foo, baz: o.foo}, 123, 123.4, 123, 123.4);
// Check that getter object values are converted properly.
var o = {
get baz() {
return 123.4;
}
};
TestCase(o, 0, NaN, 123, 123.4);
// Check that objects with valueOf are converted properly.
var o = {
valueOf: function() { return 99; }
};
TestCase({foo: o, bar: o, baz: o}, 99, 99, 99, 99);
// Check that function values are converted properly.
TestCase({foo: TestCase, bar: TestCase, qux: TestCase}, 0, NaN, 0, NaN);
// Check that a missing ffi object is safe.
TestCase(undefined, 0, NaN, 0, NaN);
}
TestForeignVariables();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment