+ bdescr *bd;
+ generation *gen, *new_gen;
+ nat gen_no, new_gen_no;
+ gen_workspace *ws;
+
+ bd = Bdescr(p);
+ gen = bd->gen;
+ gen_no = bd->gen_no;
+ ACQUIRE_SPIN_LOCK(&gen->sync);
+
+ // already evacuated?
+ if (bd->flags & BF_EVACUATED) {
+ /* Don't forget to set the gct->failed_to_evac flag if we didn't get
+ * the desired destination (see comments in evacuate()).
+ */
+ if (gen_no < gct->evac_gen_no) {
+ gct->failed_to_evac = rtsTrue;
+ TICK_GC_FAILED_PROMOTION();
+ }
+ RELEASE_SPIN_LOCK(&gen->sync);
+ return;
+ }
+
+ // remove from large_object list
+ if (bd->u.back) {
+ bd->u.back->link = bd->link;
+ } else { // first object in the list
+ gen->large_objects = bd->link;
+ }
+ if (bd->link) {
+ bd->link->u.back = bd->u.back;
+ }
+
+ /* link it on to the evacuated large object list of the destination gen
+ */
+ new_gen_no = bd->dest_no;
+
+ if (new_gen_no < gct->evac_gen_no) {
+ if (gct->eager_promotion) {
+ new_gen_no = gct->evac_gen_no;
+ } else {
+ gct->failed_to_evac = rtsTrue;
+ }
+ }
+
+ ws = &gct->gens[new_gen_no];
+ new_gen = &generations[new_gen_no];
+
+ bd->flags |= BF_EVACUATED;
+ initBdescr(bd, new_gen, new_gen->to);
+
+ // If this is a block of pinned objects, we don't have to scan
+ // these objects, because they aren't allowed to contain any
+ // pointers. For these blocks, we skip the scavenge stage and put
+ // them straight on the scavenged_large_objects list.
+ if (bd->flags & BF_PINNED) {
+ ASSERT(get_itbl((StgClosure *)p)->type == ARR_WORDS);
+ if (new_gen != gen) { ACQUIRE_SPIN_LOCK(&new_gen->sync); }
+ dbl_link_onto(bd, &new_gen->scavenged_large_objects);
+ new_gen->n_scavenged_large_blocks += bd->blocks;
+ if (new_gen != gen) { RELEASE_SPIN_LOCK(&new_gen->sync); }
+ } else {
+ bd->link = ws->todo_large_objects;
+ ws->todo_large_objects = bd;
+ }
+
+ RELEASE_SPIN_LOCK(&gen->sync);