/* Determine which generation will be collected next, and approximate * the maximum amount of memory that will be required to do the GC, * taking into account data that will be copied, and the space needed * to store bitmaps and the mark stack. Note: blocks_needed does not * include the blocks in the nursery. * * Assume: all data currently live will remain live. Generationss * that will be collected next time will therefore need twice as many * blocks since all the data will be copied. */ extern W_ calcNeeded (rtsBool force_major, memcount *blocks_needed) { W_ needed = 0, blocks; nat g, N; generation *gen; if (force_major) { N = RtsFlags.GcFlags.generations - 1; } else { N = 0; } for (g = 0; g < RtsFlags.GcFlags.generations; g++) { gen = &generations[g]; blocks = gen->n_blocks // or: gen->n_words / BLOCK_SIZE_W (?) + gen->n_large_blocks; // we need at least this much space needed += blocks; // are we collecting this gen? if (g == 0 || // always collect gen 0 blocks > gen->max_blocks) { N = stg_max(N,g); // we will collect this gen next time if (gen->mark) { // bitmap: needed += gen->n_blocks / BITS_IN(W_); // mark stack: needed += gen->n_blocks / 100; } if (gen->compact) { continue; // no additional space needed for compaction } else { needed += gen->n_blocks; } } } if (blocks_needed != NULL) { *blocks_needed = needed; } return N; }
StgPtr alloc_todo_block (gen_workspace *ws, uint32_t size) { bdescr *bd/*, *hd, *tl */; // Grab a part block if we have one, and it has enough room bd = ws->part_list; if (bd != NULL && bd->start + bd->blocks * BLOCK_SIZE_W - bd->free > (int)size) { ws->part_list = bd->link; ws->n_part_blocks -= bd->blocks; ws->n_part_words -= bd->free - bd->start; } else { if (size > BLOCK_SIZE_W) { bd = allocGroup_sync((W_)BLOCK_ROUND_UP(size*sizeof(W_)) / BLOCK_SIZE); } else { if (gct->free_blocks) { bd = gct->free_blocks; gct->free_blocks = bd->link; } else { allocBlocks_sync(16, &bd); gct->free_blocks = bd->link; } } // blocks in to-space get the BF_EVACUATED flag. bd->flags = BF_EVACUATED; bd->u.scan = bd->start; initBdescr(bd, ws->gen, ws->gen->to); } bd->link = NULL; ws->todo_bd = bd; ws->todo_free = bd->free; ws->todo_lim = stg_min(bd->start + bd->blocks * BLOCK_SIZE_W, bd->free + stg_max(WORK_UNIT_WORDS,size)); // See Note [big objects] debugTrace(DEBUG_gc, "alloc new todo block %p for gen %d", bd->free, ws->gen->no); return ws->todo_free; }
AdjustorWritable allocateExec (W_ bytes, AdjustorExecutable *exec_ret) { void *ret; W_ n; ACQUIRE_SM_LOCK; // round up to words. n = (bytes + sizeof(W_) + 1) / sizeof(W_); if (n+1 > BLOCK_SIZE_W) { barf("allocateExec: can't handle large objects"); } if (exec_block == NULL || exec_block->free + n + 1 > exec_block->start + BLOCK_SIZE_W) { bdescr *bd; W_ pagesize = getPageSize(); bd = allocGroup(stg_max(1, pagesize / BLOCK_SIZE)); debugTrace(DEBUG_gc, "allocate exec block %p", bd->start); bd->gen_no = 0; bd->flags = BF_EXEC; bd->link = exec_block; if (exec_block != NULL) { exec_block->u.back = bd; } bd->u.back = NULL; setExecutable(bd->start, bd->blocks * BLOCK_SIZE, rtsTrue); exec_block = bd; } *(exec_block->free) = n; // store the size of this chunk exec_block->gen_no += n; // gen_no stores the number of words allocated ret = exec_block->free + 1; exec_block->free += n + 1; RELEASE_SM_LOCK *exec_ret = ret; return ret; }
void * allocateForCompact (Capability *cap, StgCompactNFData *str, StgWord sizeW) { StgPtr to; StgWord next_size; StgCompactNFDataBlock *block; bdescr *bd; ASSERT(str->nursery != NULL); ASSERT(str->hp > Bdescr((P_)str->nursery)->start); ASSERT(str->hp <= Bdescr((P_)str->nursery)->start + Bdescr((P_)str->nursery)->blocks * BLOCK_SIZE_W); retry: if (str->hp + sizeW < str->hpLim) { to = str->hp; str->hp += sizeW; return to; } bd = Bdescr((P_)str->nursery); bd->free = str->hp; // We know it doesn't fit in the nursery // if it is a large object, allocate a new block if (sizeW > LARGE_OBJECT_THRESHOLD/sizeof(W_)) { next_size = BLOCK_ROUND_UP(sizeW*sizeof(W_) + sizeof(StgCompactNFData)); block = compactAppendBlock(cap, str, next_size); bd = Bdescr((P_)block); to = bd->free; bd->free += sizeW; return to; } // move the nursery past full blocks if (block_is_full (str->nursery)) { do { str->nursery = str->nursery->next; } while (str->nursery && block_is_full(str->nursery)); if (str->nursery == NULL) { str->nursery = compactAppendBlock(cap, str, str->autoBlockW * sizeof(W_)); } bd = Bdescr((P_)str->nursery); str->hp = bd->free; str->hpLim = bd->start + bd->blocks * BLOCK_SIZE_W; goto retry; } // try subsequent blocks for (block = str->nursery->next; block != NULL; block = block->next) { bd = Bdescr((P_)block); if (has_room_for(bd,sizeW)) { to = bd->free; bd->free += sizeW; return to; } } // If all else fails, allocate a new block of the right size. next_size = stg_max(str->autoBlockW * sizeof(StgWord), BLOCK_ROUND_UP(sizeW * sizeof(StgWord) + sizeof(StgCompactNFDataBlock))); block = compactAppendBlock(cap, str, next_size); bd = Bdescr((P_)block); to = bd->free; bd->free += sizeW; return to; }
StgPtr todo_block_full (uint32_t size, gen_workspace *ws) { bool urgent_to_push, can_extend; StgPtr p; bdescr *bd; // todo_free has been pre-incremented by Evac.c:alloc_for_copy(). We // are expected to leave it bumped when we've finished here. ws->todo_free -= size; bd = ws->todo_bd; ASSERT(bd != NULL); ASSERT(bd->link == NULL); ASSERT(bd->gen == ws->gen); // We intentionally set ws->todo_lim lower than the full size of // the block, so that we can push out some work to the global list // and get the parallel threads working as soon as possible. // // So when ws->todo_lim is reached, we end up here and have to // decide whether it's worth pushing out the work we have or not. // If we have enough room in the block to evacuate the current // object, and it's not urgent to push this work, then we just // extend the limit and keep going. Where "urgent" is defined as: // the global pool is empty, and there's enough work in this block // to make it worth pushing. // urgent_to_push = looksEmptyWSDeque(ws->todo_q) && (ws->todo_free - bd->u.scan >= WORK_UNIT_WORDS / 2); // We can extend the limit for the current block if there's enough // room for the current object, *and* we're not into the second or // subsequent block of a large block (see Note [big objects]). can_extend = ws->todo_free + size <= bd->start + bd->blocks * BLOCK_SIZE_W && ws->todo_free < ws->todo_bd->start + BLOCK_SIZE_W; if (!urgent_to_push && can_extend) { ws->todo_lim = stg_min(bd->start + bd->blocks * BLOCK_SIZE_W, ws->todo_lim + stg_max(WORK_UNIT_WORDS,size)); debugTrace(DEBUG_gc, "increasing limit for %p to %p", bd->start, ws->todo_lim); p = ws->todo_free; ws->todo_free += size; return p; } gct->copied += ws->todo_free - bd->free; bd->free = ws->todo_free; ASSERT(bd->u.scan >= bd->start && bd->u.scan <= bd->free); // If this block is not the scan block, we want to push it out and // make room for a new todo block. if (bd != gct->scan_bd) { // If this block does not have enough space to allocate the // current object, but it also doesn't have any work to push, then // push it on to the scanned list. if (bd->u.scan == bd->free) { if (bd->free == bd->start) { // Normally the block would not be empty, because then // there would be enough room to copy the current // object. However, if the object we're copying is // larger than a block, then we might have an empty // block here. freeGroup_sync(bd); } else { push_scanned_block(bd, ws); } } // Otherwise, push this block out to the global list. else { DEBUG_ONLY( generation *gen ); DEBUG_ONLY( gen = ws->gen ); debugTrace(DEBUG_gc, "push todo block %p (%ld words), step %d, todo_q: %ld", bd->start, (unsigned long)(bd->free - bd->u.scan), gen->no, dequeElements(ws->todo_q)); if (!pushWSDeque(ws->todo_q, bd)) { bd->link = ws->todo_overflow; ws->todo_overflow = bd; ws->n_todo_overflow++; } } } ws->todo_bd = NULL; ws->todo_free = NULL; ws->todo_lim = NULL; alloc_todo_block(ws, size); p = ws->todo_free; ws->todo_free += size; return p; }
STATIC_INLINE StgPtr allocate_NONUPD (Capability *cap, int n_words) { return allocate(cap, stg_max(sizeofW(StgHeader)+MIN_PAYLOAD_SIZE, n_words)); }