From 48deef316350d95581de9625d942fd704972f8b5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 30 Apr 2014 17:52:53 -0700 Subject: [PATCH] Bug 1004790 (part 1) - Split off a new class FreeList from FreeSpan. r=billm. --- js/src/gc/Heap.h | 155 +++++++++++++++++-------------- js/src/jit/IonMacroAssembler.cpp | 8 +- js/src/jsgc.cpp | 15 +-- js/src/jsgc.h | 40 ++++---- 4 files changed, 115 insertions(+), 103 deletions(-) diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index 5662560afabd..58a14bd738a2 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -206,13 +206,6 @@ struct FreeSpan return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset); } - void initAsEmpty(uintptr_t arenaAddr = 0) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - first = arenaAddr + ArenaSize; - last = arenaAddr | (ArenaSize - 1); - JS_ASSERT(isEmpty()); - } - bool isEmpty() const { checkSpan(); return first > last; @@ -246,16 +239,6 @@ struct FreeSpan return arenaAddressUnchecked(); } - ArenaHeader *arenaHeader() const { - return reinterpret_cast(arenaAddress()); - } - - bool isSameNonEmptySpan(const FreeSpan *another) const { - JS_ASSERT(!isEmpty()); - JS_ASSERT(!another->isEmpty()); - return first == another->first && last == another->last; - } - bool isWithinArena(uintptr_t arenaAddr) const { JS_ASSERT(!(arenaAddr & ArenaMask)); @@ -272,61 +255,6 @@ struct FreeSpan return encodeOffsets(first - arenaAddr, last & ArenaMask); } - /* See comments before FreeSpan for details. */ - MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) { - JS_ASSERT(thingSize % CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - /* Bump-allocate from the current span. */ - first = thing + thingSize; - } else if (MOZ_LIKELY(thing == last)) { - /* - * Move to the next span. We use MOZ_LIKELY as without PGO - * compilers mis-predict == here as unlikely to succeed. - */ - *this = *reinterpret_cast(thing); - } else { - return nullptr; - } - checkSpan(); - JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); - return reinterpret_cast(thing); - } - - /* A version of allocate when we know that the span is not empty. */ - MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { - JS_ASSERT(thingSize % CellSize == 0); - checkSpan(); - uintptr_t thing = first; - if (thing < last) { - first = thing + thingSize; - } else { - JS_ASSERT(thing == last); - *this = *reinterpret_cast(thing); - } - checkSpan(); - JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); - return reinterpret_cast(thing); - } - - /* - * Allocate from a newly allocated arena. We do not move the free list - * from the arena. Rather we set the arena up as fully used during the - * initialization so to allocate we simply return the first thing in the - * arena and set the free list to point to the second. - */ - MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, - size_t thingSize) { - JS_ASSERT(!(arenaAddr & ArenaMask)); - uintptr_t thing = arenaAddr | firstThingOffset; - first = thing + thingSize; - last = arenaAddr | ArenaMask; - checkSpan(); - JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); - return reinterpret_cast(thing); - } - void checkSpan() const { #ifdef DEBUG /* We do not allow spans at the end of the address space. */ @@ -388,7 +316,90 @@ struct FreeSpan } #endif } +}; +struct FreeList : public FreeSpan +{ + FreeList() {} + + void initAsEmpty(uintptr_t arenaAddr = 0) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + first = arenaAddr + ArenaSize; + last = arenaAddr | (ArenaSize - 1); + JS_ASSERT(isEmpty()); + } + + void setHead(FreeSpan *span) { + first = span->first; + last = span->last; + } + + ArenaHeader *arenaHeader() const { + return reinterpret_cast(arenaAddress()); + } + +#ifdef DEBUG + bool isSameNonEmptySpan(const FreeSpan &another) const { + JS_ASSERT(!isEmpty()); + JS_ASSERT(!another.isEmpty()); + return first == another.first && last == another.last; + } +#endif + + /* See comments before FreeSpan for details. */ + MOZ_ALWAYS_INLINE void *allocate(size_t thingSize) { + JS_ASSERT(thingSize % CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + /* Bump-allocate from the current span. */ + first = thing + thingSize; + } else if (MOZ_LIKELY(thing == last)) { + /* + * Move to the next span. We use MOZ_LIKELY as without PGO + * compilers mis-predict == here as unlikely to succeed. + */ + setHead(reinterpret_cast(thing)); + } else { + return nullptr; + } + checkSpan(); + JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); + return reinterpret_cast(thing); + } + + /* A version of allocate when we know that the span is not empty. */ + MOZ_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { + JS_ASSERT(thingSize % CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + first = thing + thingSize; + } else { + JS_ASSERT(thing == last); + setHead(reinterpret_cast(thing)); + } + checkSpan(); + JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); + return reinterpret_cast(thing); + } + + /* + * Allocate from a newly allocated arena. We do not move the free list + * from the arena. Rather we set the arena up as fully used during the + * initialization so to allocate we simply return the first thing in the + * arena and set the free list to point to the second. + */ + MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, + size_t thingSize) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + uintptr_t thing = arenaAddr | firstThingOffset; + first = thing + thingSize; + last = arenaAddr | ArenaMask; + checkSpan(); + JS_EXTRA_POISON(reinterpret_cast(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize); + return reinterpret_cast(thing); + } }; /* Every arena has a header. */ diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index ea7611cd9224..3e56c23a9024 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -463,9 +463,9 @@ MacroAssembler::newGCThing(Register result, Register temp, gc::AllocKind allocKi CompileZone *zone = GetIonContext()->compartment->zone(); - // Inline FreeSpan::allocate. - // There is always exactly one FreeSpan per allocKind per JSCompartment. - // If a FreeSpan is replaced, its members are updated in the freeLists table, + // Inline FreeList::allocate. + // There is always exactly one FreeList per allocKind per JSCompartment. + // If a FreeList is replaced, its members are updated in the freeLists table, // which the code below always re-reads. loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result); branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail); @@ -515,7 +515,7 @@ MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, R tempReg1); // Get a pointer to the relevant free list: - // tempReg1 = (FreeSpan*) &tempReg1->arenas.freeLists[(allocKind)] + // tempReg1 = (FreeList*) &tempReg1->arenas.freeLists[(allocKind)] uint32_t offset = (offsetof(Allocator, arenas) + js::gc::ArenaLists::getFreeListOffset(allocKind)); addPtr(Imm32(offset), tempReg1); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d0dd8f3b0dd5..b67e28c956a2 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -399,15 +399,15 @@ ArenaHeader::checkSynchronizedWithFreeList() const FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); if (firstSpan.isEmpty()) return; - const FreeSpan *list = zone->allocator.arenas.getFreeList(getAllocKind()); - if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress()) + const FreeList *freeList = zone->allocator.arenas.getFreeList(getAllocKind()); + if (freeList->isEmpty() || firstSpan.arenaAddress() != freeList->arenaAddress()) return; /* * Here this arena has free things, FreeList::lists[thingKind] is not * empty and also points to this arena. Thus they must the same. */ - JS_ASSERT(firstSpan.isSameNonEmptySpan(list)); + JS_ASSERT(freeList->isSameNonEmptySpan(firstSpan)); } #endif @@ -1433,9 +1433,9 @@ inline void ArenaLists::prepareForIncrementalGC(JSRuntime *rt) { for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { - FreeSpan *headSpan = &freeLists[i]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); + FreeList *freeList = &freeLists[i]; + if (!freeList->isEmpty()) { + ArenaHeader *aheader = freeList->arenaHeader(); aheader->allocatedDuringIncremental = true; rt->gc.marker.delayMarkingArena(aheader); } @@ -1511,7 +1511,8 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind) * Move the free span stored in the arena to the free list and * allocate from it. */ - freeLists[thingKind] = aheader->getFirstFreeSpan(); + FreeSpan firstFreeSpan = aheader->getFirstFreeSpan(); + freeLists[thingKind].setHead(&firstFreeSpan); aheader->setAsFullyUsed(); if (MOZ_UNLIKELY(zone->wasGCStarted())) { if (zone->needsBarrier()) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 97c391a674f9..e93a4f6e5a20 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -537,7 +537,7 @@ class ArenaLists * GC we only move the head of the of the list of spans back to the arena * only for the arena that was not fully allocated. */ - FreeSpan freeLists[FINALIZE_LIMIT]; + FreeList freeLists[FINALIZE_LIMIT]; ArenaList arenaLists[FINALIZE_LIMIT]; @@ -601,10 +601,10 @@ class ArenaLists static uintptr_t getFreeListOffset(AllocKind thingKind) { uintptr_t offset = offsetof(ArenaLists, freeLists); - return offset + thingKind * sizeof(FreeSpan); + return offset + thingKind * sizeof(FreeList); } - const FreeSpan *getFreeList(AllocKind thingKind) const { + const FreeList *getFreeList(AllocKind thingKind) const { return &freeLists[thingKind]; } @@ -661,11 +661,11 @@ class ArenaLists } void purge(AllocKind i) { - FreeSpan *headSpan = &freeLists[i]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - aheader->setFirstFreeSpan(headSpan); - headSpan->initAsEmpty(); + FreeList *freeList = &freeLists[i]; + if (!freeList->isEmpty()) { + ArenaHeader *aheader = freeList->arenaHeader(); + aheader->setFirstFreeSpan(freeList); + freeList->initAsEmpty(); } } @@ -682,11 +682,11 @@ class ArenaLists } void copyFreeListToArena(AllocKind thingKind) { - FreeSpan *headSpan = &freeLists[thingKind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); + FreeList *freeList = &freeLists[thingKind]; + if (!freeList->isEmpty()) { + ArenaHeader *aheader = freeList->arenaHeader(); JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(headSpan); + aheader->setFirstFreeSpan(freeList); } } @@ -701,10 +701,10 @@ class ArenaLists void clearFreeListInArena(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (!headSpan->isEmpty()) { - ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); + FreeList *freeList = &freeLists[kind]; + if (!freeList->isEmpty()) { + ArenaHeader *aheader = freeList->arenaHeader(); + JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan())); aheader->setAsFullyUsed(); } } @@ -714,16 +714,16 @@ class ArenaLists * arena using copyToArena(). */ bool isSynchronizedFreeList(AllocKind kind) { - FreeSpan *headSpan = &freeLists[kind]; - if (headSpan->isEmpty()) + FreeList *freeList = &freeLists[kind]; + if (freeList->isEmpty()) return true; - ArenaHeader *aheader = headSpan->arenaHeader(); + ArenaHeader *aheader = freeList->arenaHeader(); if (aheader->hasFreeThings()) { /* * If the arena has a free list, it must be the same as one in * lists. */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); + JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan())); return true; } return false;