js/src/jit/AsmJSLink.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:6537cf88d223
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jit/AsmJSLink.h"
8
9 #include "mozilla/BinarySearch.h"
10 #include "mozilla/PodOperations.h"
11
12 #ifdef MOZ_VTUNE
13 # include "vtune/VTuneWrapper.h"
14 #endif
15
16 #include "jscntxt.h"
17 #include "jsmath.h"
18 #include "jsprf.h"
19 #include "jswrapper.h"
20
21 #include "frontend/BytecodeCompiler.h"
22 #include "jit/AsmJSModule.h"
23 #include "jit/Ion.h"
24 #include "jit/JitCommon.h"
25 #ifdef JS_ION_PERF
26 # include "jit/PerfSpewer.h"
27 #endif
28 #include "vm/StringBuffer.h"
29
30 #include "jsobjinlines.h"
31
32 using namespace js;
33 using namespace js::jit;
34
35 using mozilla::BinarySearch;
36 using mozilla::IsNaN;
37 using mozilla::PodZero;
38
39 AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
40 {
41 if (!activation || activation->isInterruptedSP()) {
42 PodZero(this);
43 JS_ASSERT(done());
44 return;
45 }
46
47 module_ = &activation->module();
48 sp_ = activation->exitSP();
49
50 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
51 // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
52 // to C++. Since the call instruction pushes the return address, we know
53 // that the return address is 1 word below exitSP.
54 returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
55 #else
56 // For calls to Ion/C++ on ARM, the *caller* pushes the return address on
57 // the stack. For Ion, this is just part of the ABI. For C++, the return
58 // address is explicitly pushed before the call since we cannot expect the
59 // callee to immediately push lr. This means that exitSP points to the
60 // return address.
61 returnAddress_ = *(uint8_t**)sp_;
62 #endif
63
64 settle();
65 }
66
67 struct GetCallSite
68 {
69 const AsmJSModule &module;
70 GetCallSite(const AsmJSModule &module) : module(module) {}
71 uint32_t operator[](size_t index) const {
72 return module.callSite(index).returnAddressOffset();
73 }
74 };
75
76 void
77 AsmJSFrameIterator::popFrame()
78 {
79 // After adding stackDepth, sp points to the word before the return address,
80 // on both ARM and x86/x64.
81 sp_ += callsite_->stackDepth();
82 returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
83 }
84
85 void
86 AsmJSFrameIterator::settle()
87 {
88 while (true) {
89 uint32_t target = returnAddress_ - module_->codeBase();
90 size_t lowerBound = 0;
91 size_t upperBound = module_->numCallSites();
92
93 size_t match;
94 if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
95 callsite_ = nullptr;
96 return;
97 }
98
99 callsite_ = &module_->callSite(match);
100
101 if (callsite_->isExit()) {
102 popFrame();
103 continue;
104 }
105
106 if (callsite_->isEntry()) {
107 callsite_ = nullptr;
108 return;
109 }
110
111 JS_ASSERT(callsite_->isNormal());
112 return;
113 }
114 }
115
116 JSAtom *
117 AsmJSFrameIterator::functionDisplayAtom() const
118 {
119 JS_ASSERT(!done());
120 return module_->functionName(callsite_->functionNameIndex());
121 }
122
123 unsigned
124 AsmJSFrameIterator::computeLine(uint32_t *column) const
125 {
126 JS_ASSERT(!done());
127 if (column)
128 *column = callsite_->column();
129 return callsite_->line();
130 }
131
132 static bool
133 CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
134 {
135 ScopedJSDeletePtr<AsmJSModule> module;
136 if (!moduleObj->module().clone(cx, &module))
137 return false;
138
139 module->staticallyLink(cx);
140
141 AsmJSModuleObject *newModuleObj = AsmJSModuleObject::create(cx, &module);
142 if (!newModuleObj)
143 return false;
144
145 moduleObj.set(newModuleObj);
146 return true;
147 }
148
149 static bool
150 LinkFail(JSContext *cx, const char *str)
151 {
152 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
153 nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
154 return false;
155 }
156
157 static bool
158 GetDataProperty(JSContext *cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
159 {
160 if (!objVal.isObject())
161 return LinkFail(cx, "accessing property of non-object");
162
163 Rooted<JSPropertyDescriptor> desc(cx);
164 RootedObject obj(cx, &objVal.toObject());
165 RootedId id(cx, NameToId(field));
166 if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
167 return false;
168
169 if (!desc.object())
170 return LinkFail(cx, "property not present on object");
171
172 if (desc.hasGetterOrSetterObject())
173 return LinkFail(cx, "property is not a data property");
174
175 v.set(desc.value());
176 return true;
177 }
178
179 static bool
180 ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
181 HandleValue importVal)
182 {
183 JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
184
185 void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());
186
187 switch (global.varInitKind()) {
188 case AsmJSModule::Global::InitConstant: {
189 const Value &v = global.varInitConstant();
190 switch (global.varInitCoercion()) {
191 case AsmJS_ToInt32:
192 *(int32_t *)datum = v.toInt32();
193 break;
194 case AsmJS_ToNumber:
195 *(double *)datum = v.toDouble();
196 break;
197 case AsmJS_FRound:
198 *(float *)datum = static_cast<float>(v.toDouble());
199 break;
200 }
201 break;
202 }
203 case AsmJSModule::Global::InitImport: {
204 RootedPropertyName field(cx, global.varImportField());
205 RootedValue v(cx);
206 if (!GetDataProperty(cx, importVal, field, &v))
207 return false;
208
209 switch (global.varInitCoercion()) {
210 case AsmJS_ToInt32:
211 if (!ToInt32(cx, v, (int32_t *)datum))
212 return false;
213 break;
214 case AsmJS_ToNumber:
215 if (!ToNumber(cx, v, (double *)datum))
216 return false;
217 break;
218 case AsmJS_FRound:
219 if (!RoundFloat32(cx, v, (float *)datum))
220 return false;
221 break;
222 }
223 break;
224 }
225 }
226
227 return true;
228 }
229
230 static bool
231 ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal,
232 AutoObjectVector *ffis)
233 {
234 RootedPropertyName field(cx, global.ffiField());
235 RootedValue v(cx);
236 if (!GetDataProperty(cx, importVal, field, &v))
237 return false;
238
239 if (!v.isObject() || !v.toObject().is<JSFunction>())
240 return LinkFail(cx, "FFI imports must be functions");
241
242 (*ffis)[global.ffiIndex()] = &v.toObject().as<JSFunction>();
243 return true;
244 }
245
246 static bool
247 ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
248 HandleValue bufferVal)
249 {
250 RootedPropertyName field(cx, global.viewName());
251 RootedValue v(cx);
252 if (!GetDataProperty(cx, globalVal, field, &v))
253 return false;
254
255 if (!IsTypedArrayConstructor(v, global.viewType()))
256 return LinkFail(cx, "bad typed array constructor");
257
258 return true;
259 }
260
261 static bool
262 ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
263 {
264 RootedValue v(cx);
265 if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
266 return false;
267 RootedPropertyName field(cx, global.mathName());
268 if (!GetDataProperty(cx, v, field, &v))
269 return false;
270
271 Native native = nullptr;
272 switch (global.mathBuiltinFunction()) {
273 case AsmJSMathBuiltin_sin: native = math_sin; break;
274 case AsmJSMathBuiltin_cos: native = math_cos; break;
275 case AsmJSMathBuiltin_tan: native = math_tan; break;
276 case AsmJSMathBuiltin_asin: native = math_asin; break;
277 case AsmJSMathBuiltin_acos: native = math_acos; break;
278 case AsmJSMathBuiltin_atan: native = math_atan; break;
279 case AsmJSMathBuiltin_ceil: native = math_ceil; break;
280 case AsmJSMathBuiltin_floor: native = math_floor; break;
281 case AsmJSMathBuiltin_exp: native = math_exp; break;
282 case AsmJSMathBuiltin_log: native = math_log; break;
283 case AsmJSMathBuiltin_pow: native = js_math_pow; break;
284 case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break;
285 case AsmJSMathBuiltin_min: native = js_math_min; break;
286 case AsmJSMathBuiltin_max: native = js_math_max; break;
287 case AsmJSMathBuiltin_abs: native = js_math_abs; break;
288 case AsmJSMathBuiltin_atan2: native = math_atan2; break;
289 case AsmJSMathBuiltin_imul: native = math_imul; break;
290 case AsmJSMathBuiltin_fround: native = math_fround; break;
291 }
292
293 if (!IsNativeFunction(v, native))
294 return LinkFail(cx, "bad Math.* builtin function");
295
296 return true;
297 }
298
299 static bool
300 ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
301 {
302 RootedPropertyName field(cx, global.constantName());
303 RootedValue v(cx, globalVal);
304
305 if (global.constantKind() == AsmJSModule::Global::MathConstant) {
306 if (!GetDataProperty(cx, v, cx->names().Math, &v))
307 return false;
308 }
309
310 if (!GetDataProperty(cx, v, field, &v))
311 return false;
312 if (!v.isNumber())
313 return LinkFail(cx, "math / global constant value needs to be a number");
314
315 // NaN != NaN
316 if (IsNaN(global.constantValue())) {
317 if (!IsNaN(v.toNumber()))
318 return LinkFail(cx, "global constant value needs to be NaN");
319 } else {
320 if (v.toNumber() != global.constantValue())
321 return LinkFail(cx, "global constant value mismatch");
322 }
323
324 return true;
325 }
326
327 static bool
328 LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
329 {
330 uint32_t heapLength = heap->byteLength();
331 if (!IsValidAsmJSHeapLength(heapLength)) {
332 ScopedJSFreePtr<char> msg(
333 JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
334 "valid length is 0x%x",
335 heapLength,
336 RoundUpToNextValidAsmJSHeapLength(heapLength)));
337 return LinkFail(cx, msg.get());
338 }
339
340 // This check is sufficient without considering the size of the loaded datum because heap
341 // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
342 JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
343 if (heapLength < module.minHeapLength()) {
344 ScopedJSFreePtr<char> msg(
345 JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
346 "largest constant heap access offset rounded up to the next valid "
347 "heap size).",
348 heapLength,
349 module.minHeapLength()));
350 return LinkFail(cx, msg.get());
351 }
352
353 if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
354 return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
355
356 module.initHeap(heap, cx);
357 return true;
358 }
359
360 static bool
361 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
362 {
363 module.setIsDynamicallyLinked();
364
365 RootedValue globalVal(cx);
366 if (args.length() > 0)
367 globalVal = args[0];
368
369 RootedValue importVal(cx);
370 if (args.length() > 1)
371 importVal = args[1];
372
373 RootedValue bufferVal(cx);
374 if (args.length() > 2)
375 bufferVal = args[2];
376
377 Rooted<ArrayBufferObject*> heap(cx);
378 if (module.hasArrayView()) {
379 if (!IsTypedArrayBuffer(bufferVal))
380 return LinkFail(cx, "bad ArrayBuffer argument");
381
382 heap = &AsTypedArrayBuffer(bufferVal);
383 if (!LinkModuleToHeap(cx, module, heap))
384 return false;
385 }
386
387 AutoObjectVector ffis(cx);
388 if (!ffis.resize(module.numFFIs()))
389 return false;
390
391 for (unsigned i = 0; i < module.numGlobals(); i++) {
392 AsmJSModule::Global &global = module.global(i);
393 switch (global.which()) {
394 case AsmJSModule::Global::Variable:
395 if (!ValidateGlobalVariable(cx, module, global, importVal))
396 return false;
397 break;
398 case AsmJSModule::Global::FFI:
399 if (!ValidateFFI(cx, global, importVal, &ffis))
400 return false;
401 break;
402 case AsmJSModule::Global::ArrayView:
403 if (!ValidateArrayView(cx, global, globalVal, bufferVal))
404 return false;
405 break;
406 case AsmJSModule::Global::MathBuiltinFunction:
407 if (!ValidateMathBuiltinFunction(cx, global, globalVal))
408 return false;
409 break;
410 case AsmJSModule::Global::Constant:
411 if (!ValidateConstant(cx, global, globalVal))
412 return false;
413 break;
414 }
415 }
416
417 for (unsigned i = 0; i < module.numExits(); i++)
418 module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
419
420 return true;
421 }
422
423 static const unsigned ASM_MODULE_SLOT = 0;
424 static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
425
426 static unsigned
427 FunctionToExportedFunctionIndex(HandleFunction fun)
428 {
429 Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
430 return v.toInt32();
431 }
432
433 static const AsmJSModule::ExportedFunction &
434 FunctionToExportedFunction(HandleFunction fun, AsmJSModule &module)
435 {
436 unsigned funIndex = FunctionToExportedFunctionIndex(fun);
437 return module.exportedFunction(funIndex);
438 }
439
440 static AsmJSModule &
441 FunctionToEnclosingModule(HandleFunction fun)
442 {
443 return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
444 }
445
446 // The JSNative for the functions nested in an asm.js module. Calling this
447 // native will trampoline into generated code.
448 static bool
449 CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
450 {
451 CallArgs callArgs = CallArgsFromVp(argc, vp);
452 RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());
453
454 // An asm.js function stores, in its extended slots:
455 // - a pointer to the module from which it was returned
456 // - its index in the ordered list of exported functions
457 AsmJSModule &module = FunctionToEnclosingModule(callee);
458
459 // An exported function points to the code as well as the exported
460 // function's signature, which implies the dynamic coercions performed on
461 // the arguments.
462 const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);
463
464 // The calling convention for an external call into asm.js is to pass an
465 // array of 8-byte values where each value contains either a coerced int32
466 // (in the low word) or double value, with the coercions specified by the
467 // asm.js signature. The external entry point unpacks this array into the
468 // system-ABI-specified registers and stack memory and then calls into the
469 // internal entry point. The return value is stored in the first element of
470 // the array (which, therefore, must have length >= 1).
471
472 js::Vector<uint64_t, 8> coercedArgs(cx);
473 if (!coercedArgs.resize(Max<size_t>(1, func.numArgs())))
474 return false;
475
476 RootedValue v(cx);
477 for (unsigned i = 0; i < func.numArgs(); ++i) {
478 v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
479 switch (func.argCoercion(i)) {
480 case AsmJS_ToInt32:
481 if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
482 return false;
483 break;
484 case AsmJS_ToNumber:
485 if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
486 return false;
487 break;
488 case AsmJS_FRound:
489 if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
490 return false;
491 break;
492 }
493 }
494
495 // An asm.js module is specialized to its heap's base address and length
496 // which is normally immutable except for the neuter operation that occurs
497 // when an ArrayBuffer is transfered. Throw an internal error if we're
498 // about to run with a neutered heap.
499 if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
500 js_ReportOverRecursed(cx);
501 return false;
502 }
503
504 {
505 // Push an AsmJSActivation to describe the asm.js frames we're about to
506 // push when running this module. Additionally, push a JitActivation so
507 // that the optimized asm.js-to-Ion FFI call path (which we want to be
508 // very fast) can avoid doing so. The JitActivation is marked as
509 // inactive so stack iteration will skip over it.
510 AsmJSActivation activation(cx, module);
511 JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false);
512
513 // Call the per-exported-function trampoline created by GenerateEntry.
514 AsmJSModule::CodePtr enter = module.entryTrampoline(func);
515 if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
516 return false;
517 }
518
519 switch (func.returnType()) {
520 case AsmJSModule::Return_Void:
521 callArgs.rval().set(UndefinedValue());
522 break;
523 case AsmJSModule::Return_Int32:
524 callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
525 break;
526 case AsmJSModule::Return_Double:
527 callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
528 break;
529 }
530
531 return true;
532 }
533
534 static JSFunction *
535 NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func,
536 HandleObject moduleObj, unsigned exportIndex)
537 {
538 RootedPropertyName name(cx, func.name());
539 JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(),
540 JSFunction::NATIVE_FUN, cx->global(), name,
541 JSFunction::ExtendedFinalizeKind);
542 if (!fun)
543 return nullptr;
544
545 fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj));
546 fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
547 return fun;
548 }
549
550 static bool
551 HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
552 {
553 if (cx->isExceptionPending())
554 return false;
555
556 uint32_t begin = module.offsetToEndOfUseAsm();
557 uint32_t end = module.funcEndBeforeCurly();
558 Rooted<JSFlatString*> src(cx, module.scriptSource()->substring(cx, begin, end));
559 if (!src)
560 return false;
561
562 RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
563 cx->global(), name, JSFunction::FinalizeKind,
564 TenuredObject));
565 if (!fun)
566 return false;
567
568 AutoNameVector formals(cx);
569 formals.reserve(3);
570 if (module.globalArgumentName())
571 formals.infallibleAppend(module.globalArgumentName());
572 if (module.importArgumentName())
573 formals.infallibleAppend(module.importArgumentName());
574 if (module.bufferArgumentName())
575 formals.infallibleAppend(module.bufferArgumentName());
576
577 CompileOptions options(cx);
578 options.setOriginPrincipals(module.scriptSource()->originPrincipals())
579 .setCompileAndGo(false)
580 .setNoScriptRval(false);
581
582 SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership);
583 if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
584 return false;
585
586 // Call the function we just recompiled.
587 args.setCallee(ObjectValue(*fun));
588 return Invoke(cx, args);
589 }
590
591 #ifdef MOZ_VTUNE
592 static bool
593 SendFunctionsToVTune(JSContext *cx, AsmJSModule &module)
594 {
595 uint8_t *base = module.codeBase();
596
597 for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
598 const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
599
600 uint8_t *start = base + func.pod.startCodeOffset;
601 uint8_t *end = base + func.pod.endCodeOffset;
602 JS_ASSERT(end >= start);
603
604 unsigned method_id = iJIT_GetNewMethodID();
605 if (method_id == 0)
606 return false;
607
608 JSAutoByteString bytes;
609 const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
610 if (!method_name)
611 return false;
612
613 iJIT_Method_Load method;
614 method.method_id = method_id;
615 method.method_name = const_cast<char *>(method_name);
616 method.method_load_address = (void *)start;
617 method.method_size = unsigned(end - start);
618 method.line_number_size = 0;
619 method.line_number_table = nullptr;
620 method.class_id = 0;
621 method.class_file_name = nullptr;
622 method.source_file_name = nullptr;
623
624 iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void *)&method);
625 }
626
627 return true;
628 }
629 #endif
630
631 #ifdef JS_ION_PERF
632 static bool
633 SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
634 {
635 if (!PerfFuncEnabled())
636 return true;
637
638 uintptr_t base = (uintptr_t) module.codeBase();
639 const char *filename = module.scriptSource()->filename();
640
641 for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
642 const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
643 uintptr_t start = base + (unsigned long) func.pod.startCodeOffset;
644 uintptr_t end = base + (unsigned long) func.pod.endCodeOffset;
645 JS_ASSERT(end >= start);
646 size_t size = end - start;
647
648 JSAutoByteString bytes;
649 const char *name = AtomToPrintableString(cx, func.name, &bytes);
650 if (!name)
651 return false;
652
653 writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno,
654 func.pod.columnIndex, name);
655 }
656
657 return true;
658 }
659
660 static bool
661 SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
662 {
663 if (!PerfBlockEnabled())
664 return true;
665
666 unsigned long funcBaseAddress = (unsigned long) module.codeBase();
667 const char *filename = module.scriptSource()->filename();
668
669 for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
670 const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
671
672 size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset;
673
674 JSAutoByteString bytes;
675 const char *name = AtomToPrintableString(cx, func.name, &bytes);
676 if (!name)
677 return false;
678
679 writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset,
680 func.endInlineCodeOffset, size, filename, name, func.blocks);
681 }
682
683 return true;
684 }
685 #endif
686
687 static bool
688 SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module)
689 {
690 #if defined(MOZ_VTUNE)
691 if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
692 return false;
693 #endif
694
695 #if defined(JS_ION_PERF)
696 if (module.numExportedFunctions() > 0) {
697 size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0));
698 writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode);
699 }
700 if (!SendBlocksToPerf(cx, module))
701 return false;
702 if (!SendFunctionsToPerf(cx, module))
703 return false;
704 #endif
705
706 return true;
707 }
708
709
710 static JSObject *
711 CreateExportObject(JSContext *cx, Handle<AsmJSModuleObject*> moduleObj)
712 {
713 AsmJSModule &module = moduleObj->module();
714
715 if (module.numExportedFunctions() == 1) {
716 const AsmJSModule::ExportedFunction &func = module.exportedFunction(0);
717 if (!func.maybeFieldName())
718 return NewExportedFunction(cx, func, moduleObj, 0);
719 }
720
721 gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions());
722 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
723 if (!obj)
724 return nullptr;
725
726 for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
727 const AsmJSModule::ExportedFunction &func = module.exportedFunction(i);
728
729 RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
730 if (!fun)
731 return nullptr;
732
733 JS_ASSERT(func.maybeFieldName() != nullptr);
734 RootedId id(cx, NameToId(func.maybeFieldName()));
735 RootedValue val(cx, ObjectValue(*fun));
736 if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
737 return nullptr;
738 }
739
740 return obj;
741 }
742
743 static const unsigned MODULE_FUN_SLOT = 0;
744
745 static AsmJSModuleObject &
746 ModuleFunctionToModuleObject(JSFunction *fun)
747 {
748 return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>();
749 }
750
751 // Implements the semantics of an asm.js module function that has been successfully validated.
752 static bool
753 LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp)
754 {
755 CallArgs args = CallArgsFromVp(argc, vp);
756
757 // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
758 // function and stores its module in an extended slot.
759 RootedFunction fun(cx, &args.callee().as<JSFunction>());
760 Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun));
761
762 // All ICache flushing of the module being linked has been inhibited under the
763 // assumption that the module is flushed after dynamic linking (when the last code
764 // mutation occurs). Thus, enter an AutoFlushICache context for the entire module
765 // now. The module range is set below.
766 AutoFlushICache afc("LinkAsmJS");
767
768 // When a module is linked, it is dynamically specialized to the given
769 // arguments (buffer, ffis). Thus, if the module is linked again (it is just
770 // a function so it can be called multiple times), we need to clone a new
771 // module.
772 if (moduleObj->module().isDynamicallyLinked()) {
773 if (!CloneModule(cx, &moduleObj))
774 return false;
775 } else {
776 // CloneModule already calls setAutoFlushICacheRange internally before patching
777 // the cloned module, so avoid calling twice.
778 moduleObj->module().setAutoFlushICacheRange();
779 }
780
781 AsmJSModule &module = moduleObj->module();
782
783 // Link the module by performing the link-time validation checks in the
784 // asm.js spec and then patching the generated module to associate it with
785 // the given heap (ArrayBuffer) and a new global data segment (the closure
786 // state shared by the inner asm.js functions).
787 if (!DynamicallyLinkModule(cx, args, module)) {
788 // Linking failed, so reparse the entire asm.js module from scratch to
789 // get normal interpreted bytecode which we can simply Invoke. Very slow.
790 RootedPropertyName name(cx, fun->name());
791 return HandleDynamicLinkFailure(cx, args, module, name);
792 }
793
794 // Notify profilers so that asm.js generated code shows up with JS function
795 // names and lines in native (i.e., not SPS) profilers.
796 if (!SendModuleToAttachedProfiler(cx, module))
797 return false;
798
799 // Link-time validation succeeded, so wrap all the exported functions with
800 // CallAsmJS builtins that trampoline into the generated code.
801 JSObject *obj = CreateExportObject(cx, moduleObj);
802 if (!obj)
803 return false;
804
805 args.rval().set(ObjectValue(*obj));
806 return true;
807 }
808
809 JSFunction *
810 js::NewAsmJSModuleFunction(ExclusiveContext *cx, JSFunction *origFun, HandleObject moduleObj)
811 {
812 RootedPropertyName name(cx, origFun->name());
813
814 JSFunction::Flags flags = origFun->isLambda() ? JSFunction::NATIVE_LAMBDA_FUN
815 : JSFunction::NATIVE_FUN;
816 JSFunction *moduleFun = NewFunction(cx, NullPtr(), LinkAsmJS, origFun->nargs(),
817 flags, NullPtr(), name,
818 JSFunction::ExtendedFinalizeKind, TenuredObject);
819 if (!moduleFun)
820 return nullptr;
821
822 moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj));
823 return moduleFun;
824 }
825
826 bool
827 js::IsAsmJSModuleNative(js::Native native)
828 {
829 return native == LinkAsmJS;
830 }
831
832 static bool
833 IsMaybeWrappedNativeFunction(const Value &v, Native native, JSFunction **fun = nullptr)
834 {
835 if (!v.isObject())
836 return false;
837
838 JSObject *obj = CheckedUnwrap(&v.toObject());
839 if (!obj)
840 return false;
841
842 if (!obj->is<JSFunction>())
843 return false;
844
845 if (fun)
846 *fun = &obj->as<JSFunction>();
847
848 return obj->as<JSFunction>().maybeNative() == native;
849 }
850
851 bool
852 js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
853 {
854 CallArgs args = CallArgsFromVp(argc, vp);
855 bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
856 args.rval().set(BooleanValue(rval));
857 return true;
858 }
859
860 bool
861 js::IsAsmJSModule(HandleFunction fun)
862 {
863 return fun->isNative() && fun->maybeNative() == LinkAsmJS;
864 }
865
866 JSString *
867 js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda)
868 {
869 AsmJSModule &module = ModuleFunctionToModuleObject(fun).module();
870
871 uint32_t begin = module.funcStart();
872 uint32_t end = module.funcEndAfterCurly();
873 ScriptSource *source = module.scriptSource();
874 StringBuffer out(cx);
875
876 // Whether the function has been created with a Function ctor
877 bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
878
879 if (addParenToLambda && fun->isLambda() && !out.append("("))
880 return nullptr;
881
882 if (!out.append("function "))
883 return nullptr;
884
885 if (fun->atom() && !out.append(fun->atom()))
886 return nullptr;
887
888 if (funCtor) {
889 // Functions created with the function constructor don't have arguments in their source.
890 if (!out.append("("))
891 return nullptr;
892
893 if (PropertyName *argName = module.globalArgumentName()) {
894 if (!out.append(argName->chars(), argName->length()))
895 return nullptr;
896 }
897 if (PropertyName *argName = module.importArgumentName()) {
898 if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
899 return nullptr;
900 }
901 if (PropertyName *argName = module.bufferArgumentName()) {
902 if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
903 return nullptr;
904 }
905
906 if (!out.append(") {\n"))
907 return nullptr;
908 }
909
910 Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
911 if (!src)
912 return nullptr;
913
914 if (module.strict()) {
915 // We need to add "use strict" in the body right after the opening
916 // brace.
917 size_t bodyStart = 0, bodyEnd;
918
919 // No need to test for functions created with the Function ctor as
920 // these doesn't implicitly inherit the "use strict" context. Strict mode is
921 // enabled for functions created with the Function ctor only if they begin with
922 // the "use strict" directive, but these functions won't validate as asm.js
923 // modules.
924
925 ConstTwoByteChars chars(src->chars(), src->length());
926 if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd))
927 return nullptr;
928
929 if (!out.append(chars, bodyStart) ||
930 !out.append("\n\"use strict\";\n") ||
931 !out.append(chars + bodyStart, src->length() - bodyStart))
932 {
933 return nullptr;
934 }
935 } else {
936 if (!out.append(src->chars(), src->length()))
937 return nullptr;
938 }
939
940 if (funCtor && !out.append("\n}"))
941 return nullptr;
942
943 if (addParenToLambda && fun->isLambda() && !out.append(")"))
944 return nullptr;
945
946 return out.finishString();
947 }
948
949 bool
950 js::IsAsmJSModuleLoadedFromCache(JSContext *cx, unsigned argc, Value *vp)
951 {
952 CallArgs args = CallArgsFromVp(argc, vp);
953
954 JSFunction *fun;
955 if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
956 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
957 "argument passed to isAsmJSModuleLoadedFromCache is not a "
958 "validated asm.js module");
959 return false;
960 }
961
962 bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().loadedFromCache();
963
964 args.rval().set(BooleanValue(loadedFromCache));
965 return true;
966 }
967
968 bool
969 js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
970 {
971 CallArgs args = CallArgsFromVp(argc, vp);
972 bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
973 args.rval().set(BooleanValue(rval));
974 return true;
975 }
976
977 bool
978 js::IsAsmJSFunction(HandleFunction fun)
979 {
980 return fun->isNative() && fun->maybeNative() == CallAsmJS;
981 }
982
983 JSString *
984 js::AsmJSFunctionToString(JSContext *cx, HandleFunction fun)
985 {
986 AsmJSModule &module = FunctionToEnclosingModule(fun);
987 const AsmJSModule::ExportedFunction &f = FunctionToExportedFunction(fun, module);
988 uint32_t begin = module.funcStart() + f.startOffsetInModule();
989 uint32_t end = module.funcStart() + f.endOffsetInModule();
990
991 ScriptSource *source = module.scriptSource();
992 StringBuffer out(cx);
993
994 // asm.js functions cannot have been created with a Function constructor
995 // as they belong within a module.
996 JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
997
998 if (!out.append("function "))
999 return nullptr;
1000
1001 Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
1002 if (!src)
1003 return nullptr;
1004
1005 if (!out.append(src->chars(), src->length()))
1006 return nullptr;
1007
1008 return out.finishString();
1009 }

mercurial