1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/asmjs/asm-js.h"
6
7 #include "src/api-natives.h"
8 #include "src/api.h"
9 #include "src/asmjs/asm-typer.h"
10 #include "src/asmjs/asm-wasm-builder.h"
11 #include "src/assert-scope.h"
12 #include "src/base/platform/elapsed-timer.h"
13 #include "src/compilation-info.h"
14 #include "src/execution.h"
15 #include "src/factory.h"
16 #include "src/handles.h"
17 #include "src/isolate.h"
18 #include "src/objects-inl.h"
19 #include "src/objects.h"
20 #include "src/parsing/parse-info.h"
21
22 #include "src/wasm/module-decoder.h"
23 #include "src/wasm/wasm-js.h"
24 #include "src/wasm/wasm-module-builder.h"
25 #include "src/wasm/wasm-module.h"
26 #include "src/wasm/wasm-objects.h"
27 #include "src/wasm/wasm-result.h"
28
29 typedef uint8_t byte;
30
31 using v8::internal::wasm::ErrorThrower;
32
33 namespace v8 {
34 namespace internal {
35
36 namespace {
37 enum WasmDataEntries {
38 kWasmDataCompiledModule,
39 kWasmDataForeignGlobals,
40 kWasmDataUsesArray,
41 kWasmDataScript,
42 kWasmDataScriptPosition,
43 kWasmDataEntryCount,
44 };
45
StdlibMathMember(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<Name> name)46 Handle<i::Object> StdlibMathMember(i::Isolate* isolate,
47 Handle<JSReceiver> stdlib,
48 Handle<Name> name) {
49 if (stdlib.is_null()) {
50 return Handle<i::Object>();
51 }
52 Handle<i::Name> math_name(
53 isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
54 MaybeHandle<i::Object> maybe_math = i::Object::GetProperty(stdlib, math_name);
55 if (maybe_math.is_null()) {
56 return Handle<i::Object>();
57 }
58 Handle<i::Object> math = maybe_math.ToHandleChecked();
59 if (!math->IsJSReceiver()) {
60 return Handle<i::Object>();
61 }
62 MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(math, name);
63 if (maybe_value.is_null()) {
64 return Handle<i::Object>();
65 }
66 return maybe_value.ToHandleChecked();
67 }
68
IsStdlibMemberValid(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<i::Object> member_id)69 bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
70 Handle<i::Object> member_id) {
71 int32_t member_kind;
72 if (!member_id->ToInt32(&member_kind)) {
73 UNREACHABLE();
74 }
75 switch (member_kind) {
76 case wasm::AsmTyper::StandardMember::kNone:
77 case wasm::AsmTyper::StandardMember::kModule:
78 case wasm::AsmTyper::StandardMember::kStdlib:
79 case wasm::AsmTyper::StandardMember::kHeap:
80 case wasm::AsmTyper::StandardMember::kFFI: {
81 // Nothing to check for these.
82 return true;
83 }
84 case wasm::AsmTyper::StandardMember::kInfinity: {
85 if (stdlib.is_null()) {
86 return false;
87 }
88 Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
89 STATIC_CHAR_VECTOR("Infinity")));
90 MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
91 if (maybe_value.is_null()) {
92 return false;
93 }
94 Handle<i::Object> value = maybe_value.ToHandleChecked();
95 return value->IsNumber() && std::isinf(value->Number());
96 }
97 case wasm::AsmTyper::StandardMember::kNaN: {
98 if (stdlib.is_null()) {
99 return false;
100 }
101 Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
102 STATIC_CHAR_VECTOR("NaN")));
103 MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
104 if (maybe_value.is_null()) {
105 return false;
106 }
107 Handle<i::Object> value = maybe_value.ToHandleChecked();
108 return value->IsNaN();
109 }
110 #define STDLIB_MATH_FUNC(CamelName, fname) \
111 case wasm::AsmTyper::StandardMember::k##CamelName: { \
112 Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
113 STATIC_CHAR_VECTOR(#fname))); \
114 Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
115 if (value.is_null() || !value->IsJSFunction()) { \
116 return false; \
117 } \
118 Handle<i::JSFunction> func(i::JSFunction::cast(*value)); \
119 return func->shared()->code() == \
120 isolate->builtins()->builtin(Builtins::k##CamelName); \
121 }
122 STDLIB_MATH_FUNC(MathAcos, acos)
123 STDLIB_MATH_FUNC(MathAsin, asin)
124 STDLIB_MATH_FUNC(MathAtan, atan)
125 STDLIB_MATH_FUNC(MathCos, cos)
126 STDLIB_MATH_FUNC(MathSin, sin)
127 STDLIB_MATH_FUNC(MathTan, tan)
128 STDLIB_MATH_FUNC(MathExp, exp)
129 STDLIB_MATH_FUNC(MathLog, log)
130 STDLIB_MATH_FUNC(MathCeil, ceil)
131 STDLIB_MATH_FUNC(MathFloor, floor)
132 STDLIB_MATH_FUNC(MathSqrt, sqrt)
133 STDLIB_MATH_FUNC(MathAbs, abs)
134 STDLIB_MATH_FUNC(MathClz32, clz32)
135 STDLIB_MATH_FUNC(MathMin, min)
136 STDLIB_MATH_FUNC(MathMax, max)
137 STDLIB_MATH_FUNC(MathAtan2, atan2)
138 STDLIB_MATH_FUNC(MathPow, pow)
139 STDLIB_MATH_FUNC(MathImul, imul)
140 STDLIB_MATH_FUNC(MathFround, fround)
141 #undef STDLIB_MATH_FUNC
142 #define STDLIB_MATH_CONST(cname, const_value) \
143 case wasm::AsmTyper::StandardMember::kMath##cname: { \
144 i::Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
145 STATIC_CHAR_VECTOR(#cname))); \
146 i::Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
147 return !value.is_null() && value->IsNumber() && \
148 value->Number() == const_value; \
149 }
150 STDLIB_MATH_CONST(E, 2.718281828459045)
151 STDLIB_MATH_CONST(LN10, 2.302585092994046)
152 STDLIB_MATH_CONST(LN2, 0.6931471805599453)
153 STDLIB_MATH_CONST(LOG2E, 1.4426950408889634)
154 STDLIB_MATH_CONST(LOG10E, 0.4342944819032518)
155 STDLIB_MATH_CONST(PI, 3.141592653589793)
156 STDLIB_MATH_CONST(SQRT1_2, 0.7071067811865476)
157 STDLIB_MATH_CONST(SQRT2, 1.4142135623730951)
158 #undef STDLIB_MATH_CONST
159 default: { UNREACHABLE(); }
160 }
161 return false;
162 }
163
164 } // namespace
165
CompileAsmViaWasm(CompilationInfo * info)166 MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
167 ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
168 base::ElapsedTimer asm_wasm_timer;
169 asm_wasm_timer.Start();
170 wasm::AsmWasmBuilder builder(info);
171 Handle<FixedArray> foreign_globals;
172 auto asm_wasm_result = builder.Run(&foreign_globals);
173 if (!asm_wasm_result.success) {
174 DCHECK(!info->isolate()->has_pending_exception());
175 if (!FLAG_suppress_asm_messages) {
176 MessageHandler::ReportMessage(info->isolate(),
177 builder.typer()->message_location(),
178 builder.typer()->error_message());
179 }
180 return MaybeHandle<FixedArray>();
181 }
182 double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
183
184 wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
185 wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
186 Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
187 static_cast<int>(asm_offsets->size()));
188
189 base::ElapsedTimer compile_timer;
190 compile_timer.Start();
191 MaybeHandle<JSObject> compiled = SyncCompileTranslatedAsmJs(
192 info->isolate(), &thrower,
193 wasm::ModuleWireBytes(module->begin(), module->end()), info->script(),
194 asm_offsets_vec);
195 DCHECK(!compiled.is_null());
196 double compile_time = compile_timer.Elapsed().InMillisecondsF();
197 DCHECK_GE(module->end(), module->begin());
198 uintptr_t wasm_size = module->end() - module->begin();
199
200 wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
201 Handle<FixedArray> uses_array =
202 info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
203 int count = 0;
204 for (auto i : uses) {
205 uses_array->set(count++, Smi::FromInt(i));
206 }
207
208 Handle<FixedArray> result =
209 info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
210 result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
211 result->set(kWasmDataForeignGlobals, *foreign_globals);
212 result->set(kWasmDataUsesArray, *uses_array);
213 result->set(kWasmDataScript, *info->script());
214 result->set(kWasmDataScriptPosition,
215 Smi::FromInt(info->literal()->position()));
216
217 MessageLocation location(info->script(), info->literal()->position(),
218 info->literal()->position());
219 char text[100];
220 int length;
221 if (FLAG_predictable) {
222 length = base::OS::SNPrintF(text, arraysize(text), "success");
223 } else {
224 length = base::OS::SNPrintF(
225 text, arraysize(text),
226 "success, asm->wasm: %0.3f ms, compile: %0.3f ms, %" PRIuPTR " bytes",
227 asm_wasm_time, compile_time, wasm_size);
228 }
229 DCHECK_NE(-1, length);
230 USE(length);
231 Handle<String> stext(info->isolate()->factory()->InternalizeUtf8String(text));
232 Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
233 info->isolate(), MessageTemplate::kAsmJsCompiled, &location, stext,
234 Handle<JSArray>::null());
235 message->set_error_level(v8::Isolate::kMessageInfo);
236 if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
237 MessageHandler::ReportMessage(info->isolate(), &location, message);
238 }
239
240 return result;
241 }
242
IsStdlibValid(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSReceiver> stdlib)243 bool AsmJs::IsStdlibValid(i::Isolate* isolate, Handle<FixedArray> wasm_data,
244 Handle<JSReceiver> stdlib) {
245 i::Handle<i::FixedArray> uses(
246 i::FixedArray::cast(wasm_data->get(kWasmDataUsesArray)));
247 for (int i = 0; i < uses->length(); ++i) {
248 if (!IsStdlibMemberValid(isolate, stdlib,
249 uses->GetValueChecked<i::Object>(isolate, i))) {
250 return false;
251 }
252 }
253 return true;
254 }
255
InstantiateAsmWasm(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSArrayBuffer> memory,Handle<JSReceiver> foreign)256 MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
257 Handle<FixedArray> wasm_data,
258 Handle<JSArrayBuffer> memory,
259 Handle<JSReceiver> foreign) {
260 base::ElapsedTimer instantiate_timer;
261 instantiate_timer.Start();
262 i::Handle<i::WasmModuleObject> module(
263 i::WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
264 i::Handle<i::FixedArray> foreign_globals(
265 i::FixedArray::cast(wasm_data->get(kWasmDataForeignGlobals)));
266
267 ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
268
269 // Create the ffi object for foreign functions {"": foreign}.
270 Handle<JSObject> ffi_object;
271 if (!foreign.is_null()) {
272 Handle<JSFunction> object_function = Handle<JSFunction>(
273 isolate->native_context()->object_function(), isolate);
274 ffi_object = isolate->factory()->NewJSObject(object_function);
275 JSObject::AddProperty(ffi_object, isolate->factory()->empty_string(),
276 foreign, NONE);
277 }
278
279 i::MaybeHandle<i::Object> maybe_module_object =
280 i::wasm::SyncInstantiate(isolate, &thrower, module, ffi_object, memory);
281 if (maybe_module_object.is_null()) {
282 return MaybeHandle<Object>();
283 }
284 i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
285
286 i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
287 wasm::AsmWasmBuilder::foreign_init_name));
288 i::Handle<i::Object> init =
289 i::Object::GetProperty(module_object, init_name).ToHandleChecked();
290
291 i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
292 i::Handle<i::Object>* foreign_args_array =
293 new i::Handle<i::Object>[foreign_globals->length()];
294 for (int j = 0; j < foreign_globals->length(); j++) {
295 if (!foreign.is_null()) {
296 i::MaybeHandle<i::Name> name = i::Object::ToName(
297 isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
298 if (!name.is_null()) {
299 i::MaybeHandle<i::Object> val =
300 i::Object::GetProperty(foreign, name.ToHandleChecked());
301 if (!val.is_null()) {
302 foreign_args_array[j] = val.ToHandleChecked();
303 continue;
304 }
305 }
306 }
307 foreign_args_array[j] = undefined;
308 }
309 i::MaybeHandle<i::Object> retval = i::Execution::Call(
310 isolate, init, undefined, foreign_globals->length(), foreign_args_array);
311 delete[] foreign_args_array;
312 DCHECK(!retval.is_null());
313
314 i::Handle<i::Name> single_function_name(
315 isolate->factory()->InternalizeUtf8String(
316 wasm::AsmWasmBuilder::single_function_name));
317 i::MaybeHandle<i::Object> single_function =
318 i::Object::GetProperty(module_object, single_function_name);
319 if (!single_function.is_null() &&
320 !single_function.ToHandleChecked()->IsUndefined(isolate)) {
321 return single_function;
322 }
323
324 i::Handle<i::Script> script(i::Script::cast(wasm_data->get(kWasmDataScript)));
325 int32_t position = 0;
326 if (!wasm_data->get(kWasmDataScriptPosition)->ToInt32(&position)) {
327 UNREACHABLE();
328 }
329 MessageLocation location(script, position, position);
330 char text[50];
331 int length;
332 if (FLAG_predictable) {
333 length = base::OS::SNPrintF(text, arraysize(text), "success");
334 } else {
335 length = base::OS::SNPrintF(text, arraysize(text), "success, %0.3f ms",
336 instantiate_timer.Elapsed().InMillisecondsF());
337 }
338 DCHECK_NE(-1, length);
339 USE(length);
340 Handle<String> stext(isolate->factory()->InternalizeUtf8String(text));
341 Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
342 isolate, MessageTemplate::kAsmJsInstantiated, &location, stext,
343 Handle<JSArray>::null());
344 message->set_error_level(v8::Isolate::kMessageInfo);
345 if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
346 MessageHandler::ReportMessage(isolate, &location, message);
347 }
348
349 Handle<String> exports_name =
350 isolate->factory()->InternalizeUtf8String("exports");
351 return i::Object::GetProperty(module_object, exports_name);
352 }
353
354 } // namespace internal
355 } // namespace v8
356