[asm.js] Switch StdlibSet to be a uint64_t bit set.

This switches the {StdlibSet} to be a data structure that does not
require dynamic memory allocation. This makes it easier to carry it
around as part of a {CompilationJob} and serialize it into the heap.

R=clemensh@chromium.org

Change-Id: I77b2353cfdcd9438a26f04d00749159fed9b9b6c
Reviewed-on: https://chromium-review.googlesource.com/584868
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46889}
This commit is contained in:
Michael Starzinger 2017-07-25 17:00:30 +02:00 committed by Commit Bot
parent 6e4d2a60ba
commit 637b7d645c
3 changed files with 77 additions and 76 deletions

View File

@ -34,7 +34,7 @@ const char* const AsmJs::kSingleFunctionName = "__single_function__";
namespace { namespace {
enum WasmDataEntries { enum WasmDataEntries {
kWasmDataCompiledModule, kWasmDataCompiledModule,
kWasmDataUsesArray, kWasmDataUsesBitSet,
kWasmDataEntryCount, kWasmDataEntryCount,
}; };
@ -49,62 +49,69 @@ Handle<Object> StdlibMathMember(Isolate* isolate, Handle<JSReceiver> stdlib,
return value; return value;
} }
bool IsStdlibMemberValid(Isolate* isolate, Handle<JSReceiver> stdlib, bool AreStdlibMembersValid(Isolate* isolate, Handle<JSReceiver> stdlib,
wasm::AsmJsParser::StandardMember member, wasm::AsmJsParser::StdlibSet members,
bool* is_typed_array) { bool* is_typed_array) {
switch (member) { if (members.Contains(wasm::AsmJsParser::StandardMember::kInfinity)) {
case wasm::AsmJsParser::StandardMember::kInfinity: { members.Remove(wasm::AsmJsParser::StandardMember::kInfinity);
Handle<Name> name = isolate->factory()->Infinity_string(); Handle<Name> name = isolate->factory()->Infinity_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
return value->IsNumber() && std::isinf(value->Number()); if (!value->IsNumber() || !std::isinf(value->Number())) return false;
}
case wasm::AsmJsParser::StandardMember::kNaN: {
Handle<Name> name = isolate->factory()->NaN_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
return value->IsNaN();
}
#define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \
case wasm::AsmJsParser::StandardMember::kMath##FName: { \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#fname))); \
Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
if (!value->IsJSFunction()) return false; \
Handle<JSFunction> func = Handle<JSFunction>::cast(value); \
return func->shared()->code() == \
isolate->builtins()->builtin(Builtins::kMath##FName); \
} }
STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC) if (members.Contains(wasm::AsmJsParser::StandardMember::kNaN)) {
members.Remove(wasm::AsmJsParser::StandardMember::kNaN);
Handle<Name> name = isolate->factory()->NaN_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
if (!value->IsNaN()) return false;
}
#define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \
if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##FName)) { \
members.Remove(wasm::AsmJsParser::StandardMember::kMath##FName); \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#fname))); \
Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
if (!value->IsJSFunction()) return false; \
Handle<JSFunction> func = Handle<JSFunction>::cast(value); \
if (func->shared()->code() != \
isolate->builtins()->builtin(Builtins::kMath##FName)) { \
return false; \
} \
}
STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC)
#undef STDLIB_MATH_FUNC #undef STDLIB_MATH_FUNC
#define STDLIB_MATH_CONST(cname, const_value) \ #define STDLIB_MATH_CONST(cname, const_value) \
case wasm::AsmJsParser::StandardMember::kMath##cname: { \ if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##cname)) { \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \ members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname); \
STATIC_CHAR_VECTOR(#cname))); \ Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \ STATIC_CHAR_VECTOR(#cname))); \
return value->IsNumber() && value->Number() == const_value; \ Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
if (!value->IsNumber() || value->Number() != const_value) return false; \
} }
STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST) STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST)
#undef STDLIB_MATH_CONST #undef STDLIB_MATH_CONST
#define STDLIB_ARRAY_TYPE(fname, FName) \ #define STDLIB_ARRAY_TYPE(fname, FName) \
case wasm::AsmJsParser::StandardMember::k##FName: { \ if (members.Contains(wasm::AsmJsParser::StandardMember::k##FName)) { \
*is_typed_array = true; \ members.Remove(wasm::AsmJsParser::StandardMember::k##FName); \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \ *is_typed_array = true; \
STATIC_CHAR_VECTOR(#FName))); \ Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); \ STATIC_CHAR_VECTOR(#FName))); \
if (!value->IsJSFunction()) return false; \ Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); \
Handle<JSFunction> func = Handle<JSFunction>::cast(value); \ if (!value->IsJSFunction()) return false; \
return func.is_identical_to(isolate->fname()); \ Handle<JSFunction> func = Handle<JSFunction>::cast(value); \
if (!func.is_identical_to(isolate->fname())) return false; \
} }
STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array) STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array) STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array) STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array) STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array) STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array) STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array) STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array) STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
#undef STDLIB_ARRAY_TYPE #undef STDLIB_ARRAY_TYPE
} // All members accounted for.
UNREACHABLE(); DCHECK(members.IsEmpty());
return true;
} }
void Report(Handle<Script> script, int position, Vector<const char> text, void Report(Handle<Script> script, int position, Vector<const char> text,
@ -171,7 +178,7 @@ void ReportInstantiationFailure(Handle<Script> script, int position,
// [1] PrepareJobImpl: The asm.js module source is parsed, validated, and // [1] PrepareJobImpl: The asm.js module source is parsed, validated, and
// translated to a valid WebAssembly module. The result are two vectors // translated to a valid WebAssembly module. The result are two vectors
// representing the encoded module as well as encoded source position // representing the encoded module as well as encoded source position
// information and a StdlibSet. // information and a StdlibSet bit set.
// [2] FinalizeJobImp: The module is handed to WebAssembly which decodes it // [2] FinalizeJobImp: The module is handed to WebAssembly which decodes it
// into an internal representation and eventually compiles it to machine // into an internal representation and eventually compiles it to machine
// code. // code.
@ -192,7 +199,6 @@ class AsmJsCompilationJob final : public CompilationJob {
private: private:
wasm::ZoneBuffer* module_; wasm::ZoneBuffer* module_;
wasm::ZoneBuffer* asm_offsets_; wasm::ZoneBuffer* asm_offsets_;
// TODO(mstarzinger) Replace with a bitvector.
wasm::AsmJsParser::StdlibSet stdlib_uses_; wasm::AsmJsParser::StdlibSet stdlib_uses_;
double translate_time_; // Time (milliseconds) taken to execute step [1]. double translate_time_; // Time (milliseconds) taken to execute step [1].
@ -228,7 +234,7 @@ CompilationJob::Status AsmJsCompilationJob::PrepareJobImpl() {
parser.module_builder()->WriteTo(*module_); parser.module_builder()->WriteTo(*module_);
asm_offsets_ = new (compile_zone) wasm::ZoneBuffer(compile_zone); asm_offsets_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_); parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_);
stdlib_uses_.swap(*parser.stdlib_uses()); stdlib_uses_ = *parser.stdlib_uses();
size_t compile_zone_size = size_t compile_zone_size =
info()->zone()->allocation_size() - compile_zone_start; info()->zone()->allocation_size() - compile_zone_start;
@ -259,12 +265,9 @@ CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
base::ElapsedTimer compile_timer; base::ElapsedTimer compile_timer;
compile_timer.Start(); compile_timer.Start();
Handle<FixedArray> uses_array = info()->isolate()->factory()->NewFixedArray( Handle<HeapNumber> uses_bitset =
static_cast<int>(stdlib_uses_.size())); info()->isolate()->factory()->NewHeapNumberFromBits(
int count = 0; stdlib_uses_.ToIntegral());
for (auto i : stdlib_uses_) {
uses_array->set(count++, Smi::FromInt(i));
}
wasm::ErrorThrower thrower(info()->isolate(), "AsmJs::Compile"); wasm::ErrorThrower thrower(info()->isolate(), "AsmJs::Compile");
Handle<WasmModuleObject> compiled = Handle<WasmModuleObject> compiled =
@ -281,7 +284,7 @@ CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
Handle<FixedArray> result = Handle<FixedArray> result =
info()->isolate()->factory()->NewFixedArray(kWasmDataEntryCount); info()->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled); result->set(kWasmDataCompiledModule, *compiled);
result->set(kWasmDataUsesArray, *uses_array); result->set(kWasmDataUsesBitSet, *uses_bitset);
info()->SetAsmWasmData(result); info()->SetAsmWasmData(result);
info()->SetCode(info()->isolate()->builtins()->InstantiateAsmJs()); info()->SetCode(info()->isolate()->builtins()->InstantiateAsmJs());
@ -302,8 +305,8 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
Handle<JSArrayBuffer> memory) { Handle<JSArrayBuffer> memory) {
base::ElapsedTimer instantiate_timer; base::ElapsedTimer instantiate_timer;
instantiate_timer.Start(); instantiate_timer.Start();
Handle<FixedArray> stdlib_uses( Handle<HeapNumber> uses_bitset(
FixedArray::cast(wasm_data->get(kWasmDataUsesArray))); HeapNumber::cast(wasm_data->get(kWasmDataUsesBitSet)));
Handle<WasmModuleObject> module( Handle<WasmModuleObject> module(
WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule))); WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
Handle<Script> script(Script::cast(shared->script())); Handle<Script> script(Script::cast(shared->script()));
@ -313,16 +316,14 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
// Check that all used stdlib members are valid. // Check that all used stdlib members are valid.
bool stdlib_use_of_typed_array_present = false; bool stdlib_use_of_typed_array_present = false;
for (int i = 0; i < stdlib_uses->length(); ++i) { wasm::AsmJsParser::StdlibSet stdlib_uses(uses_bitset->value_as_bits());
if (!stdlib_uses.IsEmpty()) { // No checking needed if no uses.
if (stdlib.is_null()) { if (stdlib.is_null()) {
ReportInstantiationFailure(script, position, "Requires standard library"); ReportInstantiationFailure(script, position, "Requires standard library");
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }
int member_id = Smi::ToInt(stdlib_uses->get(i)); if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
wasm::AsmJsParser::StandardMember member = &stdlib_use_of_typed_array_present)) {
static_cast<wasm::AsmJsParser::StandardMember>(member_id);
if (!IsStdlibMemberValid(isolate, stdlib, member,
&stdlib_use_of_typed_array_present)) {
ReportInstantiationFailure(script, position, "Unexpected stdlib member"); ReportInstantiationFailure(script, position, "Unexpected stdlib member");
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }

View File

@ -550,7 +550,7 @@ void AsmJsParser::ValidateModuleVarNewStdlib(VarInfo* info) {
#define V(name, _junk1, _junk2, _junk3) \ #define V(name, _junk1, _junk2, _junk3) \
case TOK(name): \ case TOK(name): \
DeclareStdlibFunc(info, VarKind::kSpecial, AsmType::name()); \ DeclareStdlibFunc(info, VarKind::kSpecial, AsmType::name()); \
stdlib_uses_.insert(StandardMember::k##name); \ stdlib_uses_.Add(StandardMember::k##name); \
break; break;
STDLIB_ARRAY_TYPE_LIST(V) STDLIB_ARRAY_TYPE_LIST(V)
#undef V #undef V
@ -573,14 +573,14 @@ void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
case TOK(name): \ case TOK(name): \
DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \ DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
WasmInitExpr(const_value)); \ WasmInitExpr(const_value)); \
stdlib_uses_.insert(StandardMember::kMath##name); \ stdlib_uses_.Add(StandardMember::kMath##name); \
break; break;
STDLIB_MATH_VALUE_LIST(V) STDLIB_MATH_VALUE_LIST(V)
#undef V #undef V
#define V(name, Name, op, sig) \ #define V(name, Name, op, sig) \
case TOK(name): \ case TOK(name): \
DeclareStdlibFunc(info, VarKind::kMath##Name, stdlib_##sig##_); \ DeclareStdlibFunc(info, VarKind::kMath##Name, stdlib_##sig##_); \
stdlib_uses_.insert(StandardMember::kMath##Name); \ stdlib_uses_.Add(StandardMember::kMath##Name); \
break; break;
STDLIB_MATH_FUNCTION_LIST(V) STDLIB_MATH_FUNCTION_LIST(V)
#undef V #undef V
@ -590,11 +590,11 @@ void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
} else if (Check(TOK(Infinity))) { } else if (Check(TOK(Infinity))) {
DeclareGlobal(info, false, AsmType::Double(), kWasmF64, DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
WasmInitExpr(std::numeric_limits<double>::infinity())); WasmInitExpr(std::numeric_limits<double>::infinity()));
stdlib_uses_.insert(StandardMember::kInfinity); stdlib_uses_.Add(StandardMember::kInfinity);
} else if (Check(TOK(NaN))) { } else if (Check(TOK(NaN))) {
DeclareGlobal(info, false, AsmType::Double(), kWasmF64, DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
WasmInitExpr(std::numeric_limits<double>::quiet_NaN())); WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
stdlib_uses_.insert(StandardMember::kNaN); stdlib_uses_.Add(StandardMember::kNaN);
} else { } else {
FAIL("Invalid member of stdlib"); FAIL("Invalid member of stdlib");
} }

View File

@ -47,7 +47,7 @@ class AsmJsParser {
}; };
// clang-format on // clang-format on
typedef std::unordered_set<StandardMember, std::hash<int>> StdlibSet; typedef EnumSet<StandardMember, uint64_t> StdlibSet;
explicit AsmJsParser(Zone* zone, uintptr_t stack_limit, explicit AsmJsParser(Zone* zone, uintptr_t stack_limit,
std::unique_ptr<Utf16CharacterStream> stream); std::unique_ptr<Utf16CharacterStream> stream);
@ -55,7 +55,7 @@ class AsmJsParser {
const char* failure_message() const { return failure_message_; } const char* failure_message() const { return failure_message_; }
int failure_location() const { return failure_location_; } int failure_location() const { return failure_location_; }
WasmModuleBuilder* module_builder() { return module_builder_; } WasmModuleBuilder* module_builder() { return module_builder_; }
StdlibSet* stdlib_uses() { return &stdlib_uses_; } const StdlibSet* stdlib_uses() const { return &stdlib_uses_; }
private: private:
// clang-format off // clang-format off