blob: 593498e7b20f0fa68ee357dda17375bcfe3e567f [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/asmjs/asm-js.h"
#include "src/api-natives.h"
#include "src/api.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/assert-scope.h"
#include "src/compilation-info.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/objects.h"
#include "src/parsing/parse-info.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
typedef uint8_t byte;
using v8::internal::wasm::ErrorThrower;
namespace v8 {
namespace internal {
namespace {
enum WasmDataEntries {
kWasmDataCompiledModule,
kWasmDataForeignGlobals,
kWasmDataUsesArray,
kWasmDataScript,
kWasmDataScriptPosition,
kWasmDataEntryCount,
};
Handle<i::Object> StdlibMathMember(i::Isolate* isolate,
Handle<JSReceiver> stdlib,
Handle<Name> name) {
if (stdlib.is_null()) {
return Handle<i::Object>();
}
Handle<i::Name> math_name(
isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
MaybeHandle<i::Object> maybe_math = i::Object::GetProperty(stdlib, math_name);
if (maybe_math.is_null()) {
return Handle<i::Object>();
}
Handle<i::Object> math = maybe_math.ToHandleChecked();
if (!math->IsJSReceiver()) {
return Handle<i::Object>();
}
MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(math, name);
if (maybe_value.is_null()) {
return Handle<i::Object>();
}
return maybe_value.ToHandleChecked();
}
bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
Handle<i::Object> member_id) {
int32_t member_kind;
if (!member_id->ToInt32(&member_kind)) {
UNREACHABLE();
}
switch (member_kind) {
case wasm::AsmTyper::StandardMember::kNone:
case wasm::AsmTyper::StandardMember::kModule:
case wasm::AsmTyper::StandardMember::kStdlib:
case wasm::AsmTyper::StandardMember::kHeap:
case wasm::AsmTyper::StandardMember::kFFI: {
// Nothing to check for these.
return true;
}
case wasm::AsmTyper::StandardMember::kInfinity: {
if (stdlib.is_null()) {
return false;
}
Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("Infinity")));
MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
if (maybe_value.is_null()) {
return false;
}
Handle<i::Object> value = maybe_value.ToHandleChecked();
return value->IsNumber() && std::isinf(value->Number());
}
case wasm::AsmTyper::StandardMember::kNaN: {
if (stdlib.is_null()) {
return false;
}
Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("NaN")));
MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
if (maybe_value.is_null()) {
return false;
}
Handle<i::Object> value = maybe_value.ToHandleChecked();
return value->IsNaN();
}
#define STDLIB_MATH_FUNC(CamelName, fname) \
case wasm::AsmTyper::StandardMember::k##CamelName: { \
Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#fname))); \
Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
if (value.is_null() || !value->IsJSFunction()) { \
return false; \
} \
Handle<i::JSFunction> func(i::JSFunction::cast(*value)); \
return func->shared()->code() == \
isolate->builtins()->builtin(Builtins::k##CamelName); \
}
STDLIB_MATH_FUNC(MathAcos, acos)
STDLIB_MATH_FUNC(MathAsin, asin)
STDLIB_MATH_FUNC(MathAtan, atan)
STDLIB_MATH_FUNC(MathCos, cos)
STDLIB_MATH_FUNC(MathSin, sin)
STDLIB_MATH_FUNC(MathTan, tan)
STDLIB_MATH_FUNC(MathExp, exp)
STDLIB_MATH_FUNC(MathLog, log)
STDLIB_MATH_FUNC(MathCeil, ceil)
STDLIB_MATH_FUNC(MathFloor, floor)
STDLIB_MATH_FUNC(MathSqrt, sqrt)
STDLIB_MATH_FUNC(MathAbs, abs)
STDLIB_MATH_FUNC(MathClz32, clz32)
STDLIB_MATH_FUNC(MathMin, min)
STDLIB_MATH_FUNC(MathMax, max)
STDLIB_MATH_FUNC(MathAtan2, atan2)
STDLIB_MATH_FUNC(MathPow, pow)
STDLIB_MATH_FUNC(MathImul, imul)
STDLIB_MATH_FUNC(MathFround, fround)
#undef STDLIB_MATH_FUNC
#define STDLIB_MATH_CONST(cname, const_value) \
case wasm::AsmTyper::StandardMember::kMath##cname: { \
i::Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
STATIC_CHAR_VECTOR(#cname))); \
i::Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
return !value.is_null() && value->IsNumber() && \
value->Number() == const_value; \
}
STDLIB_MATH_CONST(E, 2.718281828459045)
STDLIB_MATH_CONST(LN10, 2.302585092994046)
STDLIB_MATH_CONST(LN2, 0.6931471805599453)
STDLIB_MATH_CONST(LOG2E, 1.4426950408889634)
STDLIB_MATH_CONST(LOG10E, 0.4342944819032518)
STDLIB_MATH_CONST(PI, 3.141592653589793)
STDLIB_MATH_CONST(SQRT1_2, 0.7071067811865476)
STDLIB_MATH_CONST(SQRT2, 1.4142135623730951)
#undef STDLIB_MATH_CONST
default: { UNREACHABLE(); }
}
return false;
}
} // namespace
MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
base::ElapsedTimer asm_wasm_timer;
asm_wasm_timer.Start();
wasm::AsmWasmBuilder builder(info);
Handle<FixedArray> foreign_globals;
auto asm_wasm_result = builder.Run(&foreign_globals);
if (!asm_wasm_result.success) {
DCHECK(!info->isolate()->has_pending_exception());
if (!FLAG_suppress_asm_messages) {
MessageHandler::ReportMessage(info->isolate(),
builder.typer()->message_location(),
builder.typer()->error_message());
}
return MaybeHandle<FixedArray>();
}
double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
static_cast<int>(asm_offsets->size()));
base::ElapsedTimer compile_timer;
compile_timer.Start();
MaybeHandle<JSObject> compiled = wasm::CreateModuleObjectFromBytes(
info->isolate(), module->begin(), module->end(), &thrower,
internal::wasm::kAsmJsOrigin, info->script(), asm_offsets_vec);
DCHECK(!compiled.is_null());
double compile_time = compile_timer.Elapsed().InMillisecondsF();
wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
Handle<FixedArray> uses_array =
info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
int count = 0;
for (auto i : uses) {
uses_array->set(count++, Smi::FromInt(i));
}
Handle<FixedArray> result =
info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
result->set(kWasmDataForeignGlobals, *foreign_globals);
result->set(kWasmDataUsesArray, *uses_array);
result->set(kWasmDataScript, *info->script());
result->set(kWasmDataScriptPosition,
Smi::FromInt(info->literal()->position()));
MessageLocation location(info->script(), info->literal()->position(),
info->literal()->position());
char text[100];
int length = base::OS::SNPrintF(
text, arraysize(text), "success, asm->wasm: %0.3f ms, compile: %0.3f ms",
asm_wasm_time, compile_time);
DCHECK_NE(-1, length);
USE(length);
Handle<String> stext(info->isolate()->factory()->InternalizeUtf8String(text));
Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
info->isolate(), MessageTemplate::kAsmJsCompiled, &location, stext,
Handle<JSArray>::null());
message->set_error_level(v8::Isolate::kMessageInfo);
if (FLAG_trace_asm_time && !FLAG_suppress_asm_messages) {
MessageHandler::ReportMessage(info->isolate(), &location, message);
}
return result;
}
bool AsmJs::IsStdlibValid(i::Isolate* isolate, Handle<FixedArray> wasm_data,
Handle<JSReceiver> stdlib) {
i::Handle<i::FixedArray> uses(
i::FixedArray::cast(wasm_data->get(kWasmDataUsesArray)));
for (int i = 0; i < uses->length(); ++i) {
if (!IsStdlibMemberValid(isolate, stdlib,
uses->GetValueChecked<i::Object>(isolate, i))) {
return false;
}
}
return true;
}
MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
Handle<FixedArray> wasm_data,
Handle<JSArrayBuffer> memory,
Handle<JSReceiver> foreign) {
base::ElapsedTimer instantiate_timer;
instantiate_timer.Start();
i::Handle<i::JSObject> module(
i::JSObject::cast(wasm_data->get(kWasmDataCompiledModule)));
i::Handle<i::FixedArray> foreign_globals(
i::FixedArray::cast(wasm_data->get(kWasmDataForeignGlobals)));
ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
// Create the ffi object for foreign functions {"": foreign}.
Handle<JSObject> ffi_object;
if (!foreign.is_null()) {
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate->native_context()->object_function(), isolate);
ffi_object = isolate->factory()->NewJSObject(object_function);
JSObject::AddProperty(ffi_object, isolate->factory()->empty_string(),
foreign, NONE);
}
i::MaybeHandle<i::JSObject> maybe_module_object =
i::wasm::WasmModule::Instantiate(isolate, &thrower, module, ffi_object,
memory);
if (maybe_module_object.is_null()) {
return MaybeHandle<Object>();
}
i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
wasm::AsmWasmBuilder::foreign_init_name));
i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
i::MaybeHandle<i::Object> maybe_init =
i::Object::GetProperty(module_object, init_name);
DCHECK(!maybe_init.is_null());
i::Handle<i::Object> init = maybe_init.ToHandleChecked();
i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
i::Handle<i::Object>* foreign_args_array =
new i::Handle<i::Object>[foreign_globals->length()];
for (int j = 0; j < foreign_globals->length(); j++) {
if (!foreign.is_null()) {
i::MaybeHandle<i::Name> name = i::Object::ToName(
isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
if (!name.is_null()) {
i::MaybeHandle<i::Object> val =
i::Object::GetProperty(foreign, name.ToHandleChecked());
if (!val.is_null()) {
foreign_args_array[j] = val.ToHandleChecked();
continue;
}
}
}
foreign_args_array[j] = undefined;
}
i::MaybeHandle<i::Object> retval = i::Execution::Call(
isolate, init, undefined, foreign_globals->length(), foreign_args_array);
delete[] foreign_args_array;
DCHECK(!retval.is_null());
i::Handle<i::Name> single_function_name(
isolate->factory()->InternalizeUtf8String(
wasm::AsmWasmBuilder::single_function_name));
i::MaybeHandle<i::Object> single_function =
i::Object::GetProperty(module_object, single_function_name);
if (!single_function.is_null() &&
!single_function.ToHandleChecked()->IsUndefined(isolate)) {
return single_function;
}
i::Handle<i::Script> script(i::Script::cast(wasm_data->get(kWasmDataScript)));
int32_t position = 0;
if (!wasm_data->get(kWasmDataScriptPosition)->ToInt32(&position)) {
UNREACHABLE();
}
MessageLocation location(script, position, position);
char text[50];
int length =
base::OS::SNPrintF(text, arraysize(text), "success, %0.3f ms",
instantiate_timer.Elapsed().InMillisecondsF());
DCHECK_NE(-1, length);
USE(length);
Handle<String> stext(isolate->factory()->InternalizeUtf8String(text));
Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
isolate, MessageTemplate::kAsmJsInstantiated, &location, stext,
Handle<JSArray>::null());
message->set_error_level(v8::Isolate::kMessageInfo);
if (FLAG_trace_asm_time && !FLAG_suppress_asm_messages) {
MessageHandler::ReportMessage(isolate, &location, message);
}
return module_object;
}
} // namespace internal
} // namespace v8