[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 {
enum WasmDataEntries {
kWasmDataCompiledModule,
kWasmDataUsesArray,
kWasmDataUsesBitSet,
kWasmDataEntryCount,
};
@ -49,62 +49,69 @@ Handle<Object> StdlibMathMember(Isolate* isolate, Handle<JSReceiver> stdlib,
return value;
}
bool IsStdlibMemberValid(Isolate* isolate, Handle<JSReceiver> stdlib,
wasm::AsmJsParser::StandardMember member,
bool* is_typed_array) {
switch (member) {
case wasm::AsmJsParser::StandardMember::kInfinity: {
Handle<Name> name = isolate->factory()->Infinity_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
return value->IsNumber() && std::isinf(value->Number());
}
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); \
bool AreStdlibMembersValid(Isolate* isolate, Handle<JSReceiver> stdlib,
wasm::AsmJsParser::StdlibSet members,
bool* is_typed_array) {
if (members.Contains(wasm::AsmJsParser::StandardMember::kInfinity)) {
members.Remove(wasm::AsmJsParser::StandardMember::kInfinity);
Handle<Name> name = isolate->factory()->Infinity_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
if (!value->IsNumber() || !std::isinf(value->Number())) return false;
}
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
#define STDLIB_MATH_CONST(cname, const_value) \
case wasm::AsmJsParser::StandardMember::kMath##cname: { \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#cname))); \
Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
return value->IsNumber() && value->Number() == const_value; \
#define STDLIB_MATH_CONST(cname, const_value) \
if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##cname)) { \
members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname); \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#cname))); \
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
#define STDLIB_ARRAY_TYPE(fname, FName) \
case wasm::AsmJsParser::StandardMember::k##FName: { \
*is_typed_array = true; \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#FName))); \
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); \
if (!value->IsJSFunction()) return false; \
Handle<JSFunction> func = Handle<JSFunction>::cast(value); \
return func.is_identical_to(isolate->fname()); \
#define STDLIB_ARRAY_TYPE(fname, FName) \
if (members.Contains(wasm::AsmJsParser::StandardMember::k##FName)) { \
members.Remove(wasm::AsmJsParser::StandardMember::k##FName); \
*is_typed_array = true; \
Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#FName))); \
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); \
if (!value->IsJSFunction()) return false; \
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(uint8_array_fun, Uint8Array)
STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
#undef STDLIB_ARRAY_TYPE
}
UNREACHABLE();
// All members accounted for.
DCHECK(members.IsEmpty());
return true;
}
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
// translated to a valid WebAssembly module. The result are two vectors
// 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
// into an internal representation and eventually compiles it to machine
// code.
@ -192,7 +199,6 @@ class AsmJsCompilationJob final : public CompilationJob {
private:
wasm::ZoneBuffer* module_;
wasm::ZoneBuffer* asm_offsets_;
// TODO(mstarzinger) Replace with a bitvector.
wasm::AsmJsParser::StdlibSet stdlib_uses_;
double translate_time_; // Time (milliseconds) taken to execute step [1].
@ -228,7 +234,7 @@ CompilationJob::Status AsmJsCompilationJob::PrepareJobImpl() {
parser.module_builder()->WriteTo(*module_);
asm_offsets_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_);
stdlib_uses_.swap(*parser.stdlib_uses());
stdlib_uses_ = *parser.stdlib_uses();
size_t compile_zone_size =
info()->zone()->allocation_size() - compile_zone_start;
@ -259,12 +265,9 @@ CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
base::ElapsedTimer compile_timer;
compile_timer.Start();
Handle<FixedArray> uses_array = info()->isolate()->factory()->NewFixedArray(
static_cast<int>(stdlib_uses_.size()));
int count = 0;
for (auto i : stdlib_uses_) {
uses_array->set(count++, Smi::FromInt(i));
}
Handle<HeapNumber> uses_bitset =
info()->isolate()->factory()->NewHeapNumberFromBits(
stdlib_uses_.ToIntegral());
wasm::ErrorThrower thrower(info()->isolate(), "AsmJs::Compile");
Handle<WasmModuleObject> compiled =
@ -281,7 +284,7 @@ CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
Handle<FixedArray> result =
info()->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled);
result->set(kWasmDataUsesArray, *uses_array);
result->set(kWasmDataUsesBitSet, *uses_bitset);
info()->SetAsmWasmData(result);
info()->SetCode(info()->isolate()->builtins()->InstantiateAsmJs());
@ -302,8 +305,8 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
Handle<JSArrayBuffer> memory) {
base::ElapsedTimer instantiate_timer;
instantiate_timer.Start();
Handle<FixedArray> stdlib_uses(
FixedArray::cast(wasm_data->get(kWasmDataUsesArray)));
Handle<HeapNumber> uses_bitset(
HeapNumber::cast(wasm_data->get(kWasmDataUsesBitSet)));
Handle<WasmModuleObject> module(
WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
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.
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()) {
ReportInstantiationFailure(script, position, "Requires standard library");
return MaybeHandle<Object>();
}
int member_id = Smi::ToInt(stdlib_uses->get(i));
wasm::AsmJsParser::StandardMember member =
static_cast<wasm::AsmJsParser::StandardMember>(member_id);
if (!IsStdlibMemberValid(isolate, stdlib, member,
&stdlib_use_of_typed_array_present)) {
if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
&stdlib_use_of_typed_array_present)) {
ReportInstantiationFailure(script, position, "Unexpected stdlib member");
return MaybeHandle<Object>();
}

View File

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

View File

@ -47,7 +47,7 @@ class AsmJsParser {
};
// 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,
std::unique_ptr<Utf16CharacterStream> stream);
@ -55,7 +55,7 @@ class AsmJsParser {
const char* failure_message() const { return failure_message_; }
int failure_location() const { return failure_location_; }
WasmModuleBuilder* module_builder() { return module_builder_; }
StdlibSet* stdlib_uses() { return &stdlib_uses_; }
const StdlibSet* stdlib_uses() const { return &stdlib_uses_; }
private:
// clang-format off