| 1 | module c |
| 2 | |
| 3 | import runtime |
| 4 | import strings |
| 5 | import v3.flat |
| 6 | import v3.types |
| 7 | |
| 8 | const max_flat_cgen_jobs = 2 |
| 9 | const min_flat_cgen_parallel_items = 1024 |
| 10 | |
| 11 | // FlatFnGenItem represents flat fn gen item data used by c. |
| 12 | struct FlatFnGenItem { |
| 13 | node_id flat.NodeId |
| 14 | file string |
| 15 | module string |
| 16 | cost int |
| 17 | } |
| 18 | |
| 19 | $if !windows { |
| 20 | // FlatCgenChunkArgs represents flat cgen chunk args data used by c. |
| 21 | struct FlatCgenChunkArgs { |
| 22 | worker voidptr |
| 23 | work_items_ptr voidptr |
| 24 | } |
| 25 | |
| 26 | // C.pthread_t declares C pthread t data used by c. |
| 27 | @[typedef] |
| 28 | struct C.pthread_t {} |
| 29 | |
| 30 | // C.pthread_create declares the C pthread_create symbol used by c. |
| 31 | fn C.pthread_create(thread &C.pthread_t, attr voidptr, start_routine fn (voidptr) voidptr, arg voidptr) int |
| 32 | |
| 33 | // C.pthread_join declares the C pthread_join symbol used by c. |
| 34 | fn C.pthread_join(thread C.pthread_t, retval voidptr) int |
| 35 | |
| 36 | // C.pthread_attr_init declares the C pthread_attr_init symbol used by c. |
| 37 | fn C.pthread_attr_init(attr voidptr) int |
| 38 | |
| 39 | // C.pthread_attr_setstacksize declares the C pthread_attr_setstacksize symbol used by c. |
| 40 | fn C.pthread_attr_setstacksize(attr voidptr, stacksize usize) int |
| 41 | |
| 42 | // C.pthread_attr_destroy declares the C pthread_attr_destroy symbol used by c. |
| 43 | fn C.pthread_attr_destroy(attr voidptr) int |
| 44 | |
| 45 | // flat_cgen_chunk_thread supports flat cgen chunk thread handling for c. |
| 46 | fn flat_cgen_chunk_thread(arg voidptr) voidptr { |
| 47 | a := unsafe { &FlatCgenChunkArgs(arg) } |
| 48 | mut w := unsafe { &FlatGen(a.worker) } |
| 49 | items := unsafe { &[]FlatFnGenItem(a.work_items_ptr) } |
| 50 | w.gen_fn_items(*items) |
| 51 | return unsafe { nil } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // gen_fns_dispatch emits fns dispatch output for c. |
| 56 | fn (mut g FlatGen) gen_fns_dispatch(no_parallel bool) { |
| 57 | if no_parallel { |
| 58 | g.gen_fns() |
| 59 | g.gen_synthetic_main_after_fns() |
| 60 | return |
| 61 | } |
| 62 | items := g.collect_fn_gen_items() |
| 63 | n_items := items.len |
| 64 | n_jobs := flat_cgen_job_count(runtime.nr_jobs(), n_items) |
| 65 | $if windows { |
| 66 | g.gen_fn_items(items) |
| 67 | g.gen_synthetic_main_after_fns() |
| 68 | return |
| 69 | } $else { |
| 70 | if n_items < min_flat_cgen_parallel_items || n_jobs <= 1 { |
| 71 | g.gen_fn_items(items) |
| 72 | g.gen_synthetic_main_after_fns() |
| 73 | return |
| 74 | } |
| 75 | g.parallel_used = true |
| 76 | g.prepare_parallel_items(items) |
| 77 | mut chunk_items := split_flat_cgen_items(items, n_jobs) |
| 78 | chunk_count := chunk_items.len |
| 79 | // chunk[0] is emitted by the master (this thread) directly into its own builder |
| 80 | // — no worker clone. Only chunks[1..] get helper threads with a cloned FlatGen. |
| 81 | // This drops one full FlatGen clone from the peak and uses the master thread that |
| 82 | // would otherwise just block in join. |
| 83 | thread_count := chunk_count - 1 |
| 84 | mut thread_ids := []C.pthread_t{len: thread_count} |
| 85 | mut args := []FlatCgenChunkArgs{cap: thread_count} |
| 86 | mut workers := []voidptr{cap: thread_count} |
| 87 | |
| 88 | // Helper workers keep the same temp-name base they had when every chunk was a |
| 89 | // worker (worker_id ci+1 -> base (ci+2)*100_000), and the master adopts what was |
| 90 | // worker 0's base. This keeps each chunk in its own disjoint _tN range AND makes |
| 91 | // the output byte-identical to the all-workers version. |
| 92 | for ci := 0; ci < thread_count; ci++ { |
| 93 | w := g.new_parallel_worker(ci + 1) |
| 94 | workers << voidptr(w) |
| 95 | } |
| 96 | for ci := 0; ci < thread_count; ci++ { |
| 97 | args << FlatCgenChunkArgs{ |
| 98 | worker: workers[ci] |
| 99 | work_items_ptr: unsafe { voidptr(&chunk_items[ci + 1]) } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | attr_buf := [64]u8{} |
| 104 | attr := unsafe { voidptr(&attr_buf[0]) } |
| 105 | C.pthread_attr_init(attr) |
| 106 | C.pthread_attr_setstacksize(attr, 64 * 1024 * 1024) |
| 107 | for ci := 0; ci < thread_count; ci++ { |
| 108 | C.pthread_create(unsafe { &thread_ids[ci] }, attr, flat_cgen_chunk_thread, |
| 109 | unsafe { voidptr(&args[ci]) }) |
| 110 | } |
| 111 | C.pthread_attr_destroy(attr) |
| 112 | // Master emits chunk[0] into g.sb while the helper threads run, using worker 0's |
| 113 | // old temp base so its emitted temps match the all-workers numbering. |
| 114 | g.tmp_count = 100_000 |
| 115 | g.gen_fn_items(chunk_items[0]) |
| 116 | for ci := 0; ci < thread_count; ci++ { |
| 117 | C.pthread_join(thread_ids[ci], unsafe { nil }) |
| 118 | } |
| 119 | // Merge helper output after the master's chunk[0], in fixed order. |
| 120 | for ci := 0; ci < thread_count; ci++ { |
| 121 | w := unsafe { &FlatGen(workers[ci]) } |
| 122 | g.merge_parallel_worker(w) |
| 123 | } |
| 124 | g.gen_synthetic_main_after_fns() |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | // flat_cgen_job_count supports flat cgen job count handling for c. |
| 129 | fn flat_cgen_job_count(n_runtime_jobs int, n_items int) int { |
| 130 | if n_runtime_jobs <= 0 || n_items <= 0 { |
| 131 | return 0 |
| 132 | } |
| 133 | mut n_jobs := n_runtime_jobs |
| 134 | if n_jobs > max_flat_cgen_jobs { |
| 135 | n_jobs = max_flat_cgen_jobs |
| 136 | } |
| 137 | if n_jobs > n_items { |
| 138 | n_jobs = n_items |
| 139 | } |
| 140 | return n_jobs |
| 141 | } |
| 142 | |
| 143 | // split_flat_cgen_items supports split flat cgen items handling for c. |
| 144 | fn split_flat_cgen_items(items []FlatFnGenItem, n_jobs int) [][]FlatFnGenItem { |
| 145 | mut chunks := [][]FlatFnGenItem{} |
| 146 | if n_jobs <= 0 || items.len == 0 { |
| 147 | return chunks |
| 148 | } |
| 149 | mut total_cost := 0 |
| 150 | for item in items { |
| 151 | total_cost += item.cost |
| 152 | } |
| 153 | mut target_cost := total_cost / n_jobs |
| 154 | if total_cost % n_jobs != 0 { |
| 155 | target_cost++ |
| 156 | } |
| 157 | if target_cost < 1 { |
| 158 | target_cost = 1 |
| 159 | } |
| 160 | mut current := []FlatFnGenItem{} |
| 161 | mut current_cost := 0 |
| 162 | mut chunks_left := n_jobs |
| 163 | for idx, item in items { |
| 164 | remaining_items := items.len - idx |
| 165 | if current.len > 0 && current_cost >= target_cost && chunks_left > 1 |
| 166 | && remaining_items >= chunks_left { |
| 167 | chunks << current |
| 168 | current = []FlatFnGenItem{} |
| 169 | current_cost = 0 |
| 170 | chunks_left-- |
| 171 | } |
| 172 | current << item |
| 173 | current_cost += item.cost |
| 174 | } |
| 175 | if current.len > 0 { |
| 176 | chunks << current |
| 177 | } |
| 178 | return chunks |
| 179 | } |
| 180 | |
| 181 | // collect_fn_gen_items updates collect fn gen items state for c. |
| 182 | fn (mut g FlatGen) collect_fn_gen_items() []FlatFnGenItem { |
| 183 | mut items := []FlatFnGenItem{} |
| 184 | mut cur_module := '' |
| 185 | mut cur_file := '' |
| 186 | for i in 0 .. g.a.nodes.len { |
| 187 | node := g.a.nodes[i] |
| 188 | kind_id := node_kind_id(node) |
| 189 | if kind_id == 77 { |
| 190 | cur_file = node.value |
| 191 | g.tc.cur_file = cur_file |
| 192 | cur_module = '' |
| 193 | g.tc.cur_module = cur_module |
| 194 | continue |
| 195 | } |
| 196 | if kind_id == 73 { |
| 197 | cur_module = node.value |
| 198 | g.tc.cur_file = cur_file |
| 199 | g.tc.cur_module = cur_module |
| 200 | continue |
| 201 | } |
| 202 | |
| 203 | if kind_id != 61 { |
| 204 | continue |
| 205 | } |
| 206 | if !g.should_emit_fn_node_in_module(node, i, cur_module) { |
| 207 | continue |
| 208 | } |
| 209 | qfn := qualified_fn_name_in_module(cur_module, node.value) |
| 210 | if g.emitted_fn_contains(qfn) { |
| 211 | continue |
| 212 | } |
| 213 | g.emitted_fns[qfn] = true |
| 214 | items << FlatFnGenItem{ |
| 215 | node_id: flat.NodeId(i) |
| 216 | file: cur_file |
| 217 | module: cur_module |
| 218 | cost: node.children_count + 1 |
| 219 | } |
| 220 | } |
| 221 | return items |
| 222 | } |
| 223 | |
| 224 | // gen_fn_items emits fn items output for c. |
| 225 | fn (mut g FlatGen) gen_fn_items(items []FlatFnGenItem) { |
| 226 | for item in items { |
| 227 | if int(item.node_id) < 0 || int(item.node_id) >= g.a.nodes.len { |
| 228 | continue |
| 229 | } |
| 230 | g.tc.cur_file = item.file |
| 231 | g.tc.cur_module = item.module |
| 232 | node := g.a.nodes[int(item.node_id)] |
| 233 | g.gen_fn_in_module(node, item.module) |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | // prepare_parallel_items supports prepare parallel items handling for FlatGen. |
| 238 | fn (mut g FlatGen) prepare_parallel_items(items []FlatFnGenItem) { |
| 239 | for item in items { |
| 240 | g.tc.cur_file = item.file |
| 241 | g.tc.cur_module = item.module |
| 242 | g.prepare_parallel_node(item.node_id) |
| 243 | } |
| 244 | g.register_interface_strings() |
| 245 | } |
| 246 | |
| 247 | // prepare_parallel_node supports prepare parallel node handling for FlatGen. |
| 248 | fn (mut g FlatGen) prepare_parallel_node(id flat.NodeId) { |
| 249 | if int(id) < 0 || int(id) >= g.a.nodes.len { |
| 250 | return |
| 251 | } |
| 252 | node := g.a.nodes[int(id)] |
| 253 | if node.kind == .string_literal { |
| 254 | g.intern_string(node.value) |
| 255 | } |
| 256 | if g.should_preseed_parallel_type_text(node.typ) { |
| 257 | g.preseed_parallel_fn_ptr_type(g.tc.parse_type(node.typ)) |
| 258 | } |
| 259 | if expr_type := g.tc.expr_type(id) { |
| 260 | g.preseed_parallel_fn_ptr_type(expr_type) |
| 261 | } |
| 262 | for i in 0 .. node.children_count { |
| 263 | g.prepare_parallel_node(g.a.child(&node, i)) |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | // should_preseed_parallel_type_text reports whether should preseed parallel type text applies in c. |
| 268 | fn (g &FlatGen) should_preseed_parallel_type_text(typ string) bool { |
| 269 | if typ.len == 0 { |
| 270 | return false |
| 271 | } |
| 272 | clean := g.parallel_base_type_text(typ) |
| 273 | if clean.starts_with('fn(') || clean.starts_with('fn (') { |
| 274 | return true |
| 275 | } |
| 276 | if clean in g.tc.type_aliases { |
| 277 | return true |
| 278 | } |
| 279 | qtyp := g.tc.qualify_name(clean) |
| 280 | return qtyp in g.tc.type_aliases |
| 281 | } |
| 282 | |
| 283 | // parallel_base_type_text supports parallel base type text handling for FlatGen. |
| 284 | fn (g &FlatGen) parallel_base_type_text(typ string) string { |
| 285 | mut clean := typ.trim_space() |
| 286 | for clean.len > 0 { |
| 287 | if clean.starts_with('shared ') { |
| 288 | clean = clean[7..].trim_space() |
| 289 | } else if clean[0] == `&` || clean[0] == `?` || clean[0] == `!` { |
| 290 | clean = clean[1..].trim_space() |
| 291 | } else if clean.starts_with('...') { |
| 292 | clean = clean[3..].trim_space() |
| 293 | } else if clean.starts_with('[]') { |
| 294 | clean = clean[2..].trim_space() |
| 295 | } else { |
| 296 | break |
| 297 | } |
| 298 | } |
| 299 | return clean |
| 300 | } |
| 301 | |
| 302 | // preseed_parallel_fn_ptr_type supports preseed parallel fn ptr type handling for FlatGen. |
| 303 | fn (mut g FlatGen) preseed_parallel_fn_ptr_type(typ types.Type) { |
| 304 | if typ is types.FnType { |
| 305 | g.resolve_fn_ptr_type(g.fn_ptr_type_key(typ)) |
| 306 | for param in typ.params { |
| 307 | g.preseed_parallel_fn_ptr_type(param) |
| 308 | } |
| 309 | g.preseed_parallel_fn_ptr_type(typ.return_type) |
| 310 | } else if typ is types.Pointer { |
| 311 | g.preseed_parallel_fn_ptr_type(typ.base_type) |
| 312 | } else if typ is types.Array { |
| 313 | g.preseed_parallel_fn_ptr_type(typ.elem_type) |
| 314 | } else if typ is types.ArrayFixed { |
| 315 | g.preseed_parallel_fn_ptr_type(typ.elem_type) |
| 316 | } else if typ is types.Map { |
| 317 | g.preseed_parallel_fn_ptr_type(typ.key_type) |
| 318 | g.preseed_parallel_fn_ptr_type(typ.value_type) |
| 319 | } else if typ is types.OptionType { |
| 320 | g.preseed_parallel_fn_ptr_type(typ.base_type) |
| 321 | } else if typ is types.ResultType { |
| 322 | g.preseed_parallel_fn_ptr_type(typ.base_type) |
| 323 | } else if typ is types.Alias { |
| 324 | g.preseed_parallel_fn_ptr_type(typ.base_type) |
| 325 | } else if typ is types.MultiReturn { |
| 326 | for item in typ.types { |
| 327 | g.preseed_parallel_fn_ptr_type(item) |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | // fn_ptr_type_key supports fn ptr type key handling for FlatGen. |
| 333 | fn (mut g FlatGen) fn_ptr_type_key(typ types.FnType) string { |
| 334 | ret := if typ.return_type is types.Void { 'void' } else { g.tc.c_type(typ.return_type) } |
| 335 | if typ.params.len == 0 { |
| 336 | return 'fn_ptr:${ret}|void' |
| 337 | } |
| 338 | mut params := []string{} |
| 339 | for param in typ.params { |
| 340 | params << g.tc.c_type(param) |
| 341 | } |
| 342 | return 'fn_ptr:${ret}|${params.join(', ')}' |
| 343 | } |
| 344 | |
| 345 | // new_parallel_worker builds a per-worker FlatGen for parallel codegen. |
| 346 | // |
| 347 | // The lookup tables populated before gen_fns_dispatch (in collect_gen_info, |
| 348 | // collect_interface_impls and the precompute_* passes) are READ-ONLY during codegen, so |
| 349 | // they are SHARED by reference instead of cloned — V maps/arrays are reference types and |
| 350 | // concurrent readers are safe. Only the state a worker actually mutates while emitting is |
| 351 | // kept private: the output builder; the string-literal table (interned during gen); the |
| 352 | // fn_ptr_types / needed_optional_types / emitted_* sets and the param_types_cache / |
| 353 | // array_method_cache memoization caches (all written during gen); the per-function |
| 354 | // cur_param_* scratch; and runtime_inits (kept private out of caution). This drops the |
| 355 | // bulk of each worker's clone cost — previously the whole table set was duplicated per |
| 356 | // worker and, under -gc none, never freed. |
| 357 | fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen { |
| 358 | return &FlatGen{ |
| 359 | sb: strings.new_builder(64_000) |
| 360 | a: unsafe { g.a } |
| 361 | used_fns: g.used_fns |
| 362 | used_fn_names: g.used_fn_names |
| 363 | test_files: g.test_files.clone() |
| 364 | str_lits: g.str_lits.clone() |
| 365 | str_lit_ids: g.str_lit_ids.clone() |
| 366 | global_types: g.global_types |
| 367 | enum_vals: g.enum_vals |
| 368 | interfaces: g.interfaces |
| 369 | const_vals: g.const_vals |
| 370 | const_modules: g.const_modules |
| 371 | const_init_order: g.const_init_order |
| 372 | global_modules: g.global_modules |
| 373 | global_inits: g.global_inits |
| 374 | global_init_order: g.global_init_order |
| 375 | iface_impls: g.iface_impls |
| 376 | iface_type_ids: g.iface_type_ids |
| 377 | module_init_fns: g.module_init_fns |
| 378 | module_init_fn_modules: g.module_init_fn_modules |
| 379 | module_imports: g.module_imports |
| 380 | libc_compat_fns: g.libc_compat_fns.clone() |
| 381 | tc: g.clone_parallel_type_checker() |
| 382 | has_builtins: g.has_builtins |
| 383 | tmp_count: (worker_id + 1) * 100_000 |
| 384 | line_start: true |
| 385 | modules: g.modules |
| 386 | fn_ptr_types: g.fn_ptr_types.clone() |
| 387 | fixed_array_ret_wrappers: g.fixed_array_ret_wrappers |
| 388 | fn_decl_param_types: g.fn_decl_param_types |
| 389 | fn_decl_ret_types: g.fn_decl_ret_types |
| 390 | struct_decl_infos: g.struct_decl_infos |
| 391 | struct_decl_short_infos: g.struct_decl_short_infos |
| 392 | runtime_inits: g.runtime_inits.clone() |
| 393 | compiler_vroot: g.compiler_vroot |
| 394 | cur_param_names: g.cur_param_names.clone() |
| 395 | cur_param_type_values: g.cur_param_type_values.clone() |
| 396 | cur_param_types: g.cur_param_types.clone() |
| 397 | cur_fn_ret: g.cur_fn_ret |
| 398 | cur_fn_ret_is_optional: g.cur_fn_ret_is_optional |
| 399 | cur_fn_ret_base: g.cur_fn_ret_base |
| 400 | expected_expr_type: g.expected_expr_type |
| 401 | expected_enum: g.expected_enum |
| 402 | needed_optional_types: g.needed_optional_types.clone() |
| 403 | emitted_optional_types: g.emitted_optional_types.clone() |
| 404 | emitted_fns: g.emitted_fns.clone() |
| 405 | array_method_cache: g.array_method_cache.clone() |
| 406 | param_types_cache: g.param_types_cache.clone() |
| 407 | embedded_fields_by_type: g.embedded_fields_by_type |
| 408 | param_types_by_short: g.param_types_by_short |
| 409 | spawn_wrapper_names: g.spawn_wrapper_names.clone() |
| 410 | spawn_wrapper_defs: g.spawn_wrapper_defs.clone() |
| 411 | callback_wrapper_names: g.callback_wrapper_names.clone() |
| 412 | callback_wrapper_defs: g.callback_wrapper_defs.clone() |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // clone_parallel_type_checker builds a per-worker TypeChecker for parallel codegen. |
| 417 | // |
| 418 | // During codegen the checker's lookup tables are READ-ONLY: cgen only ever assigns the |
| 419 | // scalar `cur_file`/`cur_module` fields, and the read paths it uses (expr_type, c_type, |
| 420 | // parse_type, resolve_type, cached_resolved_call) never write into the big maps — the only |
| 421 | // memoizing write is into `type_cache`, which is left nil here so workers take the uncached |
| 422 | // path. V maps and arrays are reference types, so the read-only tables are SHARED by |
| 423 | // reference (no `.clone()`), exactly like the already-shared `a` FlatAst. This avoids |
| 424 | // deep-copying the program-wide `expr_type_*`/`structs`/signature tables once per worker, |
| 425 | // which was the bulk of parallel cgen's extra RAM and serial setup time. |
| 426 | // |
| 427 | // Only genuinely per-worker mutable state is given its own copy: the scope chain (gen pushes |
| 428 | // child scopes) and `errors` (avoid a concurrent append race, though gen does not emit any). |
| 429 | fn (g &FlatGen) clone_parallel_type_checker() &types.TypeChecker { |
| 430 | mut fs := types.new_scope(unsafe { nil }) |
| 431 | fs.names = g.tc.file_scope.names.clone() |
| 432 | fs.types = g.tc.file_scope.types.clone() |
| 433 | return &types.TypeChecker{ |
| 434 | a: unsafe { g.tc.a } |
| 435 | fn_ret_types: g.tc.fn_ret_types |
| 436 | fn_param_types: g.tc.fn_param_types |
| 437 | fn_ret_type_texts: g.tc.fn_ret_type_texts |
| 438 | fn_param_type_texts: g.tc.fn_param_type_texts |
| 439 | fn_type_files: g.tc.fn_type_files |
| 440 | fn_type_modules: g.tc.fn_type_modules |
| 441 | fn_variadic: g.tc.fn_variadic |
| 442 | fn_implicit_veb_ctx: g.tc.fn_implicit_veb_ctx |
| 443 | c_variadic_fns: g.tc.c_variadic_fns |
| 444 | structs: g.tc.structs |
| 445 | struct_generic_params: g.tc.struct_generic_params |
| 446 | struct_field_c_abi_fns: g.tc.struct_field_c_abi_fns |
| 447 | unions: g.tc.unions |
| 448 | type_aliases: g.tc.type_aliases |
| 449 | type_alias_c_abi_fns: g.tc.type_alias_c_abi_fns |
| 450 | sum_types: g.tc.sum_types |
| 451 | enum_names: g.tc.enum_names |
| 452 | enum_fields: g.tc.enum_fields |
| 453 | flag_enums: g.tc.flag_enums |
| 454 | interface_names: g.tc.interface_names |
| 455 | interface_fields: g.tc.interface_fields |
| 456 | interface_embeds: g.tc.interface_embeds |
| 457 | interface_abstract_methods: g.tc.interface_abstract_methods |
| 458 | c_globals: g.tc.c_globals |
| 459 | const_types: g.tc.const_types |
| 460 | const_exprs: g.tc.const_exprs |
| 461 | const_modules: g.tc.const_modules |
| 462 | const_suffixes: g.tc.const_suffixes |
| 463 | imports: g.tc.imports |
| 464 | file_imports: g.tc.file_imports |
| 465 | file_selective_imports: g.tc.file_selective_imports |
| 466 | file_modules: g.tc.file_modules |
| 467 | file_scope: fs |
| 468 | cur_scope: fs |
| 469 | scope_pool: []&types.Scope{} |
| 470 | has_builtins: g.tc.has_builtins |
| 471 | cur_module: g.tc.cur_module |
| 472 | cur_file: g.tc.cur_file |
| 473 | errors: g.tc.errors.clone() |
| 474 | resolved_call_names: g.tc.resolved_call_names |
| 475 | resolved_call_set: g.tc.resolved_call_set |
| 476 | resolved_fn_value_names: g.tc.resolved_fn_value_names |
| 477 | resolved_fn_value_set: g.tc.resolved_fn_value_set |
| 478 | expr_type_values: g.tc.expr_type_values |
| 479 | expr_type_set: g.tc.expr_type_set |
| 480 | checking_nodes: g.tc.checking_nodes |
| 481 | diagnose_unknown_calls: g.tc.diagnose_unknown_calls |
| 482 | reject_unlowered_map_mutation: g.tc.reject_unlowered_map_mutation |
| 483 | diagnostic_files: g.tc.diagnostic_files |
| 484 | cur_fn_ret_type: g.tc.cur_fn_ret_type |
| 485 | smartcasts: g.tc.smartcasts |
| 486 | // Read-only map cgen uses to recover substituted signatures for generic-receiver |
| 487 | // method values (`Box[int].method` as a callback); without it a parallel worker |
| 488 | // sees an empty map and gen_method_value_closure falls through. |
| 489 | generic_method_value_info: g.tc.generic_method_value_info |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | // merge_parallel_worker supports merge parallel worker handling for FlatGen. |
| 494 | fn (mut g FlatGen) merge_parallel_worker(w &FlatGen) { |
| 495 | mut ww := unsafe { w } |
| 496 | worker_output := ww.sb.str() |
| 497 | if worker_output.len > 0 { |
| 498 | g.sb.write_string(worker_output) |
| 499 | } |
| 500 | // The master builder has copied the worker output; release the worker buffers. |
| 501 | unsafe { |
| 502 | worker_output.free() |
| 503 | ww.sb.free() |
| 504 | } |
| 505 | for opt_name, val_type in w.needed_optional_types { |
| 506 | g.needed_optional_types[opt_name] = val_type |
| 507 | } |
| 508 | for encoded, name in w.fn_ptr_types { |
| 509 | if encoded !in g.fn_ptr_types { |
| 510 | g.fn_ptr_types[encoded] = name |
| 511 | } |
| 512 | } |
| 513 | for name, enabled in w.libc_compat_fns { |
| 514 | if enabled { |
| 515 | g.libc_compat_fns[name] = true |
| 516 | } |
| 517 | } |
| 518 | // Spawn wrappers (thread arg structs + trampoline fns) are generated on demand |
| 519 | // inside fn bodies, so a worker that emits a `spawn` produces wrapper defs the |
| 520 | // master must also emit. Deduplicate by their deterministic key/def. |
| 521 | for key, name in w.spawn_wrapper_names { |
| 522 | if key !in g.spawn_wrapper_names { |
| 523 | g.spawn_wrapper_names[key] = name |
| 524 | } |
| 525 | } |
| 526 | for def in w.spawn_wrapper_defs { |
| 527 | if def !in g.spawn_wrapper_defs { |
| 528 | g.spawn_wrapper_defs << def |
| 529 | } |
| 530 | } |
| 531 | for key, name in w.callback_wrapper_names { |
| 532 | if key !in g.callback_wrapper_names { |
| 533 | g.callback_wrapper_names[key] = name |
| 534 | } |
| 535 | } |
| 536 | for def in w.callback_wrapper_defs { |
| 537 | if def !in g.callback_wrapper_defs { |
| 538 | g.callback_wrapper_defs << def |
| 539 | } |
| 540 | } |
| 541 | } |
| 542 | |