author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 11 Oct 2013 11:24:32 +0200 | |
changeset 150500 | a91e7a44eb857d9c0635ff0974bd3804c1ec9878 |
parent 150499 | a658fcd210b5fad7e8f18d2fa73483e9da39676d (current diff) |
parent 150438 | 6b101d4c6d241e3fa39b5ce71f122243fe2c118c (diff) |
child 150501 | a82f2ffec20007a7ee376fb88777c110811c0292 |
push id | 25444 |
push user | ryanvm@gmail.com |
push date | Fri, 11 Oct 2013 21:00:01 +0000 |
treeherder | mozilla-central@558cf02e6b9b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
media/webrtc/shared_libs.mk | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/preferences.xml.in | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/preferences_customize.xml.in | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/searchable.xml.in | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/sync_authenticator.xml.in | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/sync_options.xml.in | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/xml/sync_syncadapter.xml.in | file | annotate | diff | comparison | revisions |
--- a/configure.in +++ b/configure.in @@ -4009,17 +4009,16 @@ MOZ_OPUS=1 MOZ_WEBM=1 MOZ_DASH= MOZ_DIRECTSHOW= MOZ_WMF= MOZ_WEBRTC=1 MOZ_PEERCONNECTION= MOZ_SRTP= MOZ_WEBRTC_SIGNALING= -MOZ_WEBRTC_IN_LIBXUL= MOZ_WEBRTC_ASSERT_ALWAYS=1 MOZ_SCTP= MOZ_MEDIA_PLUGINS= MOZ_MEDIA_NAVIGATOR= MOZ_OMX_PLUGIN= MOZ_VP8= MOZ_VP8_ERROR_CONCEALMENT= MOZ_VP8_ENCODER= @@ -5186,33 +5185,29 @@ if test -n "$MOZ_WEBRTC"; then MOZ_RAW=1 MOZ_VP8=1 MOZ_VP8_ENCODER=1 MOZ_VP8_ERROR_CONCEALMENT=1 dnl enable once Signaling lands MOZ_WEBRTC_SIGNALING=1 AC_DEFINE(MOZ_WEBRTC_SIGNALING) - if test "${OS_TARGET}" = "WINNT"; then - MOZ_WEBRTC_IN_LIBXUL=1 - fi dnl enable once PeerConnection lands MOZ_PEERCONNECTION=1 AC_DEFINE(MOZ_PEERCONNECTION) MOZ_SCTP=1 MOZ_SRTP=1 AC_DEFINE(MOZ_SCTP) AC_DEFINE(MOZ_SRTP) fi AC_SUBST(MOZ_WEBRTC) AC_SUBST(MOZ_WEBRTC_LEAKING_TESTS) AC_SUBST(MOZ_WEBRTC_SIGNALING) AC_SUBST(MOZ_PEERCONNECTION) -AC_SUBST(MOZ_WEBRTC_IN_LIBXUL) AC_SUBST(MOZ_WEBRTC_ASSERT_ALWAYS) AC_SUBST(MOZ_SCTP) AC_SUBST(MOZ_SRTP) dnl Use integers over floats for audio on B2G and Android, because audio dnl backends for those platforms don't support floats. if test "$OS_TARGET" = "Android"; then MOZ_SAMPLE_TYPE_S16=1
--- a/content/canvas/test/webgl/non-conf-tests/mochitest.ini +++ b/content/canvas/test/webgl/non-conf-tests/mochitest.ini @@ -1,8 +1,9 @@ [DEFAULT] support-files = driver-info.js webgl-util.js +[test_highp_fs.html] [test_webgl_available.html] [test_webgl_conformance.html] [test_webgl_request_mismatch.html]
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/non-conf-tests/test_highp_fs.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<title>WebGL test: `highp` support</title> +<script src="/MochiKit/MochiKit.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"> +<script src="driver-info.js"></script> +<script src="webgl-util.js"></script> +<script id="shader-vs" type="x-shader/x-vertex"> + +void main(void) { + gl_Position = vec4(vec3(0.0), 1.0); +} + +</script> +<script id="shader-fs" type="x-shader/x-fragment"> + +precision highp float; + +void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} + +</script> +<body> +<canvas id="c"></canvas> +<script> + +// Give ourselves a scope to return early from: +(function() { + var gl = WebGLUtil.getWebGL('c'); + if (!gl) { + todo(false, 'WebGL is unavailable.'); + return; + } + + // Catch actual WebGLUtil errors, not GL errors. + function errorFunc(str) { + ok(false, 'Error: ' + str); + } + WebGLUtil.setErrorFunc(errorFunc); + + function checkGLError(func, info) { + var error = gl.getError(); + var prefix = info ? ('[' + info + '] ') : '' + func(!error, prefix + 'gl.getError should be 0x0, was 0x' + error.toString(16) + '.'); + } + + var format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); + var prog = WebGLUtil.createProgramByIds(gl, 'shader-vs', 'shader-fs'); + checkGLError(ok); + + if (format) { + ok(prog, 'Frag shader with unconditional `precision highp float` should ' + + 'link if `getShaderPrecisionFormat` gives a format for it.'); + } else { + ok(!prog, 'Frag shader with unconditional `precision highp float` should ' + + 'NOT link if `getShaderPrecisionFormat` gives NO format for it.'); + } +})(); + +</script> +
--- a/content/canvas/test/webgl/non-conf-tests/webgl-util.js +++ b/content/canvas/test/webgl/non-conf-tests/webgl-util.js @@ -1,26 +1,42 @@ WebGLUtil = (function() { // --------------------------------------------------------------------------- - // Error handling + // Error handling (for obvious failures, such as invalid element ids) function defaultErrorFunc(str) { console.log('Error: ' + str); } var gErrorFunc = defaultErrorFunc; function setErrorFunc(func) { gErrorFunc = func; } function error(str) { gErrorFunc(str); } // --------------------------------------------------------------------------- + // Warning handling (for failures that may be intentional) + + function defaultWarningFunc(str) { + console.log('Warning: ' + str); + } + + var gWarningFunc = defaultWarningFunc; + function setWarningFunc(func) { + gWarningFunc = func; + } + + function warning(str) { + gWarningFunc(str); + } + + // --------------------------------------------------------------------------- // WebGL helpers function getWebGL(canvasId, requireConformant) { // `requireConformant` will default to falsey if it is not supplied. var canvas = document.getElementById(canvasId); var gl = null; @@ -58,29 +74,29 @@ WebGLUtil = (function() { // Returns a valid shader, or null on errors. function createShaderById(gl, id) { var elem = document.getElementById(id); if (!elem) { error('Failed to create shader from non-existent id \'' + id + '\'.'); return null; } - var src = getContentById(id); + var src = getContentFromElem(elem); var shader; if (elem.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); - } else if (shaderScript.type == "x-shader/x-vertex") { + } else if (elem.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { error('Bad MIME type for shader \'' + id + '\': ' + elem.type + '.'); return null; } - gl.shaderSource(shader, str); + gl.shaderSource(shader, src); gl.compileShader(shader); return shader; } function createProgramByIds(gl, vsId, fsId) { var vs = createShaderById(gl, vsId); var fs = createShaderById(gl, fsId); @@ -88,27 +104,28 @@ WebGLUtil = (function() { return null; var prog = gl.createProgram(); gl.attachShader(prog, vs); gl.attachShader(prog, fs); gl.linkProgram(prog); if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { - var str = "Shader program linking failed:\n"; - str += "Shader program info log:\n" + gl.getProgramInfoLog(prog) + "\n\n"; - str += "Vert shader log:\n" + gl.getShaderInfoLog(vs) + "\n\n"; - str += "Frag shader log:\n" + gl.getShaderInfoLog(fs); - error(str); + var str = "Shader program linking failed:"; + str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog); + str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs); + str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs); + warning(str); return null; } return prog; } return { setErrorFunc: setErrorFunc, + setWarningFunc: setWarningFunc, getWebGL: getWebGL, createShaderById: createShaderById, createProgramByIds: createProgramByIds, }; })();
--- a/content/media/directshow/moz.build +++ b/content/media/directshow/moz.build @@ -21,17 +21,17 @@ CPP_SOURCES += [ 'DirectShowReader.cpp', 'DirectShowUtils.cpp', 'SampleSink.cpp', 'SourceFilter.cpp', ] # If WebRTC isn't being built, we need to compile the DirectShow base classes so that # they're available at link time. -if not CONFIG['MOZ_WEBRTC_IN_LIBXUL']: +if not CONFIG['MOZ_WEBRTC']: CPP_SOURCES += [ TOPSRCDIR + '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp', TOPSRCDIR + '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp', TOPSRCDIR + '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp', TOPSRCDIR + '/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp', ] FAIL_ON_WARNINGS = True
--- a/gfx/2d/convolver.cpp +++ b/gfx/2d/convolver.cpp @@ -24,17 +24,16 @@ // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. #include "convolver.h" #include <algorithm> -#include "nsAlgorithm.h" #include "skia/SkTypes.h" // note: SIMD_SSE2 is not enabled because of bugs, apparently #if defined(SIMD_SSE2) #include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h #endif
--- a/gfx/2d/image_operations.cpp +++ b/gfx/2d/image_operations.cpp @@ -30,17 +30,16 @@ #define _USE_MATH_DEFINES #include <algorithm> #include <cmath> #include <limits> #include "image_operations.h" -#include "nsAlgorithm.h" #include "base/stack_container.h" #include "convolver.h" #include "skia/SkColorPriv.h" #include "skia/SkBitmap.h" #include "skia/SkRect.h" #include "skia/SkFontHost.h" namespace skia {
--- a/js/public/CallArgs.h +++ b/js/public/CallArgs.h @@ -38,17 +38,17 @@ #include "js/RootingAPI.h" #include "js/Value.h" /* Typedef for native functions called by the JS VM. */ typedef bool (* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp); /* Typedef for native functions that may be called in parallel. */ -typedef js::ParallelResult +typedef bool (* JSParallelNative)(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp); /* * Typedef for native functions that may be called either in parallel or * sequential execution. */ typedef bool (* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp); @@ -57,17 +57,17 @@ typedef bool * Convenience wrappers for passing in ThreadSafeNative to places that expect * a JSNative or a JSParallelNative. */ template <JSThreadSafeNative threadSafeNative> inline bool JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp); template <JSThreadSafeNative threadSafeNative> -inline js::ParallelResult +inline bool JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp); /* * Compute |this| for the |vp| inside a JSNative, either boxing primitives or * replacing with the global object as necessary. * * This method will go away at some point: instead use |args.thisv()|. If the * value is an object, no further work is required. If that value is |null| or
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug925305.js @@ -0,0 +1,14 @@ +function testFloat32SetElemIC(a) { + for (var i = 0; i < a.length; i++) { + var r = Math.fround(Math.random()); + a[i] = r; + assertEq(a[i], r); + } +} + +testFloat32SetElemIC(new Array(2048)); +testFloat32SetElemIC(new Array(2048)); + +enableOsiPointRegisterChecks(); +testFloat32SetElemIC(new Array(2048)); +testFloat32SetElemIC(new Float64Array(2048));
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/parallel/bug909743.js @@ -0,0 +1,21 @@ +if (getBuildConfiguration().parallelJS) { + function assertParallelExecSucceeds(opFunction) { + for (var i = 0; i < 100; ++i) { + opFunction({mode:"compile"}); + } + } + function assertArraySeqParResultsEq(arr, op, func) { + assertParallelExecSucceeds( + function (m) { + return arr[op + "Par"].apply(arr, [func, m]); + } + ); + } + function range(n, m) { + var result = []; + for (var i = n; i < m; i++) + result.push(i); + return result; + } + assertArraySeqParResultsEq(range(0, 512), "map", function(e) { return e+'x'; }); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/parallel/ic-setelement.js @@ -0,0 +1,135 @@ +load(libdir + "parallelarray-helpers.js"); + +function set(a, n) { + // Padding to prevent inlining. + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + for (var i = 0; i < n; i++) + a[i] = i; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; +} +set({}, 1024); +set({}, 1024); +function Foo() { } +set(new Foo, 1024); + +function testSetDense() { + assertArraySeqParResultsEq( + range(0, minItemsTestingThreshold), + "map", + function (i) { + var a1 = []; + // Defines .foo + set(a1, i+1); + return a1[i]; + }); +} + +if (getBuildConfiguration().parallelJS) { + testSetDense(); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/parallel/ic-setproperty.js @@ -0,0 +1,111 @@ +load(libdir + "parallelarray-helpers.js"); + +function set(o, v) { + // Padding to prevent inlining. + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + o.foo = v; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; + var foo = 0; +} +set({ foo: 0 }, 42); + +function testSetPropertySlot() { + assertArraySeqParResultsEq( + range(0, minItemsTestingThreshold), + "map", + function (i) { + var o1 = {}; + var o2 = {}; + var o3 = {}; + var o4 = {}; + // Defines .foo + set(o1, i + 1); + set(o2, i + 2); + set(o3, i + 3); + set(o4, i + 4); + // Sets .foo + set(o1, i + 5); + set(o2, i + 6); + set(o3, i + 7); + set(o4, i + 8); + return o1.foo + o2.foo + o3.foo + o4.foo; + }); +} + +function testSetArrayLength() { + assertArraySeqParResultsEq( + range(0, minItemsTestingThreshold), + "map", + function (i) { + var a = []; + a.length = i; + return a.length; + }); +} + +if (getBuildConfiguration().parallelJS) { + testSetPropertySlot(); + testSetArrayLength(); +}
--- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1420,17 +1420,17 @@ jit::FinishBailoutToBaseline(BaselineBai return false; // Create arguments objects for bailed out frames, to maintain the invariant // that script->needsArgsObj() implies frame->hasArgsObj(). RootedScript innerScript(cx, nullptr); RootedScript outerScript(cx, nullptr); JS_ASSERT(cx->currentlyRunningInJit()); - IonFrameIterator iter(cx->mainThread().ionTop); + IonFrameIterator iter(cx); uint32_t frameno = 0; while (frameno < numFrames) { JS_ASSERT(!iter.isOptimizedJS()); if (iter.isBaselineJS()) { BaselineFrame *frame = iter.baselineFrame();
--- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -152,17 +152,17 @@ BaselineFrame::initForOsr(StackFrame *fp if (cx->compartment()->debugMode()) { // In debug mode, update any Debugger.Frame objects for the StackFrame to // point to the BaselineFrame. // The caller pushed a fake return address. ScriptFrameIter, used by the // debugger, wants a valid return address, but it's okay to just pick one. // In debug mode there's always at least 1 ICEntry (since there are always // debug prologue/epilogue calls). - IonFrameIterator iter(cx->mainThread().ionTop); + IonFrameIterator iter(cx); JS_ASSERT(iter.returnAddress() == nullptr); BaselineScript *baseline = fp->script()->baselineScript(); iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); if (!Debugger::handleBaselineOsr(cx, fp, this)) return false; }
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -18,16 +18,17 @@ #ifdef JS_ION_PERF # include "jit/PerfSpewer.h" #endif #include "jit/VMFunctions.h" #include "jsboolinlines.h" #include "jsscriptinlines.h" +#include "jit/IonFrames-inl.h" #include "vm/Interpreter-inl.h" #include "vm/ScopeObject-inl.h" namespace js { namespace jit { #ifdef DEBUG void
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -149,17 +149,17 @@ CodeGenerator::CodeGenerator(MIRGenerato CodeGenerator::~CodeGenerator() { JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0); js_delete(unassociatedScriptCounts_); } typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *); -typedef ParallelResult (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *); +typedef bool (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *); static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal( FunctionInfo<StringToNumberFn>(StringToNumber), FunctionInfo<StringToNumberParFn>(StringToNumberPar)); bool CodeGenerator::visitValueToInt32(LValueToInt32 *lir) { ValueOperand operand = ToValue(lir, LValueToInt32::Input); @@ -644,17 +644,17 @@ CodeGenerator::visitTypeObjectDispatch(L // Unknown function: jump to fallback block. LBlock *fallback = mir->getFallback()->lir(); masm.jump(fallback->label()); return true; } typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int); -typedef ParallelResult (*IntToStringParFn)(ForkJoinSlice *, int, MutableHandleString); +typedef JSFlatString *(*IntToStringParFn)(ForkJoinSlice *, int); static const VMFunctionsModal IntToStringInfo = VMFunctionsModal( FunctionInfo<IntToStringFn>(Int32ToString<CanGC>), FunctionInfo<IntToStringParFn>(IntToStringPar)); bool CodeGenerator::visitIntToString(LIntToString *lir) { Register input = ToRegister(lir->input()); @@ -671,17 +671,17 @@ CodeGenerator::visitIntToString(LIntToSt masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings.intStaticTable), output); masm.loadPtr(BaseIndex(output, input, ScalePointer), output); masm.bind(ool->rejoin()); return true; } typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double); -typedef ParallelResult (*DoubleToStringParFn)(ForkJoinSlice *, double, MutableHandleString); +typedef JSString *(*DoubleToStringParFn)(ForkJoinSlice *, double); static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal( FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>), FunctionInfo<DoubleToStringParFn>(DoubleToStringPar)); bool CodeGenerator::visitDoubleToString(LDoubleToString *lir) { FloatRegister input = ToFloatRegister(lir->input()); @@ -985,17 +985,16 @@ CodeGenerator::visitParameter(LParameter bool CodeGenerator::visitCallee(LCallee *lir) { // read number of actual arguments from the JS frame. Register callee = ToRegister(lir->output()); Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfCalleeToken()); masm.loadPtr(ptr, callee); - masm.clearCalleeTag(callee, gen->info().executionMode()); return true; } bool CodeGenerator::visitStart(LStart *lir) { return true; } @@ -1850,17 +1849,17 @@ CodeGenerator::visitCallGeneric(LCallGen masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke); // Nestle the StackPointer up to the argument vector. masm.freeStack(unusedStack); // Construct the IonFramePrefix. uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), IonFrame_OptimizedJS); masm.Push(Imm32(call->numActualArgs())); - masm.PushCalleeToken(calleereg, executionMode); + masm.Push(calleereg); masm.Push(Imm32(descriptor)); // Check whether the provided arguments satisfy target argc. masm.load16ZeroExtend(Address(calleereg, offsetof(JSFunction, nargs)), nargsreg); masm.cmp32(nargsreg, Imm32(call->numStackArgs())); masm.j(Assembler::Above, &thunk); masm.jump(&makeCall); @@ -1971,17 +1970,17 @@ CodeGenerator::visitCallKnown(LCallKnown masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled); // Nestle the StackPointer up to the argument vector. masm.freeStack(unusedStack); // Construct the IonFramePrefix. uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), IonFrame_OptimizedJS); masm.Push(Imm32(call->numActualArgs())); - masm.PushCalleeToken(calleereg, executionMode); + masm.Push(calleereg); masm.Push(Imm32(descriptor)); // Finally call the function in objreg. uint32_t callOffset = masm.callIon(objreg); if (!markSafepointAt(callOffset, call)) return false; // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass. @@ -3858,17 +3857,17 @@ CodeGenerator::visitModD(LModD *ins) if (gen->compilingAsmJS()) masm.callWithABI(AsmJSImm_ModD, MacroAssembler::DOUBLE); else masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE); return true; } typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, Value *); -typedef ParallelResult (*BinaryParFn)(ForkJoinSlice *, HandleValue, HandleValue, Value *); +typedef bool (*BinaryParFn)(ForkJoinSlice *, HandleValue, HandleValue, Value *); static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues); static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues); static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues); static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues); static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues); static const VMFunctionsModal UrshInfo = VMFunctionsModal( FunctionInfo<BinaryFn>(js::UrshValues), @@ -3900,17 +3899,17 @@ CodeGenerator::visitBinaryV(LBinaryV *li return callVM(UrshInfo, lir); default: MOZ_ASSUME_UNREACHABLE("Unexpected binary op"); } } typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *); -typedef ParallelResult (*StringCompareParFn)(ForkJoinSlice *, HandleString, HandleString, bool *); +typedef bool (*StringCompareParFn)(ForkJoinSlice *, HandleString, HandleString, bool *); static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal( FunctionInfo<StringCompareFn>(jit::StringsEqual<true>), FunctionInfo<StringCompareParFn>(jit::StringsEqualPar)); static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal( FunctionInfo<StringCompareFn>(jit::StringsEqual<false>), FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar)); bool @@ -3972,18 +3971,17 @@ CodeGenerator::visitCompareS(LCompareS * Register right = ToRegister(lir->right()); Register output = ToRegister(lir->output()); Register temp = ToRegister(lir->temp()); return emitCompareS(lir, op, left, right, output, temp); } typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *); -typedef ParallelResult (*CompareParFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, - bool *); +typedef bool (*CompareParFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, bool *); static const VMFunctionsModal EqInfo = VMFunctionsModal( FunctionInfo<CompareFn>(jit::LooselyEqual<true>), FunctionInfo<CompareParFn>(jit::LooselyEqualPar)); static const VMFunctionsModal NeInfo = VMFunctionsModal( FunctionInfo<CompareFn>(jit::LooselyEqual<false>), FunctionInfo<CompareParFn>(jit::LooselyUnequalPar)); static const VMFunctionsModal StrictEqInfo = VMFunctionsModal( FunctionInfo<CompareFn>(jit::StrictlyEqual<true>), @@ -4256,18 +4254,17 @@ CodeGenerator::visitEmulatesUndefinedAnd Register objreg = ToRegister(lir->input()); testObjectTruthy(objreg, unequal, equal, ToRegister(lir->temp()), ool); return true; } typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString); -typedef ParallelResult (*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString, - MutableHandleString); +typedef JSString *(*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString); static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal( FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>), FunctionInfo<ConcatStringsParFn>(ConcatStringsPar)); bool CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output) { OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs), @@ -4452,25 +4449,36 @@ IonCompartment::generateStringConcatStub masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2); masm.orPtr(Imm32(JSString::FIXED_FLAGS), temp2); masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags())); // Set chars pointer, keep in temp2 for copy loop below. masm.computeEffectiveAddress(Address(output, JSShortString::offsetOfInlineStorage()), temp2); masm.storePtr(temp2, Address(output, JSShortString::offsetOfChars())); - // Copy lhs chars. Temp1 still holds the lhs length. Note that this - // advances temp2 to point to the next char. - masm.loadPtr(Address(lhs, JSString::offsetOfChars()), temp3); - CopyStringChars(masm, temp2, temp3, temp1, temp4); - - // Copy rhs chars. - masm.loadPtr(Address(rhs, JSString::offsetOfChars()), temp3); - masm.loadStringLength(rhs, temp1); - CopyStringChars(masm, temp2, temp3, temp1, temp4); + { + // We use temp4 in this block, which in parallel execution also holds + // a live ForkJoinSlice pointer. If we are compiling for parallel + // execution, be sure to save and restore the ForkJoinSlice. + if (mode == ParallelExecution) + masm.push(temp4); + + // Copy lhs chars. Temp1 still holds the lhs length. Note that this + // advances temp2 to point to the next char. + masm.loadPtr(Address(lhs, JSString::offsetOfChars()), temp3); + CopyStringChars(masm, temp2, temp3, temp1, temp4); + + // Copy rhs chars. + masm.loadPtr(Address(rhs, JSString::offsetOfChars()), temp3); + masm.loadStringLength(rhs, temp1); + CopyStringChars(masm, temp2, temp3, temp1, temp4); + + if (mode == ParallelExecution) + masm.pop(temp4); + } // Null-terminate. masm.store16(Imm32(0), Address(temp2, 0)); masm.ret(); masm.bind(&failurePopTemps); masm.pop(temp2); masm.pop(temp1); @@ -4838,18 +4846,17 @@ CodeGenerator::visitStoreElementHoleV(LS masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight)); masm.bind(ool->rejoin()); return true; } typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue, bool strict); -typedef ParallelResult (*SetElementParFn)(ForkJoinSlice *, HandleObject, HandleValue, - HandleValue, bool); +typedef bool (*SetElementParFn)(ForkJoinSlice *, HandleObject, HandleValue, HandleValue, bool); static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal( FunctionInfo<SetObjectElementFn>(SetObjectElement), FunctionInfo<SetElementParFn>(SetElementPar)); bool CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool) { Register object, elements; @@ -5446,19 +5453,18 @@ CodeGenerator::visitRunOncePrologue(LRun { pushArg(ImmGCPtr(lir->mir()->block()->info().script())); return callVM(RunOnceScriptPrologueInfo, lir); } typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject, HandleObject); -typedef ParallelResult (*InitRestParameterParFn)(ForkJoinSlice *, uint32_t, Value *, - HandleObject, HandleObject, - MutableHandleObject); +typedef JSObject *(*InitRestParameterParFn)(ForkJoinSlice *, uint32_t, Value *, + HandleObject, HandleObject); static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal( FunctionInfo<InitRestParameterFn>(InitRestParameter), FunctionInfo<InitRestParameterParFn>(InitRestParameterPar)); bool CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals, Register temp0, Register temp1, unsigned numFormals, JSObject *templateObject) @@ -6024,16 +6030,54 @@ CodeGenerator::addGetPropertyCache(LInst return addCache(ins, allocateCache(cache)); } default: MOZ_ASSUME_UNREACHABLE("Bad execution mode"); } } bool +CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg, + PropertyName *name, ConstantOrRegister value, bool strict, + bool needsTypeBarrier) +{ + switch (gen->info().executionMode()) { + case SequentialExecution: { + SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier); + return addCache(ins, allocateCache(cache)); + } + case ParallelExecution: { + SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier); + return addCache(ins, allocateCache(cache)); + } + default: + MOZ_ASSUME_UNREACHABLE("Bad execution mode"); + } +} + +bool +CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, + Register temp, FloatRegister tempFloat, ValueOperand index, + ConstantOrRegister value, bool strict) +{ + switch (gen->info().executionMode()) { + case SequentialExecution: { + SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict); + return addCache(ins, allocateCache(cache)); + } + case ParallelExecution: { + SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict); + return addCache(ins, allocateCache(cache)); + } + default: + MOZ_ASSUME_UNREACHABLE("Bad execution mode"); + } +} + +bool CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins) { RegisterSet liveRegs = ins->safepoint()->liveRegs(); Register objReg = ToRegister(ins->getOperand(0)); PropertyName *name = ins->mir()->name(); bool allowGetters = ins->mir()->allowGetters(); TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins)); @@ -6076,18 +6120,17 @@ CodeGenerator::visitGetPropertyIC(OutOfL return false; StoreValueTo(ic->output()).generate(this); restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered()); masm.jump(ool->rejoin()); return true; } -typedef ParallelResult (*GetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, - MutableHandleValue); +typedef bool (*GetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, MutableHandleValue); const VMFunction GetPropertyParIC::UpdateInfo = FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update); bool CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic) { LInstruction *lir = ool->lir(); saveLive(lir); @@ -6168,19 +6211,18 @@ CodeGenerator::visitSetElementCacheV(LSe { Register obj = ToRegister(ins->object()); Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex()); Register temp = ToRegister(ins->temp()); FloatRegister tempFloat = ToFloatRegister(ins->tempFloat()); ValueOperand index = ToValue(ins, LSetElementCacheV::Index); ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value)); - SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict()); - - return addCache(ins, allocateCache(cache)); + return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, + ins->mir()->strict()); } bool CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins) { Register obj = ToRegister(ins->object()); Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex()); Register temp = ToRegister(ins->temp()); @@ -6188,19 +6230,18 @@ CodeGenerator::visitSetElementCacheT(LSe ValueOperand index = ToValue(ins, LSetElementCacheT::Index); ConstantOrRegister value; const LAllocation *tmp = ins->value(); if (tmp->isConstant()) value = *tmp->toConstant(); else value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp)); - SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict()); - - return addCache(ins, allocateCache(cache)); + return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, + ins->mir()->strict()); } typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue); const VMFunction SetElementIC::UpdateInfo = FunctionInfo<SetElementICFn>(SetElementIC::update); bool CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic) @@ -6215,18 +6256,40 @@ CodeGenerator::visitSetElementIC(OutOfLi if (!callVM(SetElementIC::UpdateInfo, lir)) return false; restoreLive(lir); masm.jump(ool->rejoin()); return true; } -typedef ParallelResult (*GetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, - HandleValue, MutableHandleValue); +typedef bool (*SetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue, HandleValue); +const VMFunction SetElementParIC::UpdateInfo = + FunctionInfo<SetElementParICFn>(SetElementParIC::update); + +bool +CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic) +{ + LInstruction *lir = ool->lir(); + saveLive(lir); + + pushArg(ic->value()); + pushArg(ic->index()); + pushArg(ic->object()); + pushArg(Imm32(ool->getCacheIndex())); + if (!callVM(SetElementParIC::UpdateInfo, lir)) + return false; + restoreLive(lir); + + masm.jump(ool->rejoin()); + return true; +} + +typedef bool (*GetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue, + MutableHandleValue); const VMFunction GetElementParIC::UpdateInfo = FunctionInfo<GetElementParICFn>(GetElementParIC::update); bool CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic) { LInstruction *lir = ool->lir(); saveLive(lir); @@ -6271,18 +6334,21 @@ CodeGenerator::visitBindNameIC(OutOfLine restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered()); masm.jump(ool->rejoin()); return true; } typedef bool (*SetPropertyFn)(JSContext *, HandleObject, HandlePropertyName, const HandleValue, bool, jsbytecode *); -static const VMFunction SetPropertyInfo = - FunctionInfo<SetPropertyFn>(SetProperty); +typedef bool (*SetPropertyParFn)(ForkJoinSlice *, HandleObject, + HandlePropertyName, const HandleValue, bool, jsbytecode *); +static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal( + FunctionInfo<SetPropertyFn>(SetProperty), + FunctionInfo<SetPropertyParFn>(SetPropertyPar)); bool CodeGenerator::visitCallSetProperty(LCallSetProperty *ins) { ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value)); const Register objReg = ToRegister(ins->getOperand(0)); @@ -6334,36 +6400,34 @@ CodeGenerator::visitCallDeleteElement(LC bool CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins) { RegisterSet liveRegs = ins->safepoint()->liveRegs(); Register objReg = ToRegister(ins->getOperand(0)); ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value)); - SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(), - ins->mir()->needsTypeBarrier()); - return addCache(ins, allocateCache(cache)); + return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value, + ins->mir()->strict(), ins->mir()->needsTypeBarrier()); } bool CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins) { RegisterSet liveRegs = ins->safepoint()->liveRegs(); Register objReg = ToRegister(ins->getOperand(0)); ConstantOrRegister value; if (ins->getOperand(1)->isConstant()) value = ConstantOrRegister(*ins->getOperand(1)->toConstant()); else value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1))); - SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(), - ins->mir()->needsTypeBarrier()); - return addCache(ins, allocateCache(cache)); + return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value, + ins->mir()->strict(), ins->mir()->needsTypeBarrier()); } typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue); const VMFunction SetPropertyIC::UpdateInfo = FunctionInfo<SetPropertyICFn>(SetPropertyIC::update); bool CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic) @@ -6377,41 +6441,62 @@ CodeGenerator::visitSetPropertyIC(OutOfL if (!callVM(SetPropertyIC::UpdateInfo, lir)) return false; restoreLive(lir); masm.jump(ool->rejoin()); return true; } +typedef bool (*SetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue); +const VMFunction SetPropertyParIC::UpdateInfo = + FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update); + +bool +CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic) +{ + LInstruction *lir = ool->lir(); + saveLive(lir); + + pushArg(ic->value()); + pushArg(ic->object()); + pushArg(Imm32(ool->getCacheIndex())); + if (!callVM(SetPropertyParIC::UpdateInfo, lir)) + return false; + restoreLive(lir); + + masm.jump(ool->rejoin()); + return true; +} + typedef bool (*ThrowFn)(JSContext *, HandleValue); static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw); bool CodeGenerator::visitThrow(LThrow *lir) { pushArg(ToValue(lir, LThrow::Value)); return callVM(ThrowInfo, lir); } typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p); -typedef ParallelResult (*BitNotParFn)(ForkJoinSlice *, HandleValue, int32_t *); +typedef bool (*BitNotParFn)(ForkJoinSlice *, HandleValue, int32_t *); static const VMFunctionsModal BitNotInfo = VMFunctionsModal( FunctionInfo<BitNotFn>(BitNot), FunctionInfo<BitNotParFn>(BitNotPar)); bool CodeGenerator::visitBitNotV(LBitNotV *lir) { pushArg(ToValue(lir, LBitNotV::Input)); return callVM(BitNotInfo, lir); } typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p); -typedef ParallelResult (*BitopParFn)(ForkJoinSlice *, HandleValue, HandleValue, int32_t *); +typedef bool (*BitopParFn)(ForkJoinSlice *, HandleValue, HandleValue, int32_t *); static const VMFunctionsModal BitAndInfo = VMFunctionsModal( FunctionInfo<BitopFn>(BitAnd), FunctionInfo<BitopParFn>(BitAndPar)); static const VMFunctionsModal BitOrInfo = VMFunctionsModal( FunctionInfo<BitopFn>(BitOr), FunctionInfo<BitopParFn>(BitOrPar)); static const VMFunctionsModal BitXorInfo = VMFunctionsModal( FunctionInfo<BitopFn>(BitXor),
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -307,19 +307,21 @@ class CodeGenerator : public CodeGenerat bool visitSetPropertyCacheV(LSetPropertyCacheV *ins); bool visitSetPropertyCacheT(LSetPropertyCacheT *ins); bool visitGetNameCache(LGetNameCache *ins); bool visitCallsiteCloneCache(LCallsiteCloneCache *ins); bool visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic); bool visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic); bool visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic); + bool visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic); bool visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic); bool visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic); bool visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic); + bool visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic); bool visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic); bool visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic); bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic); bool visitAssertRangeI(LAssertRangeI *ins); bool visitAssertRangeD(LAssertRangeD *ins); bool visitAssertRangeF(LAssertRangeF *ins); bool visitAssertRangeV(LAssertRangeV *ins); @@ -331,16 +333,22 @@ class CodeGenerator : public CodeGenerat } private: bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg, PropertyName *name, TypedOrValueRegister output, bool allowGetters); bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index, TypedOrValueRegister output, bool monitoredResult); + bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg, + PropertyName *name, ConstantOrRegister value, bool strict, + bool needsTypeBarrier); + bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp, + FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, + bool strict); bool checkForAbortPar(LInstruction *lir); bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr); bool emitAllocateGCThingPar(LInstruction *lir, const Register &objReg, const Register &sliceReg, const Register &tempReg1, const Register &tempReg2, JSObject *templateObj);
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2176,17 +2176,17 @@ jit::FastInvoke(JSContext *cx, HandleFun static void InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll) { IonSpew(IonSpew_Invalidate, "BEGIN invalidating activation"); size_t frameno = 1; - for (IonFrameIterator it(ionTop); !it.done(); ++it, ++frameno) { + for (IonFrameIterator it(ionTop, SequentialExecution); !it.done(); ++it, ++frameno) { JS_ASSERT_IF(frameno == 1, it.type() == IonFrame_Exit); #ifdef DEBUG switch (it.type()) { case IonFrame_Exit: IonSpew(IonSpew_Invalidate, "#%d exit frame @ %p", frameno, it.fp()); break; case IonFrame_BaselineJS:
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -1911,17 +1911,17 @@ AnalyzePoppedThis(JSContext *cx, types:: bool *phandled) { // Determine the effect that a use of the |this| value when calling |new| // on a script has on the properties definitely held by the new object. if (ins->isCallSetProperty()) { MCallSetProperty *setprop = ins->toCallSetProperty(); - if (setprop->obj() != thisValue) + if (setprop->object() != thisValue) return true; // Don't use GetAtomId here, we need to watch for SETPROP on // integer properties and bail out. We can't mark the aggregate // JSID_VOID type property as being in a definite slot. if (setprop->name() == cx->names().classPrototype || setprop->name() == cx->names().proto || setprop->name() == cx->names().constructor)
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -13,19 +13,21 @@ #include "builtin/TypeRepresentation.h" #include "jit/Ion.h" #include "jit/IonLinker.h" #include "jit/IonSpewer.h" #include "jit/Lowering.h" #ifdef JS_ION_PERF # include "jit/PerfSpewer.h" #endif +#include "jit/ParallelFunctions.h" #include "jit/VMFunctions.h" #include "vm/Shape.h" +#include "jit/IonFrames-inl.h" #include "vm/Interpreter-inl.h" #include "vm/Shape-inl.h" using namespace js; using namespace js::jit; using mozilla::DebugOnly; @@ -1219,17 +1221,17 @@ GetPropertyIC::tryAttachNative(JSContext MacroAssembler masm(cx); RepatchStubAppender attacher(*this); const char *attachKind; switch (type) { case CanAttachReadSlot: GenerateReadSlot(cx, ion, masm, attacher, obj, holder, - shape, object(), output()); + shape, object(), output()); attachKind = idempotent() ? "idempotent reading" : "non idempotent reading"; break; case CanAttachCallGetter: masm.setFramePushed(ion->frameSize()); if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name, holder, shape, liveRegs_, object(), output(), returnAddr)) { @@ -1844,83 +1846,83 @@ GetPropertyParIC::attachTypedArrayLength DispatchStubPrepender attacher(*this); GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output()); JS_ASSERT(!hasTypedArrayLengthStub_); hasTypedArrayLengthStub_ = true; return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length"); } -ParallelResult +bool GetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, MutableHandleValue vp) { AutoFlushCache afc("GetPropertyParCache", slice->runtime()->ionRuntime()); IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript(); GetPropertyParIC &cache = ion->getCache(cacheIndex).toGetPropertyPar(); // Grab the property early, as the pure path is fast anyways and doesn't // need a lock. If we can't do it purely, bail out of parallel execution. if (!GetPropertyPure(slice, obj, NameToId(cache.name()), vp.address())) - return TP_RETRY_SEQUENTIALLY; + return false; // Avoid unnecessary locking if cannot attach stubs. if (!cache.canAttachStub()) - return TP_SUCCESS; + return true; { // Lock the context before mutating the cache. Ideally we'd like to do // finer-grained locking, with one lock per cache. However, generating // new jitcode uses a global ExecutableAllocator tied to the runtime. LockedJSContext cx(slice); if (cache.canAttachStub()) { bool alreadyStubbed; if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); if (alreadyStubbed) - return TP_SUCCESS; + return true; // See note about the stub limit in GetPropertyCache. bool attachedStub = false; { RootedShape shape(cx); RootedObject holder(cx); RootedPropertyName name(cx, cache.name()); GetPropertyIC::NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape); if (canCache == GetPropertyIC::CanAttachReadSlot) { if (!cache.attachReadSlot(cx, ion, obj, holder, shape)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) { if (!cache.attachArrayLength(cx, ion, obj)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } } if (!attachedStub && !cache.hasTypedArrayLengthStub() && obj->is<TypedArrayObject>() && slice->names().length == cache.name() && (cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32)) { if (!cache.attachTypedArrayLength(cx, ion, obj)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } } } - return TP_SUCCESS; + return true; } void IonCache::disable() { reset(); this->disabled_ = 1; } @@ -1931,108 +1933,112 @@ IonCache::reset() this->stubCount_ = 0; } void IonCache::destroy() { } -bool -SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, - HandleShape shape, bool checkTypeset) +static void +GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, Shape *shape, Register object, ConstantOrRegister value, + bool needsTypeBarrier, bool checkTypeset) { JS_ASSERT(obj->isNative()); - MacroAssembler masm(cx); - RepatchStubAppender attacher(*this); - Label failures, barrierFailure; masm.branchPtr(Assembler::NotEqual, - Address(object(), JSObject::offsetOfShape()), + Address(object, JSObject::offsetOfShape()), ImmGCPtr(obj->lastProperty()), &failures); // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (needsTypeBarrier()) { + if (needsTypeBarrier) { // We can't do anything that would change the HeapTypeSet, so // just guard that it's already there. // Obtain and guard on the TypeObject of the object. - types::TypeObject *type = obj->getType(cx); + types::TypeObject *type = obj->type(); masm.branchPtr(Assembler::NotEqual, - Address(object(), JSObject::offsetOfType()), + Address(object, JSObject::offsetOfType()), ImmGCPtr(type), &failures); if (checkTypeset) { - TypedOrValueRegister valReg = value().reg(); - types::HeapTypeSet *propTypes = type->maybeGetProperty(NameToId(name())); + TypedOrValueRegister valReg = value.reg(); + types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid()); JS_ASSERT(propTypes); JS_ASSERT(!propTypes->unknown()); - Register scratchReg = object(); + Register scratchReg = object; masm.push(scratchReg); masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure); - masm.pop(object()); + masm.pop(object); } } if (obj->isFixedSlot(shape->slot())) { - Address addr(object(), JSObject::getFixedSlotOffset(shape->slot())); + Address addr(object, JSObject::getFixedSlotOffset(shape->slot())); if (cx->zone()->needsBarrier()) masm.callPreBarrier(addr, MIRType_Value); - masm.storeConstantOrRegister(value(), addr); + masm.storeConstantOrRegister(value, addr); } else { - Register slotsReg = object(); - masm.loadPtr(Address(object(), JSObject::offsetOfSlots()), slotsReg); + Register slotsReg = object; + masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg); Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value)); if (cx->zone()->needsBarrier()) masm.callPreBarrier(addr, MIRType_Value); - masm.storeConstantOrRegister(value(), addr); + masm.storeConstantOrRegister(value, addr); } attacher.jumpRejoin(masm); if (barrierFailure.used()) { masm.bind(&barrierFailure); - masm.pop(object()); + masm.pop(object); } masm.bind(&failures); attacher.jumpNextStub(masm); - +} + +bool +SetPropertyIC::attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, + HandleShape shape, bool checkTypeset) +{ + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(), + checkTypeset); return linkAndAttachStub(cx, masm, attacher, ion, "setting"); } static bool IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape) { - if (!obj->isNative()) - return false; + JS_ASSERT(obj->isNative()); if (!shape || !IsCacheableProtoChain(obj, holder)) return false; return shape->hasSetterValue() && shape->setterObject() && shape->setterObject()->is<JSFunction>() && shape->setterObject()->as<JSFunction>().isNative(); } static bool -IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, - HandleShape shape) +IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape) { - if (!obj->isNative()) - return false; + JS_ASSERT(obj->isNative()); if (!shape) return false; if (!IsCacheableProtoChain(obj, holder)) return false; if (shape->hasSlot()) @@ -2477,17 +2483,17 @@ SetPropertyIC::attachDOMProxyUnshadowed( // Failure. masm.bind(&failures); attacher.jumpNextStub(masm); return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy unshadowed set"); } bool -SetPropertyIC::attachSetterCall(JSContext *cx, IonScript *ion, +SetPropertyIC::attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr) { JS_ASSERT(obj->isNative()); MacroAssembler masm(cx); RepatchStubAppender attacher(*this); @@ -2512,120 +2518,125 @@ SetPropertyIC::attachSetterCall(JSContex // Jump to next stub. masm.bind(&failure); attacher.jumpNextStub(masm); return linkAndAttachStub(cx, masm, attacher, ion, "setter call"); } -bool -SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, - HandleShape oldShape, HandleShape newShape, - HandleShape propShape) +static void +GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, Shape *oldShape, Register object, ConstantOrRegister value) { JS_ASSERT(obj->isNative()); - MacroAssembler masm(cx); - RepatchStubAppender attacher(*this); - Label failures; - /* Guard the type of the object */ - masm.branchPtr(Assembler::NotEqual, Address(object(), JSObject::offsetOfType()), + // Guard the type of the object + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()), ImmGCPtr(obj->type()), &failures); - /* Guard shapes along prototype chain. */ - masm.branchTestObjShape(Assembler::NotEqual, object(), oldShape, &failures); + // Guard shapes along prototype chain. + masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures); Label protoFailures; - masm.push(object()); // save object reg because we clobber it + masm.push(object); // save object reg because we clobber it JSObject *proto = obj->getProto(); - Register protoReg = object(); + Register protoReg = object; while (proto) { Shape *protoShape = proto->lastProperty(); // load next prototype masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg); masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg); // Ensure that its shape matches. masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &protoFailures); proto = proto->getProto(); } - masm.pop(object()); // restore object reg - - /* Changing object shape. Write the object's new shape. */ - Address shapeAddr(object(), JSObject::offsetOfShape()); + masm.pop(object); // restore object reg + + // Changing object shape. Write the object's new shape. + Shape *newShape = obj->lastProperty(); + Address shapeAddr(object, JSObject::offsetOfShape()); if (cx->zone()->needsBarrier()) masm.callPreBarrier(shapeAddr, MIRType_Shape); masm.storePtr(ImmGCPtr(newShape), shapeAddr); - /* Set the value on the object. */ - if (obj->isFixedSlot(propShape->slot())) { - Address addr(object(), JSObject::getFixedSlotOffset(propShape->slot())); - masm.storeConstantOrRegister(value(), addr); + // Set the value on the object. Since this is an add, obj->lastProperty() + // must be the shape of the property we are adding. + if (obj->isFixedSlot(newShape->slot())) { + Address addr(object, JSObject::getFixedSlotOffset(newShape->slot())); + masm.storeConstantOrRegister(value, addr); } else { - Register slotsReg = object(); - - masm.loadPtr(Address(object(), JSObject::offsetOfSlots()), slotsReg); - - Address addr(slotsReg, obj->dynamicSlotIndex(propShape->slot()) * sizeof(Value)); - masm.storeConstantOrRegister(value(), addr); + Register slotsReg = object; + + masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg); + + Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value)); + masm.storeConstantOrRegister(value, addr); } - /* Success. */ + // Success. attacher.jumpRejoin(masm); - /* Failure. */ + // Failure. masm.bind(&protoFailures); - masm.pop(object()); + masm.pop(object); masm.bind(&failures); attacher.jumpNextStub(masm); - +} + +bool +SetPropertyIC::attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape) +{ + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value()); return linkAndAttachStub(cx, masm, attacher, ion, "adding"); } static bool -IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj, - HandleId id, MutableHandleShape pshape, bool *checkTypeset) +IsPropertySetInlineable(HandleObject obj, HandleId id, MutableHandleShape pshape, + ConstantOrRegister val, bool needsTypeBarrier, bool *checkTypeset) { - if (!obj->isNative()) - return false; - - pshape.set(obj->nativeLookup(cx, id)); + JS_ASSERT(obj->isNative()); + + // Do a pure non-proto chain climbing lookup. See note in + // CanAttachNativeGetProp. + pshape.set(obj->nativeLookupPure(id)); if (!pshape) return false; if (!pshape->hasSlot()) return false; if (!pshape->hasDefaultSetter()) return false; if (!pshape->writable()) return false; bool shouldCheck = false; - types::TypeObject *type = obj->getType(cx); - if (cache.needsTypeBarrier() && !type->unknownProperties()) { + types::TypeObject *type = obj->type(); + if (needsTypeBarrier && !type->unknownProperties()) { types::HeapTypeSet *propTypes = type->maybeGetProperty(id); if (!propTypes) return false; if (!propTypes->unknown()) { shouldCheck = true; - ConstantOrRegister val = cache.value(); if (val.constant()) { // If the input is a constant, then don't bother if the barrier will always fail. - if (!propTypes->hasType(types::GetValueType(cache.value().value()))) + if (!propTypes->hasType(types::GetValueType(val.value()))) return false; shouldCheck = false; } else { TypedOrValueRegister reg = val.reg(); // We can do the same trick as above for primitive types of specialized registers. // TIs handling of objects is complicated enough to warrant a runtime // check, as we can't statically handle the case where the typeset // contains the specific object, but doesn't have ANYOBJECT set. @@ -2640,89 +2651,121 @@ IsPropertySetInlineable(JSContext *cx, c } *checkTypeset = shouldCheck; return true; } static bool -IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t oldSlots, - MutableHandleShape pShape) +IsPropertyAddInlineable(HandleObject obj, HandleId id, uint32_t oldSlots, HandleShape oldShape) { - if (!obj->isNative()) + JS_ASSERT(obj->isNative()); + + // If the shape of the object did not change, then this was not an add. + if (obj->lastProperty() == oldShape) return false; - // This is not a Add, the property exists. - if (pShape.get()) - return false; - - RootedShape shape(cx, obj->nativeLookup(cx, id)); + Shape *shape = obj->nativeLookupPure(id); if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter()) return false; + // If we have a shape at this point and the object's shape changed, then + // the shape must be the one we just added. + JS_ASSERT(shape == obj->lastProperty()); + // If object has a non-default resolve hook, don't inline if (obj->getClass()->resolve != JS_ResolveStub) return false; // Likewise for a non-default addProperty hook, since we'll need // to invoke it. if (obj->getClass()->addProperty != JS_PropertyStub) return false; if (!obj->nonProxyIsExtensible() || !shape->writable()) return false; - // walk up the object prototype chain and ensure that all prototypes + // Walk up the object prototype chain and ensure that all prototypes // are native, and that all prototypes have no getter or setter // defined on the property for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) { - // if prototype is non-native, don't optimize + // If prototype is non-native, don't optimize if (!proto->isNative()) return false; - // if prototype defines this property in a non-plain way, don't optimize - Shape *protoShape = proto->nativeLookup(cx, id); + // If prototype defines this property in a non-plain way, don't optimize + Shape *protoShape = proto->nativeLookupPure(id); if (protoShape && !protoShape->hasDefaultSetter()) return false; - // Otherise, if there's no such property, watch out for a resolve hook that would need - // to be invoked and thus prevent inlining of property addition. + // Otherwise, if there's no such property, watch out for a resolve + // hook that would need to be invoked and thus prevent inlining of + // property addition. if (proto->getClass()->resolve != JS_ResolveStub) return false; } // Only add a IC entry if the dynamic slots didn't change when the shapes // changed. Need to ensure that a shape change for a subsequent object // won't involve reallocating the slot array. if (obj->numDynamicSlots() != oldSlots) return false; - pShape.set(shape); return true; } +static SetPropertyIC::NativeSetPropCacheability +CanAttachNativeSetProp(HandleObject obj, HandleId id, ConstantOrRegister val, + bool needsTypeBarrier, MutableHandleObject holder, + MutableHandleShape shape, bool *checkTypeset) +{ + if (!obj->isNative()) + return SetPropertyIC::CanAttachNone; + + // See if the property exists on the object. + if (IsPropertySetInlineable(obj, id, shape, val, needsTypeBarrier, checkTypeset)) + return SetPropertyIC::CanAttachSetSlot; + + // If we couldn't find the property on the object itself, do a full, but + // still pure lookup for setters. + if (!LookupPropertyPure(obj, id, holder.address(), shape.address())) + return SetPropertyIC::CanAttachNone; + + // If the object doesn't have the property, we don't know if we can attach + // a stub to add the property until we do the VM call to add. + if (!shape) + return SetPropertyIC::MaybeCanAttachAddSlot; + + if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) || + IsCacheableSetPropCallNative(obj, holder, shape)) + { + return SetPropertyIC::CanAttachCallSetter; + } + + return SetPropertyIC::CanAttachNone; +} + bool SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value) { AutoFlushCache afc ("SetPropertyCache", cx->runtime()->ionRuntime()); void *returnAddr; RootedScript script(cx, GetTopIonJSScript(cx, &returnAddr)); IonScript *ion = script->ionScript(); SetPropertyIC &cache = ion->getCache(cacheIndex).toSetProperty(); RootedPropertyName name(cx, cache.name()); RootedId id(cx, AtomToId(name)); - RootedShape shape(cx); - RootedObject holder(cx); // Stop generating new stubs once we hit the stub count limit, see // GetPropertyCache. bool inlinable = cache.canAttachStub() && !obj->watched(); + NativeSetPropCacheability canCache = CanAttachNone; bool addedSetterStub = false; if (inlinable) { if (!addedSetterStub && obj->is<ProxyObject>()) { if (IsCacheableDOMProxy(obj)) { DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id); if (shadows == ShadowCheckFailed) return false; if (shadows == Shadows) { @@ -2740,64 +2783,162 @@ SetPropertyIC::update(JSContext *cx, siz } if (!addedSetterStub && !cache.hasGenericProxyStub()) { if (!cache.attachGenericProxy(cx, ion, returnAddr)) return false; addedSetterStub = true; } } + + // Make sure the object de-lazifies its type. We do this here so that + // the parallel IC can share code that assumes that native objects all + // have a type object. + if (obj->isNative() && !obj->getType(cx)) + return false; + RootedShape shape(cx); + RootedObject holder(cx); bool checkTypeset; - if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape, &checkTypeset)) { - if (!cache.attachNativeExisting(cx, ion, obj, shape, checkTypeset)) + canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(), + &holder, &shape, &checkTypeset); + + if (!addedSetterStub && canCache == CanAttachSetSlot) { + if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset)) return false; addedSetterStub = true; - } else if (!addedSetterStub) { - RootedObject holder(cx); - if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape)) + } + + if (!addedSetterStub && canCache == CanAttachCallSetter) { + if (!cache.attachCallSetter(cx, ion, obj, holder, shape, returnAddr)) return false; - - if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) || - IsCacheableSetPropCallNative(obj, holder, shape)) - { - if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr)) - return false; - addedSetterStub = true; - } + addedSetterStub = true; } } uint32_t oldSlots = obj->numDynamicSlots(); RootedShape oldShape(cx, obj->lastProperty()); // Set/Add the property on the object, the inlined cache are setup for the next execution. if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc())) return false; // The property did not exist before, now we can try to inline the property add. - if (inlinable && !addedSetterStub && !cache.needsTypeBarrier() && - obj->lastProperty() != oldShape && - IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) + if (!addedSetterStub && canCache == MaybeCanAttachAddSlot && + !cache.needsTypeBarrier() && + IsPropertyAddInlineable(obj, id, oldSlots, oldShape)) { - RootedShape newShape(cx, obj->lastProperty()); - if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape)) + if (!cache.attachAddSlot(cx, ion, obj, oldShape)) return false; } return true; } void SetPropertyIC::reset() { RepatchIonCache::reset(); hasGenericProxyStub_ = false; } +bool +SetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, + HandleValue value) +{ + JS_ASSERT(slice->isThreadLocal(obj)); + + AutoFlushCache afc("SetPropertyParCache", slice->runtime()->ionRuntime()); + + IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript(); + SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar(); + + RootedValue v(slice, value); + RootedId id(slice, AtomToId(cache.name())); + + // Avoid unnecessary locking if cannot attach stubs. + if (!cache.canAttachStub()) { + return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, + &v, cache.strict()); + } + + SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone; + bool attachedStub = false; + + { + LockedJSContext cx(slice); + + if (cache.canAttachStub()) { + bool alreadyStubbed; + if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + if (alreadyStubbed) { + return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, + &v, cache.strict()); + } + + // If the object has a lazy type, we need to de-lazify it, but + // this is not safe in parallel. + if (obj->hasLazyType()) + return false; + + { + RootedShape shape(slice); + RootedObject holder(slice); + bool checkTypeset; + canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(), + &holder, &shape, &checkTypeset); + + if (canCache == SetPropertyIC::CanAttachSetSlot) { + if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + attachedStub = true; + } + } + } + } + + uint32_t oldSlots = obj->numDynamicSlots(); + RootedShape oldShape(slice, obj->lastProperty()); + + if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, cache.strict())) + return false; + + if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot && + !cache.needsTypeBarrier() && + IsPropertyAddInlineable(obj, id, oldSlots, oldShape)) + { + LockedJSContext cx(slice); + if (cache.canAttachStub() && !cache.attachAddSlot(cx, ion, obj, oldShape)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + } + + return true; +} + +bool +SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape, + bool checkTypeset) +{ + MacroAssembler masm(cx); + DispatchStubPrepender attacher(*this); + GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(), + checkTypeset); + return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting"); +} + +bool +SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape) +{ + MacroAssembler masm(cx); + DispatchStubPrepender attacher(*this); + GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value()); + return linkAndAttachStub(cx, masm, attacher, ion, "parallel adding"); +} + const size_t GetElementIC::MAX_FAILED_UPDATES = 16; /* static */ bool GetElementIC::canAttachGetProp(JSObject *obj, const Value &idval, jsid id) { uint32_t dummy; return (obj->isNative() && idval.isString() && @@ -3235,25 +3376,25 @@ GetElementIC::reset() { RepatchIonCache::reset(); hasDenseStub_ = false; hasStrictArgumentsStub_ = false; hasNormalArgumentsStub_ = false; } static bool -IsElementSetInlineable(HandleObject obj, HandleValue index) +IsDenseElementSetInlineable(JSObject *obj, const Value &idval) { if (!obj->is<ArrayObject>()) return false; if (obj->watched()) return false; - if (!index.isInt32()) + if (!idval.isInt32()) return false; // The object may have a setter definition, // either directly, or via a prototype, or via the target object for a prototype // which is a proxy, that handles a particular integer write. // Scan the prototype and shape chain to make sure that this is not the case. JSObject *curObj = obj; while (curObj) { @@ -3266,45 +3407,51 @@ IsElementSetInlineable(HandleObject obj, return false; curObj = curObj->getProto(); } return true; } -bool -SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval) +static bool +IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value &value) +{ + // Don't bother attaching stubs for assigning strings and objects. + return (obj->is<TypedArrayObject>() && idval.isInt32() && + !value.isString() && !value.isObject()); +} + +static bool +GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, + JSObject *obj, const Value &idval, Register object, ValueOperand indexVal, + ConstantOrRegister value, Register tempToUnboxIndex, Register temp) { JS_ASSERT(obj->isNative()); JS_ASSERT(idval.isInt32()); Label failures; Label outOfBounds; // index >= capacity || index > initialized length - MacroAssembler masm(cx); - RepatchStubAppender attacher(*this); - // Guard object is a dense array. - RootedShape shape(cx, obj->lastProperty()); + Shape *shape = obj->lastProperty(); if (!shape) return false; - masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures); + masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures); // Ensure the index is an int32 value. - ValueOperand indexVal = index(); masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures); // Unbox the index. - Register index = masm.extractInt32(indexVal, tempToUnboxIndex()); + Register index = masm.extractInt32(indexVal, tempToUnboxIndex); { // Load obj->elements. - Register elements = temp(); - masm.loadPtr(Address(object(), JSObject::offsetOfElements()), elements); + Register elements = temp; + masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements); // Compute the location of the element. BaseIndex target(elements, index, TimesEight); // Guard that we can increase the initialized length. Address capacity(elements, ObjectElements::offsetOfCapacity()); masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds); @@ -3337,30 +3484,45 @@ SetElementIC::attachDenseElement(JSConte // Mark old element. masm.bind(&markElem); if (cx->zone()->needsBarrier()) masm.callPreBarrier(target, MIRType_Value); } // Call post barrier if necessary, and recalculate elements pointer if it got cobbered. Register postBarrierScratch = elements; - if (masm.maybeCallPostBarrier(object(), value(), postBarrierScratch)) - masm.loadPtr(Address(object(), JSObject::offsetOfElements()), elements); + if (masm.maybeCallPostBarrier(object, value, postBarrierScratch)) + masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements); // Store the value. masm.bind(&storeElem); - masm.storeConstantOrRegister(value(), target); + masm.storeConstantOrRegister(value, target); } attacher.jumpRejoin(masm); // All failures flow to here. masm.bind(&outOfBounds); masm.bind(&failures); attacher.jumpNextStub(masm); + return true; +} + +bool +SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval) +{ + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval, + object(), index(), value(), + tempToUnboxIndex(), temp())) + { + return false; + } + setHasDenseStub(); return linkAndAttachStub(cx, masm, attacher, ion, "dense array"); } static bool GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, TypedArrayObject *tarr, Register object, ValueOperand indexVal, ConstantOrRegister value, @@ -3438,24 +3600,16 @@ GenerateSetTypedArrayElement(JSContext * masm.pop(object); } masm.bind(&failures); attacher.jumpNextStub(masm); return true; } -/* static */ bool -SetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value) -{ - // Don't bother attaching stubs for assigning strings and objects. - return (obj->is<TypedArrayObject>() && idval.isInt32() && - !value.isString() && !value.isObject()); -} - bool SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr) { MacroAssembler masm(cx); RepatchStubAppender attacher(*this); if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr, object(), index(), value(), tempToUnboxIndex(), temp(), tempFloat())) @@ -3470,22 +3624,22 @@ bool SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, HandleValue value) { IonScript *ion = GetTopIonJSScript(cx)->ionScript(); SetElementIC &cache = ion->getCache(cacheIndex).toSetElement(); bool attachedStub = false; if (cache.canAttachStub()) { - if (!cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) { + if (!cache.hasDenseStub() && IsDenseElementSetInlineable(obj, idval)) { if (!cache.attachDenseElement(cx, ion, obj, idval)) return false; attachedStub = true; } - if (!attachedStub && canAttachTypedArrayElement(obj, idval, value)) { + if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) { TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); if (!cache.attachTypedArrayElement(cx, ion, tarr)) return false; } } if (!SetObjectElement(cx, obj, idval, value, cache.strict())) return false; @@ -3495,16 +3649,86 @@ SetElementIC::update(JSContext *cx, size void SetElementIC::reset() { RepatchIonCache::reset(); hasDenseStub_ = false; } bool +SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, + const Value &idval) +{ + MacroAssembler masm(cx); + DispatchStubPrepender attacher(*this); + if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval, + object(), index(), value(), + tempToUnboxIndex(), temp())) + { + return false; + } + + return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense array"); +} + +bool +SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, + TypedArrayObject *tarr) +{ + MacroAssembler masm(cx); + DispatchStubPrepender attacher(*this); + if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr, + object(), index(), value(), + tempToUnboxIndex(), temp(), tempFloat())) + { + return false; + } + + return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array"); +} + +bool +SetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, + HandleValue idval, HandleValue value) +{ + IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript(); + SetElementParIC &cache = ion->getCache(cacheIndex).toSetElementPar(); + + // Avoid unnecessary locking if cannot attach stubs. + if (!cache.canAttachStub()) + return SetElementPar(slice, obj, idval, value, cache.strict()); + + { + LockedJSContext cx(slice); + + if (cache.canAttachStub()) { + bool alreadyStubbed; + if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + if (alreadyStubbed) + return SetElementPar(slice, obj, idval, value, cache.strict()); + + bool attachedStub = false; + if (IsDenseElementSetInlineable(obj, idval)) { + if (!cache.attachDenseElement(cx, ion, obj, idval)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + attachedStub = true; + } + if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) { + TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); + if (!cache.attachTypedArrayElement(cx, ion, tarr)) + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); + } + } + } + + return SetElementPar(slice, obj, idval, value, cache.strict()); +} + +bool GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval, PropertyName *name, JSObject *holder, Shape *shape) { MacroAssembler masm(cx); DispatchStubPrepender attacher(*this); // Guard on the index value. @@ -3535,85 +3759,84 @@ GetElementParIC::attachTypedArrayElement TypedArrayObject *tarr, const Value &idval) { MacroAssembler masm(cx); DispatchStubPrepender attacher(*this); GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output()); return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array"); } -ParallelResult +bool GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval, MutableHandleValue vp) { AutoFlushCache afc("GetElementParCache", slice->runtime()->ionRuntime()); IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript(); GetElementParIC &cache = ion->getCache(cacheIndex).toGetElementPar(); // Try to get the element early, as the pure path doesn't need a lock. If // we can't do it purely, bail out of parallel execution. if (!GetObjectElementOperationPure(slice, obj, idval, vp.address())) - return TP_RETRY_SEQUENTIALLY; + return false; // Avoid unnecessary locking if cannot attach stubs. if (!cache.canAttachStub()) - return TP_SUCCESS; + return true; { LockedJSContext cx(slice); if (cache.canAttachStub()) { bool alreadyStubbed; if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); if (alreadyStubbed) - return TP_SUCCESS; + return true; jsid id; if (!ValueToIdPure(idval, &id)) - return TP_FATAL; + return false; bool attachedStub = false; - if (cache.monitoredResult() && GetElementIC::canAttachGetProp(obj, idval, id)) { RootedShape shape(cx); RootedObject holder(cx); RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); GetPropertyIC::NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape); if (canCache == GetPropertyIC::CanAttachReadSlot) { if (!cache.attachReadSlot(cx, ion, obj, idval, name, holder, shape)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } } if (!attachedStub && GetElementIC::canAttachDenseElement(obj, idval)) { if (!cache.attachDenseElement(cx, ion, obj, idval)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } if (!attachedStub && GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output())) { if (!cache.attachTypedArrayElement(cx, ion, &obj->as<TypedArrayObject>(), idval)) - return TP_FATAL; + return slice->setPendingAbortFatal(ParallelBailoutFailedIC); attachedStub = true; } } } - return TP_SUCCESS; + return true; } bool BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain) { JS_ASSERT(scopeChain->is<GlobalObject>()); MacroAssembler masm(cx);
--- a/js/src/jit/IonCaches.h +++ b/js/src/jit/IonCaches.h @@ -24,17 +24,19 @@ namespace jit { _(GetProperty) \ _(SetProperty) \ _(GetElement) \ _(SetElement) \ _(BindName) \ _(Name) \ _(CallsiteClone) \ _(GetPropertyPar) \ - _(GetElementPar) + _(GetElementPar) \ + _(SetPropertyPar) \ + _(SetElementPar) // Forward declarations of Cache kinds. #define FORWARD_DECLARE(kind) class kind##IC; IONCACHE_KIND_LIST(FORWARD_DECLARE) #undef FORWARD_DECLARE class IonCacheVisitor { @@ -673,22 +675,28 @@ class SetPropertyIC : public RepatchIonC } bool needsTypeBarrier() const { return needsTypeBarrier_; } bool hasGenericProxyStub() const { return hasGenericProxyStub_; } - bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, - HandleShape shape, bool checkTypeset); - bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj, + enum NativeSetPropCacheability { + CanAttachNone, + CanAttachSetSlot, + MaybeCanAttachAddSlot, + CanAttachCallSetter + }; + + bool attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape, + bool checkTypeset); + bool attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, HandleObject holder, HandleShape shape, void *returnAddr); - bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape, - HandleShape newshape, HandleShape propshape); + bool attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape); bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr); bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, void *returnAddr); bool attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj, void *returnAddr); static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value); @@ -767,17 +775,17 @@ class GetElementIC : public RepatchIonCa bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, HandlePropertyName name); bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr, const Value &idval); bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj); static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, - MutableHandleValue vp); + MutableHandleValue vp); void incFailedUpdates() { failedUpdates_++; } void resetFailedUpdates() { failedUpdates_ = 0; } bool shouldDisable() const { @@ -843,18 +851,16 @@ class SetElementIC : public RepatchIonCa bool hasDenseStub() const { return hasDenseStub_; } void setHasDenseStub() { JS_ASSERT(!hasDenseStub()); hasDenseStub_ = true; } - static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value); - bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr); static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, HandleValue value); }; @@ -1044,18 +1050,18 @@ class GetPropertyParIC : public Parallel bool allowGetters() const { return false; } bool allowArrayLength(Context, HandleObject) const { return true; } bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder, Shape *shape); bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); - static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, - MutableHandleValue vp); + static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, + MutableHandleValue vp); }; class GetElementParIC : public ParallelIonCache { protected: Register object_; ConstantOrRegister index_; TypedOrValueRegister output_; @@ -1100,19 +1106,133 @@ class GetElementParIC : public ParallelI bool allowArrayLength(Context, HandleObject) const { return false; } bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval, PropertyName *name, JSObject *holder, Shape *shape); bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr, const Value &idval); - static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval, - MutableHandleValue vp); + static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval, + MutableHandleValue vp); + +}; + +class SetPropertyParIC : public ParallelIonCache +{ + protected: + Register object_; + PropertyName *name_; + ConstantOrRegister value_; + bool strict_; + bool needsTypeBarrier_; + + public: + SetPropertyParIC(Register object, PropertyName *name, ConstantOrRegister value, + bool strict, bool needsTypeBarrier) + : object_(object), + name_(name), + value_(value), + strict_(strict), + needsTypeBarrier_(needsTypeBarrier) + { + } + + CACHE_HEADER(SetPropertyPar) + +#ifdef JS_CPU_X86 + // x86 lacks a general purpose scratch register for dispatch caches and + // must be given one manually. + void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); +#endif + + Register object() const { + return object_; + } + PropertyName *name() const { + return name_; + } + ConstantOrRegister value() const { + return value_; + } + bool strict() const { + return strict_; + } + bool needsTypeBarrier() const { + return needsTypeBarrier_; + } + + bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape, + bool checkTypeset); + bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape); + + static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, + HandleValue value); +}; +class SetElementParIC : public ParallelIonCache +{ + protected: + Register object_; + Register tempToUnboxIndex_; + Register temp_; + FloatRegister tempFloat_; + ValueOperand index_; + ConstantOrRegister value_; + bool strict_; + + public: + SetElementParIC(Register object, Register tempToUnboxIndex, Register temp, + FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, + bool strict) + : object_(object), + tempToUnboxIndex_(tempToUnboxIndex), + temp_(temp), + tempFloat_(tempFloat), + index_(index), + value_(value), + strict_(strict) + { + } + + CACHE_HEADER(SetElementPar) + +#ifdef JS_CPU_X86 + // x86 lacks a general purpose scratch register for dispatch caches and + // must be given one manually. + void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); +#endif + + Register object() const { + return object_; + } + Register tempToUnboxIndex() const { + return tempToUnboxIndex_; + } + Register temp() const { + return temp_; + } + FloatRegister tempFloat() const { + return tempFloat_; + } + ValueOperand index() const { + return index_; + } + ConstantOrRegister value() const { + return value_; + } + bool strict() const { + return strict_; + } + + bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); + bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr); + + static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, + HandleValue idval, HandleValue value); }; #undef CACHE_HEADER // Implement cache casts now that the compiler can see the inheritance. #define CACHE_CASTS(ickind) \ ickind##IC &IonCache::to##ickind() \ { \
--- a/js/src/jit/IonFrameIterator.h +++ b/js/src/jit/IonFrameIterator.h @@ -80,31 +80,34 @@ class IonFrameIterator uint8_t *current_; FrameType type_; uint8_t *returnAddressToFp_; size_t frameSize_; private: mutable const SafepointIndex *cachedSafepointIndex_; const JitActivation *activation_; + ExecutionMode mode_; void dumpBaseline() const; public: - IonFrameIterator(uint8_t *top) + explicit IonFrameIterator(uint8_t *top, ExecutionMode mode) : current_(top), type_(IonFrame_Exit), returnAddressToFp_(nullptr), frameSize_(0), cachedSafepointIndex_(nullptr), - activation_(nullptr) + activation_(nullptr), + mode_(mode) { } - IonFrameIterator(const ActivationIterator &activations); - IonFrameIterator(IonJSFrameLayout *fp); + explicit IonFrameIterator(JSContext *cx); + explicit IonFrameIterator(const ActivationIterator &activations); + explicit IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode); // Current frame information. FrameType type() const { return type_; } uint8_t *fp() const { return current_; } @@ -146,17 +149,16 @@ class IonFrameIterator bool isOOLNative() const; bool isOOLPropertyOp() const; bool isOOLProxy() const; bool isDOMExit() const; bool isEntry() const { return type_ == IonFrame_Entry; } bool isFunctionFrame() const; - bool isParallelFunctionFrame() const; bool isConstructing() const; void *calleeToken() const; JSFunction *callee() const; JSFunction *maybeCallee() const; unsigned numActualArgs() const; JSScript *script() const;
--- a/js/src/jit/IonFrames-inl.h +++ b/js/src/jit/IonFrames-inl.h @@ -8,16 +8,17 @@ #define jit_IonFrames_inl_h #ifdef JS_ION #include "jit/IonFrames.h" #include "jit/IonFrameIterator.h" #include "jit/LIR.h" +#include "vm/ForkJoin.h" #include "jit/IonFrameIterator-inl.h" namespace js { namespace jit { inline void SafepointIndex::resolve() @@ -64,23 +65,35 @@ IonFrameIterator::exitFrame() const JS_ASSERT(type() == IonFrame_Exit); JS_ASSERT(!isFakeExitFrame()); return (IonExitFrameLayout *) fp(); } inline BaselineFrame * GetTopBaselineFrame(JSContext *cx) { - IonFrameIterator iter(cx->mainThread().ionTop); + IonFrameIterator iter(cx); JS_ASSERT(iter.type() == IonFrame_Exit); ++iter; if (iter.isBaselineStub()) ++iter; JS_ASSERT(iter.isBaselineJS()); return iter.baselineFrame(); } +inline JSScript * +GetTopIonJSScript(JSContext *cx, void **returnAddrOut = nullptr) +{ + return GetTopIonJSScript(cx->mainThread().ionTop, returnAddrOut, SequentialExecution); +} + +inline JSScript * +GetTopIonJSScript(ForkJoinSlice *slice, void **returnAddrOut = nullptr) +{ + return GetTopIonJSScript(slice->perThreadData->ionTop, returnAddrOut, ParallelExecution); +} + } // namespace jit } // namespace js #endif // JS_ION #endif /* jit_IonFrames_inl_h */
--- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -27,31 +27,44 @@ #include "vm/Interpreter.h" #include "jit/IonFrameIterator-inl.h" #include "vm/Probes-inl.h" namespace js { namespace jit { +IonFrameIterator::IonFrameIterator(JSContext *cx) + : current_(cx->mainThread().ionTop), + type_(IonFrame_Exit), + returnAddressToFp_(nullptr), + frameSize_(0), + cachedSafepointIndex_(nullptr), + activation_(nullptr), + mode_(SequentialExecution) +{ +} + IonFrameIterator::IonFrameIterator(const ActivationIterator &activations) : current_(activations.jitTop()), type_(IonFrame_Exit), returnAddressToFp_(nullptr), frameSize_(0), cachedSafepointIndex_(nullptr), - activation_(activations.activation()->asJit()) + activation_(activations.activation()->asJit()), + mode_(SequentialExecution) { } -IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp) +IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode) : current_((uint8_t *)fp), type_(IonFrame_OptimizedJS), returnAddressToFp_(fp->returnAddress()), - frameSize_(fp->prevFrameLocalSize()) + frameSize_(fp->prevFrameLocalSize()), + mode_(mode) { } bool IonFrameIterator::checkInvalidation() const { IonScript *dummy; return checkInvalidation(&dummy); @@ -60,19 +73,19 @@ IonFrameIterator::checkInvalidation() co bool IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const { uint8_t *returnAddr = returnAddressToFp(); JSScript *script = this->script(); // N.B. the current IonScript is not the same as the frame's // IonScript if the frame has since been invalidated. bool invalidated; - if (isParallelFunctionFrame()) { - invalidated = !script->hasParallelIonScript() || - !script->parallelIonScript()->containsReturnAddress(returnAddr); + if (mode_ == ParallelExecution) { + // Parallel execution does not have invalidating bailouts. + invalidated = false; } else { invalidated = !script->hasIonScript() || !script->ionScript()->containsReturnAddress(returnAddr); } if (!invalidated) return false; int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1]; @@ -88,26 +101,24 @@ IonFrameIterator::calleeToken() const { return ((IonJSFrameLayout *) current_)->calleeToken(); } JSFunction * IonFrameIterator::callee() const { JS_ASSERT(isScripted()); - JS_ASSERT(isFunctionFrame() || isParallelFunctionFrame()); - if (isFunctionFrame()) - return CalleeTokenToFunction(calleeToken()); - return CalleeTokenToParallelFunction(calleeToken()); + JS_ASSERT(isFunctionFrame()); + return CalleeTokenToFunction(calleeToken()); } JSFunction * IonFrameIterator::maybeCallee() const { - if (isScripted() && (isFunctionFrame() || isParallelFunctionFrame())) + if (isScripted() && (isFunctionFrame())) return callee(); return nullptr; } bool IonFrameIterator::isNative() const { if (type_ != IonFrame_Exit || isFakeExitFrame()) @@ -148,22 +159,16 @@ IonFrameIterator::isDOMExit() const } bool IonFrameIterator::isFunctionFrame() const { return CalleeTokenIsFunction(calleeToken()); } -bool -IonFrameIterator::isParallelFunctionFrame() const -{ - return GetCalleeTokenTag(calleeToken()) == CalleeToken_ParallelFunction; -} - JSScript * IonFrameIterator::script() const { JS_ASSERT(isScripted()); if (isBaselineJS()) return baselineFrame()->script(); JSScript *script = ScriptFromCalleeToken(calleeToken()); JS_ASSERT(script); @@ -521,17 +526,17 @@ HandleException(ResumeFromException *rfe // Clear any Ion return override that's been set. // This may happen if a callVM function causes an invalidation (setting the // override), and then fails, bypassing the bailout handlers that would // otherwise clear the return override. if (cx->runtime()->hasIonReturnOverride()) cx->runtime()->takeIonReturnOverride(); - IonFrameIterator iter(cx->mainThread().ionTop); + IonFrameIterator iter(cx); while (!iter.isEntry()) { bool overrecursed = false; if (iter.isOptimizedJS()) { // Search each inlined frame for live iterator objects, and close // them. InlineFrameIterator frames(cx, &iter); for (;;) { HandleExceptionIon(cx, frames, rfe, &overrecursed); @@ -611,24 +616,24 @@ HandleException(ResumeFromException *rfe rfe->stackPointer = iter.fp(); } void HandleParallelFailure(ResumeFromException *rfe) { ForkJoinSlice *slice = ForkJoinSlice::Current(); - IonFrameIterator iter(slice->perThreadData->ionTop); + IonFrameIterator iter(slice->perThreadData->ionTop, ParallelExecution); parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry"); while (!iter.isEntry()) { if (iter.isScripted()) { - slice->bailoutRecord->setCause(ParallelBailoutFailedIC, - iter.script(), iter.script(), nullptr); + slice->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM, + iter.script(), iter.script(), nullptr); break; } ++iter; } while (!iter.isEntry()) { if (iter.isScripted()) PropagateAbortPar(iter.script(), iter.script()); @@ -849,17 +854,17 @@ MarkBaselineStubFrame(JSTracer *trc, con JS_ASSERT(ICStub::CanMakeCalls(stub->kind())); stub->trace(trc); } } void JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end) { - IonFrameIterator frames(jitTop()); + IonFrameIterator frames(jitTop(), SequentialExecution); if (frames.isFakeExitFrame()) { min = reinterpret_cast<uintptr_t *>(frames.fp()); } else { IonExitFrameLayout *exitFrame = frames.exitFrame(); IonExitFooterFrame *footer = exitFrame->footer(); const VMFunction *f = footer->function(); if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) { @@ -1089,17 +1094,17 @@ AutoTempAllocatorRooter::trace(JSTracer void GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes) { IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame."); JSRuntime *rt = cx->runtime(); // Recover the return address. - IonFrameIterator it(rt->mainThread.ionTop); + IonFrameIterator it(rt->mainThread.ionTop, SequentialExecution); // If the previous frame is a rectifier frame (maybe unwound), // skip past it. if (it.prevType() == IonFrame_Rectifier || it.prevType() == IonFrame_Unwound_Rectifier) { ++it; JS_ASSERT(it.prevType() == IonFrame_BaselineStub || it.prevType() == IonFrame_BaselineJS || it.prevType() == IonFrame_OptimizedJS); @@ -1313,19 +1318,17 @@ IonFrameIterator::ionScript() const JS_ASSERT(type() == IonFrame_OptimizedJS); IonScript *ionScript = nullptr; if (checkInvalidation(&ionScript)) return ionScript; switch (GetCalleeTokenTag(calleeToken())) { case CalleeToken_Function: case CalleeToken_Script: - return script()->ionScript(); - case CalleeToken_ParallelFunction: - return script()->parallelIonScript(); + return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript(); default: MOZ_ASSUME_UNREACHABLE("unknown callee token type"); } } const SafepointIndex * IonFrameIterator::safepoint() const {
--- a/js/src/jit/IonFrames.h +++ b/js/src/jit/IonFrames.h @@ -19,76 +19,62 @@ namespace js { namespace jit { typedef void * CalleeToken; enum CalleeTokenTag { CalleeToken_Function = 0x0, // untagged - CalleeToken_Script = 0x1, - CalleeToken_ParallelFunction = 0x2 + CalleeToken_Script = 0x1 }; static inline CalleeTokenTag GetCalleeTokenTag(CalleeToken token) { CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3); - JS_ASSERT(tag <= CalleeToken_ParallelFunction); + JS_ASSERT(tag <= CalleeToken_Script); return tag; } static inline CalleeToken CalleeToToken(JSFunction *fun) { return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_Function)); } static inline CalleeToken -CalleeToParallelToken(JSFunction *fun) -{ - return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_ParallelFunction)); -} -static inline CalleeToken CalleeToToken(JSScript *script) { return CalleeToken(uintptr_t(script) | uintptr_t(CalleeToken_Script)); } static inline bool CalleeTokenIsFunction(CalleeToken token) { return GetCalleeTokenTag(token) == CalleeToken_Function; } static inline JSFunction * CalleeTokenToFunction(CalleeToken token) { JS_ASSERT(CalleeTokenIsFunction(token)); return (JSFunction *)token; } -static inline JSFunction * -CalleeTokenToParallelFunction(CalleeToken token) -{ - JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_ParallelFunction); - return (JSFunction *)(uintptr_t(token) & ~uintptr_t(0x3)); -} static inline JSScript * CalleeTokenToScript(CalleeToken token) { JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Script); return (JSScript *)(uintptr_t(token) & ~uintptr_t(0x3)); } static inline JSScript * ScriptFromCalleeToken(CalleeToken token) { switch (GetCalleeTokenTag(token)) { case CalleeToken_Script: return CalleeTokenToScript(token); case CalleeToken_Function: return CalleeTokenToFunction(token)->nonLazyScript(); - case CalleeToken_ParallelFunction: - return CalleeTokenToParallelFunction(token)->nonLazyScript(); } MOZ_ASSUME_UNREACHABLE("invalid callee token tag"); } // In between every two frames lies a small header describing both frames. This // header, minimally, contains a returnAddress word and a descriptor word. The // descriptor describes the size and type of the previous frame, whereas the // returnAddress describes the address the newer frame (the callee) will return @@ -281,41 +267,35 @@ void MarkIonCompilerRoots(JSTracer *trc) static inline uint32_t MakeFrameDescriptor(uint32_t frameSize, FrameType type) { return (frameSize << FRAMESIZE_SHIFT) | type; } // Returns the JSScript associated with the topmost Ion frame. inline JSScript * -GetTopIonJSScript(PerThreadData *pt, void **returnAddrOut) +GetTopIonJSScript(uint8_t *ionTop, void **returnAddrOut, ExecutionMode mode) { - IonFrameIterator iter(pt->ionTop); + IonFrameIterator iter(ionTop, mode); JS_ASSERT(iter.type() == IonFrame_Exit); ++iter; JS_ASSERT(iter.returnAddressToFp() != nullptr); if (returnAddrOut) *returnAddrOut = (void *) iter.returnAddressToFp(); if (iter.isBaselineStub()) { ++iter; JS_ASSERT(iter.isBaselineJS()); } JS_ASSERT(iter.isScripted()); return iter.script(); } -inline JSScript * -GetTopIonJSScript(ThreadSafeContext *cx, void **returnAddrOut = nullptr) -{ - return GetTopIonJSScript(cx->perThreadData, returnAddrOut); -} - } // namespace jit } // namespace js #if defined(JS_CPU_X86) || defined (JS_CPU_X64) # include "jit/shared/IonFrames-x86-shared.h" #elif defined (JS_CPU_ARM) # include "jit/arm/IonFrames-arm.h" #else
--- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -1306,65 +1306,16 @@ MacroAssembler::handleFailure(ExecutionM } MacroAssemblerSpecific::handleFailureWithHandler(handler); // Doesn't actually emit code, but balances the leave() if (sps_) sps_->reenter(*this, InvalidReg); } -void -MacroAssembler::pushCalleeToken(Register callee, ExecutionMode mode) -{ - // Tag and push a callee, then clear the tag after pushing. This is needed - // if we dereference the callee pointer after pushing it as part of a - // frame. - tagCallee(callee, mode); - push(callee); - clearCalleeTag(callee, mode); -} - -void -MacroAssembler::PushCalleeToken(Register callee, ExecutionMode mode) -{ - tagCallee(callee, mode); - Push(callee); - clearCalleeTag(callee, mode); -} - -void -MacroAssembler::tagCallee(Register callee, ExecutionMode mode) -{ - switch (mode) { - case SequentialExecution: - // CalleeToken_Function is untagged, so we don't need to do anything. - return; - case ParallelExecution: - orPtr(Imm32(CalleeToken_ParallelFunction), callee); - return; - default: - MOZ_ASSUME_UNREACHABLE("No such execution mode"); - } -} - -void -MacroAssembler::clearCalleeTag(Register callee, ExecutionMode mode) -{ - switch (mode) { - case SequentialExecution: - // CalleeToken_Function is untagged, so we don't need to do anything. - return; - case ParallelExecution: - andPtr(Imm32(~0x3), callee); - return; - default: - MOZ_ASSUME_UNREACHABLE("No such execution mode"); - } -} - static void printf0_(const char *output) { printf("%s", output); } void MacroAssembler::printf(const char *output) { RegisterSet regs = RegisterSet::Volatile();
--- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -402,18 +402,20 @@ class MacroAssembler : public MacroAssem } template <typename T> void storeTypedOrValue(TypedOrValueRegister src, const T &dest) { if (src.hasValue()) { storeValue(src.valueReg(), dest); } else if (IsFloatingPointType(src.type())) { FloatRegister reg = src.typedReg().fpu(); - if (src.type() == MIRType_Float32) - convertFloatToDouble(reg, reg); + if (src.type() == MIRType_Float32) { + convertFloatToDouble(reg, ScratchFloatReg); + reg = ScratchFloatReg; + } storeDouble(reg, dest); } else { storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest); } } template <typename T> void storeConstantOrRegister(ConstantOrRegister src, const T &dest) { @@ -548,18 +550,20 @@ class MacroAssembler : public MacroAssem } } void Push(TypedOrValueRegister v) { if (v.hasValue()) { Push(v.valueReg()); } else if (IsFloatingPointType(v.type())) { FloatRegister reg = v.typedReg().fpu(); - if (v.type() == MIRType_Float32) - convertFloatToDouble(reg, reg); + if (v.type() == MIRType_Float32) { + convertFloatToDouble(reg, ScratchFloatReg); + reg = ScratchFloatReg; + } Push(reg); } else { Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr()); } } void Push(ConstantOrRegister v) { if (v.constant()) @@ -930,21 +934,16 @@ class MacroAssembler : public MacroAssem branchPtr(Assembler::Equal, scratch, ImmPtr(&ProxyObject::callableClass_), slowCheck); branchPtr(Assembler::Equal, scratch, ImmPtr(&ProxyObject::uncallableClass_), slowCheck); branchPtr(Assembler::Equal, scratch, ImmPtr(&OuterWindowProxyObject::class_), slowCheck); test32(Address(scratch, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED)); return truthy ? Assembler::Zero : Assembler::NonZero; } - void pushCalleeToken(Register callee, ExecutionMode mode); - void PushCalleeToken(Register callee, ExecutionMode mode); - void tagCallee(Register callee, ExecutionMode mode); - void clearCalleeTag(Register callee, ExecutionMode mode); - private: // These two functions are helpers used around call sites throughout the // assembler. They are called from the above call wrappers to emit the // necessary instrumentation. void leaveSPSFrame() { if (!sps_ || !sps_->enabled()) return; // No registers are guaranteed to be available, so push/pop a register
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -4351,60 +4351,72 @@ class LCallDeleteElement : public LCallI MDeleteElement *mir() const { return mir_->toDeleteElement(); } }; // Patchable jump to stubs generated for a SetProperty cache, which stores a // boxed value. -class LSetPropertyCacheV : public LInstructionHelper<0, 1 + BOX_PIECES, 1> +class LSetPropertyCacheV : public LInstructionHelper<0, 1 + BOX_PIECES, 2> { public: LIR_HEADER(SetPropertyCacheV) - LSetPropertyCacheV(const LAllocation &object, const LDefinition &slots) { + LSetPropertyCacheV(const LAllocation &object, const LDefinition &slots, + const LDefinition &temp) { setOperand(0, object); setTemp(0, slots); + setTemp(1, temp); } static const size_t Value = 1; const MSetPropertyCache *mir() const { return mir_->toSetPropertyCache(); } + + const LDefinition *tempForDispatchCache() { + return getTemp(1); + } }; // Patchable jump to stubs generated for a SetProperty cache, which stores a // value of a known type. -class LSetPropertyCacheT : public LInstructionHelper<0, 2, 1> +class LSetPropertyCacheT : public LInstructionHelper<0, 2, 2> { MIRType valueType_; public: LIR_HEADER(SetPropertyCacheT) LSetPropertyCacheT(const LAllocation &object, const LDefinition &slots, - const LAllocation &value, MIRType valueType) + const LAllocation &value, const LDefinition &temp, + MIRType valueType) : valueType_(valueType) { setOperand(0, object); setOperand(1, value); setTemp(0, slots); + setTemp(1, temp); } const MSetPropertyCache *mir() const { return mir_->toSetPropertyCache(); } MIRType valueType() { return valueType_; } const char *extraName() const { return StringFromMIRType(valueType_); } + + const LDefinition *tempForDispatchCache() { + return getTemp(1); + } }; class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3> { public: LIR_HEADER(SetElementCacheV); static const size_t Index = 1;
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2598,17 +2598,18 @@ LIRGenerator::visitGetPropertyCache(MGet JS_ASSERT(ins->object()->type() == MIRType_Object); if (ins->type() == MIRType_Value) { LGetPropertyCacheV *lir = new LGetPropertyCacheV(useRegister(ins->object())); if (!defineBox(lir, ins)) return false; return assignSafepoint(lir, ins); } - LGetPropertyCacheT *lir = newLGetPropertyCacheT(ins); + LGetPropertyCacheT *lir = new LGetPropertyCacheT(useRegister(ins->object()), + tempForDispatchCache(ins->type())); if (!define(lir, ins)) return false; return assignSafepoint(lir, ins); } bool LIRGenerator::visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins) { @@ -2651,17 +2652,19 @@ LIRGenerator::visitGetElementCache(MGetE JS_ASSERT(ins->index()->type() == MIRType_Value); LGetElementCacheV *lir = new LGetElementCacheV(useRegister(ins->object())); if (!useBox(lir, LGetElementCacheV::Index, ins->index())) return false; return defineBox(lir, ins) && assignSafepoint(lir, ins); } JS_ASSERT(ins->index()->type() == MIRType_Int32); - LGetElementCacheT *lir = newLGetElementCacheT(ins); + LGetElementCacheT *lir = new LGetElementCacheT(useRegister(ins->object()), + useRegister(ins->index()), + tempForDispatchCache(ins->type())); return define(lir, ins) && assignSafepoint(lir, ins); } bool LIRGenerator::visitBindNameCache(MBindNameCache *ins) { JS_ASSERT(ins->scopeChain()->type() == MIRType_Object); JS_ASSERT(ins->type() == MIRType_Object); @@ -2753,17 +2756,17 @@ LIRGenerator::visitCallGetElement(MCallG if (!defineReturn(lir, ins)) return false; return assignSafepoint(lir, ins); } bool LIRGenerator::visitCallSetProperty(MCallSetProperty *ins) { - LInstruction *lir = new LCallSetProperty(useRegisterAtStart(ins->obj())); + LInstruction *lir = new LCallSetProperty(useRegisterAtStart(ins->object())); if (!useBoxAtStart(lir, LCallSetProperty::Value, ins->value())) return false; if (!add(lir, ins)) return false; return assignSafepoint(lir, ins); } bool @@ -2784,27 +2787,28 @@ LIRGenerator::visitDeleteElement(MDelete if(!useBoxAtStart(lir, LCallDeleteElement::Index, ins->index())) return false; return defineReturn(lir, ins) && assignSafepoint(lir, ins); } bool LIRGenerator::visitSetPropertyCache(MSetPropertyCache *ins) { - LUse obj = useRegisterAtStart(ins->obj()); - LDefinition slots = tempCopy(ins->obj(), 0); + LUse obj = useRegisterAtStart(ins->object()); + LDefinition slots = tempCopy(ins->object(), 0); + LDefinition dispatchTemp = tempForDispatchCache(); LInstruction *lir; if (ins->value()->type() == MIRType_Value) { - lir = new LSetPropertyCacheV(obj, slots); + lir = new LSetPropertyCacheV(obj, slots, dispatchTemp); if (!useBox(lir, LSetPropertyCacheV::Value, ins->value())) return false; } else { LAllocation value = useRegisterOrConstant(ins->value()); - lir = new LSetPropertyCacheT(obj, slots, value, ins->value()->type()); + lir = new LSetPropertyCacheT(obj, slots, value, dispatchTemp, ins->value()->type()); } if (!add(lir, ins)) return false; return assignSafepoint(lir, ins); }
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -6961,17 +6961,17 @@ class MSetPropertyInstruction : public M protected: MSetPropertyInstruction(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) : MBinaryInstruction(obj, value), name_(name), strict_(strict), needsBarrier_(true) {} public: - MDefinition *obj() const { + MDefinition *object() const { return getOperand(0); } MDefinition *value() const { return getOperand(1); } PropertyName *name() const { return name_; }
--- a/js/src/jit/ParallelFunctions.cpp +++ b/js/src/jit/ParallelFunctions.cpp @@ -120,18 +120,17 @@ jit::CheckOverRecursedPar(ForkJoinSlice uintptr_t realStackLimit; if (slice->isMainThread()) realStackLimit = GetNativeStackLimit(slice); else realStackLimit = slice->perThreadData->ionStackLimit; if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) { - slice->bailoutRecord->setCause(ParallelBailoutOverRecursed, - nullptr, nullptr, nullptr); + slice->bailoutRecord->setCause(ParallelBailoutOverRecursed); return false; } return CheckInterruptPar(slice); } bool jit::CheckInterruptPar(ForkJoinSlice *slice) @@ -154,70 +153,77 @@ jit::ExtendArrayPar(ForkJoinSlice *slice { JSObject::EnsureDenseResult res = array->ensureDenseElementsPreservePackedFlag(slice, 0, length); if (res != JSObject::ED_OK) return nullptr; return array; } -ParallelResult +bool +jit::SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name, + HandleValue value, bool strict, jsbytecode *pc) +{ + JS_ASSERT(slice->isThreadLocal(obj)); + + if (*pc == JSOP_SETALIASEDVAR) { + // See comment in jit::SetProperty. + Shape *shape = obj->nativeLookupPure(name); + JS_ASSERT(shape && shape->hasSlot()); + return obj->nativeSetSlotIfHasType(shape, value); + } + + // Fail early on hooks. + if (obj->getOps()->setProperty) + return TP_RETRY_SEQUENTIALLY; + + RootedValue v(slice, value); + RootedId id(slice, NameToId(name)); + return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict); +} + +bool jit::SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, HandleValue value, bool strict) { RootedId id(slice); if (!ValueToIdPure(index, id.address())) - return TP_RETRY_SEQUENTIALLY; + return false; // SetObjectElementOperation, the sequential version, has several checks // for certain deoptimizing behaviors, such as marking having written to // holes and non-indexed element accesses. We don't do that here, as we // can't modify any TI state anyways. If we need to add a new type, we // would bail out. RootedValue v(slice, value); - if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict)) - return TP_RETRY_SEQUENTIALLY; - return TP_SUCCESS; + return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict); } -ParallelResult -jit::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right, - MutableHandleString out) +JSString * +jit::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right) { - JSString *str = ConcatStrings<NoGC>(slice, left, right); - if (!str) - return TP_RETRY_SEQUENTIALLY; - out.set(str); - return TP_SUCCESS; + return ConcatStrings<NoGC>(slice, left, right); } -ParallelResult -jit::IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out) +JSFlatString * +jit::IntToStringPar(ForkJoinSlice *slice, int i) { - JSFlatString *str = Int32ToString<NoGC>(slice, i); - if (!str) - return TP_RETRY_SEQUENTIALLY; - out.set(str); - return TP_SUCCESS; + return Int32ToString<NoGC>(slice, i); } -ParallelResult -jit::DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out) +JSString * +jit::DoubleToStringPar(ForkJoinSlice *slice, double d) { - JSString *str = NumberToString<NoGC>(slice, d); - if (!str) - return TP_RETRY_SEQUENTIALLY; - out.set(str); - return TP_SUCCESS; + return NumberToString<NoGC>(slice, d); } -ParallelResult +bool jit::StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out) { - return StringToNumber(slice, str, out) ? TP_SUCCESS : TP_FATAL; + return StringToNumber(slice, str, out); } #define PAR_RELATIONAL_OP(OP, EXPECTED) \ do { \ /* Optimize for two int-tagged operands (typical loop control). */ \ if (lhs.isInt32() && rhs.isInt32()) { \ *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED; \ } else if (lhs.isNumber() && rhs.isNumber()) { \ @@ -232,238 +238,234 @@ do { double r = rhs.toNumber(); \ *res = (l OP r) == EXPECTED; \ } else if (lhs.isNumber() && rhs.isBoolean()) { \ double l = lhs.toNumber(); \ bool r = rhs.toBoolean(); \ *res = (l OP r) == EXPECTED; \ } else { \ int32_t vsZero; \ - ParallelResult ret = CompareMaybeStringsPar(slice, lhs, rhs, &vsZero); \ - if (ret != TP_SUCCESS) \ - return ret; \ + if (!CompareMaybeStringsPar(slice, lhs, rhs, &vsZero)) \ + return false; \ *res = (vsZero OP 0) == EXPECTED; \ } \ - return TP_SUCCESS; \ + return true; \ } while(0) -static ParallelResult +static bool CompareStringsPar(ForkJoinSlice *slice, JSString *left, JSString *right, int32_t *res) { ScopedThreadSafeStringInspector leftInspector(left); ScopedThreadSafeStringInspector rightInspector(right); if (!leftInspector.ensureChars(slice) || !rightInspector.ensureChars(slice)) - return TP_FATAL; + return false; - if (!CompareChars(leftInspector.chars(), left->length(), - rightInspector.chars(), right->length(), - res)) - return TP_FATAL; - - return TP_SUCCESS; + return CompareChars(leftInspector.chars(), left->length(), + rightInspector.chars(), right->length(), + res); } -static ParallelResult +static bool CompareMaybeStringsPar(ForkJoinSlice *slice, HandleValue v1, HandleValue v2, int32_t *res) { if (!v1.isString()) - return TP_RETRY_SEQUENTIALLY; + return false; if (!v2.isString()) - return TP_RETRY_SEQUENTIALLY; + return false; return CompareStringsPar(slice, v1.toString(), v2.toString(), res); } template<bool Equal> -ParallelResult +bool LooselyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { PAR_RELATIONAL_OP(==, Equal); } -ParallelResult +bool js::jit::LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { return LooselyEqualImplPar<true>(slice, lhs, rhs, res); } -ParallelResult +bool js::jit::LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { return LooselyEqualImplPar<false>(slice, lhs, rhs, res); } template<bool Equal> -ParallelResult +bool StrictlyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { if (lhs.isNumber()) { if (rhs.isNumber()) { *res = (lhs.toNumber() == rhs.toNumber()) == Equal; - return TP_SUCCESS; + return true; } } else if (lhs.isBoolean()) { if (rhs.isBoolean()) { *res = (lhs.toBoolean() == rhs.toBoolean()) == Equal; - return TP_SUCCESS; + return true; } } else if (lhs.isNull()) { if (rhs.isNull()) { *res = Equal; - return TP_SUCCESS; + return true; } } else if (lhs.isUndefined()) { if (rhs.isUndefined()) { *res = Equal; - return TP_SUCCESS; + return true; } } else if (lhs.isObject()) { if (rhs.isObject()) { *res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal; - return TP_SUCCESS; + return true; } } else if (lhs.isString()) { if (rhs.isString()) return LooselyEqualImplPar<Equal>(slice, lhs, rhs, res); } *res = false; - return TP_SUCCESS; + return true; } -ParallelResult +bool js::jit::StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { return StrictlyEqualImplPar<true>(slice, lhs, rhs, res); } -ParallelResult +bool js::jit::StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { return StrictlyEqualImplPar<false>(slice, lhs, rhs, res); } -ParallelResult +bool js::jit::LessThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { PAR_RELATIONAL_OP(<, true); } -ParallelResult +bool js::jit::LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { PAR_RELATIONAL_OP(<=, true); } -ParallelResult +bool js::jit::GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { PAR_RELATIONAL_OP(>, true); } -ParallelResult +bool js::jit::GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { PAR_RELATIONAL_OP(>=, true); } template<bool Equal> -ParallelResult +bool StringsEqualImplPar(ForkJoinSlice *slice, HandleString lhs, HandleString rhs, bool *res) { int32_t vsZero; - ParallelResult ret = CompareStringsPar(slice, lhs, rhs, &vsZero); - if (ret != TP_SUCCESS) + bool ret = CompareStringsPar(slice, lhs, rhs, &vsZero); + if (ret != true) return ret; *res = (vsZero == 0) == Equal; - return TP_SUCCESS; + return true; } -ParallelResult +bool js::jit::StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res) { return StringsEqualImplPar<true>(slice, v1, v2, res); } -ParallelResult +bool js::jit::StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res) { return StringsEqualImplPar<false>(slice, v1, v2, res); } -ParallelResult +bool jit::BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out) { if (in.isObject()) - return TP_RETRY_SEQUENTIALLY; + return false; int i; if (!NonObjectToInt32(slice, in, &i)) - return TP_FATAL; + return false; *out = ~i; - return TP_SUCCESS; + return true; } #define BIT_OP(OP) \ JS_BEGIN_MACRO \ int32_t left, right; \ if (lhs.isObject() || rhs.isObject()) \ return TP_RETRY_SEQUENTIALLY; \ if (!NonObjectToInt32(slice, lhs, &left) || \ !NonObjectToInt32(slice, rhs, &right)) \ { \ - return TP_FATAL; \ + return false; \ } \ *out = (OP); \ - return TP_SUCCESS; \ + return true; \ JS_END_MACRO -ParallelResult +bool jit::BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out) { BIT_OP(left ^ right); } -ParallelResult +bool jit::BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out) { BIT_OP(left | right); } -ParallelResult +bool jit::BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out) { BIT_OP(left & right); } -ParallelResult +bool jit::BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out) { BIT_OP(left << (right & 31)); } -ParallelResult +bool jit::BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out) { BIT_OP(left >> (right & 31)); } #undef BIT_OP -ParallelResult +bool jit::UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, Value *out) { uint32_t left; int32_t right; if (lhs.isObject() || rhs.isObject()) - return TP_RETRY_SEQUENTIALLY; + return false; if (!NonObjectToUint32(slice, lhs, &left) || !NonObjectToInt32(slice, rhs, &right)) - return TP_FATAL; + return false; left >>= right & 31; out->setNumber(uint32_t(left)); - return TP_SUCCESS; + return true; } void jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript, jsbytecode *bytecode) { // Spew before asserts to help with diagnosing failures. Spew(SpewBailouts, @@ -477,18 +479,17 @@ jit::AbortPar(ParallelBailoutCause cause JS_ASSERT(InParallelSection()); JS_ASSERT(outermostScript != nullptr); JS_ASSERT(currentScript != nullptr); JS_ASSERT(outermostScript->hasParallelIonScript()); ForkJoinSlice *slice = ForkJoinSlice::Current(); JS_ASSERT(slice->bailoutRecord->depth == 0); - slice->bailoutRecord->setCause(cause, outermostScript, - currentScript, bytecode); + slice->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode); } void jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript) { Spew(SpewBailouts, "Propagate parallel abort via %p:%s:%d (%p:%s:%d)", outermostScript, outermostScript->filename(), outermostScript->lineno, @@ -543,33 +544,31 @@ jit::CallToUncompiledScriptPar(JSObject } } else { JS_ASSERT(func->isNative()); Spew(SpewBailouts, "Call to native function"); } #endif } -ParallelResult +JSObject * jit::InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest, - HandleObject templateObj, HandleObject res, - MutableHandleObject out) + HandleObject templateObj, HandleObject res) { // In parallel execution, we should always have succeeded in allocation // before this point. We can do the allocation here like in the sequential // path, but duplicating the initGCThing logic is too tedious. JS_ASSERT(res); JS_ASSERT(res->is<ArrayObject>()); JS_ASSERT(!res->getDenseInitializedLength()); JS_ASSERT(res->type() == templateObj->type()); if (length > 0) { JSObject::EnsureDenseResult edr = res->ensureDenseElementsPreservePackedFlag(slice, 0, length); if (edr != JSObject::ED_OK) - return TP_FATAL; + return nullptr; res->initDenseElements(0, rest, length); res->as<ArrayObject>().setLengthInt32(length); } - out.set(res); - return TP_SUCCESS; + return res; }
--- a/js/src/jit/ParallelFunctions.h +++ b/js/src/jit/ParallelFunctions.h @@ -20,55 +20,55 @@ bool CheckOverRecursedPar(ForkJoinSlice bool CheckInterruptPar(ForkJoinSlice *slice); // Extends the given array with `length` new holes. Returns nullptr on // failure or else `array`, which is convenient during code // generation. JSObject *ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length); // Set properties and elements on thread local objects. -ParallelResult SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, - HandleValue value, bool strict); +bool SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name, + HandleValue value, bool strict, jsbytecode *pc); +bool SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, + HandleValue value, bool strict); // String related parallel functions. These tend to call existing VM functions // that take a ThreadSafeContext. -ParallelResult ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right, - MutableHandleString out); -ParallelResult IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out); -ParallelResult DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out); -ParallelResult StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out); +JSString *ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right); +JSFlatString *IntToStringPar(ForkJoinSlice *slice, int i); +JSString *DoubleToStringPar(ForkJoinSlice *slice, double d); +bool StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out); // Binary and unary operator functions on values. These tend to return // RETRY_SEQUENTIALLY if the values are objects. -ParallelResult StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); +bool GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *); -ParallelResult StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *); -ParallelResult StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *); +bool StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *); +bool StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *); -ParallelResult BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out); -ParallelResult BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); -ParallelResult BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); -ParallelResult BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); -ParallelResult BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); -ParallelResult BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); +bool BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out); +bool BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); +bool BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); +bool BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); +bool BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); +bool BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out); -ParallelResult UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, +bool UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, Value *out); // Make a new rest parameter in parallel. -ParallelResult InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest, - HandleObject templateObj, HandleObject res, - MutableHandleObject out); +JSObject *InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest, + HandleObject templateObj, HandleObject res); // Abort and debug tracing functions. void AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript, jsbytecode *bytecode); void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript); void TraceLIR(IonLIRTraceData *current);
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -196,17 +196,17 @@ class ParallelSafetyVisitor : public MIn SAFE_OP(FunctionEnvironment) // just a load of func env ptr SAFE_OP(TypeBarrier) // causes a bailout if the type is not found: a-ok with us SAFE_OP(MonitorTypes) // causes a bailout if the type is not found: a-ok with us UNSAFE_OP(PostWriteBarrier) SAFE_OP(GetPropertyCache) SAFE_OP(GetPropertyPolymorphic) UNSAFE_OP(SetPropertyPolymorphic) SAFE_OP(GetElementCache) - UNSAFE_OP(SetElementCache) + WRITE_GUARDED_OP(SetElementCache, object) UNSAFE_OP(BindNameCache) SAFE_OP(GuardShape) SAFE_OP(GuardObjectType) SAFE_OP(GuardClass) SAFE_OP(AssertRange) SAFE_OP(ArrayLength) SAFE_OP(TypedArrayLength) SAFE_OP(TypedArrayElements) @@ -231,22 +231,22 @@ class ParallelSafetyVisitor : public MIn UNSAFE_OP(ClampToUint8) SAFE_OP(LoadFixedSlot) WRITE_GUARDED_OP(StoreFixedSlot, object) UNSAFE_OP(CallGetProperty) UNSAFE_OP(GetNameCache) UNSAFE_OP(CallGetIntrinsicValue) UNSAFE_OP(CallsiteCloneCache) UNSAFE_OP(CallGetElement) - UNSAFE_OP(CallSetElement) + WRITE_GUARDED_OP(CallSetElement, object) UNSAFE_OP(CallInitElementArray) - UNSAFE_OP(CallSetProperty) + WRITE_GUARDED_OP(CallSetProperty, object) UNSAFE_OP(DeleteProperty) UNSAFE_OP(DeleteElement) - UNSAFE_OP(SetPropertyCache) + WRITE_GUARDED_OP(SetPropertyCache, object) UNSAFE_OP(IteratorStart) UNSAFE_OP(IteratorNext) UNSAFE_OP(IteratorMore) UNSAFE_OP(IteratorEnd) SAFE_OP(StringLength) SAFE_OP(ArgumentsLength) SAFE_OP(GetFrameArgument) UNSAFE_OP(SetFrameArgument)
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -14,29 +14,37 @@ #include "jit/IonFrames.h" #include "vm/ArrayObject.h" #include "vm/Debugger.h" #include "vm/Interpreter.h" #include "jsinferinlines.h" #include "jit/BaselineFrame-inl.h" +#include "jit/IonFrames-inl.h" #include "vm/Interpreter-inl.h" #include "vm/StringObject-inl.h" using namespace js; using namespace js::jit; namespace js { namespace jit { // Don't explicitly initialize, it's not guaranteed that this initializer will // run before the constructors for static VMFunctions. /* static */ VMFunction *VMFunction::functions; +AutoDetectInvalidation::AutoDetectInvalidation(JSContext *cx, Value *rval, IonScript *ionScript) + : cx_(cx), + ionScript_(ionScript ? ionScript : GetTopIonJSScript(cx)->ionScript()), + rval_(rval), + disabled_(false) +{ } + void VMFunction::addToFunctions() { static bool initialized = false; if (!initialized) { initialized = true; functions = nullptr; }
--- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -22,18 +22,17 @@ namespace jit { enum DataType { Type_Void, Type_Bool, Type_Int32, Type_Double, Type_Pointer, Type_Object, Type_Value, - Type_Handle, - Type_ParallelResult + Type_Handle }; struct PopValues { uint32_t numValues; explicit PopValues(uint32_t numValues) : numValues(numValues) @@ -215,20 +214,18 @@ struct VMFunction argumentRootTypes(argRootTypes), outParamRootType(outParamRootType), executionMode(executionMode), extraValuesToPop(extraValuesToPop) { // Check for valid failure/return type. JS_ASSERT_IF(outParam != Type_Void && executionMode == SequentialExecution, returnType == Type_Bool); - JS_ASSERT_IF(executionMode == ParallelExecution, returnType == Type_ParallelResult); JS_ASSERT(returnType == Type_Bool || - returnType == Type_Object || - returnType == Type_ParallelResult); + returnType == Type_Object); } VMFunction(const VMFunction &o) { init(o); } void init(const VMFunction &o) { JS_ASSERT(!wrapped); @@ -275,17 +272,16 @@ template <> struct TypeToDataType<JSFlat template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; }; template <> struct TypeToDataType<MutableHandleValue> { static const DataType result = Type_Handle; }; -template <> struct TypeToDataType<ParallelResult> { static const DataType result = Type_ParallelResult; }; // Convert argument types to properties of the argument known by the jit. template <class T> struct TypeToArgProperties { static const uint32_t result = (sizeof(T) <= sizeof(void *) ? VMFunction::Word : VMFunction::Double); }; template <> struct TypeToArgProperties<const Value &> { static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef; @@ -390,18 +386,18 @@ template <> struct MatchContext<JSContex template <> struct MatchContext<ExclusiveContext *> { static const ExecutionMode execMode = SequentialExecution; }; template <> struct MatchContext<ForkJoinSlice *> { static const ExecutionMode execMode = ParallelExecution; }; template <> struct MatchContext<ThreadSafeContext *> { // ThreadSafeContext functions can be called from either mode, but for - // calling from parallel they need to be wrapped first to return a - // ParallelResult, so we default to SequentialExecution here. + // calling from parallel they should be wrapped first, so we default to + // SequentialExecution here. static const ExecutionMode execMode = SequentialExecution; }; #define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1) #define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2) #define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3) #define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4) #define FOR_EACH_ARGS_5(Macro, Sep, Last) FOR_EACH_ARGS_4(Macro, Sep, Sep) Macro(5) Last(5) @@ -551,22 +547,17 @@ template <class R, class Context, class class AutoDetectInvalidation { JSContext *cx_; IonScript *ionScript_; Value *rval_; bool disabled_; public: - AutoDetectInvalidation(JSContext *cx, Value *rval, IonScript *ionScript = nullptr) - : cx_(cx), - ionScript_(ionScript ? ionScript : GetTopIonJSScript(cx)->ionScript()), - rval_(rval), - disabled_(false) - { } + AutoDetectInvalidation(JSContext *cx, Value *rval, IonScript *ionScript = nullptr); void disable() { JS_ASSERT(!disabled_); disabled_ = true; } ~AutoDetectInvalidation() { if (!disabled_ && ionScript_->invalidated())
--- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -359,30 +359,16 @@ LIRGeneratorARM::newLTableSwitch(const L } LTableSwitchV * LIRGeneratorARM::newLTableSwitchV(MTableSwitch *tableswitch) { return new LTableSwitchV(temp(), tempFloat(), tableswitch); } -LGetPropertyCacheT * -LIRGeneratorARM::newLGetPropertyCacheT(MGetPropertyCache *ins) -{ - return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp()); -} - -LGetElementCacheT * -LIRGeneratorARM::newLGetElementCacheT(MGetElementCache *ins) -{ - return new LGetElementCacheT(useRegister(ins->object()), - useRegister(ins->index()), - LDefinition::BogusTemp()); -} - bool LIRGeneratorARM::visitGuardShape(MGuardShape *ins) { JS_ASSERT(ins->obj()->type() == MIRType_Object); LDefinition tempObj = temp(LDefinition::OBJECT); LGuardShape *guard = new LGuardShape(useRegister(ins->obj()), tempObj); if (!assignSnapshot(guard, ins->bailoutKind()))
--- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -30,16 +30,22 @@ class LIRGeneratorARM : public LIRGenera // stores and loads; on ARM all registers are okay. LAllocation useByteOpRegister(MDefinition *mir); LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir); inline LDefinition tempToUnbox() { return LDefinition::BogusTemp(); } + // x64 has a scratch register, so no need for another temp for dispatch + // ICs. + LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) { + return LDefinition::BogusTemp(); + } + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs); bool lowerUrshD(MUrsh *mir); bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input); @@ -63,18 +69,16 @@ class LIRGeneratorARM : public LIRGenera bool visitPowHalf(MPowHalf *ins); bool visitAsmJSNeg(MAsmJSNeg *ins); bool visitAsmJSUDiv(MAsmJSUDiv *ins); bool visitAsmJSUMod(MAsmJSUMod *ins); LTableSwitch *newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy, MTableSwitch *ins); LTableSwitchV *newLTableSwitchV(MTableSwitch *ins); - LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); - LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins); public: bool visitConstant(MConstant *ins); bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool lowerPhi(MPhi *phi); bool visitGuardShape(MGuardShape *ins);
--- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -418,17 +418,16 @@ IonRuntime::generateArgumentsRectifier(J // Including |this|, there are (|nargs| + 1) arguments to copy. JS_ASSERT(ArgumentsRectifierReg == r8); // Copy number of actual arguments into r0 masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfNumActualArgs())), r0); // Load the number of |undefined|s to push into r6. masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfCalleeToken())), r1); - masm.clearCalleeTag(r1, mode); masm.ma_ldrh(EDtrAddr(r1, EDtrOffImm(offsetof(JSFunction, nargs))), r6); masm.ma_sub(r6, r8, r2); masm.moveValue(UndefinedValue(), r5, r4); masm.ma_mov(sp, r3); // Save %sp. masm.ma_mov(sp, r7); // Save %sp again. @@ -463,17 +462,17 @@ IonRuntime::generateArgumentsRectifier(J masm.ma_add(r6, Imm32(1), r6); masm.ma_lsl(Imm32(3), r6, r6); // Construct sizeDescriptor. masm.makeFrameDescriptor(r6, IonFrame_Rectifier); // Construct IonJSFrameLayout. masm.ma_push(r0); // actual arguments. - masm.pushCalleeToken(r1, mode); + masm.ma_push(r1); // callee token masm.ma_push(r6); // frame descriptor. // Call the target function. // Note that this code assumes the function is JITted. masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3); masm.loadBaselineOrIonRaw(r3, r3, mode, nullptr); masm.ma_callIonHalfPush(r3); @@ -767,19 +766,16 @@ IonRuntime::generateVMWrapper(JSContext // Test for failure. switch (f.failType()) { case Type_Object: case Type_Bool: // Called functions return bools, which are 0/false and non-zero/true masm.branch32(Assembler::Equal, r0, Imm32(0), masm.failureLabel(f.executionMode)); break; - case Type_ParallelResult: - masm.branch32(Assembler::NotEqual, r0, Imm32(TP_SUCCESS), masm.failureLabel(f.executionMode)); - break; default: MOZ_ASSUME_UNREACHABLE("unknown failure kind"); } // Load the outparam and free any allocated stack. switch (f.outParam) { case Type_Handle: masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -162,27 +162,13 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAs } bool LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) { return define(new LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins); } -LGetPropertyCacheT * -LIRGeneratorX64::newLGetPropertyCacheT(MGetPropertyCache *ins) -{ - return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp()); -} - -LGetElementCacheT * -LIRGeneratorX64::newLGetElementCacheT(MGetElementCache *ins) -{ - return new LGetElementCacheT(useRegister(ins->object()), - useRegister(ins->index()), - LDefinition::BogusTemp()); -} - bool LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) { MOZ_ASSUME_UNREACHABLE("NYI"); }
--- a/js/src/jit/x64/Lowering-x64.h +++ b/js/src/jit/x64/Lowering-x64.h @@ -30,18 +30,21 @@ class LIRGeneratorX64 : public LIRGenera // x86 has constraints on what registers can be formatted for 1-byte // stores and loads; on x64 all registers are okay. LAllocation useByteOpRegister(MDefinition *mir); LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir); LDefinition tempToUnbox(); - LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); - LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins); + // x64 has a scratch register, so no need for another temp for dispatch + // ICs. + LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) { + return LDefinition::BogusTemp(); + } public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
--- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -358,17 +358,16 @@ IonRuntime::generateArgumentsRectifier(J MacroAssembler masm(cx); // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. // Including |this|, there are (|nargs| + 1) arguments to copy. JS_ASSERT(ArgumentsRectifierReg == r8); // Load the number of |undefined|s to push into %rcx. masm.loadPtr(Address(rsp, IonRectifierFrameLayout::offsetOfCalleeToken()), rax); - masm.clearCalleeTag(rax, mode); masm.movzwl(Operand(rax, offsetof(JSFunction, nargs)), rcx); masm.subq(r8, rcx); // Copy the number of actual arguments masm.loadPtr(Address(rsp, IonRectifierFrameLayout::offsetOfNumActualArgs()), rdx); masm.moveValue(UndefinedValue(), r10); @@ -401,17 +400,17 @@ IonRuntime::generateArgumentsRectifier(J } // Construct descriptor. masm.subq(rsp, r9); masm.makeFrameDescriptor(r9, IonFrame_Rectifier); // Construct IonJSFrameLayout. masm.push(rdx); // numActualArgs - masm.pushCalleeToken(rax, mode); + masm.push(rax); // callee token masm.push(r9); // descriptor // Call the target function. // Note that this code assumes the function is JITted. masm.loadPtr(Address(rax, JSFunction::offsetOfNativeOrScript()), rax); masm.loadBaselineOrIonRaw(rax, rax, mode, nullptr); masm.call(rax); uint32_t returnOffset = masm.currentOffset(); @@ -621,20 +620,16 @@ IonRuntime::generateVMWrapper(JSContext switch (f.failType()) { case Type_Object: masm.branchTestPtr(Assembler::Zero, rax, rax, masm.failureLabel(f.executionMode)); break; case Type_Bool: masm.testb(rax, rax); masm.j(Assembler::Zero, masm.failureLabel(f.executionMode)); break; - case Type_ParallelResult: - masm.branchPtr(Assembler::NotEqual, rax, Imm32(TP_SUCCESS), - masm.failureLabel(f.executionMode)); - break; default: MOZ_ASSUME_UNREACHABLE("unknown failure kind"); } // Load the outparam and free any allocated stack. switch (f.outParam) { case Type_Handle: masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -742,16 +742,39 @@ GetElementParIC::initializeAddCacheState // one, it's BogusTemp otherwise. JS_ASSERT(ins->isGetElementCacheV() || ins->isGetElementCacheT()); if (ins->isGetElementCacheV() || ins->toGetElementCacheT()->temp()->isBogusTemp()) addState->dispatchScratch = output_.scratchReg().gpr(); else addState->dispatchScratch = ToRegister(ins->toGetElementCacheT()->temp()); } +void +SetPropertyParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) +{ + // We don't have an output register to reuse, so we always need a temp. + JS_ASSERT(ins->isSetPropertyCacheV() || ins->isSetPropertyCacheT()); + if (ins->isSetPropertyCacheV()) + addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheV()->tempForDispatchCache()); + else + addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheT()->tempForDispatchCache()); +} + +void +SetElementParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) +{ + // We don't have an output register to reuse, but luckily SetElementCache + // already needs a temp. + JS_ASSERT(ins->isSetElementCacheV() || ins->isSetElementCacheT()); + if (ins->isSetElementCacheV()) + addState->dispatchScratch = ToRegister(ins->toSetElementCacheV()->temp()); + else + addState->dispatchScratch = ToRegister(ins->toSetElementCacheT()->temp()); +} + namespace js { namespace jit { class OutOfLineTruncate : public OutOfLineCodeBase<CodeGeneratorX86> { LTruncateDToInt32 *ins_; public:
--- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -9,16 +9,39 @@ #include "jit/MIR.h" #include "jit/x86/Assembler-x86.h" #include "jit/shared/Lowering-shared-inl.h" using namespace js; using namespace js::jit; +LDefinition +LIRGeneratorX86::tempForDispatchCache(MIRType outputType) +{ + // x86 doesn't have a scratch register and we need one for the + // indirect jump for dispatch-style ICs. + // + // Note that currently we only install dispatch-style ICs for parallel + // execution. If this assumption changes, please change it here. + if (gen->info().executionMode() != ParallelExecution) + return LDefinition::BogusTemp(); + + // If we don't have an output register, we need a temp. + if (outputType == MIRType_None) + return temp(); + + // If we have a double output register, we need a temp. + if (outputType == MIRType_Double) + return temp(); + + // Otherwise we have a non-double output register and we can reuse it. + return LDefinition::BogusTemp(); +} + bool LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy, bool useAtStart) { JS_ASSERT(mir->type() == MIRType_Value); if (!ensureDefined(mir)) return false; @@ -270,35 +293,8 @@ LIRGeneratorX86::visitStoreTypedArrayEle return add(lir, ins); } bool LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) { return define(new LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins); } - -LGetPropertyCacheT * -LIRGeneratorX86::newLGetPropertyCacheT(MGetPropertyCache *ins) -{ - // Since x86 doesn't have a scratch register and we need one for the - // indirect jump for dispatch-style ICs, we need a temporary in the case - // of a double output type as we can't get a scratch from the output. - LDefinition scratch; - if (ins->type() == MIRType_Double) - scratch = temp(); - else - scratch = LDefinition::BogusTemp(); - return new LGetPropertyCacheT(useRegister(ins->object()), scratch); -} - -LGetElementCacheT * -LIRGeneratorX86::newLGetElementCacheT(MGetElementCache *ins) -{ - LDefinition scratch; - if (ins->type() == MIRType_Double) - scratch = temp(); - else - scratch = LDefinition::BogusTemp(); - return new LGetElementCacheT(useRegister(ins->object()), - useRegister(ins->index()), - scratch); -}
--- a/js/src/jit/x86/Lowering-x86.h +++ b/js/src/jit/x86/Lowering-x86.h @@ -33,22 +33,21 @@ class LIRGeneratorX86 : public LIRGenera // give us one of {al,bl,cl,dl}. For now, just useFixed(al). LAllocation useByteOpRegister(MDefinition *mir); LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir); inline LDefinition tempToUnbox() { return LDefinition::BogusTemp(); } + LDefinition tempForDispatchCache(MIRType outputType = MIRType_None); + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); - LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); - LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins); - public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins); bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
--- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -348,17 +348,16 @@ IonRuntime::generateArgumentsRectifier(J MacroAssembler masm(cx); // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. // Including |this|, there are (|nargs| + 1) arguments to copy. JS_ASSERT(ArgumentsRectifierReg == esi); // Load the number of |undefined|s to push into %ecx. masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax); - masm.clearCalleeTag(eax, mode); masm.movzwl(Operand(eax, offsetof(JSFunction, nargs)), ecx); masm.subl(esi, ecx); // Copy the number of actual arguments. masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx); masm.moveValue(UndefinedValue(), ebx, edi); @@ -401,17 +400,17 @@ IonRuntime::generateArgumentsRectifier(J // Construct descriptor, accounting for pushed frame pointer above masm.lea(Operand(FramePointer, sizeof(void*)), ebx); masm.subl(esp, ebx); masm.makeFrameDescriptor(ebx, IonFrame_Rectifier); // Construct IonJSFrameLayout. masm.push(edx); // number of actual arguments - masm.pushCalleeToken(eax, mode); + masm.push(eax); // callee token masm.push(ebx); // descriptor // Call the target function. // Note that this assumes the function is JITted. masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax); masm.loadBaselineOrIonRaw(eax, eax, mode, nullptr); masm.call(eax); uint32_t returnOffset = masm.currentOffset(); @@ -655,20 +654,16 @@ IonRuntime::generateVMWrapper(JSContext switch (f.failType()) { case Type_Object: masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel(f.executionMode)); break; case Type_Bool: masm.testb(eax, eax); masm.j(Assembler::Zero, masm.failureLabel(f.executionMode)); break; - case Type_ParallelResult: - masm.branchPtr(Assembler::NotEqual, eax, Imm32(TP_SUCCESS), - masm.failureLabel(f.executionMode)); - break; default: MOZ_ASSUME_UNREACHABLE("unknown failure kind"); } // Load the outparam and free any allocated stack. switch (f.outParam) { case Type_Handle: masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -754,27 +754,30 @@ js::WouldDefinePastNonwritableLength(Thr if (arr->lengthIsWritable()) { *definesPast = false; return true; } *definesPast = true; + // Error in strict mode code or warn with strict option. + unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING); + if (cx->isForkJoinSlice()) + return cx->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, flags); + if (!cx->isJSContext()) return true; JSContext *ncx = cx->asJSContext(); if (!strict && !ncx->hasExtraWarningsOption()) return true; - // Error in strict mode code or warn with strict option. // XXX include the index and maybe array length in the error message - unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING); return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); } static bool array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) {
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -344,16 +344,21 @@ PopulateReportBlame(JSContext *cx, JSErr * error reporter directly. * * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does * not occur, so GC must be avoided or suppressed. */ void js_ReportOutOfMemory(ThreadSafeContext *cxArg) { + if (cxArg->isForkJoinSlice()) { + cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory); + return; + } + if (!cxArg->isJSContext()) return; JSContext *cx = cxArg->asJSContext(); cx->runtime()->hadOutOfMemory = true; if (JS_IsRunning(cx)) { cx->setPendingException(StringValue(cx->names().outOfMemory)); @@ -413,17 +418,25 @@ void js_ReportOverRecursed(ThreadSafeContext *cx) { js_ReportOverRecursed(cx->maybeJSContext()); } void js_ReportAllocationOverflow(ThreadSafeContext *cxArg) { - if (!cxArg || !cxArg->isJSContext()) + if (!cxArg) + return; + + if (cxArg->isForkJoinSlice()) { + cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory); + return; + } + + if (!cxArg->isJSContext()) return; JSContext *cx = cxArg->asJSContext(); AutoSuppressGC suppressGC(cx); JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW); } /*
--- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -552,20 +552,20 @@ JSContext::currentScript(jsbytecode **pp template <JSThreadSafeNative threadSafeNative> inline bool JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp) { return threadSafeNative(cx, argc, vp); } template <JSThreadSafeNative threadSafeNative> -inline js::ParallelResult +inline bool JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp) { - return threadSafeNative(slice, argc, vp) ? js::TP_SUCCESS : js::TP_FATAL; + return threadSafeNative(slice, argc, vp); } /* static */ inline JSContext * js::ExecutionModeTraits<js::SequentialExecution>::toContextType(ExclusiveContext *cx) { return cx->asJSContext(); }
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3497,25 +3497,18 @@ DefinePropertyOrElement(typename Executi setter == JS_StrictPropertyStub && attrs == JSPROP_ENUMERATE && (!obj->isIndexed() || !obj->nativeContainsPure(id))) { uint32_t index = JSID_TO_INT(id); bool definesPast; if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast)) return false; - if (definesPast) { - /* - * Strict mode changes semantics and we must throw, so bail out of - * parallel execution if we are strict. - */ - if (mode == ParallelExecution) - return !setterIsStrict; + if (definesPast) return true; - } JSObject::EnsureDenseResult result; if (mode == ParallelExecution) { if (obj->writeToIndexWouldMarkNotPacked(index)) return false; result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1); } else { result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1); @@ -3538,22 +3531,18 @@ DefinePropertyOrElement(typename Executi attrs, value, setterIsStrict); } uint32_t index; if (js_IdIsIndex(id, &index)) { bool definesPast; if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast)) return false; - if (definesPast) { - /* Bail out of parallel execution if we are strict to throw. */ - if (mode == ParallelExecution) - return !setterIsStrict; + if (definesPast) return true; - } } } AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT, attrs, flags, shortid)); @@ -4581,36 +4570,57 @@ js::ReportIfUndeclaredVarAssignment(JSCo JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT | JSREPORT_STRICT_MODE_ERROR, js_GetErrorMessage, nullptr, JSMSG_UNDECLARED_VAR, bytes.ptr()); } bool -JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report) +JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report) { + if (cxArg->isForkJoinSlice()) + return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report); + + if (!cxArg->isJSContext()) + return true; + + JSContext *cx = cxArg->asJSContext(); RootedValue val(cx, IdToValue(id)); return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY, JSDVG_IGNORE_STACK, val, NullPtr(), nullptr, nullptr); } bool -JSObject::reportNotConfigurable(JSContext *cx, jsid id, unsigned report) +JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report) { + if (cxArg->isForkJoinSlice()) + return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report); + + if (!cxArg->isJSContext()) + return true; + + JSContext *cx = cxArg->asJSContext(); RootedValue val(cx, IdToValue(id)); return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, val, NullPtr(), nullptr, nullptr); } bool -JSObject::reportNotExtensible(JSContext *cx, unsigned report) +JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report) { + if (cxArg->isForkJoinSlice()) + return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report); + + if (!cxArg->isJSContext()) + return true; + + JSContext *cx = cxArg->asJSContext(); RootedValue val(cx, ObjectValue(*this)); return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE, JSDVG_IGNORE_STACK, val, NullPtr(), nullptr, nullptr); } bool JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp) @@ -4822,26 +4832,21 @@ baseops::SetPropertyHelper(typename Exec return false; extensible = obj->nonProxyIsExtensible(); } else { if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible)) return false; } if (!extensible) { - /* Bail out of parallel execution if we are strict to throw. */ - if (mode == ParallelExecution) - return !strict; - /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */ - JSContext *cx = cxArg->asJSContext(); if (strict) - return obj->reportNotExtensible(cx); - if (cx->hasExtraWarningsOption()) - return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING); + return obj->reportNotExtensible(cxArg); + if (mode == SequentialExecution && cxArg->asJSContext()->hasExtraWarningsOption()) + return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING); return true; } if (mode == ParallelExecution) { if (obj->isDelegate()) return false; if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -802,19 +802,19 @@ class JSObject : public js::ObjectImpl * FIXME: bug 593129 -- slot allocation should be done by object methods * after calling object-parameter-free shape methods, avoiding coupling * logic across the object vs. shape module wall. */ static bool allocSlot(js::ThreadSafeContext *cx, JS::HandleObject obj, uint32_t *slotp); void freeSlot(uint32_t slot); public: - static bool reportReadOnly(JSContext *cx, jsid id, unsigned report = JSREPORT_ERROR); - bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); - bool reportNotExtensible(JSContext *cx, unsigned report = JSREPORT_ERROR); + static bool reportReadOnly(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR); + bool reportNotConfigurable(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR); + bool reportNotExtensible(js::ThreadSafeContext *cx, unsigned report = JSREPORT_ERROR); /* * Get the property with the given id, then call it as a function with the * given arguments, providing this object as |this|. If the property isn't * callable a TypeError will be thrown. On success the value returned by * the call is stored in *vp. */ bool callMethod(JSContext *cx, js::HandleId id, unsigned argc, js::Value *argv,
--- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -95,26 +95,42 @@ ForkJoinSlice::requestGC(JS::gcreason::R } void ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason) { MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build"); } +bool +ForkJoinSlice::setPendingAbortFatal(ParallelBailoutCause cause) +{ + MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build"); + return false; +} + void ParallelBailoutRecord::setCause(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript, jsbytecode *currentPc) { MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build"); } void +js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause, + JSScript *outermostScript, + JSScript *currentScript, + jsbytecode *currentPc) +{ + MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build"); +} + +void ParallelBailoutRecord::addTrace(JSScript *script, jsbytecode *pc) { MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build"); } bool js::InExclusiveParallelSection() @@ -383,16 +399,19 @@ class ForkJoinShared : public TaskExecut // Requests a GC, either full or specific to a zone. void requestGC(JS::gcreason::Reason reason); void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason); // Requests that computation abort. void setAbortFlag(bool fatal); + // Set the fatal flag for the next abort. + void setPendingAbortFatal() { fatal_ = true; } + JSRuntime *runtime() { return cx_->runtime(); } JS::Zone *zone() { return cx_->zone(); } JSContext *acquireContext() { PR_Lock(cxLock_); return cx_; } void releaseContext() { PR_Unlock(cxLock_); } }; class AutoEnterWarmup @@ -970,37 +989,37 @@ BailoutExplanation(ParallelBailoutCause return "no particular reason"; case ParallelBailoutCompilationSkipped: return "compilation failed (method skipped)"; case ParallelBailoutCompilationFailure: return "compilation failed"; case ParallelBailoutInterrupt: return "interrupted"; case ParallelBailoutFailedIC: - return "at runtime, the behavior changed, invalidating compiled code (IC update)"; + return "failed to attach stub to IC"; case ParallelBailoutHeapBusy: return "heap busy flag set during interrupt"; case ParallelBailoutMainScriptNotPresent: return "main script not present"; case ParallelBailoutCalledToUncompiledScript: return "called to uncompiled script"; case ParallelBailoutIllegalWrite: return "illegal write"; case ParallelBailoutAccessToIntrinsic: return "access to intrinsic"; case ParallelBailoutOverRecursed: return "over recursed"; case ParallelBailoutOutOfMemory: return "out of memory"; case ParallelBailoutUnsupported: return "unsupported"; + case ParallelBailoutUnsupportedVM: + return "unsupported operation in VM call"; case ParallelBailoutUnsupportedStringComparison: return "unsupported string comparison"; - case ParallelBailoutUnsupportedSparseArray: - return "unsupported sparse array"; case ParallelBailoutRequestedGC: return "requested GC"; case ParallelBailoutRequestedZoneGC: return "requested zone GC"; default: return "no known reason"; } } @@ -1117,61 +1136,28 @@ js::ParallelDo::warmupExecution(bool sto Spew(SpewOps, "Warmup execution finished all the work."); *status = ExecutionWarmup; return RedLight; } return GreenLight; } -class AutoEnterParallelSection -{ - private: - JSContext *cx_; - uint8_t *prevIonTop_; - - public: - AutoEnterParallelSection(JSContext *cx) - : cx_(cx), - prevIonTop_(cx->mainThread().ionTop) - { - // Note: we do not allow GC during parallel sections. - // Moreover, we do not wish to worry about making - // write barriers thread-safe. Therefore, we guarantee - // that there is no incremental GC in progress and force - // a minor GC to ensure no cross-generation pointers get - // created: - - if (JS::IsIncrementalGCInProgress(cx->runtime())) { - JS::PrepareForIncrementalGC(cx->runtime()); - JS::FinishIncrementalGC(cx->runtime(), JS::gcreason::API); - } - - MinorGC(cx->runtime(), JS::gcreason::API); - - cx->runtime()->gcHelperThread.waitBackgroundSweepEnd(); - } - - ~AutoEnterParallelSection() { - cx_->mainThread().ionTop = prevIonTop_; - } -}; - js::ParallelDo::TrafficLight js::ParallelDo::parallelExecution(ExecutionStatus *status) { // GreenLight: bailout occurred, keep trying // RedLight: fatal error or all work completed // Recursive use of the ThreadPool is not supported. Right now we // cannot get here because parallel code cannot invoke native // functions such as ForkJoin(). JS_ASSERT(ForkJoinSlice::Current() == nullptr); - AutoEnterParallelSection enter(cx_); + ForkJoinActivation activation(cx_); ThreadPool *threadPool = &cx_->runtime()->threadPool; uint32_t numSlices = ForkJoinSlices(cx_); RootedObject rootedFun(cx_, fun_); ForkJoinShared shared(cx_, threadPool, rootedFun, numSlices, numSlices - 1, &bailoutRecords_[0]); if (!shared.init()) { @@ -1259,17 +1245,17 @@ class ParallelIonInvoke argv_[0] = ObjectValue(*callee); argv_[1] = UndefinedValue(); // Find JIT code pointer. IonScript *ion = callee->nonLazyScript()->parallelIonScript(); IonCode *code = ion->method(); jitcode_ = code->raw(); enter_ = rt->ionRuntime()->enterIon(); - calleeToken_ = CalleeToParallelToken(callee); + calleeToken_ = CalleeToToken(callee); } bool invoke(PerThreadData *perThread) { RootedValue result(perThread); enter_(jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_, nullptr, 0, result.address()); return !result.isMagic(); } @@ -1466,18 +1452,17 @@ ForkJoinShared::executePortion(PerThread JS_ASSERT(fun->is<JSFunction>()); RootedFunction callee(perThread, &fun->as<JSFunction>()); if (!callee->nonLazyScript()->hasParallelIonScript()) { // Sometimes, particularly with GCZeal, the parallel ion // script can be collected between starting the parallel // op and reaching this point. In that case, we just fail // and fallback. Spew(SpewOps, "Down (Script no longer present)"); - slice.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent, - nullptr, nullptr, nullptr); + slice.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent); setAbortFlag(false); } else { ParallelIonInvoke<3> fii(cx_->runtime(), callee, 3); fii.args[0] = Int32Value(slice.sliceId); fii.args[1] = Int32Value(slice.numSlices); fii.args[2] = BooleanValue(false); @@ -1507,18 +1492,17 @@ ForkJoinShared::check(ForkJoinSlice &sli // requestZoneGC() methods should be invoked. JS_ASSERT(!cx_->runtime()->gcIsNeeded); // If interrupt is requested, bring worker threads to a halt, // service the interrupt, then let them start back up again. // AutoRendezvous autoRendezvous(slice); // if (!js_HandleExecutionInterrupt(cx_)) // return setAbortFlag(true); - slice.bailoutRecord->setCause(ParallelBailoutInterrupt, - nullptr, nullptr, nullptr); + slice.bailoutRecord->setCause(ParallelBailoutInterrupt); setAbortFlag(false); return false; } } else if (rendezvous_) { joinRendezvous(slice); } return true; @@ -1725,30 +1709,36 @@ ForkJoinSlice::InitializeTLS() } return true; } void ForkJoinSlice::requestGC(JS::gcreason::Reason reason) { shared->requestGC(reason); - bailoutRecord->setCause(ParallelBailoutRequestedGC, - nullptr, nullptr, nullptr); + bailoutRecord->setCause(ParallelBailoutRequestedGC); shared->setAbortFlag(false); } void ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason) { shared->requestZoneGC(zone, reason); - bailoutRecord->setCause(ParallelBailoutRequestedZoneGC, - nullptr, nullptr, nullptr); + bailoutRecord->setCause(ParallelBailoutRequestedZoneGC); shared->setAbortFlag(false); } +bool +ForkJoinSlice::setPendingAbortFatal(ParallelBailoutCause cause) +{ + shared->setPendingAbortFatal(); + bailoutRecord->setCause(cause); + return false; +} + ///////////////////////////////////////////////////////////////////////////// uint32_t js::ForkJoinSlices(JSContext *cx) { // Parallel workers plus this main thread. return cx->runtime()->threadPool.numWorkers() + 1; } @@ -1771,30 +1761,39 @@ js::ParallelBailoutRecord::reset(JSConte } void js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript, jsbytecode *currentPc) { + this->cause = cause; + updateCause(cause, outermostScript, currentScript, currentPc); +} + +void +js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause, + JSScript *outermostScript, + JSScript *currentScript, + jsbytecode *currentPc) +{ JS_ASSERT_IF(outermostScript, currentScript); JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript()); JS_ASSERT_IF(currentScript, outermostScript); JS_ASSERT_IF(!currentScript, !currentPc); - this->cause = cause; + if (this->cause == ParallelBailoutNone) + this->cause = cause; - if (outermostScript) { + if (outermostScript) this->topScript = outermostScript; - } - if (currentScript) { + if (currentScript) addTrace(currentScript, currentPc); - } } void js::ParallelBailoutRecord::addTrace(JSScript *script, jsbytecode *pc) { // Ideally, this should never occur, because we should always have // a script when we invoke setCause, but I havent' fully
--- a/js/src/vm/ForkJoin.h +++ b/js/src/vm/ForkJoin.h @@ -218,41 +218,41 @@ struct IonLIRTraceData { }; /////////////////////////////////////////////////////////////////////////// // Bailout tracking enum ParallelBailoutCause { ParallelBailoutNone, - // compiler returned Method_Skipped + // Compiler returned Method_Skipped ParallelBailoutCompilationSkipped, - // compiler returned Method_CantCompile + // Compiler returned Method_CantCompile ParallelBailoutCompilationFailure, - // the periodic interrupt failed, which can mean that either + // The periodic interrupt failed, which can mean that either // another thread canceled, the user interrupted us, etc ParallelBailoutInterrupt, - // an IC update failed + // An IC update failed ParallelBailoutFailedIC, // Heap busy flag was set during interrupt ParallelBailoutHeapBusy, ParallelBailoutMainScriptNotPresent, ParallelBailoutCalledToUncompiledScript, ParallelBailoutIllegalWrite, ParallelBailoutAccessToIntrinsic, ParallelBailoutOverRecursed, ParallelBailoutOutOfMemory, ParallelBailoutUnsupported, + ParallelBailoutUnsupportedVM, ParallelBailoutUnsupportedStringComparison, - ParallelBailoutUnsupportedSparseArray, ParallelBailoutRequestedGC, ParallelBailoutRequestedZoneGC, }; struct ParallelBailoutTrace { JSScript *script; jsbytecode *bytecode; }; @@ -266,19 +266,23 @@ struct ParallelBailoutRecord { // but for now we gather at most a single frame. static const uint32_t MaxDepth = 1; uint32_t depth; ParallelBailoutTrace trace[MaxDepth]; void init(JSContext *cx); void reset(JSContext *cx); void setCause(ParallelBailoutCause cause, - JSScript *outermostScript, // inliner (if applicable) - JSScript *currentScript, // inlinee (if applicable) - jsbytecode *currentPc); + JSScript *outermostScript = nullptr, // inliner (if applicable) + JSScript *currentScript = nullptr, // inlinee (if applicable) + jsbytecode *currentPc = nullptr); + void updateCause(ParallelBailoutCause cause, + JSScript *outermostScript, + JSScript *currentScript, + jsbytecode *currentPc); void addTrace(JSScript *script, jsbytecode *pc); }; struct ForkJoinShared; class ForkJoinSlice : public ThreadSafeContext { @@ -310,16 +314,28 @@ class ForkJoinSlice : public ThreadSafeC // |TriggerCompartmentGC()| as appropriate once the parallel // section is complete. This is done because those routines do // various preparations that are not thread-safe, and because the // full set of arenas is not available until the end of the // parallel section. void requestGC(JS::gcreason::Reason reason); void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason); + // Set the fatal flag for the next abort. Used to distinguish retry or + // fatal aborts from VM functions. + bool setPendingAbortFatal(ParallelBailoutCause cause); + + // Reports an unsupported operation, returning false if we are reporting + // an error. Otherwise drop the warning on the floor. + bool reportError(ParallelBailoutCause cause, unsigned report) { + if (report & JSREPORT_ERROR) + return setPendingAbortFatal(cause); + return true; + } + // During the parallel phase, this method should be invoked // periodically, for example on every backedge, similar to the // interrupt check. If it returns false, then the parallel phase // has been aborted and so you should bailout. The function may // also rendesvous to perform GC or do other similar things. // // This function is guaranteed to have no effect if both // runtime()->interrupt is zero. Ion-generated code takes
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -609,17 +609,17 @@ ScriptFrameIter::Data::Data(JSContext *c : perThread_(perThread), cx_(cx), savedOption_(savedOption), contextOption_(contextOption), pc_(nullptr), interpFrames_(nullptr), activations_(cx->runtime()) #ifdef JS_ION - , ionFrames_((uint8_t *)nullptr) + , ionFrames_((uint8_t *)nullptr, SequentialExecution) #endif { } ScriptFrameIter::Data::Data(const ScriptFrameIter::Data &other) : perThread_(other.perThread_), cx_(other.cx_), savedOption_(other.savedOption_), @@ -1331,16 +1331,42 @@ jit::JitActivation::setActive(JSContext prevIonJSContext_ = cx->mainThread().ionJSContext; cx->mainThread().ionJSContext = cx; } else { cx->mainThread().ionTop = prevIonTop_; cx->mainThread().ionJSContext = prevIonJSContext_; } } +ForkJoinActivation::ForkJoinActivation(JSContext *cx) + : Activation(cx, ForkJoin), + prevIonTop_(cx->mainThread().ionTop) +{ + // Note: we do not allow GC during parallel sections. + // Moreover, we do not wish to worry about making + // write barriers thread-safe. Therefore, we guarantee + // that there is no incremental GC in progress and force + // a minor GC to ensure no cross-generation pointers get + // created: + + if (JS::IsIncrementalGCInProgress(cx->runtime())) { + JS::PrepareForIncrementalGC(cx->runtime()); + JS::FinishIncrementalGC(cx->runtime(), JS::gcreason::API); + } + + MinorGC(cx->runtime(), JS::gcreason::API); + + cx->runtime()->gcHelperThread.waitBackgroundSweepEnd(); +} + +ForkJoinActivation::~ForkJoinActivation() +{ + cx_->mainThread().ionTop = prevIonTop_; +} + InterpreterFrameIterator & InterpreterFrameIterator::operator++() { JS_ASSERT(!done()); if (fp_ != activation_->entry_) { pc_ = fp_->prevpc(); sp_ = fp_->prevsp(); fp_ = fp_->prev();
--- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1170,16 +1170,17 @@ struct DefaultHasher<AbstractFramePtr> { static bool match(const AbstractFramePtr &k, const Lookup &l) { return k == l; } }; /*****************************************************************************/ class InterpreterActivation; +class ForkJoinActivation; namespace jit { class JitActivation; }; class Activation { protected: @@ -1188,17 +1189,17 @@ class Activation Activation *prev_; // Counter incremented by JS_SaveFrameChain on the top-most activation and // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop // iterating when it reaches this activation (if GO_THROUGH_SAVED is not // set). size_t savedFrameChain_; - enum Kind { Interpreter, Jit }; + enum Kind { Interpreter, Jit, ForkJoin }; Kind kind_; inline Activation(JSContext *cx, Kind kind_); inline ~Activation(); public: JSContext *cx() const { return cx_; @@ -1211,25 +1212,32 @@ class Activation } bool isInterpreter() const { return kind_ == Interpreter; } bool isJit() const { return kind_ == Jit; } + bool isForkJoin() const { + return kind_ == ForkJoin; + } InterpreterActivation *asInterpreter() const { JS_ASSERT(isInterpreter()); return (InterpreterActivation *)this; } jit::JitActivation *asJit() const { JS_ASSERT(isJit()); return (jit::JitActivation *)this; } + ForkJoinActivation *asForkJoin() const { + JS_ASSERT(isForkJoin()); + return (ForkJoinActivation *)this; + } void saveFrameChain() { savedFrameChain_++; } void restoreFrameChain() { JS_ASSERT(savedFrameChain_ > 0); savedFrameChain_--; } @@ -1387,16 +1395,25 @@ class JitActivationIterator : public Act } // Returns the bottom and top addresses of the current JitActivation. void jitStackRange(uintptr_t *&min, uintptr_t *&end); }; } // namespace jit +class ForkJoinActivation : public Activation +{ + uint8_t *prevIonTop_; + + public: + ForkJoinActivation(JSContext *cx); + ~ForkJoinActivation(); +}; + // Iterates over the frames of a single InterpreterActivation. class InterpreterFrameIterator { InterpreterActivation *activation_; StackFrame *fp_; jsbytecode *pc_; Value *sp_;
--- a/layout/media/Makefile.in +++ b/layout/media/Makefile.in @@ -101,40 +101,19 @@ SHARED_LIBRARY_LIBS += \ SHARED_LIBRARY_LIBS += \ $(DEPTH)/gfx/2d/$(LIB_PREFIX)gfx2d.$(LIB_SUFFIX) \ $(NULL) ifdef MOZ_ENABLE_SKIA SHARED_LIBRARY_LIBS += $(MOZ_SKIA_LIBS) endif -ifdef MOZ_WEBRTC -ifndef MOZ_WEBRTC_IN_LIBXUL -DEFINES += -DMOZ_WEBRTC_GKMEDIA=1 -include $(topsrcdir)/media/webrtc/shared_libs.mk -SHARED_LIBRARY_LIBS += $(WEBRTC_LIBS) -endif -endif - ifeq (WINNT,$(OS_TARGET)) EXTRA_DSO_LDOPTS = $(MOZALLOC_LIB) $(NSPR_LIBS) OS_LIBS += $(call EXPAND_LIBNAME,usp10 ole32) - -ifdef MOZ_WEBRTC -EXTRA_DSO_LDOPTS += \ - -LIBPATH:"$(MOZ_DIRECTX_SDK_PATH)/lib/$(MOZ_DIRECTX_SDK_CPU_SUFFIX)" \ - $(NULL) -OS_LIBS += $(call EXPAND_LIBNAME,secur32 crypt32 iphlpapi strmiids dmoguids wmcodecdspuuid amstrmid msdmo wininet) -ifdef _MSC_VER -OS_LIBS += $(call EXPAND_LIBNAME,delayimp) -EXTRA_DSO_LDOPTS += \ - -DELAYLOAD:msdmo.dll \ - $(NULL) -endif -endif DEFFILE = symbols.def endif include $(topsrcdir)/config/rules.mk ifeq (WINNT,$(OS_TARGET)) symbols.def: symbols.def.in $(GLOBAL_DEPS) $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(ACDEFINES) $< > $@
--- a/layout/media/moz.build +++ b/layout/media/moz.build @@ -3,8 +3,9 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. MODULE = 'layout' LIBRARY_NAME = 'gkmedias' +DIRS += ['webrtc']
--- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -160,31 +160,16 @@ opus_encode_float ShInitialize ShFinalize ShGetObjectCode ShDestruct ShGetInfoLog ShCompile ShGetInfo ShConstructCompiler -#ifdef MOZ_WEBRTC_GKMEDIA -#ifdef HAVE_64BIT_OS -?GetInterface@ViERender@webrtc@@SAPEAV12@PEAVVideoEngine@2@@Z -?GetInterface@ViECapture@webrtc@@SAPEAV12@PEAVVideoEngine@2@@Z -?GetInterface@ViEBase@webrtc@@SAPEAV12@PEAVVideoEngine@2@@Z -?Create@VideoEngine@webrtc@@SAPEAV12@XZ -?Delete@VideoEngine@webrtc@@SA_NAEAPEAV12@@Z -#else -?GetInterface@ViERender@webrtc@@SAPAV12@PAVVideoEngine@2@@Z -?GetInterface@ViECapture@webrtc@@SAPAV12@PAVVideoEngine@2@@Z -?GetInterface@ViEBase@webrtc@@SAPAV12@PAVVideoEngine@2@@Z -?Create@VideoEngine@webrtc@@SAPAV12@XZ -?Delete@VideoEngine@webrtc@@SA_NAAPAV12@@Z -#endif -#endif ShGetActiveAttrib ShGetActiveUniform #ifndef MOZ_NATIVE_PNG MOZ_APNG_get_first_frame_is_hidden MOZ_APNG_get_next_frame_blend_op MOZ_APNG_get_next_frame_delay_den MOZ_APNG_get_next_frame_delay_num MOZ_APNG_get_next_frame_dispose_op
rename from media/webrtc/shared_libs.mk rename to layout/media/webrtc/Makefile.in --- a/media/webrtc/shared_libs.mk +++ b/layout/media/webrtc/Makefile.in @@ -1,14 +1,14 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # shared libs for webrtc -WEBRTC_LIBS = \ +SHARED_LIBRARY_LIBS = \ $(call EXPAND_LIBNAME_PATH,common_video,$(DEPTH)/media/webrtc/trunk/webrtc/common_video/common_video_common_video) \ $(call EXPAND_LIBNAME_PATH,common_audio,$(DEPTH)/media/webrtc/trunk/webrtc/common_audio/common_audio_common_audio) \ $(call EXPAND_LIBNAME_PATH,video_capture_module,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_video_capture_module) \ $(call EXPAND_LIBNAME_PATH,webrtc_utility,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_webrtc_utility) \ $(call EXPAND_LIBNAME_PATH,audio_coding_module,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_audio_coding_module) \ $(call EXPAND_LIBNAME_PATH,CNG,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_CNG) \ $(call EXPAND_LIBNAME_PATH,G711,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_G711) \ $(call EXPAND_LIBNAME_PATH,PCM16B,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_PCM16B) \ @@ -33,34 +33,34 @@ WEBRTC_LIBS = \ $(call EXPAND_LIBNAME_PATH,audio_processing,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_audio_processing) \ $(call EXPAND_LIBNAME_PATH,yuv,$(DEPTH)/media/webrtc/trunk/third_party/libyuv/libyuv_libyuv) \ $(call EXPAND_LIBNAME_PATH,nicer,$(DEPTH)/media/mtransport/third_party/nICEr/nicer_nicer) \ $(call EXPAND_LIBNAME_PATH,nrappkit,$(DEPTH)/media/mtransport/third_party/nrappkit/nrappkit_nrappkit) \ $(NULL) # if we're on an intel arch, we want SSE2 optimizations ifneq (,$(INTEL_ARCHITECTURE)) -WEBRTC_LIBS += \ +SHARED_LIBRARY_LIBS += \ $(call EXPAND_LIBNAME_PATH,video_processing_sse2,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_video_processing_sse2) \ $(call EXPAND_LIBNAME_PATH,audio_processing_sse2,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_audio_processing_sse2) \ $(call EXPAND_LIBNAME_PATH,common_audio_sse2,$(DEPTH)/media/webrtc/trunk/webrtc/common_audio/common_audio_common_audio_sse2) \ $(NULL) endif ifeq ($(CPU_ARCH), arm) ifeq (Android,$(OS_TARGET)) # NEON detection on WebRTC is Android only. If WebRTC supports Linux/arm etc, # we should remove OS check # extra ARM libs -WEBRTC_LIBS += \ +SHARED_LIBRARY_LIBS += \ $(call EXPAND_LIBNAME_PATH,cpu_features_android,$(DEPTH)/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_cpu_features_android) \ $(NULL) # neon for ARM ifeq ($(BUILD_ARM_NEON),1) -WEBRTC_LIBS += \ +SHARED_LIBRARY_LIBS += \ $(call EXPAND_LIBNAME_PATH,common_audio_neon,$(DEPTH)/media/webrtc/trunk/webrtc/common_audio/common_audio_common_audio_neon) \ $(call EXPAND_LIBNAME_PATH,audio_processing_neon,$(DEPTH)/media/webrtc/trunk/webrtc/modules/modules_audio_processing_neon) \ $(NULL) endif endif endif
new file mode 100644 --- /dev/null +++ b/layout/media/webrtc/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +LIBRARY_NAME = 'webrtc'
--- a/layout/reftests/scoped-style/reftest.list +++ b/layout/reftests/scoped-style/reftest.list @@ -15,16 +15,18 @@ == scoped-style-015.html scoped-style-015-ref.html == scoped-style-016.html scoped-style-016-ref.html == scoped-style-017.html scoped-style-017-ref.html == scoped-style-018.html scoped-style-018-ref.html == scoped-style-019.svg scoped-style-019-ref.svg == scoped-style-020.html scoped-style-020-ref.html == scoped-style-021.html scoped-style-021-ref.html == scoped-style-022.html scoped-style-022-ref.html +== scoped-style-023.html scoped-style-023-ref.html +== scoped-style-024.html scoped-style-024-ref.html == scoped-style-important-001.html scoped-style-important-001-ref.html == scoped-style-important-002.html scoped-style-important-002-ref.html == scoped-style-important-003.html scoped-style-important-003-ref.html == scoped-style-important-004.html scoped-style-important-004-ref.html == scoped-style-important-005.html scoped-style-important-005-ref.html == scoped-style-important-006.html scoped-style-important-006-ref.html == scoped-style-important-007.html scoped-style-important-007-ref.html == scoped-style-dynamic-001.html scoped-style-dynamic-001-ref.html
new file mode 100644 --- /dev/null +++ b/layout/reftests/scoped-style/scoped-style-023-ref.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<body> + <p><span style="color: green">First line</span><br><span style="color: blue">Second line</span></p> +</body>
new file mode 100644 --- /dev/null +++ b/layout/reftests/scoped-style/scoped-style-023.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<body> + <style scoped> + p::first-line { color: green; } + span { color: blue; } + </style> + <p>First line<br><span>Second line</span></p> +</body>
new file mode 100644 --- /dev/null +++ b/layout/reftests/scoped-style/scoped-style-024-ref.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<body> + <p style="color: green">Hello.</p> +</body>
new file mode 100644 --- /dev/null +++ b/layout/reftests/scoped-style/scoped-style-024.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<body> + <style scoped> + p[data-test] { color: green; } + </style> + <p>Hello.</p> + <script> + document.body.offsetHeight; + document.querySelector("p").setAttribute("data-test", ""); + </script> +</body>
--- a/layout/style/nsRuleProcessorData.h +++ b/layout/style/nsRuleProcessorData.h @@ -168,19 +168,43 @@ struct MOZ_STACK_CLASS TreeMatchContext return false; } if (mCurrentStyleScope == aElement) { mCurrentStyleScope = nullptr; } return true; } +#ifdef DEBUG + void AssertHasAllStyleScopes(mozilla::dom::Element* aElement) + { + int32_t i = mStyleScopes.Length() - 1; + nsINode* node = aElement->GetParentNode(); + while (node && i != -1) { + if (node->IsScopedStyleRoot()) { + MOZ_ASSERT(mStyleScopes[i] == node); + --i; + } + node = node->GetParentNode(); + } + while (node) { + MOZ_ASSERT(!node->IsScopedStyleRoot()); + node = node->GetParentNode(); + } + MOZ_ASSERT(i == -1); + } +#endif + bool SetStyleScopeForSelectorMatching(mozilla::dom::Element* aSubject, mozilla::dom::Element* aScope) { +#ifdef DEBUG + AssertHasAllStyleScopes(aSubject); +#endif + mForScopedStyle = !!aScope; if (!aScope) { // This is not for a scoped style sheet; return true, as we want // selector matching to proceed. mCurrentStyleScope = nullptr; return true; } if (aScope == aSubject) {
--- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1152,22 +1152,31 @@ nsStyleSet::WalkRuleProcessors(nsIStyleR if (mRuleProcessors[eStyleAttrSheet]) (*aFunc)(mRuleProcessors[eStyleAttrSheet], aData); if (mRuleProcessors[eOverrideSheet]) (*aFunc)(mRuleProcessors[eOverrideSheet], aData); (*aFunc)(mRuleProcessors[eAnimationSheet], aData); (*aFunc)(mRuleProcessors[eTransitionSheet], aData); } +static void +InitAncestorsIfInStyleScope(TreeMatchContext& aTreeContext, Element* aElement) +{ + if (aElement->IsElementInStyleScope()) { + aTreeContext.InitAncestors(aElement->GetParentElement()); + } +} + already_AddRefed<nsStyleContext> nsStyleSet::ResolveStyleFor(Element* aElement, nsStyleContext* aParentContext) { TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, aElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aElement); return ResolveStyleFor(aElement, aParentContext, treeContext); } already_AddRefed<nsStyleContext> nsStyleSet::ResolveStyleFor(Element* aElement, nsStyleContext* aParentContext, TreeMatchContext& aTreeMatchContext) { @@ -1332,16 +1341,17 @@ nsStyleSet::ResolvePseudoElementStyle(El NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount, "must have pseudo element type"); NS_ASSERTION(aParentElement, "Must have parent element"); nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, aParentElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aParentElement); PseudoElementRuleProcessorData data(PresContext(), aParentElement, &ruleWalker, aType, treeContext); WalkRestrictionRule(aType, &ruleWalker); FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data, aParentElement, &ruleWalker); nsRuleNode *ruleNode = ruleWalker.CurrentNode(); nsRuleNode *visitedRuleNode = nullptr; @@ -1375,16 +1385,17 @@ nsStyleSet::ResolvePseudoElementStyle(El already_AddRefed<nsStyleContext> nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, nsCSSPseudoElements::Type aType, nsStyleContext* aParentContext) { TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, aParentElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aParentElement); return ProbePseudoElementStyle(aParentElement, aType, aParentContext, treeContext); } already_AddRefed<nsStyleContext> nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, nsCSSPseudoElements::Type aType, nsStyleContext* aParentContext, @@ -1513,16 +1524,17 @@ nsStyleSet::ResolveXULTreePseudoStyle(El NS_ASSERTION(aPseudoTag, "must have pseudo tag"); NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag), "Unexpected pseudo"); nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, aParentElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aParentElement); XULTreeRuleProcessorData data(PresContext(), aParentElement, &ruleWalker, aPseudoTag, aComparator, treeContext); FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data, aParentElement, &ruleWalker); nsRuleNode *ruleNode = ruleWalker.CurrentNode(); nsRuleNode *visitedRuleNode = nullptr; @@ -1875,16 +1887,17 @@ nsStyleSet::HasDocumentStateDependentSty nsIContent* aContent, nsEventStates aStateMask) { if (!aContent || !aContent->IsElement()) return false; TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, aContent->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aContent->AsElement()); StatefulData data(aPresContext, aContent->AsElement(), aStateMask, treeContext); WalkRuleProcessors(SheetHasDocumentStateStyle, &data, true); return data.mHint != 0; } static bool SheetHasStatefulStyle(nsIStyleRuleProcessor* aProcessor, void *aData) @@ -1898,16 +1911,17 @@ static bool SheetHasStatefulStyle(nsISty // Test if style is dependent on content state nsRestyleHint nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext, Element* aElement, nsEventStates aStateMask) { TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, aElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aElement); StatefulData data(aPresContext, aElement, aStateMask, treeContext); WalkRuleProcessors(SheetHasStatefulStyle, &data, false); return data.mHint; } struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData { AttributeData(nsPresContext* aPresContext, Element* aElement, nsIAtom* aAttribute, int32_t aModType, @@ -1933,16 +1947,17 @@ nsRestyleHint nsStyleSet::HasAttributeDependentStyle(nsPresContext* aPresContext, Element* aElement, nsIAtom* aAttribute, int32_t aModType, bool aAttrHasChanged) { TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, aElement->OwnerDoc()); + InitAncestorsIfInStyleScope(treeContext, aElement); AttributeData data(aPresContext, aElement, aAttribute, aModType, aAttrHasChanged, treeContext); WalkRuleProcessors(SheetHasAttributeStyle, &data, false); return data.mHint; } bool nsStyleSet::MediumFeaturesChanged(nsPresContext* aPresContext)
--- a/media/webrtc/signaling/test/Makefile.in +++ b/media/webrtc/signaling/test/Makefile.in @@ -6,16 +6,17 @@ LIBS = \ $(XPCOM_LIBS) \ $(NSPR_LIBS) \ $(NSS_LIBS) \ $(REALTIME_LIBS) \ $(DEPTH)/xpcom/glue/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \ $(DEPTH)/media/mtransport/standalone/$(LIB_PREFIX)mtransport_s.$(LIB_SUFFIX) \ $(DEPTH)/media/webrtc/signalingtest/signaling_ecc/$(LIB_PREFIX)ecc.$(LIB_SUFFIX) \ $(DEPTH)/media/webrtc/signalingtest/signaling_sipcc/$(LIB_PREFIX)sipcc.$(LIB_SUFFIX) \ + $(DEPTH)/layout/media/webrtc/$(LIB_PREFIX)webrtc.$(LIB_SUFFIX) \ $(DEPTH)/layout/media/$(LIB_PREFIX)gkmedias.$(LIB_SUFFIX) \ $(DEPTH)/media/webrtc/trunk/testing/gtest_gtest/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \ $(DEPTH)/netwerk/srtp/src/$(LIB_PREFIX)nksrtp_s.$(LIB_SUFFIX) \ $(NULL) ifdef MOZ_CUBEB ifdef MOZ_ALSA LIBS += \
--- a/media/webrtc/webrtc_config.gypi +++ b/media/webrtc/webrtc_config.gypi @@ -36,17 +36,17 @@ }], ], # (for vp8) chromium sets to 0 also 'use_temporal_layers': 0, # Creates AEC internal sample dump files in current directory # 'aec_debug_dump': 1, # codec enable/disables: - # Note: if you change one here, you must modify shared_libs.mk! + # Note: if you change one here, you must modify layout/media/webrtc/Makefile.in! 'include_g711': 1, 'include_opus': 1, 'include_g722': 0, 'include_ilbc': 0, 'include_isac': 0, 'include_pcm16b': 1, } }
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -308,22 +308,16 @@ FENNEC_PP_JAVA_FILES := \ widget/GeckoRelativeLayout.java \ widget/GeckoTextSwitcher.java \ widget/GeckoTextView.java \ SysInfo.java \ WebApp.java \ WebApps.java \ $(NULL) -FENNEC_PP_XML_FILES = \ - res/xml/preferences.xml \ - res/xml/preferences_customize.xml \ - res/xml/searchable.xml \ - $(NULL) - ifneq (,$(findstring -march=armv7,$(OS_CFLAGS))) MIN_CPU_VERSION=7 else MIN_CPU_VERSION=5 endif MOZ_APP_BUILDID=$(shell cat $(DEPTH)/config/buildid) @@ -353,16 +347,18 @@ endif DEFINES += \ -DMANGLED_ANDROID_PACKAGE_NAME=$(subst fennec,f3nn3c,$(ANDROID_PACKAGE_NAME)) \ -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ -DANDROID_CPU_ARCH=$(ANDROID_CPU_ARCH) \ -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \ -DCPU_ARCH=$(CPU_ARCH) \ -DGRE_MILESTONE=$(GRE_MILESTONE) \ -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \ + -DMOZ_ANDROID_SHARED_ID="$(MOZ_ANDROID_SHARED_ID)" \ + -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE="$(MOZ_ANDROID_SHARED_ACCOUNT_TYPE)" \ -DMOZ_APP_ABI=$(TARGET_XPCOM_ABI) \ -DMOZ_APP_BASENAME=$(MOZ_APP_BASENAME) \ -DMOZ_APP_BUILDID=$(MOZ_APP_BUILDID) \ -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \ -DMOZ_APP_ID=$(MOZ_APP_ID) \ -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ -DMOZ_APP_VENDOR=$(MOZ_APP_VENDOR) \ -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \ @@ -389,66 +385,38 @@ GARBAGE += \ AndroidManifest.xml \ classes.dex \ $(MOZGLUE_PP_JAVA_FILES) \ $(FENNEC_PP_JAVA_FILES) \ $(SYNC_PP_JAVA_FILES) \ gecko.ap_ \ res/values/strings.xml \ R.java \ - $(FENNEC_PP_XML_FILES) \ - $(SYNC_PP_RES_XML) \ package-name.txt \ fennec_ids.txt \ Manifest.java \ javah.out \ jni-stubs.inc \ GeneratedJNIWrappers.cpp \ GeneratedJNIWrappers.h \ $(NULL) GARBAGE_DIRS += classes db jars res sync services -MOZ_ANDROID_SHARED_ID = "$(ANDROID_PACKAGE_NAME).sharedID" -MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "$(ANDROID_PACKAGE_NAME)_sync" - # Bug 567884 - Need a way to find appropriate icons during packaging ifeq ($(MOZ_APP_NAME),fennec) ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png ICON_PATH_XHDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_96x96.png ICON_PATH_XXHDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_144x144.png - -# we released these builds to the public with shared IDs and need to keep them -ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME)) -MOZ_ANDROID_SHARED_ID = "org.mozilla.firefox.sharedID" -MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.firefox_sync" -else ifeq (org.mozilla.firefox_beta,$(ANDROID_PACKAGE_NAME)) -MOZ_ANDROID_SHARED_ID = "org.mozilla.firefox.sharedID" -MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.firefox_sync" -else ifeq (org.mozilla.fennec_aurora,$(ANDROID_PACKAGE_NAME)) -MOZ_ANDROID_SHARED_ID = "org.mozilla.fennec.sharedID" -MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.fennec_sync" -else ifeq (org.mozilla.fennec,$(ANDROID_PACKAGE_NAME)) -MOZ_ANDROID_SHARED_ID = "org.mozilla.fennec.sharedID" -MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.fennec_sync" -endif - else ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png endif -ifdef MOZ_ANDROID_SHARED_ID -DEFINES += -DMOZ_ANDROID_SHARED_ID="$(MOZ_ANDROID_SHARED_ID)" -endif -ifdef MOZ_ANDROID_SHARED_ACCOUNT_TYPE -DEFINES += -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE="$(MOZ_ANDROID_SHARED_ACCOUNT_TYPE)" -endif - RES_LAYOUT = \ $(SYNC_RES_LAYOUT) \ res/layout/arrow_popup.xml \ res/layout/autocomplete_list.xml \ res/layout/autocomplete_list_item.xml \ res/layout/bookmark_edit.xml \ res/layout/bookmark_folder_row.xml \ res/layout/bookmark_item_row.xml \ @@ -593,21 +561,24 @@ RES_VALUES_V14 = \ res/values-v14/styles.xml \ $(NULL) RES_VALUES_V16 = \ res/values-v16/styles.xml \ $(NULL) RES_XML = \ + res/xml/preferences.xml \ + res/xml/preferences_customize.xml \ res/xml/preferences_display.xml \ res/xml/preferences_search.xml \ res/xml/preferences_privacy.xml \ res/xml/preferences_vendor.xml \ res/xml/preferences_devtools.xml \ + res/xml/searchable.xml \ $(SYNC_RES_XML) \ $(NULL) RES_XML_V11 = \ res/xml-v11/preferences_customize.xml \ res/xml-v11/preference_headers.xml \ res/xml-v11/preferences_customize_tablet.xml \ res/xml-v11/preferences.xml \ @@ -1328,26 +1299,16 @@ jni-stubs.inc: jars/gecko-browser.jar ja $(PYTHON) $(topsrcdir)/mobile/android/base/jni-generator.py javah.out $@ ANNOTATION_PROCESSOR_JAR_FILES := $(DEPTH)/build/annotationProcessors/annotationProcessors.jar GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) GeneratedJNIWrappers.cpp: $(ALL_JARS) $(JAVA) -classpath $(JAVA_BOOTCLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) org.mozilla.gecko.annotationProcessors.AnnotationProcessor $(ALL_JARS) -PP_RES_XML= \ - $(SYNC_PP_RES_XML) \ - $(FENNEC_PP_XML_FILES) \ - $(NULL) - -# This is kinda awful; if any of the source files change, we remake them all. -$(PP_RES_XML): $(patsubst res/%,$(srcdir)/resources/%.in,$(PP_RES_XML)) - $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ - $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $(subst res/,$(srcdir)/resources/,$@).in > $@ - # AndroidManifest.xml includes these files, so they need to be marked as dependencies. SERVICES_MANIFEST_FRAGMENTS = $(wildcard $(topsrcdir)/mobile/android/services/manifests/*.in) android-tgts = \ AndroidManifest.xml \ $(MOZGLUE_PP_JAVA_FILES) \ $(FENNEC_PP_JAVA_FILES) \ $(SYNC_PP_JAVA_FILES) \ @@ -1402,17 +1363,16 @@ all_resources = \ res/drawable-mdpi/icon.png \ res/drawable-hdpi/icon.png \ res/drawable-xhdpi/icon.png \ res/drawable-xxhdpi/icon.png \ res/values/strings.xml \ $(MULTILOCALE_STRINGS_XML_FILES) \ AndroidManifest.xml \ $(RESOURCES) \ - $(PP_RES_XML) \ $(NULL) R.java: $(all_resources) $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko --non-constant-id gecko.ap_: $(all_resources) $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
--- a/mobile/android/base/android-services-files.mk +++ b/mobile/android/base/android-services-files.mk @@ -338,22 +338,19 @@ SYNC_RES_VALUES_V11 := \ res/values-v11/sync_styles.xml \ $(NULL) SYNC_RES_VALUES_LARGE_V11 := \ res/values-large-v11/sync_styles.xml \ $(NULL) SYNC_RES_XML := \ - $(NULL) - -SYNC_PP_RES_XML := \ + res/xml/sync_authenticator.xml \ res/xml/sync_syncadapter.xml \ res/xml/sync_options.xml \ - res/xml/sync_authenticator.xml \ $(NULL) SYNC_THIRDPARTY_JAVA_FILES := \ httpclientandroidlib/androidextra/HttpClientAndroidLog.java \ httpclientandroidlib/annotation/GuardedBy.java \ httpclientandroidlib/annotation/Immutable.java \ httpclientandroidlib/annotation/NotThreadSafe.java \ httpclientandroidlib/annotation/ThreadSafe.java \
--- a/mobile/android/base/home/BrowserSearch.java +++ b/mobile/android/base/home/BrowserSearch.java @@ -211,18 +211,20 @@ public class BrowserSearch extends HomeF } @Override public void onDestroyView() { super.onDestroyView(); unregisterEventListener("SearchEngines:Data"); + mList.setAdapter(null); + mList = null; + mView = null; - mList = null; mSuggestionsOptInPrompt = null; mSuggestClient = null; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mList.setTag(HomePager.LIST_TAG_BROWSER_SEARCH);
--- a/mobile/android/base/locales/Makefile.in +++ b/mobile/android/base/locales/Makefile.in @@ -54,20 +54,24 @@ strings-xml-preqs =\ $(strings-xml-in) \ $(BRANDPATH) \ $(STRINGSPATH) \ $(SYNCSTRINGSPATH) \ $(BOOKMARKSPATH) \ $(if $(IS_LANGUAGE_REPACK),FORCE) \ $(NULL) +$(if $(MOZ_ANDROID_SHARED_ACCOUNT_TYPE),,$(error Missing MOZ_ANDROID_SHARED_ACCOUNT_TYPE)) + $(dir-strings-xml)/strings.xml: $(strings-xml-preqs) $(NSINSTALL) -D $(dir-strings-xml) $(TOUCH) $(call mkdir_deps,$(dir-strings-xml)) $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(DEFINES) \ + -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ + -DBOOKMARKSPATH="$(BOOKMARKSPATH)" \ -DBRANDPATH="$(BRANDPATH)" \ + -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE=$(MOZ_ANDROID_SHARED_ACCOUNT_TYPE) \ + -DMOZ_APP_DISPLAYNAME="@MOZ_APP_DISPLAYNAME@" \ -DSTRINGSPATH="$(STRINGSPATH)" \ -DSYNCSTRINGSPATH="$(SYNCSTRINGSPATH)" \ - -DBOOKMARKSPATH="$(BOOKMARKSPATH)" \ - -DMOZ_APP_DISPLAYNAME="@MOZ_APP_DISPLAYNAME@" \ $< \ > $@
rename from mobile/android/base/resources/xml/preferences.xml.in rename to mobile/android/base/resources/xml/preferences.xml --- a/mobile/android/base/resources/xml/preferences.xml.in +++ b/mobile/android/base/resources/xml/preferences.xml @@ -1,9 +1,8 @@ -#filter substitution <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <!-- Preferences screen for pre-v11 Android devices that do not support PreferenceFragment or ActionBar. Preference changes here should be mirrored to xml-v11/preferences.xml. --> @@ -13,56 +12,56 @@ android:enabled="false"> <org.mozilla.gecko.SyncPreference android:key="android.not_a_preference.sync" android:title="@string/pref_sync" android:persistent="false" /> <PreferenceScreen android:title="@string/pref_category_customize" > <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_customize" /> </intent> </PreferenceScreen> <PreferenceScreen android:title="@string/pref_category_display" > <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_display" /> </intent> </PreferenceScreen> <PreferenceScreen android:title="@string/pref_category_privacy_short" > <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_privacy" /> </intent> </PreferenceScreen> <PreferenceScreen android:title="@string/pref_category_vendor"> <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_vendor" /> </intent> </PreferenceScreen> <PreferenceScreen android:title="@string/pref_category_devtools"> <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_devtools" /> </intent> </PreferenceScreen> </PreferenceScreen>
rename from mobile/android/base/resources/xml/preferences_customize.xml.in rename to mobile/android/base/resources/xml/preferences_customize.xml --- a/mobile/android/base/resources/xml/preferences_customize.xml.in +++ b/mobile/android/base/resources/xml/preferences_customize.xml @@ -1,20 +1,19 @@ -#filter substitution <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:gecko="http://schemas.android.com/apk/res-auto" android:enabled="false"> <PreferenceScreen android:title="@string/pref_category_search" > <intent android:action="android.intent.action.VIEW" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.GeckoPreferences" > <extra android:name="resource" android:value="preferences_search" /> </intent> </PreferenceScreen> <org.mozilla.gecko.AndroidImportPreference
rename from mobile/android/base/resources/xml/searchable.xml.in rename to mobile/android/base/resources/xml/searchable.xml --- a/mobile/android/base/resources/xml/searchable.xml.in +++ b/mobile/android/base/resources/xml/searchable.xml @@ -1,8 +1,7 @@ -#filter substitution <?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/moz_app_displayname" - android:searchSuggestAuthority="@ANDROID_PACKAGE_NAME@.db.browser" + android:searchSuggestAuthority="@string/content_authority_db_browser" android:searchSuggestIntentAction="android.intent.action.SEARCH" android:searchSettingsDescription="@string/searchable_description" android:includeInGlobalSearch="true"/>
rename from mobile/android/base/resources/xml/sync_authenticator.xml.in rename to mobile/android/base/resources/xml/sync_authenticator.xml --- a/mobile/android/base/resources/xml/sync_authenticator.xml.in +++ b/mobile/android/base/resources/xml/sync_authenticator.xml @@ -1,12 +1,11 @@ -#filter substitution <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" - android:accountType="@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@" + android:accountType="@string/moz_android_shared_account_type" android:icon="@drawable/icon" android:smallIcon="@drawable/icon" android:label="@string/sync_account_label" android:accountPreferences="@xml/sync_options" />
rename from mobile/android/base/resources/xml/sync_options.xml.in rename to mobile/android/base/resources/xml/sync_options.xml --- a/mobile/android/base/resources/xml/sync_options.xml.in +++ b/mobile/android/base/resources/xml/sync_options.xml @@ -1,32 +1,31 @@ -#filter substitution <?xml version="1.0" encoding="UTF-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/sync_settings_options" /> <PreferenceScreen android:key="sync_options" android:title="@string/sync_title_pair" android:summary="@string/sync_settings_summary_pair"> <intent android:action="android.intent.action.MAIN" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.sync.setup.activities.SetupSyncActivity"> <extra android:name="isSetup" android:value="false" /> </intent> </PreferenceScreen> <PreferenceScreen android:key="sync_configure_engines" android:title="@string/sync_configure_engines_title"> <intent android:action="android.intent.action.MAIN" - android:targetPackage="@ANDROID_PACKAGE_NAME@" + android:targetPackage="@string/android_package_name" android:targetClass="org.mozilla.gecko.sync.config.activities.SelectEnginesActivity"> </intent> </PreferenceScreen> </PreferenceScreen>
rename from mobile/android/base/resources/xml/sync_syncadapter.xml.in rename to mobile/android/base/resources/xml/sync_syncadapter.xml --- a/mobile/android/base/resources/xml/sync_syncadapter.xml.in +++ b/mobile/android/base/resources/xml/sync_syncadapter.xml @@ -1,12 +1,11 @@ -#filter substitution <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" - android:contentAuthority="@ANDROID_PACKAGE_NAME@.db.browser" - android:accountType="@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@" + android:contentAuthority="@string/content_authority_db_browser" + android:accountType="@string/moz_android_shared_account_type" android:supportsUploading="true" android:userVisible="true" />
--- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -14,16 +14,19 @@ <!ENTITY formatS1 "%1$s"> <!ENTITY formatS2 "%2$s"> <!ENTITY formatD "%d"> ]> #includesubst @BOOKMARKSPATH@ <resources> <string name="moz_app_displayname">@MOZ_APP_DISPLAYNAME@</string> + <string name="android_package_name">@ANDROID_PACKAGE_NAME@</string> + <string name="content_authority_db_browser">@ANDROID_PACKAGE_NAME@.db.browser</string> + <string name="moz_android_shared_account_type">@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@</string> #include ../services/strings.xml.in <string name="no_space_to_start_error">&no_space_to_start_error;</string> <string name="error_loading_file">&error_loading_file;</string> <string name="bookmarks_title">&bookmarks_title;</string> <string name="history_title">&history_title;</string> <string name="reading_list_title">&reading_list_title;</string>
new file mode 100644 --- /dev/null +++ b/mobile/android/defs.mk @@ -0,0 +1,18 @@ +MOZ_ANDROID_SHARED_ID = "$(ANDROID_PACKAGE_NAME).sharedID" +MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "$(ANDROID_PACKAGE_NAME)_sync" + +# We released these builds to the public with shared IDs and need to +# keep them consistent. +ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME)) +MOZ_ANDROID_SHARED_ID = "org.mozilla.firefox.sharedID" +MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.firefox_sync" +else ifeq (org.mozilla.firefox_beta,$(ANDROID_PACKAGE_NAME)) +MOZ_ANDROID_SHARED_ID = "org.mozilla.firefox.sharedID" +MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.firefox_sync" +else ifeq (org.mozilla.fennec_aurora,$(ANDROID_PACKAGE_NAME)) +MOZ_ANDROID_SHARED_ID = "org.mozilla.fennec.sharedID" +MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.fennec_sync" +else ifeq (org.mozilla.fennec,$(ANDROID_PACKAGE_NAME)) +MOZ_ANDROID_SHARED_ID = "org.mozilla.fennec.sharedID" +MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "org.mozilla.fennec_sync" +endif
--- a/mobile/android/tests/background/junit3/src/common/TestUtils.java +++ b/mobile/android/tests/background/junit3/src/common/TestUtils.java @@ -1,13 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.background.common; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; @@ -72,9 +74,86 @@ public class TestUtils extends AndroidSy public void testGetStagesToSyncFromBundle() { final String[] all = new String[] { "other1", "other2", "skip1", "skip2", "sync1", "sync2" }; assertStagesFromBundle(all, null, null, all); assertStagesFromBundle(all, new String[] { "sync1" }, null, new String[] { "sync1" }); assertStagesFromBundle(all, null, new String[] { "skip1", "skip2" }, new String[] { "other1", "other2", "sync1", "sync2" }); assertStagesFromBundle(all, new String[] { "sync1", "sync2" }, new String[] { "skip1", "skip2" }, new String[] { "sync1", "sync2" }); } + + public static void deleteDirectoryRecursively(final File dir) throws IOException { + if (!dir.isDirectory()) { + throw new IllegalStateException("Given directory, " + dir + ", is not a directory!"); + } + + for (File f : dir.listFiles()) { + if (f.isDirectory()) { + deleteDirectoryRecursively(f); + } else if (!f.delete()) { + // Since this method is for testing, we assume we should be able to do this. + throw new IOException("Could not delete file, " + f.getAbsolutePath() + ". Permissions?"); + } + } + + if (!dir.delete()) { + throw new IOException("Could not delete dir, " + dir.getAbsolutePath() + "."); + } + } + + public void testDeleteDirectory() throws Exception { + final String TEST_DIR = getApplicationContext().getCacheDir().getAbsolutePath() + + "-testDeleteDirectory-" + System.currentTimeMillis(); + + // Non-existent directory. + final File nonexistent = new File("nonexistentDirectory"); // Hopefully. ;) + assertFalse(nonexistent.exists()); + try { + deleteDirectoryRecursively(nonexistent); + fail("deleteDirectoryRecursively on a nonexistent directory should throw Exception"); + } catch (IllegalStateException e) { } + + // Empty dir. + File dir = mkdir(TEST_DIR); + deleteDirectoryRecursively(dir); + assertFalse(dir.exists()); + + // Filled dir. + dir = mkdir(TEST_DIR); + populateDir(dir); + deleteDirectoryRecursively(dir); + assertFalse(dir.exists()); + + // Filled dir with empty dir. + dir = mkdir(TEST_DIR); + populateDir(dir); + File subDir = new File(TEST_DIR + File.separator + "subDir"); + assertTrue(subDir.mkdir()); + deleteDirectoryRecursively(dir); + assertFalse(subDir.exists()); // For short-circuiting errors. + assertFalse(dir.exists()); + + // Filled dir with filled dir. + dir = mkdir(TEST_DIR); + populateDir(dir); + subDir = new File(TEST_DIR + File.separator + "subDir"); + assertTrue(subDir.mkdir()); + populateDir(subDir); + deleteDirectoryRecursively(dir); + assertFalse(subDir.exists()); // For short-circuiting errors. + assertFalse(dir.exists()); + } + + private File mkdir(final String name) { + final File dir = new File(name); + assertTrue(dir.mkdir()); + return dir; + } + + private void populateDir(final File dir) throws IOException { + assertTrue(dir.isDirectory()); + final String dirPath = dir.getAbsolutePath(); + for (int i = 0; i < 3; i++) { + final File f = new File(dirPath + File.separator + i); + assertTrue(f.createNewFile()); // Throws IOException if file could not be created. + } + } }
--- a/mobile/android/tests/background/junit3/src/helpers/FakeProfileTestCase.java +++ b/mobile/android/tests/background/junit3/src/helpers/FakeProfileTestCase.java @@ -4,16 +4,18 @@ package org.mozilla.gecko.background.helpers; import java.io.File; import android.app.Activity; import android.content.Context; import android.test.ActivityInstrumentationTestCase2; +import org.mozilla.gecko.background.common.TestUtils; + public abstract class FakeProfileTestCase extends ActivityInstrumentationTestCase2<Activity> { protected Context context; protected File fakeProfileDirectory; public FakeProfileTestCase() { super(Activity.class); } @@ -21,23 +23,22 @@ public abstract class FakeProfileTestCas protected abstract String getCacheSuffix(); @Override protected void setUp() throws Exception { super.setUp(); context = getInstrumentation().getTargetContext(); File cache = context.getCacheDir(); fakeProfileDirectory = new File(cache.getAbsolutePath() + getCacheSuffix()); + if (fakeProfileDirectory.exists()) { + TestUtils.deleteDirectoryRecursively(fakeProfileDirectory); + } if (!fakeProfileDirectory.mkdir()) { throw new IllegalStateException("Could not create temporary directory."); } } @Override protected void tearDown() throws Exception { - // We don't check return values. - for (File child : fakeProfileDirectory.listFiles()) { - child.delete(); - } - fakeProfileDirectory.delete(); + TestUtils.deleteDirectoryRecursively(fakeProfileDirectory); super.tearDown(); } }
--- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -439,30 +439,40 @@ class RecursiveMakeBackend(CommonBackend # Skip static dirs during export traversal, or build everything in # parallel when enabled. def export_filter(current, subdirs): if self._parallel_export: return parallel_filter(current, subdirs) return current, subdirs.parallel, \ subdirs.dirs + subdirs.tests + subdirs.tools - # Skip tools dirs during libs traversal + # Skip tools dirs during libs traversal. Because of bug 925236 and + # possible other unknown race conditions, don't parallelize the libs + # tier. def libs_filter(current, subdirs): if current in self._may_skip[tier]: current = None + return current, [], subdirs.parallel + \ + subdirs.static + subdirs.dirs + subdirs.tests + + # Because of bug 925236 and possible other unknown race conditions, + # don't parallelize the tools tier. + def tools_filter(current, subdirs): + if current in self._may_skip[tier]: + current = None return current, subdirs.parallel, \ - subdirs.static + subdirs.dirs + subdirs.tests + subdirs.static + subdirs.dirs + subdirs.tests + subdirs.tools # compile, binaries and tools tiers use the same traversal as export filters = { 'export': export_filter, 'compile': parallel_filter, 'binaries': parallel_filter, 'libs': libs_filter, - 'tools': parallel_filter, + 'tools': tools_filter, } root_deps_mk = Makefile() # Fill the dependencies for traversal of each tier. for tier, filter in filters.items(): main, all_deps = \ self._traversal.compute_dependencies(filter)
--- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -359,17 +359,17 @@ class MochitestUtilsMixin(object): # Bug 883865 - add this functionality into manifestDestiny with open('tests.json', 'w') as manifestFile: manifestFile.write(json.dumps({'tests': paths})) options.manifestFile = 'tests.json' testHost = "http://mochi.test:8888" testURL = ("/").join([testHost, self.TEST_PATH, options.testPath]) if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0: - testURL = ("/").join([testHost, self.PLAIN_LOOP_PATH]) + testURL = ("/").join([testHost, self.TEST_PATH, os.path.dirname(options.testPath)]) if options.chrome or options.a11y: testURL = ("/").join([testHost, self.CHROME_PATH]) elif options.browserChrome: testURL = "about:blank" elif options.ipcplugins: testURL = ("/").join([testHost, self.TEST_PATH, "dom/plugins/test"]) return testURL
--- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -406,33 +406,32 @@ endif EXTRA_DSO_LDOPTS += $(call EXPAND_LIBNAME_PATH,gkmedias,$(DIST)/lib) ifdef MOZ_WEBRTC ifdef MOZ_PEERCONNECTION COMPONENT_LIBS += peerconnection endif ifdef MOZ_WEBRTC_SIGNALING -EXTRA_DSO_LDOPTS += \ +SHARED_LIBRARY_LIBS += \ $(DEPTH)/media/mtransport/build/$(LIB_PREFIX)mtransport.$(LIB_SUFFIX) \ $(DEPTH)/media/webrtc/signaling/signaling_ecc/$(LIB_PREFIX)ecc.$(LIB_SUFFIX) \ $(DEPTH)/media/webrtc/signaling/signaling_sipcc/$(LIB_PREFIX)sipcc.$(LIB_SUFFIX) \ $(NULL) endif -ifdef MOZ_WEBRTC_IN_LIBXUL -include $(topsrcdir)/media/webrtc/shared_libs.mk -EXTRA_DSO_LDOPTS += $(WEBRTC_LIBS) + +SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,webrtc,$(DEPTH)/layout/media/webrtc) + ifeq (WINNT,$(OS_TARGET)) EXTRA_DSO_LDOPTS += \ -LIBPATH:"$(MOZ_DIRECTX_SDK_PATH)/lib/$(MOZ_DIRECTX_SDK_CPU_SUFFIX)" \ $(NULL) OS_LIBS += $(call EXPAND_LIBNAME,secur32 crypt32 iphlpapi strmiids dmoguids wmcodecdspuuid amstrmid msdmo wininet) endif endif -endif ifdef MOZ_CUBEB ifdef MOZ_ALSA EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS) endif ifdef MOZ_PULSEAUDIO EXTRA_DSO_LDOPTS += $(MOZ_PULSEAUDIO_LIBS) @@ -580,17 +579,17 @@ endif EXTRA_DSO_LDOPTS += -DELAYLOAD:API-MS-WIN-CORE-WINRT-L$(CRTEXPDLLVERSION).DLL EXTRA_DSO_LDOPTS += -DELAYLOAD:API-MS-WIN-CORE-WINRT-STRING-L$(CRTEXPDLLVERSION).DLL EXTRA_DSO_LDOPTS += -DELAYLOAD:uiautomationcore.dll endif ifdef ACCESSIBILITY EXTRA_DSO_LDOPTS += -DELAYLOAD:oleacc.dll endif -ifdef MOZ_WEBRTC_IN_LIBXUL +ifdef MOZ_WEBRTC EXTRA_DSO_LDOPTS += -DELAYLOAD:msdmo.dll endif endif ifdef MOZ_GAMEPAD ifndef GNU_CC DXSDK := $(subst \,/,$(MOZ_DIRECTX_SDK_PATH))/Lib/$(MOZ_DIRECTX_SDK_CPU_SUFFIX) OS_LIBS += \ "$(DXSDK)/dxguid.lib" \
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js @@ -401,18 +401,28 @@ const WITH_EXTENSION_CACHE = [{ }, screenshots: [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }], sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec }]; /* * Trigger an AddonManager background update check + * + * @param aCallback + * Callback to call once the background update is complete */ -function trigger_background_update() { +function trigger_background_update(aCallback) { + Services.obs.addObserver({ + observe: function(aSubject, aTopic, aData) { + Services.obs.removeObserver(this, "addons-background-update-complete"); + do_execute_soon(aCallback); + } + }, "addons-background-update-complete", false); + gInternalManager.notify(null); } /* * Check whether or not the add-ons database exists * * @param aExpectedExists * Whether or not the database is expected to exist @@ -692,31 +702,25 @@ function run_test_12() { } // Tests that a background update with caching disabled deletes the add-ons // database, and that XPI add-ons still do not use any of repository properties function run_test_13() { check_database_exists(true); Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERF, GETADDONS_EMPTY); - Services.obs.addObserver({ - observe: function(aSubject, aTopic, aData) { - Services.obs.removeObserver(this, "addons-background-update-complete"); - - // Database should have been deleted with the background update. - check_database_exists(false); + trigger_background_update(function() { + // Database should have been deleted + check_database_exists(false); - AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) { - check_results(aAddons, WITHOUT_CACHE); - do_execute_soon(run_test_14); - }); - } - }, "addons-background-update-complete", false); - - trigger_background_update(); + AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) { + check_results(aAddons, WITHOUT_CACHE); + do_execute_soon(run_test_14); + }); + }); } // Tests that the XPI add-ons have the correct properties if caching is // enabled but has no information function run_test_14() { Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true); waitForFlushedData(function() { @@ -731,24 +735,22 @@ function run_test_14() { trigger_background_update(); } // Tests that the XPI add-ons correctly use the repository properties when // caching is enabled and the repository information is available function run_test_15() { Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERF, GETADDONS_RESULTS); - waitForFlushedData(function() { + trigger_background_update(function() { AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) { check_results(aAddons, WITH_CACHE); do_execute_soon(run_test_16); }); }); - - trigger_background_update(); } // Tests that restarting the manager does not change the checked properties // on the XPI add-ons (repository properties still exist and are still properly // used) function run_test_16() { restartManager(); @@ -757,18 +759,16 @@ function run_test_16() { do_execute_soon(run_test_17); }); } // Tests that setting a list of types to cache works function run_test_17() { Services.prefs.setCharPref(PREF_GETADDONS_CACHE_TYPES, "foo,bar,extension,baz"); - waitForFlushedData(function() { + trigger_background_update(function() { AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) { check_results(aAddons, WITH_EXTENSION_CACHE); end_test(); }); }); - - trigger_background_update(); }