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 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 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 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 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 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