d2r2: do unique type info gathering on a sub-unit granularity, so we can subdivide a unit across all lanes; sort unique type tag slots for determinism

This commit is contained in:
Ryan Fleury
2026-05-01 14:12:35 -07:00
parent 0b5e827f6c
commit f9eb25dbcb
3 changed files with 421 additions and 302 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ commands =
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg meta telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg meta telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
.f1 = { .win = "build radbin release telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .f1 = { .win = "build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [raddbg wsl] //- rjf: [raddbg wsl]
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
+143 -42
View File
@@ -1,6 +1,16 @@
// Copyright (c) Epic Games Tools // Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Helpers
internal int
d2r2_unique_type_tag_node_is_less_than(D2R2_UniqueTypeTagNode **l, D2R2_UniqueTypeTagNode **r)
{
int is_less_than = l[0]->hash < r[0]->hash;
return is_less_than;
}
//////////////////////////////// ////////////////////////////////
//~ rjf: Main Conversion Entry Point (New) //~ rjf: Main Conversion Entry Point (New)
@@ -1429,15 +1439,6 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
//////////////////////////// ////////////////////////////
//- rjf: gather all unique type tags across all units //- rjf: gather all unique type tags across all units
// //
typedef struct UniqueTypeTagNode UniqueTypeTagNode;
struct UniqueTypeTagNode
{
UniqueTypeTagNode *next;
U64 hash;
U64 unit_idx;
U64 info_off;
U64 order_idx;
};
typedef struct UnitTypeNode UnitTypeNode; typedef struct UnitTypeNode UnitTypeNode;
struct UnitTypeNode struct UnitTypeNode
{ {
@@ -1451,39 +1452,59 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
UnitTypeNode **slots; UnitTypeNode **slots;
U64 slots_count; U64 slots_count;
}; };
UniqueTypeTagNode **unique_type_tag_slots = 0; D2R2_UniqueTypeTagNode **unique_type_tag_slots = 0;
U64 unique_type_tag_slots_count = total_tag_count_estimate/8 + 1; U64 unique_type_tag_slots_count = total_tag_count_estimate/8 + 1;
UnitTypeMap *unit_type_maps = 0; UnitTypeMap *unit_type_maps = 0;
ProfScope("gather all unique type tags across all units") ProfScope("gather all unique type tags across all units")
{ {
//- rjf: set up tables
if(lane_idx() == 0) if(lane_idx() == 0)
{ {
unique_type_tag_slots = push_array(scratch.arena, UniqueTypeTagNode *, unique_type_tag_slots_count); unique_type_tag_slots = push_array(scratch.arena, D2R2_UniqueTypeTagNode *, unique_type_tag_slots_count);
unit_type_maps = push_array(scratch.arena, UnitTypeMap, unit_count); unit_type_maps = push_array(scratch.arena, UnitTypeMap, unit_count);
} }
lane_sync_u64(&unique_type_tag_slots, 0); lane_sync_u64(&unique_type_tag_slots, 0);
lane_sync_u64(&unit_type_maps, 0); lane_sync_u64(&unit_type_maps, 0);
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_; //- rjf: set up per-unit type maps
lane_sync_u64(&unit_take_idx_ptr, 0); {
Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range)
{
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
unit_type_maps[unit_idx].slots_count = dim_1u64(unit_info_tag_range) / 256 + 1;
unit_type_maps[unit_idx].slots = push_array(scratch.arena, UnitTypeNode *, unit_type_maps[unit_idx].slots_count);
}
lane_sync();
}
//- rjf: gather unique types
{
U64 work_take_idx_ = 0;
U64 *work_take_idx_ptr = &work_take_idx_;
lane_sync_u64(&work_take_idx_ptr, 0);
for(;;) for(;;)
{ {
//- rjf: take next unit //- rjf: take next work
U64 origin_unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1;
if(origin_unit_idx >= unit_count) if(work_idx >= sub_unit_works_count)
{ {
break; break;
} }
//- rjf: unpack work
U64 origin_unit_idx = sub_unit_works[work_idx].unit_idx;
Rng1U64 origin_unit_root_tag_idx_range = sub_unit_works[work_idx].root_tag_idx_range;
//- rjf: unpack unit info //- rjf: unpack unit info
Rng1U64 origin_unit_info_tag_range = unit_info_tag_ranges[origin_unit_idx]; Rng1U64 origin_unit_info_tag_range = unit_info_tag_ranges[origin_unit_idx];
//- rjf: set up type map for this unit
unit_type_maps[origin_unit_idx].slots_count = dim_1u64(origin_unit_info_tag_range) / 256 + 1;
unit_type_maps[origin_unit_idx].slots = push_array(scratch.arena, UnitTypeNode *, unit_type_maps[origin_unit_idx].slots_count);
//- rjf: hash all type content from tags in this unit; record if unique //- rjf: hash all type content from tags in this unit; record if unique
for(U64 off = origin_unit_info_tag_range.min; off < origin_unit_info_tag_range.max;) for(U64 root_tag_idx = origin_unit_root_tag_idx_range.min; root_tag_idx < origin_unit_root_tag_idx_range.max; root_tag_idx += 1)
{
S64 origin_unit_depth = 1;
for(U64 off = unit_info_root_tag_offs[origin_unit_idx].v[root_tag_idx];
off < origin_unit_info_tag_range.max && (origin_unit_depth > 1 || off == unit_info_root_tag_offs[origin_unit_idx].v[root_tag_idx]);)
{ {
Temp scratch2 = scratch_begin(&scratch.arena, 1); Temp scratch2 = scratch_begin(&scratch.arena, 1);
U64 start_off = off; U64 start_off = off;
@@ -1499,6 +1520,8 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
// order index, instead of doing a full recursion. // order index, instead of doing a full recursion.
// //
B32 is_type_tag_tree = 0; B32 is_type_tag_tree = 0;
B32 tag_has_children = 0;
B32 tag_ends_children = 0;
U64 hash = 0; U64 hash = 0;
{ {
typedef struct TypeTagTask TypeTagTask; typedef struct TypeTagTask TypeTagTask;
@@ -1575,6 +1598,8 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
tag.kind == DW_TagKind_DynamicType || tag.kind == DW_TagKind_DynamicType ||
tag.kind == DW_TagKind_AtomicType || tag.kind == DW_TagKind_AtomicType ||
tag.kind == DW_TagKind_ImmutableType); tag.kind == DW_TagKind_ImmutableType);
tag_has_children = tag.has_children;
tag_ends_children = (tag.kind == DW_TagKind_Null);
} }
// rjf: is type -> combine tag's content into hash // rjf: is type -> combine tag's content into hash
@@ -1698,7 +1723,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
} }
} }
// rjf: is this the starter task? -> advance base reading offset // rjf: is this the starter task? -> advance base reading offset, apply depth changes to origin unit scan loop
if(t == &start_task) if(t == &start_task)
{ {
off = t_off; off = t_off;
@@ -1723,7 +1748,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
U64 slot_head_val = (U64)ins_atomic_u64_eval(&unique_type_tag_slots[slot_idx]); U64 slot_head_val = (U64)ins_atomic_u64_eval(&unique_type_tag_slots[slot_idx]);
// rjf: determine if this hash has been gathered // rjf: determine if this hash has been gathered
for(UniqueTypeTagNode *n = (UniqueTypeTagNode *)slot_head_val; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = (D2R2_UniqueTypeTagNode *)slot_head_val; n != 0; n = n->next)
{ {
if(n->hash == hash) if(n->hash == hash)
{ {
@@ -1743,8 +1768,8 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
if(!gathered) if(!gathered)
{ {
Temp insert_temp = temp_begin(scratch.arena); Temp insert_temp = temp_begin(scratch.arena);
UniqueTypeTagNode *n = push_array(scratch.arena, UniqueTypeTagNode, 1); D2R2_UniqueTypeTagNode *n = push_array(scratch.arena, D2R2_UniqueTypeTagNode, 1);
n->next = (UniqueTypeTagNode *)slot_head_val; n->next = (D2R2_UniqueTypeTagNode *)slot_head_val;
n->hash = hash; n->hash = hash;
n->unit_idx = origin_unit_idx; n->unit_idx = origin_unit_idx;
n->info_off = start_off; n->info_off = start_off;
@@ -1761,9 +1786,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
} }
} }
//- rjf: record this (info_off -> hash) mapping, so that when we have //- rjf: atomically record this (info_off -> hash) mapping, so that when we have
// later references to this type, we can redirect to the deduplicated // later references to this type, we can redirect to the deduplicated type with
// type with the right hash later. // the right hash later.
if(is_type_tag_tree) if(is_type_tag_tree)
{ {
U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off)); U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off));
@@ -1771,12 +1796,86 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
UnitTypeNode *n = push_array(scratch.arena, UnitTypeNode, 1); UnitTypeNode *n = push_array(scratch.arena, UnitTypeNode, 1);
n->src_info_off = start_off; n->src_info_off = start_off;
n->dst_hash = hash; n->dst_hash = hash;
SLLStackPush(unit_type_maps[origin_unit_idx].slots[info_off_slot_idx], n); for(B32 inserted = 0; !inserted;)
{
U64 slot_head_val = ins_atomic_u64_eval(&unit_type_maps[origin_unit_idx].slots[info_off_slot_idx]);
n->next = (UnitTypeNode *)slot_head_val;
if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unit_type_maps[origin_unit_idx].slots[info_off_slot_idx], (U64)n, slot_head_val))
{
inserted = 1;
}
}
}
//- rjf: navigate tree in origin unit - only if we are not iterating
// a type tree. the type tree will have already been processed, because
// we needed to hash all of the content. but for non-type tag trees,
// we need to descend and parse their contents, since they may contain
// types themselves.
//
if(!is_type_tag_tree)
{
if(tag_has_children)
{
origin_unit_depth += 1;
}
if(tag_ends_children)
{
origin_unit_depth -= 1;
}
} }
scratch_end(scratch2); scratch_end(scratch2);
} }
} }
}
}
lane_sync();
}
////////////////////////////
//- rjf: sort all unique type table slots
//
ProfScope("sort all unique type table slots")
{
Rng1U64 range = lane_range(unique_type_tag_slots_count);
for EachInRange(slot_idx, range)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
D2R2_UniqueTypeTagNode **slot = &unique_type_tag_slots[slot_idx];
// rjf: count nodes in this slot
U64 node_count = 0;
for EachNode(n, D2R2_UniqueTypeTagNode, slot[0])
{
node_count += 1;
}
// rjf: flatten
D2R2_UniqueTypeTagNode **slot_nodes_array = push_array(scratch2.arena, D2R2_UniqueTypeTagNode *, node_count);
{
U64 idx = 0;
for EachNode(n, D2R2_UniqueTypeTagNode, slot[0])
{
slot_nodes_array[idx] = n;
idx += 1;
}
}
// rjf: sort
radsort(slot_nodes_array, node_count, d2r2_unique_type_tag_node_is_less_than);
// rjf: re-order in the slot
{
unique_type_tag_slots[slot_idx] = 0;
for EachIndex(idx, node_count)
{
SLLStackPush(unique_type_tag_slots[slot_idx], slot_nodes_array[idx]);
}
}
scratch_end(scratch2);
}
lane_sync(); lane_sync();
} }
@@ -1784,21 +1883,21 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
//- rjf: produce [0...n) <-> unique-tag-node mapping for all types //- rjf: produce [0...n) <-> unique-tag-node mapping for all types
// //
U64 type_count = 0; U64 type_count = 0;
UniqueTypeTagNode **type_tag_nodes = 0; D2R2_UniqueTypeTagNode **type_tag_nodes = 0;
ProfScope("produce [0...n) -> hash mapping for all types") if(lane_idx() == 0) ProfScope("produce [0...n) -> hash mapping for all types") if(lane_idx() == 0)
{ {
for EachIndex(slot_idx, unique_type_tag_slots_count) for EachIndex(slot_idx, unique_type_tag_slots_count)
{ {
for EachNode(n, UniqueTypeTagNode, unique_type_tag_slots[slot_idx]) for EachNode(n, D2R2_UniqueTypeTagNode, unique_type_tag_slots[slot_idx])
{ {
type_count += 1; type_count += 1;
} }
} }
type_tag_nodes = push_array(scratch.arena, UniqueTypeTagNode *, type_count); type_tag_nodes = push_array(scratch.arena, D2R2_UniqueTypeTagNode *, type_count);
U64 idx = 0; U64 idx = 0;
for EachIndex(slot_idx, unique_type_tag_slots_count) for EachIndex(slot_idx, unique_type_tag_slots_count)
{ {
for EachNode(n, UniqueTypeTagNode, unique_type_tag_slots[slot_idx]) for EachNode(n, D2R2_UniqueTypeTagNode, unique_type_tag_slots[slot_idx])
{ {
type_tag_nodes[idx] = n; type_tag_nodes[idx] = n;
n->order_idx = idx; n->order_idx = idx;
@@ -1853,10 +1952,10 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
U64 hash = t->hash; U64 hash = t->hash;
// rjf: hash -> unique type tag node // rjf: hash -> unique type tag node
UniqueTypeTagNode *type_tag_node = 0; D2R2_UniqueTypeTagNode *type_tag_node = 0;
{ {
U64 slot_idx = hash%unique_type_tag_slots_count; U64 slot_idx = hash%unique_type_tag_slots_count;
for(UniqueTypeTagNode *n = unique_type_tag_slots[slot_idx]; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = unique_type_tag_slots[slot_idx]; n != 0; n = n->next)
{ {
if(n->hash == hash) if(n->hash == hash)
{ {
@@ -2073,6 +2172,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
for(;max_chain_count < max_U64;) for(;max_chain_count < max_U64;)
{ {
Temp scratch2 = scratch_begin(&scratch.arena, 1); Temp scratch2 = scratch_begin(&scratch.arena, 1);
ProfBegin("types build (chain count <= %I64u)", max_chain_count);
//- rjf: gather all types in this lane that fit the dependency restriction; //- rjf: gather all types in this lane that fit the dependency restriction;
// find this lane's next dependency restriction // find this lane's next dependency restriction
@@ -2100,7 +2200,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
Temp temp = temp_begin(scratch2.arena); Temp temp = temp_begin(scratch2.arena);
// rjf: idx -> type tag node // rjf: idx -> type tag node
UniqueTypeTagNode *type_tag_node = type_tag_nodes[type_idx]; D2R2_UniqueTypeTagNode *type_tag_node = type_tag_nodes[type_idx];
U64 unit_idx = type_tag_node->unit_idx; U64 unit_idx = type_tag_node->unit_idx;
U64 info_off = type_tag_node->info_off; U64 info_off = type_tag_node->info_off;
@@ -2171,7 +2271,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
} }
} }
U64 unique_type_tag_slot_idx = direct_type_hash%unique_type_tag_slots_count; U64 unique_type_tag_slot_idx = direct_type_hash%unique_type_tag_slots_count;
for(UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{ {
if(n->hash == direct_type_hash) if(n->hash == direct_type_hash)
{ {
@@ -2313,7 +2413,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
RDIM_Type *param_type = 0; RDIM_Type *param_type = 0;
{ {
U64 unique_type_tag_slot_idx = param_type_hash%unique_type_tag_slots_count; U64 unique_type_tag_slot_idx = param_type_hash%unique_type_tag_slots_count;
for(UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{ {
if(n->hash == param_type_hash) if(n->hash == param_type_hash)
{ {
@@ -2587,6 +2687,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
//- rjf: update max chain count //- rjf: update max chain count
max_chain_count = next_max_chain_count; max_chain_count = next_max_chain_count;
ProfEnd();
scratch_end(scratch2); scratch_end(scratch2);
} }
} }
@@ -2616,7 +2717,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
udt->self_type = type; udt->self_type = type;
// rjf: idx -> type tag node // rjf: idx -> type tag node
UniqueTypeTagNode *type_tag_node = type_tag_nodes[type_idx]; D2R2_UniqueTypeTagNode *type_tag_node = type_tag_nodes[type_idx];
U64 unit_idx = type_tag_node->unit_idx; U64 unit_idx = type_tag_node->unit_idx;
U64 info_off = type_tag_node->info_off; U64 info_off = type_tag_node->info_off;
@@ -2698,7 +2799,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
// rjf: hash -> type // rjf: hash -> type
{ {
U64 unique_type_tag_slot_idx = type_hash%unique_type_tag_slots_count; U64 unique_type_tag_slot_idx = type_hash%unique_type_tag_slots_count;
for(UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{ {
if(n->hash == type_hash) if(n->hash == type_hash)
{ {
@@ -3112,7 +3213,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
// rjf: hash -> type // rjf: hash -> type
{ {
U64 unique_type_tag_slot_idx = type_hash%unique_type_tag_slots_count; U64 unique_type_tag_slot_idx = type_hash%unique_type_tag_slots_count;
for(UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) for(D2R2_UniqueTypeTagNode *n = unique_type_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{ {
if(n->hash == type_hash) if(n->hash == type_hash)
{ {
+18
View File
@@ -4,6 +4,19 @@
#ifndef RDI_FROM_DWARF_2_H #ifndef RDI_FROM_DWARF_2_H
#define RDI_FROM_DWARF_2_H #define RDI_FROM_DWARF_2_H
////////////////////////////////
//~ rjf: Helper Types
typedef struct D2R2_UniqueTypeTagNode D2R2_UniqueTypeTagNode;
struct D2R2_UniqueTypeTagNode
{
D2R2_UniqueTypeTagNode *next;
U64 hash;
U64 unit_idx;
U64 info_off;
U64 order_idx;
};
//////////////////////////////// ////////////////////////////////
//~ rjf: Conversion Stage Inputs (New) //~ rjf: Conversion Stage Inputs (New)
@@ -21,6 +34,11 @@ struct D2R2_ConvertParams
B32 deterministic; B32 deterministic;
}; };
////////////////////////////////
//~ rjf: Helpers
internal int d2r2_unique_type_tag_node_is_less_than(D2R2_UniqueTypeTagNode **l, D2R2_UniqueTypeTagNode **r);
//////////////////////////////// ////////////////////////////////
//~ rjf: Main Conversion Entry Point (New) //~ rjf: Main Conversion Entry Point (New)