Bug 1215555 - Improve simulated OOM testing of ARM assembler buffer and fix bugs r=jolesen
☠☠ backed out by 9b6a1fffd326 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 19 Oct 2015 10:50:51 +0100
changeset 303529 2d0fadc97308f93872570c269d578a11157c0835
parent 303528 5f89883da0b4dc7388627a8479fa638a615076b9
child 303530 9b6a1fffd32622aeef4a9f16a873efe68e63dfe8
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjolesen
bugs1215555
milestone44.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
Bug 1215555 - Improve simulated OOM testing of ARM assembler buffer and fix bugs r=jolesen
js/src/asmjs/AsmJSCompile.cpp
js/src/jit/arm/Assembler-arm.cpp
js/src/jit/shared/IonAssemblerBuffer.h
js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
--- a/js/src/asmjs/AsmJSCompile.cpp
+++ b/js/src/asmjs/AsmJSCompile.cpp
@@ -118,16 +118,21 @@ class ModuleCompiler
     bool getOrCreateFunctionEntry(uint32_t funcIndex, Label** label)
     {
         return compileResults_->getOrCreateFunctionEntry(funcIndex, label);
     }
 
     bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen,
                                   const AsmJSFunctionLabels& labels)
     {
+        // If we have hit OOM then invariants which we assert below may not
+        // hold, so abort now.
+        if (masm().oom())
+            return false;
+
         // Code range
         unsigned line = func.lineno();
         unsigned column = func.column();
         PropertyName* funcName = func.name();
         if (!compileResults_->addCodeRange(AsmJSModule::FunctionCodeRange(funcName, line, labels)))
             return false;
 
         // Script counts
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2180,17 +2180,17 @@ Assembler::as_BranchPool(uint32_t value,
     php.phd.init(0, c, PoolHintData::PoolBranch, pc);
     BufferOffset ret = allocEntry(1, 1, (uint8_t*)&php.raw, (uint8_t*)&value, pe,
                                   /* markAsBranch = */ true, /* loadToPC = */ true);
     // If this label is already bound, then immediately replace the stub load
     // with a correct branch.
     if (label->bound()) {
         BufferOffset dest(label);
         as_b(dest.diffB<BOffImm>(ret), c, ret);
-    } else {
+    } else if (!oom()) {
         label->use(ret.getOffset());
     }
 #ifdef JS_DISASM_ARM
     if (documentation)
         spewTarget(documentation);
 #endif
     return ret;
 }
@@ -2380,49 +2380,53 @@ Assembler::as_b(BOffImm off, Condition c
 {
     BufferOffset ret = writeBranchInst(((int)c) | OpB | off.encode(), documentation);
     return ret;
 }
 
 BufferOffset
 Assembler::as_b(Label* l, Condition c)
 {
-    if (m_buffer.oom()) {
-        BufferOffset ret;
-        return ret;
-    }
-
     if (l->bound()) {
         // Note only one instruction is emitted here, the NOP is overwritten.
         BufferOffset ret = allocBranchInst();
+        if (oom())
+            return BufferOffset();
+
         as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
 #ifdef JS_DISASM_ARM
         spewBranch(m_buffer.getInstOrNull(ret), l);
 #endif
         return ret;
     }
 
+    if (oom())
+        return BufferOffset();
+
     int32_t old;
     BufferOffset ret;
     if (l->used()) {
         old = l->offset();
         // This will currently throw an assertion if we couldn't actually
         // encode the offset of the branch.
         if (!BOffImm::IsInRange(old)) {
             m_buffer.fail_bail();
             return ret;
         }
         ret = as_b(BOffImm(old), c, l);
     } else {
         old = LabelBase::INVALID_OFFSET;
         BOffImm inv;
         ret = as_b(inv, c, l);
     }
-    DebugOnly<int32_t> check = l->use(ret.getOffset());
-    MOZ_ASSERT(check == old);
+
+    if (oom())
+        return BufferOffset();
+
+    MOZ_ASSERT(l->use(ret.getOffset() == old);
     return ret;
 }
 
 BufferOffset
 Assembler::as_b(BOffImm off, Condition c, BufferOffset inst)
 {
     // JS_DISASM_ARM NOTE: Can't disassemble here, because numerous callers use this to
     // patchup old code.  Must disassemble in caller where it makes sense.  Not many callers.
@@ -2447,31 +2451,32 @@ BufferOffset
 Assembler::as_bl(BOffImm off, Condition c, Label* documentation)
 {
     return writeBranchInst(((int)c) | OpBl | off.encode(), documentation);
 }
 
 BufferOffset
 Assembler::as_bl(Label* l, Condition c)
 {
-    if (m_buffer.oom()) {
-        BufferOffset ret;
-        return ret;
-    }
-
     if (l->bound()) {
         // Note only one instruction is emitted here, the NOP is overwritten.
         BufferOffset ret = allocBranchInst();
+        if (oom())
+            return BufferOffset();
+
         as_bl(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
 #ifdef JS_DISASM_ARM
         spewBranch(m_buffer.getInstOrNull(ret), l);
 #endif
         return ret;
     }
 
+    if (oom())
+        return BufferOffset();
+
     int32_t old;
     BufferOffset ret;
     // See if the list was empty :(
     if (l->used()) {
         // This will currently throw an assertion if we couldn't actually encode
         // the offset of the branch.
         old = l->offset();
         if (!BOffImm::IsInRange(old)) {
@@ -2479,18 +2484,21 @@ Assembler::as_bl(Label* l, Condition c)
             return ret;
         }
         ret = as_bl(BOffImm(old), c, l);
     } else {
         old = LabelBase::INVALID_OFFSET;
         BOffImm inv;
         ret = as_bl(inv, c, l);
     }
-    DebugOnly<int32_t> check = l->use(ret.getOffset());
-    MOZ_ASSERT(check == old);
+
+    if (oom())
+        return BufferOffset();
+
+    MOZ_ASSERT(l->use(ret.getOffset() == old);
     return ret;
 }
 
 BufferOffset
 Assembler::as_bl(BOffImm off, Condition c, BufferOffset inst)
 {
     *editSrc(inst) = InstBLImm(off, c);
     return inst;
--- a/js/src/jit/shared/IonAssemblerBuffer.h
+++ b/js/src/jit/shared/IonAssemblerBuffer.h
@@ -147,18 +147,23 @@ class AssemblerBuffer
             fail_oom();
             return nullptr;
         }
         return new (tmp) Slice;
     }
 
     bool ensureSpace(int size) {
         // Space can exist in the most recent Slice.
-        if (tail && tail->length() + size <= tail->Capacity())
+        if (tail && tail->length() + size <= tail->Capacity()) {
+            // Simulate allocation failure even when we don't need a new slice.
+            if (js::oom::ShouldFailWithOOM())
+                return fail_oom();
+
             return true;
+        }
 
         // Otherwise, a new Slice must be added.
         Slice* slice = newSlice(lifoAlloc_);
         if (slice == nullptr)
             return fail_oom();
 
         // If this is the first Slice in the buffer, add to head position.
         if (!head) {
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -699,16 +699,18 @@ struct AssemblerBufferWithConstantPools 
         // Should not be placing a pool in a no-pool region, check.
         MOZ_ASSERT(!canNotPlacePool_);
 
         // Dump the pool with a guard branch around the pool.
         BufferOffset branch = this->nextOffset();
         // Mark and emit the guard branch.
         markNextAsBranch();
         this->putBytes(guardSize_ * InstSize, nullptr);
+        if (this->oom())
+            return;
         BufferOffset afterPool = this->nextOffset();
         Asm::WritePoolGuard(branch, this->getInst(branch), afterPool);
 
         // Perforate the buffer which finishes the current slice and allocates a
         // new slice. This is necessary because Pools are always placed after
         // the end of a slice.
         BufferSlice* perforatedSlice = getTail();
         BufferOffset perforation = this->nextOffset();
@@ -859,17 +861,17 @@ struct AssemblerBufferWithConstantPools 
         if (pool_.checkFull(poolOffset)) {
             // Alignment would cause a pool dump, so dump the pool now.
             JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %d caused a spill.", id, alignment,
                     sizeExcludingCurrentPool());
             finishPool();
         }
 
         inhibitNops_ = true;
-        while (sizeExcludingCurrentPool() & (alignment - 1))
+        while ((sizeExcludingCurrentPool() & (alignment - 1)) && !this->oom())
             putInt(alignFillInst_);
         inhibitNops_ = false;
     }
 
   private:
     void patchBranch(Inst* i, unsigned curpool, BufferOffset branch) {
         const Inst* ci = i;
         ptrdiff_t offset = Asm::GetBranchOffset(ci);