Backed out 2 changesets (bug 1138348)
authorWes Kocher <wkocher@mozilla.com>
Tue, 10 Mar 2015 19:49:58 -0700
changeset 261787 8e261a74f35034dc0426d255bde6e6b3fee6f89b
parent 261786 f8076d7dcfcbbf12f8b671f37fe2b1980833a3b7
child 261788 58782486486002668be30cc50754f64b463468a4
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1138348
milestone39.0a1
backs out9c405f41e3e7056be0476e977a2a67da9cdc8dc6
5777a98e824fec1975c0942d1873cee59fcf27f6
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
Backed out 2 changesets (bug 1138348) Backed out changeset 9c405f41e3e7 (bug 1138348) Backed out changeset 5777a98e824f (bug 1138348)
js/src/jit-test/tests/asm.js/testAtomics.js
js/src/jit/shared/Lowering-x86-shared.cpp
js/src/jit/shared/Lowering-x86-shared.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -172,330 +172,83 @@ function loadModule_int32(stdlib, foreig
 	     xor: do_xor,
 	     xor_i: do_xor_i,
 	     cas1: do_cas1,
 	     cas2: do_cas2,
 	     cas1_i: do_cas1_i,
 	     cas2_i: do_cas2_i };
 }
 
-function loadModule_int8(stdlib, foreign, heap) {
-    "use asm";
-
-    var atomic_load = stdlib.Atomics.load;
-    var atomic_store = stdlib.Atomics.store;
-    var atomic_cmpxchg = stdlib.Atomics.compareExchange;
-    var atomic_add = stdlib.Atomics.add;
-    var atomic_sub = stdlib.Atomics.sub;
-    var atomic_and = stdlib.Atomics.and;
-    var atomic_or = stdlib.Atomics.or;
-    var atomic_xor = stdlib.Atomics.xor;
-
-    var i8a = new stdlib.SharedInt8Array(heap);
-
-    // Load element 0
-    function do_load() {
-	var v = 0;
-	v = atomic_load(i8a, 0);
-	return v|0;
-    }
-
-    // Load element i
-    function do_load_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_load(i8a, i);
-	return v|0;
-    }
-
-    // Store 37 in element 0
-    function do_store() {
-	var v = 0;
-	v = atomic_store(i8a, 0, 37);
-	return v|0;
-    }
-
-    // Store 37 in element i
-    function do_store_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_store(i8a, i, 37);
-	return v|0;
-    }
-
-    // Add 37 to element 10
-    function do_add() {
-	var v = 0;
-	v = atomic_add(i8a, 10, 37);
-	return v|0;
-    }
-
-    // Add 37 to element i
-    function do_add_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_add(i8a, i, 37);
-	return v|0;
-    }
-
-    // Subtract 108 from element 20
-    function do_sub() {
-	var v = 0;
-	v = atomic_sub(i8a, 20, 108);
-	return v|0;
-    }
-
-    // Subtract 108 from element i
-    function do_sub_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_sub(i8a, i, 108);
-	return v|0;
-    }
-
-    // AND 0x33 into element 30
-    function do_and() {
-	var v = 0;
-	v = atomic_and(i8a, 30, 0x33);
-	return v|0;
-    }
-
-    // AND 0x33 into element i
-    function do_and_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_and(i8a, i, 0x33);
-	return v|0;
-    }
-
-    // OR 0x33 into element 40
-    function do_or() {
-	var v = 0;
-	v = atomic_or(i8a, 40, 0x33);
-	return v|0;
-    }
-
-    // OR 0x33 into element i
-    function do_or_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_or(i8a, i, 0x33);
-	return v|0;
-    }
-
-    // XOR 0x33 into element 50
-    function do_xor() {
-	var v = 0;
-	v = atomic_xor(i8a, 50, 0x33);
-	return v|0;
-    }
-
-    // XOR 0x33 into element i
-    function do_xor_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_xor(i8a, i, 0x33);
-	return v|0;
-    }
-
-    // CAS element 100: 0 -> -1
-    function do_cas1() {
-	var v = 0;
-	v = atomic_cmpxchg(i8a, 100, 0, -1);
-	return v|0;
-    }
-
-    // CAS element 100: -1 -> 0x5A
-    function do_cas2() {
-	var v = 0;
-	v = atomic_cmpxchg(i8a, 100, -1, 0x5A);
-	return v|0;
-    }
-
-    // CAS element i: 0 -> -1
-    function do_cas1_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_cmpxchg(i8a, i, 0, -1);
-	return v|0;
-    }
-
-    // CAS element i: -1 -> 0x5A
-    function do_cas2_i(i) {
-	i = i|0;
-	var v = 0;
-	v = atomic_cmpxchg(i8a, i, -1, 0x5A);
-	return v|0;
-    }
-
-    return { load: do_load,
-	     load_i: do_load_i,
-	     store: do_store,
-	     store_i: do_store_i,
-	     add: do_add,
-	     add_i: do_add_i,
-	     sub: do_sub,
-	     sub_i: do_sub_i,
-	     and: do_and,
-	     and_i: do_and_i,
-	     or: do_or,
-	     or_i: do_or_i,
-	     xor: do_xor,
-	     xor_i: do_xor_i,
-	     cas1: do_cas1,
-	     cas2: do_cas2,
-	     cas1_i: do_cas1_i,
-	     cas2_i: do_cas2_i };
-}
-
+// TODO: byte arrays
 // TODO: halfword arrays
 // TODO: signed vs unsigned; negative results
 
 var heap = new SharedArrayBuffer(65536);
-
-////////////////////////////////////////////////////////////
-//
-// int32 tests
-
 var i32a = new SharedInt32Array(heap);
-var i32m = loadModule_int32(this, {}, heap);
+var module = loadModule_int32(this, {}, heap);
 
 var size = 4;
 
-i32m.fence();
+module.fence();
 
 i32a[0] = 12345;
-assertEq(i32m.load(), 12345);
-assertEq(i32m.load_i(size*0), 12345);
+assertEq(module.load(), 12345);
+assertEq(module.load_i(size*0), 12345);
 
-assertEq(i32m.store(), 37);
+assertEq(module.store(), 37);
 assertEq(i32a[0], 37);
-assertEq(i32m.store_i(size*0), 37);
+assertEq(module.store_i(size*0), 37);
 
 i32a[10] = 18;
-assertEq(i32m.add(), 18);
+assertEq(module.add(), 18);
 assertEq(i32a[10], 18+37);
-assertEq(i32m.add_i(size*10), 18+37);
+assertEq(module.add_i(size*10), 18+37);
 assertEq(i32a[10], 18+37+37);
 
 i32a[20] = 4972;
-assertEq(i32m.sub(), 4972);
+assertEq(module.sub(), 4972);
 assertEq(i32a[20], 4972 - 148);
-assertEq(i32m.sub_i(size*20), 4972 - 148);
+assertEq(module.sub_i(size*20), 4972 - 148);
 assertEq(i32a[20], 4972 - 148 - 148);
 
 i32a[30] = 0x66666666;
-assertEq(i32m.and(), 0x66666666);
+assertEq(module.and(), 0x66666666);
 assertEq(i32a[30], 0x22222222);
 i32a[30] = 0x66666666;
-assertEq(i32m.and_i(size*30), 0x66666666);
+assertEq(module.and_i(size*30), 0x66666666);
 assertEq(i32a[30], 0x22222222);
 
 i32a[40] = 0x22222222;
-assertEq(i32m.or(), 0x22222222);
+assertEq(module.or(), 0x22222222);
 assertEq(i32a[40], 0x33333333);
 i32a[40] = 0x22222222;
-assertEq(i32m.or_i(size*40), 0x22222222);
+assertEq(module.or_i(size*40), 0x22222222);
 assertEq(i32a[40], 0x33333333);
 
 i32a[50] = 0x22222222;
-assertEq(i32m.xor(), 0x22222222);
+assertEq(module.xor(), 0x22222222);
 assertEq(i32a[50], 0x11111111);
 i32a[50] = 0x22222222;
-assertEq(i32m.xor_i(size*50), 0x22222222);
+assertEq(module.xor_i(size*50), 0x22222222);
 assertEq(i32a[50], 0x11111111);
 
 i32a[100] = 0;
-assertEq(i32m.cas1(), 0);
-assertEq(i32m.cas2(), -1);
+assertEq(module.cas1(), 0);
+assertEq(module.cas2(), -1);
 assertEq(i32a[100], 0x5A5A5A5A);
 
 i32a[100] = 0;
-assertEq(i32m.cas1_i(size*100), 0);
-assertEq(i32m.cas2_i(size*100), -1);
+assertEq(module.cas1_i(size*100), 0);
+assertEq(module.cas2_i(size*100), -1);
 assertEq(i32a[100], 0x5A5A5A5A);
 
 // Out-of-bounds accesses.
 
-assertEq(i32m.cas1_i(size*20000), 0);
-assertEq(i32m.cas2_i(size*20000), 0);
-
-assertEq(i32m.or_i(size*20001), 0);
-assertEq(i32m.xor_i(size*20001), 0);
-assertEq(i32m.and_i(size*20001), 0);
-assertEq(i32m.add_i(size*20001), 0);
-assertEq(i32m.sub_i(size*20001), 0);
-
-////////////////////////////////////////////////////////////
-//
-// int8 tests
-
-var i8a = new SharedInt8Array(heap);
-var i8m = loadModule_int8(this, {}, heap);
-
-for ( var i=0 ; i < i8a.length ; i++ )
-    i8a[i] = 0;
-
-var size = 1;
-
-i8a[0] = 123;
-assertEq(i8m.load(), 123);
-assertEq(i8m.load_i(0), 123);
-
-assertEq(i8m.store(), 37);
-assertEq(i8a[0], 37);
-assertEq(i8m.store_i(0), 37);
-
-i8a[10] = 18;
-assertEq(i8m.add(), 18);
-assertEq(i8a[10], 18+37);
-assertEq(i8m.add_i(10), 18+37);
-assertEq(i8a[10], 18+37+37);
-
-i8a[20] = 49;
-assertEq(i8m.sub(), 49);
-assertEq(i8a[20], 49 - 108);
-assertEq(i8m.sub_i(20), 49 - 108);
-assertEq(i8a[20], ((49 - 108 - 108) << 24) >> 24); // Byte, sign extended
+assertEq(module.cas1_i(size*20000), 0);
+assertEq(module.cas2_i(size*20000), 0);
 
-i8a[30] = 0x66;
-assertEq(i8m.and(), 0x66);
-assertEq(i8a[30], 0x22);
-i8a[30] = 0x66;
-assertEq(i8m.and_i(30), 0x66);
-assertEq(i8a[30], 0x22);
-
-i8a[40] = 0x22;
-assertEq(i8m.or(), 0x22);
-assertEq(i8a[40], 0x33);
-i8a[40] = 0x22;
-assertEq(i8m.or_i(40), 0x22);
-assertEq(i8a[40], 0x33);
-
-i8a[50] = 0x22;
-assertEq(i8m.xor(), 0x22);
-assertEq(i8a[50], 0x11);
-i8a[50] = 0x22;
-assertEq(i8m.xor_i(50), 0x22);
-assertEq(i8a[50], 0x11);
-
-i8a[100] = 0;
-assertEq(i8m.cas1(), 0);
-assertEq(i8m.cas2(), -1);
-assertEq(i8a[100], 0x5A);
-
-i8a[100] = 0;
-assertEq(i8m.cas1_i(100), 0);
-assertEq(i8m.cas2_i(100), -1);
-assertEq(i8a[100], 0x5A);
-
-// Out-of-bounds accesses.
-
-assertEq(i8m.cas1_i(80000), 0);
-assertEq(i8m.cas2_i(80000), 0);
-
-assertEq(i8m.or_i(80001), 0);
-assertEq(i8m.xor_i(80001), 0);
-assertEq(i8m.and_i(80001), 0);
-assertEq(i8m.add_i(80001), 0);
-assertEq(i8m.sub_i(80001), 0);
+assertEq(module.or_i(size*20001), 0);
+assertEq(module.xor_i(size*20001), 0);
+assertEq(module.and_i(size*20001), 0);
+assertEq(module.add_i(size*20001), 0);
+assertEq(module.sub_i(size*20001), 0);
 
 print("Done");
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -359,18 +359,17 @@ LIRGeneratorX86Shared::lowerTruncateFToI
     MDefinition *opd = ins->input();
     MOZ_ASSERT(opd->type() == MIRType_Float32);
 
     LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat32();
     define(new(alloc()) LTruncateFToInt32(useRegister(opd), maybeTemp), ins);
 }
 
 void
-LIRGeneratorX86Shared::lowerCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins,
-                                                             bool useI386ByteRegisters)
+LIRGeneratorX86Shared::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins)
 {
     MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
     MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
 
     MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     const LUse elements = useRegister(ins->elements());
@@ -381,50 +380,51 @@ LIRGeneratorX86Shared::lowerCompareExcha
     // If the target is an integer register then the target must be
     // eax.
     //
     // If the target is a floating register then we need a temp at the
     // lower level; that temp must be eax.
     //
     // oldval must be in a register.
     //
-    // newval must be in a register.  If the source is a byte array
-    // then newval must be a register that has a byte size: on x86
-    // this must be ebx, ecx, or edx (eax is taken for the output).
+    // newval will need to be in a register.  If the source is a byte
+    // array then the newval must be a register that has a byte size:
+    // ebx, ecx, or edx, since eax is taken for the output in this
+    // case.
     //
-    // Bug #1077036 describes some further optimization opportunities.
+    // Bug #1077036 describes some optimization opportunities.
 
     bool fixedOutput = false;
     LDefinition tempDef = LDefinition::BogusTemp();
     LAllocation newval;
     if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) {
         tempDef = tempFixed(eax);
         newval = useRegister(ins->newval());
     } else {
         fixedOutput = true;
-        if (useI386ByteRegisters && ins->isByteArray())
+        if (ins->isByteArray())
             newval = useFixed(ins->newval(), ebx);
         else
             newval = useRegister(ins->newval());
     }
 
+    // A register allocator limitation precludes 'useRegisterAtStart()' here.
     const LAllocation oldval = useRegister(ins->oldval());
 
     LCompareExchangeTypedArrayElement *lir =
         new(alloc()) LCompareExchangeTypedArrayElement(elements, index, oldval, newval, tempDef);
 
     if (fixedOutput)
         defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
     else
         define(lir, ins);
 }
 
 void
-LIRGeneratorX86Shared::lowerAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins,
-                                                         bool useI386ByteRegisters)
+LIRGeneratorX86Shared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins)
 {
     MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped);
     MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
     MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
 
     MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
@@ -447,17 +447,17 @@ LIRGeneratorX86Shared::lowerAtomicTypedA
     // L: mov           eax, temp
     //    andl          src, temp
     //    lock cmpxchg  temp, mem  ; reads eax also
     //    jnz           L
     //    ; result in eax
     //
     // Note the placement of L, cmpxchg will update eax with *mem if
     // *mem does not have the expected value, so reloading it at the
-    // top of the loop would be redundant.
+    // top of the loop is redundant.
     //
     // If the array is not a uint32 array then:
     //  - eax should be the output (one result of the cmpxchg)
     //  - there is a temp, which must have a byte register if
     //    the array has 1-byte elements elements
     //
     // If the array is a uint32 array then:
     //  - eax is the first temp
@@ -483,36 +483,164 @@ LIRGeneratorX86Shared::lowerAtomicTypedA
         value = useRegister(ins->value());
         fixedOutput = false;
         if (bitOp) {
             tempDef1 = tempFixed(eax);
             tempDef2 = temp();
         } else {
             tempDef1 = temp();
         }
-    } else if (useI386ByteRegisters && ins->isByteArray()) {
+    } else if (ins->isByteArray()) {
         value = useFixed(ins->value(), ebx);
         if (bitOp)
             tempDef1 = tempFixed(ecx);
-    } else {
+    }
+    else {
         value = useRegister(ins->value());
         if (bitOp)
             tempDef1 = temp();
     }
 
     LAtomicTypedArrayElementBinop *lir =
         new(alloc()) LAtomicTypedArrayElementBinop(elements, index, value, tempDef1, tempDef2);
 
     if (fixedOutput)
         defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
     else
         define(lir, ins);
 }
 
 void
+LIRGeneratorX86Shared::lowerAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins,
+						     const LDefinition& addrTemp)
+{
+    MDefinition *ptr = ins->ptr();
+    MOZ_ASSERT(ptr->type() == MIRType_Int32);
+
+    bool byteArray = false;
+    switch (ins->accessType()) {
+      case Scalar::Int8:
+      case Scalar::Uint8:
+        byteArray = true;
+        break;
+      case Scalar::Int16:
+      case Scalar::Uint16:
+      case Scalar::Int32:
+      case Scalar::Uint32:
+        break;
+      default:
+        MOZ_CRASH("Unexpected array type");
+    }
+
+    // Register allocation:
+    //
+    // The output must be eax.
+    //
+    // oldval must be in a register (it'll eventually end up in eax so
+    // ideally it's there to begin with).
+    //
+    // newval will need to be in a register.  If the source is a byte
+    // array then the newval must be a register that has a byte size:
+    // ebx, ecx, or edx, since eax is taken for the output in this
+    // case.  We pick ebx but it would be more flexible to pick any of
+    // the three that wasn't being used.
+    //
+    // Bug #1077036 describes some optimization opportunities.
+
+    const LAllocation newval = byteArray ? useFixed(ins->newValue(), ebx) : useRegister(ins->newValue());
+    const LAllocation oldval = useRegister(ins->oldValue());
+
+    LAsmJSCompareExchangeHeap *lir =
+        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), oldval, newval);
+
+    lir->setAddrTemp(addrTemp);
+    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+}
+
+void
+LIRGeneratorX86Shared::lowerAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins,
+						 const LDefinition& addrTemp)
+{
+    MDefinition *ptr = ins->ptr();
+    MOZ_ASSERT(ptr->type() == MIRType_Int32);
+
+    bool byteArray = false;
+    switch (ins->accessType()) {
+      case Scalar::Int8:
+      case Scalar::Uint8:
+        byteArray = true;
+        break;
+      case Scalar::Int16:
+      case Scalar::Uint16:
+      case Scalar::Int32:
+      case Scalar::Uint32:
+        break;
+      default:
+        MOZ_CRASH("Unexpected array type");
+    }
+
+    // Register allocation:
+    //
+    // For ADD and SUB we'll use XADD:
+    //
+    //    movl       value, output
+    //    lock xaddl output, mem
+    //
+    // For the 8-bit variants XADD needs a byte register for the
+    // output only, we can still set up with movl; just pin the output
+    // to eax (or ebx / ecx / edx).
+    //
+    // For AND/OR/XOR we need to use a CMPXCHG loop:
+    //
+    //    movl          *mem, eax
+    // L: mov           eax, temp
+    //    andl          value, temp
+    //    lock cmpxchg  temp, mem  ; reads eax also
+    //    jnz           L
+    //    ; result in eax
+    //
+    // Note the placement of L, cmpxchg will update eax with *mem if
+    // *mem does not have the expected value, so reloading it at the
+    // top of the loop is redundant.
+    //
+    // We want to fix eax as the output.  We also need a temp for
+    // the intermediate value.
+    //
+    // For the 8-bit variants the temp must have a byte register.
+    //
+    // There are optimization opportunities:
+    //  - when the result is unused, Bug #1077014.
+    //  - better register allocation and instruction selection, Bug #1077036.
+
+    bool bitOp = !(ins->operation() == AtomicFetchAddOp || ins->operation() == AtomicFetchSubOp);
+    LDefinition tempDef = LDefinition::BogusTemp();
+    LAllocation value;
+
+    // Optimization opportunity: "value" need not be pinned to something that
+    // has a byte register unless the back-end insists on using a byte move
+    // for the setup or the payload computation, which really it need not do.
+
+    if (byteArray) {
+        value = useFixed(ins->value(), ebx);
+        if (bitOp)
+            tempDef = tempFixed(ecx);
+    } else {
+        value = useRegister(ins->value());
+        if (bitOp)
+            tempDef = temp();
+    }
+
+    LAsmJSAtomicBinopHeap *lir =
+        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr), value, tempDef);
+
+    lir->setAddrTemp(addrTemp);
+    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+}
+
+void
 LIRGeneratorX86Shared::visitSimdBinaryArith(MSimdBinaryArith *ins)
 {
     MOZ_ASSERT(IsSimdType(ins->lhs()->type()));
     MOZ_ASSERT(IsSimdType(ins->rhs()->type()));
     MOZ_ASSERT(IsSimdType(ins->type()));
 
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
--- a/js/src/jit/shared/Lowering-x86-shared.h
+++ b/js/src/jit/shared/Lowering-x86-shared.h
@@ -51,18 +51,18 @@ class LIRGeneratorX86Shared : public LIR
     void lowerConstantDouble(double d, MInstruction *ins);
     void lowerConstantFloat32(float d, MInstruction *ins);
     void lowerTruncateDToInt32(MTruncateToInt32 *ins);
     void lowerTruncateFToInt32(MTruncateToInt32 *ins);
     void visitSimdBinaryArith(MSimdBinaryArith *ins);
     void visitSimdSelect(MSimdSelect *ins);
     void visitSimdSplatX4(MSimdSplatX4 *ins);
     void visitSimdValueX4(MSimdValueX4 *ins);
-    void lowerCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins,
-                                               bool useI386ByteRegisters);
-    void lowerAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins,
-                                           bool useI386ByteRegisters);
+    void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
+    void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
+    void lowerAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins, const LDefinition& addrTemp);
+    void lowerAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins, const LDefinition& addrTemp);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_x86_shared_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -127,28 +127,16 @@ LIRGeneratorX64::defineUntypedPhi(MPhi *
 
 void
 LIRGeneratorX64::lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex)
 {
     lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
 }
 
 void
-LIRGeneratorX64::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins)
-{
-    lowerCompareExchangeTypedArrayElement(ins, /* useI386ByteRegisters = */ false);
-}
-
-void
-LIRGeneratorX64::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins)
-{
-    lowerAtomicTypedArrayElementBinop(ins, /* useI386ByteRegisters = */ false);
-}
-
-void
 LIRGeneratorX64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
     LAsmJSUInt32ToDouble *lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input()));
     define(lir, ins);
 }
 
 void
@@ -207,68 +195,23 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAs
         MOZ_CRASH("unexpected array type");
     }
     add(lir, ins);
 }
 
 void
 LIRGeneratorX64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
 {
-    MDefinition *ptr = ins->ptr();
-    MOZ_ASSERT(ptr->type() == MIRType_Int32);
-
-    const LAllocation oldval = useRegister(ins->oldValue());
-    const LAllocation newval = useRegister(ins->newValue());
-
-    LAsmJSCompareExchangeHeap *lir =
-        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), oldval, newval);
-
-    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+    lowerAsmJSCompareExchangeHeap(ins, LDefinition::BogusTemp());
 }
 
 void
 LIRGeneratorX64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins)
 {
-    MDefinition *ptr = ins->ptr();
-    MOZ_ASSERT(ptr->type() == MIRType_Int32);
-
-    // Register allocation:
-    //
-    // For ADD and SUB we'll use XADD (with word and byte ops as appropriate):
-    //
-    //    movl       value, output
-    //    lock xaddl output, mem
-    //
-    // For AND/OR/XOR we need to use a CMPXCHG loop:
-    //
-    //    movl          *mem, eax
-    // L: mov           eax, temp
-    //    andl          value, temp
-    //    lock cmpxchg  temp, mem  ; reads eax also
-    //    jnz           L
-    //    ; result in eax
-    //
-    // Note the placement of L, cmpxchg will update eax with *mem if
-    // *mem does not have the expected value, so reloading it at the
-    // top of the loop would be redundant.
-    //
-    // We want to fix eax as the output.  We also need a temp for
-    // the intermediate value.
-    //
-    // There are optimization opportunities:
-    //  - when the result is unused, Bug #1077014.
-
-    bool bitOp = !(ins->operation() == AtomicFetchAddOp || ins->operation() == AtomicFetchSubOp);
-    LAllocation value = useRegister(ins->value());
-    LDefinition tempDef = bitOp ? temp() : LDefinition::BogusTemp();
-
-    LAsmJSAtomicBinopHeap *lir =
-        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr), value, tempDef);
-
-    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+    lowerAsmJSAtomicBinopHeap(ins, LDefinition::BogusTemp());
 }
 
 void
 LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -37,18 +37,16 @@ class LIRGeneratorX64 : public LIRGenera
     LDefinition tempToUnbox();
 
     bool needTempForPostBarrier() { return false; }
 
   public:
     void visitBox(MBox *box);
     void visitUnbox(MUnbox *unbox);
     void visitReturn(MReturn *ret);
-    void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
-    void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -178,28 +178,16 @@ LIRGeneratorX86::lowerUntypedPhiInput(MP
     MDefinition *operand = phi->getOperand(inputPosition);
     LPhi *type = block->getPhi(lirIndex + VREG_TYPE_OFFSET);
     LPhi *payload = block->getPhi(lirIndex + VREG_DATA_OFFSET);
     type->setOperand(inputPosition, LUse(operand->virtualRegister() + VREG_TYPE_OFFSET, LUse::ANY));
     payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY));
 }
 
 void
-LIRGeneratorX86::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins)
-{
-    lowerCompareExchangeTypedArrayElement(ins, /* useI386ByteRegisters = */ true);
-}
-
-void
-LIRGeneratorX86::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins)
-{
-    lowerAtomicTypedArrayElementBinop(ins, /* useI386ByteRegisters = */ true);
-}
-
-void
 LIRGeneratorX86::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     MOZ_ASSERT(ins->input()->type() == MIRType_Int32);
     LAsmJSUInt32ToDouble *lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input()), temp());
     define(lir, ins);
 }
 
 void
@@ -280,111 +268,23 @@ LIRGeneratorX86::visitStoreTypedArrayEle
     }
 
     add(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
 {
-    MOZ_ASSERT(ins->accessType() < Scalar::Float32);
-
-    MDefinition *ptr = ins->ptr();
-    MOZ_ASSERT(ptr->type() == MIRType_Int32);
-
-    bool byteArray = byteSize(ins->accessType()) == 1;
-
-    // Register allocation:
-    //
-    // The output must be eax.
-    //
-    // oldval must be in a register.
-    //
-    // newval must be in a register.  If the source is a byte array
-    // then newval must be a register that has a byte size: on x86
-    // this must be ebx, ecx, or edx (eax is taken for the output).
-    //
-    // Bug #1077036 describes some optimization opportunities.
-
-    const LAllocation oldval = useRegister(ins->oldValue());
-    const LAllocation newval = byteArray ? useFixed(ins->newValue(), ebx) : useRegister(ins->newValue());
-
-    LAsmJSCompareExchangeHeap *lir =
-        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), oldval, newval);
-
-    lir->setAddrTemp(temp());
-    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+    lowerAsmJSCompareExchangeHeap(ins, temp());
 }
 
 void
 LIRGeneratorX86::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins)
 {
-    MOZ_ASSERT(ins->accessType() < Scalar::Float32);
-
-    MDefinition *ptr = ins->ptr();
-    MOZ_ASSERT(ptr->type() == MIRType_Int32);
-
-    bool byteArray = byteSize(ins->accessType()) == 1;
-
-    // Register allocation:
-    //
-    // For ADD and SUB we'll use XADD:
-    //
-    //    movl       value, output
-    //    lock xaddl output, mem
-    //
-    // For the 8-bit variants XADD needs a byte register for the
-    // output only, we can still set up with movl; just pin the output
-    // to eax (or ebx / ecx / edx).
-    //
-    // For AND/OR/XOR we need to use a CMPXCHG loop:
-    //
-    //    movl          *mem, eax
-    // L: mov           eax, temp
-    //    andl          value, temp
-    //    lock cmpxchg  temp, mem  ; reads eax also
-    //    jnz           L
-    //    ; result in eax
-    //
-    // Note the placement of L, cmpxchg will update eax with *mem if
-    // *mem does not have the expected value, so reloading it at the
-    // top of the loop would be redundant.
-    //
-    // We want to fix eax as the output.  We also need a temp for
-    // the intermediate value.
-    //
-    // For the 8-bit variants the temp must have a byte register.
-    //
-    // There are optimization opportunities:
-    //  - when the result is unused, Bug #1077014.
-    //  - better register allocation and instruction selection, Bug #1077036.
-
-    bool bitOp = !(ins->operation() == AtomicFetchAddOp || ins->operation() == AtomicFetchSubOp);
-    LDefinition tempDef = LDefinition::BogusTemp();
-    LAllocation value;
-
-    // Optimization opportunity: "value" need not be pinned to something that
-    // has a byte register unless the back-end insists on using a byte move
-    // for the setup or the payload computation, which really it need not do.
-
-    if (byteArray) {
-        value = useFixed(ins->value(), ebx);
-        if (bitOp)
-            tempDef = tempFixed(ecx);
-    } else {
-        value = useRegister(ins->value());
-        if (bitOp)
-            tempDef = temp();
-    }
-
-    LAsmJSAtomicBinopHeap *lir =
-        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr), value, tempDef);
-
-    lir->setAddrTemp(temp());
-    defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
+    lowerAsmJSAtomicBinopHeap(ins, temp());
 }
 
 void
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
 
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -43,18 +43,16 @@ class LIRGeneratorX86 : public LIRGenera
 
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     void defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
   public:
     void visitBox(MBox *box);
     void visitUnbox(MUnbox *unbox);
     void visitReturn(MReturn *ret);
-    void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
-    void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);