@@ -8164,26 +8164,47 @@ fn (mut g Gen) scope_gc_pin_pregen(node_pos int) []ScopeGcPin {
81648164 opened_scope = true
81658165 }
81668166 // Snapshot nested heap buffers before the call, then keep those snapshots
8167- // reachable after it. This covers Boehm opt/noscan arrays of structs.
8167+ // reachable across it. This covers Boehm opt/noscan arrays of structs.
8168+ //
8169+ // The snapshot is normally placed in a small fixed stack array. Boehm scans
8170+ // the C stack conservatively, so the leaf pointers stored there stay rooted for
8171+ // the duration of the call without any GC root (de)registration. Only snapshots
8172+ // larger than the stack buffer fall back to an explicit `calloc` + `GC_add_roots`
8173+ // / `GC_remove_roots` pair. This avoids the per-call `GC_add_roots`/`GC_remove_roots`
8174+ // + `calloc`/`free` churn, which otherwise dominates hot loops that call functions
8175+ // while holding aggregates of pointers in scope (e.g. JSON encoding).
8176+ stack_cap := 32
81688177 tmp_name := g.new_tmp_var ()
81698178 len_tmp_name := g.new_tmp_var ()
81708179 roots_tmp_name := g.new_tmp_var ()
8180+ stack_tmp_name := g.new_tmp_var ()
8181+ on_heap_tmp_name := g.new_tmp_var ()
81718182 setup_gc_state_name := g.new_tmp_var ()
81728183 cleanup_gc_state_name := g.new_tmp_var ()
8184+ styp := g.styp (obj.typ)
81738185 g.writeln ('voidptr ${tmp_name } = &${cvar_name };' )
8174- g.writeln ('int ${len_tmp_name } = ${collect_helper_name }((${g .styp (obj .typ )}*)${tmp_name }, 0, 0);' )
8175- g.writeln ('int ${setup_gc_state_name } = GC_is_disabled();' )
8176- g.writeln ('if (!${setup_gc_state_name }) { GC_disable(); }' )
8177- g.writeln ('voidptr* ${roots_tmp_name } = 0;' )
8178- g.writeln ('if (${len_tmp_name } > 0) {' )
8186+ g.writeln ('int ${len_tmp_name } = ${collect_helper_name }((${styp }*)${tmp_name }, 0, 0);' )
8187+ g.writeln ('voidptr ${stack_tmp_name }[${stack_cap }];' )
8188+ g.writeln ('voidptr* ${roots_tmp_name } = ${stack_tmp_name };' )
8189+ g.writeln ('bool ${on_heap_tmp_name } = false;' )
8190+ g.writeln ('if (${len_tmp_name } > ${stack_cap }) {' )
8191+ // Oversized snapshot: the original explicit-roots path, guarded against a
8192+ // collection happening between allocation and registration.
8193+ g.writeln ('\t int ${setup_gc_state_name } = GC_is_disabled();' )
8194+ g.writeln ('\t if (!${setup_gc_state_name }) { GC_disable(); }' )
81798195 g.writeln ('\t ${roots_tmp_name } = (voidptr*)calloc(${len_tmp_name }, sizeof(voidptr));' )
81808196 g.writeln ('\t if (${roots_tmp_name } == 0) { builtin___memory_panic(_S("calloc"), sizeof(voidptr) * ${len_tmp_name }); }' )
8181- g.writeln ('\t ${collect_helper_name }((${g .styp (obj .typ )}*)${tmp_name }, ${roots_tmp_name }, 0);' )
8197+ g.writeln ('\t ${on_heap_tmp_name } = true;' )
8198+ g.writeln ('\t ${collect_helper_name }((${styp }*)${tmp_name }, ${roots_tmp_name }, 0);' )
81828199 g.writeln ('\t GC_add_roots(${roots_tmp_name }, ${roots_tmp_name } + ${len_tmp_name });' )
8200+ g.writeln ('\t if (!${setup_gc_state_name }) { GC_enable(); }' )
8201+ g.writeln ('} else if (${len_tmp_name } > 0) {' )
8202+ // Common case: snapshot into the stack buffer. The fill performs no allocation,
8203+ // so no collection can run while it is partially filled.
8204+ g.writeln ('\t ${collect_helper_name }((${styp }*)${tmp_name }, ${roots_tmp_name }, 0);' )
81838205 g.writeln ('}' )
8184- g.writeln ('if (!${setup_gc_state_name }) { GC_enable(); }' )
81858206 pins << ScopeGcPin{
8186- post_stmt: 'GC_reachable_here(${tmp_name }); if (${len_tmp_name } > 0) { for (int _v_keep_i = 0; _v_keep_i < ${len_tmp_name }; ++_v_keep_i) { GC_reachable_here(${roots_tmp_name }[_v_keep_i]); } } int ${cleanup_gc_state_name } = GC_is_disabled(); if (!${cleanup_gc_state_name }) { GC_disable(); } if (${ len_tmp_name } > 0) { GC_remove_roots(${roots_tmp_name }, ${roots_tmp_name } + ${len_tmp_name }); free(${roots_tmp_name }); } if (!${cleanup_gc_state_name }) { GC_enable(); }'
8207+ post_stmt: 'GC_reachable_here(${tmp_name }); if (${len_tmp_name } > 0) { for (int _v_keep_i = 0; _v_keep_i < ${len_tmp_name }; ++_v_keep_i) { GC_reachable_here(${roots_tmp_name }[_v_keep_i]); } } GC_reachable_here(${ roots_tmp_name }); if (${ on_heap_tmp_name }) { int ${cleanup_gc_state_name } = GC_is_disabled(); if (!${cleanup_gc_state_name }) { GC_disable(); } GC_remove_roots(${roots_tmp_name }, ${roots_tmp_name } + ${len_tmp_name }); free(${roots_tmp_name }); if (!${cleanup_gc_state_name }) { GC_enable(); } }'
81878208 }
81888209 }
81898210 return pins
0 commit comments