From f060922369b3127ca58bab6997f660f20fa6cd8b Mon Sep 17 00:00:00 2001 From: aseemgarg Date: Thu, 4 Feb 2016 18:07:23 -0800 Subject: [PATCH] Add Foreign Functions to asm to wasm R=bradnelson@chromium.org BUG=https://bugs.chromium.org/p/v8/issues/detail?id=4203 TEST=asm-wasm.js LOG=N Review URL: https://codereview.chromium.org/1667253003 Cr-Commit-Position: refs/heads/master@{#33757} --- src/wasm/asm-wasm-builder.cc | 138 +++++++++++++++++++++++++++------- src/wasm/encoder.cc | 2 +- src/wasm/encoder.h | 4 +- test/mjsunit/wasm/asm-wasm.js | 85 +++++++++++++++++++++ 4 files changed, 199 insertions(+), 30 deletions(-) diff --git a/src/wasm/asm-wasm-builder.cc b/src/wasm/asm-wasm-builder.cc index fae07e98e5..1d5e674e1c 100644 --- a/src/wasm/asm-wasm-builder.cc +++ b/src/wasm/asm-wasm-builder.cc @@ -51,7 +51,8 @@ class AsmWasmBuilderImpl : public AstVisitor { next_table_index_(0), function_tables_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, - ZoneAllocationPolicy(zone)) { + ZoneAllocationPolicy(zone)), + imported_function_table_(this) { InitializeAstVisitor(isolate); } @@ -367,34 +368,26 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitVariableProxy(VariableProxy* expr) { if (in_function_) { Variable* var = expr->var(); - if (var->is_function()) { - DCHECK(!is_set_op_); - std::vector index = - UnsignedLEB128From(LookupOrInsertFunction(var)); - current_function_builder_->EmitCode( - &index[0], static_cast(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 (is_set_op_) { if (var->IsContextSlot()) { - AddLeb128(LookupOrInsertGlobal(var, var_type), false); + current_function_builder_->Emit(kExprStoreGlobal); } else { - AddLeb128(LookupOrInsertLocal(var, var_type), true); + 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); } } } @@ -508,11 +501,80 @@ class AsmWasmBuilderImpl : public AstVisitor { return reinterpret_cast(entry->value); } + class ImportedFunctionTable { + private: + class ImportedFunctionIndices : public ZoneObject { + public: + const unsigned char* name_; + int name_length_; + WasmModuleBuilder::SignatureMap signature_to_index_; + + ImportedFunctionIndices(const unsigned char* name, int name_length, + Zone* zone) + : name_(name), name_length_(name_length), signature_to_index_(zone) {} + }; + ZoneHashMap table_; + AsmWasmBuilderImpl* builder_; + + public: + explicit ImportedFunctionTable(AsmWasmBuilderImpl* builder) + : table_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(builder->zone())), + builder_(builder) {} + + void AddImport(Variable* v, const unsigned char* name, int name_length) { + ImportedFunctionIndices* indices = new (builder_->zone()) + ImportedFunctionIndices(name, name_length, builder_->zone()); + ZoneHashMap::Entry* entry = table_.LookupOrInsert( + v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone())); + entry->value = indices; + } + + uint16_t GetFunctionIndex(Variable* v, FunctionSig* sig) { + ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v)); + DCHECK(entry != nullptr); + ImportedFunctionIndices* indices = + reinterpret_cast(entry->value); + WasmModuleBuilder::SignatureMap::iterator pos = + indices->signature_to_index_.find(sig); + if (pos != indices->signature_to_index_.end()) { + return pos->second; + } else { + uint16_t index = builder_->builder_->AddFunction(); + indices->signature_to_index_[sig] = index; + WasmFunctionBuilder* function = builder_->builder_->FunctionAt(index); + function->External(1); + function->SetName(indices->name_, indices->name_length_); + if (sig->return_count() > 0) { + function->ReturnType(sig->GetReturn()); + } + for (size_t i = 0; i < sig->parameter_count(); i++) { + function->AddParam(sig->GetParam(i)); + } + return index; + } + } + }; + void VisitAssignment(Assignment* expr) { bool in_init = false; if (!in_function_) { // TODO(bradnelson): Get rid of this. if (TypeOf(expr->value()) == kAstStmt) { + Property* prop = expr->value()->AsProperty(); + if (prop != nullptr) { + VariableProxy* vp = prop->obj()->AsVariableProxy(); + if (vp != nullptr && vp->var()->IsParameter() && + vp->var()->index() == 1) { + VariableProxy* target = expr->target()->AsVariableProxy(); + if (target->bounds().lower->Is(Type::Function())) { + const AstRawString* name = + prop->key()->AsLiteral()->AsRawPropertyName(); + imported_function_table_.AddImport( + target->var(), name->raw_data(), name->length()); + } + } + } ArrayLiteral* funcs = expr->value()->AsArrayLiteral(); if (funcs != nullptr && funcs->bounds().lower->AsArray()->Element()->IsFunction()) { @@ -620,8 +682,29 @@ class AsmWasmBuilderImpl : public AstVisitor { switch (call_type) { case Call::OTHER_CALL: { DCHECK(in_function_); + uint16_t index; + VariableProxy* vp = expr->expression()->AsVariableProxy(); + if (vp != nullptr && + Type::Any()->Is(vp->bounds().lower->AsFunction()->Result())) { + LocalType return_type = TypeOf(expr); + ZoneList* args = expr->arguments(); + FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1, + args->length()); + if (return_type != kAstStmt) { + sig.AddReturn(return_type); + } + for (int i = 0; i < args->length(); i++) { + sig.AddParam(TypeOf(args->at(i))); + } + index = + imported_function_table_.GetFunctionIndex(vp->var(), sig.Build()); + } else { + index = LookupOrInsertFunction(vp->var()); + } current_function_builder_->Emit(kExprCallFunction); - RECURSE(Visit(expr->expression())); + std::vector index_arr = UnsignedLEB128From(index); + current_function_builder_->EmitCode( + &index_arr[0], static_cast(index_arr.size())); break; } case Call::KEYED_PROPERTY_CALL: { @@ -1094,6 +1177,7 @@ class AsmWasmBuilderImpl : public AstVisitor { uint16_t init_function_index_; uint32_t next_table_index_; ZoneHashMap function_tables_; + ImportedFunctionTable imported_function_table_; DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); diff --git a/src/wasm/encoder.cc b/src/wasm/encoder.cc index dd690a33f3..d80a275338 100644 --- a/src/wasm/encoder.cc +++ b/src/wasm/encoder.cc @@ -263,7 +263,7 @@ uint32_t WasmFunctionEncoder::BodySize(void) const { uint32_t WasmFunctionEncoder::NameSize() const { - return exported_ ? static_cast(name_.size()) : 0; + return HasName() ? static_cast(name_.size()) : 0; } diff --git a/src/wasm/encoder.h b/src/wasm/encoder.h index e07021ded6..7b651bf95e 100644 --- a/src/wasm/encoder.h +++ b/src/wasm/encoder.h @@ -47,7 +47,7 @@ class WasmFunctionEncoder : public ZoneObject { local_f64_count_) > 0; } - bool HasName() const { return exported_ && name_.size() > 0; } + bool HasName() const { return (exported_ || external_) && name_.size() > 0; } }; class WasmFunctionBuilder : public ZoneObject { @@ -133,12 +133,12 @@ class WasmModuleBuilder : public ZoneObject { void AddIndirectFunction(uint16_t index); WasmModuleWriter* Build(Zone* zone); - private: struct CompareFunctionSigs { bool operator()(FunctionSig* a, FunctionSig* b) const; }; typedef ZoneMap SignatureMap; + private: Zone* zone_; ZoneVector signatures_; ZoneVector functions_; diff --git a/test/mjsunit/wasm/asm-wasm.js b/test/mjsunit/wasm/asm-wasm.js index 9c530a6c92..b6068a1cf3 100644 --- a/test/mjsunit/wasm/asm-wasm.js +++ b/test/mjsunit/wasm/asm-wasm.js @@ -995,3 +995,88 @@ assertEquals(9, module.caller(0, 2, 54, 45)); assertEquals(99, module.caller(0, 3, 54, 45)); assertEquals(23, module.caller(0, 4, 12, 11)); assertEquals(31, module.caller(1, 0, 30, 11)); + + +function TestForeignFunctions() { + function AsmModule(stdlib, foreign, buffer) { + "use asm"; + + var setVal = foreign.setVal; + var getVal = foreign.getVal; + + function caller(initial_value, new_value) { + initial_value = initial_value|0; + new_value = new_value|0; + if ((getVal()|0) == (initial_value|0)) { + setVal(new_value|0); + return getVal()|0; + } + return 0; + } + + return {caller:caller}; + } + + function ffi(initial_val) { + var val = initial_val; + + function getVal() { + return val; + } + + function setVal(new_val) { + val = new_val; + } + + return {getVal:getVal, setVal:setVal}; + } + + var foreign = new ffi(23); + + var module = _WASMEXP_.instantiateModuleFromAsm(AsmModule.toString(), + foreign, null); + + module.__init__(); + assertEquals(103, module.caller(23, 103)); +} + +TestForeignFunctions(); + +function TestForeignFunctionMultipleUse() { + function AsmModule(stdlib, foreign, buffer) { + "use asm"; + + var getVal = foreign.getVal; + + function caller(int_val, double_val) { + int_val = int_val|0; + double_val = +double_val; + if ((getVal()|0) == (int_val|0)) { + if ((+getVal()) == (+double_val)) { + return 89; + } + } + return 0; + } + + return {caller:caller}; + } + + function ffi() { + function getVal() { + return 83.25; + } + + return {getVal:getVal}; + } + + var foreign = new ffi(); + + var module = _WASMEXP_.instantiateModuleFromAsm(AsmModule.toString(), + foreign, null); + + module.__init__(); + assertEquals(89, module.caller(83, 83.25)); +} + +TestForeignFunctionMultipleUse();