diff --git a/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.c b/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.c new file mode 100644 index 00000000..542f8c20 --- /dev/null +++ b/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.c @@ -0,0 +1,3139 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// + +static D2R_Shared g_d2r_shared; + +//////////////////////////////// + +internal B32 +rdim_is_eval_bytecode_static(RDIM_EvalBytecode bc) +{ + B32 is_static = 1; + RDI_EvalOp dynamic_ops[] = { RDI_EvalOp_MemRead, RDI_EvalOp_RegRead, RDI_EvalOp_RegReadDyn, RDI_EvalOp_CFA }; + for EachNode(n, RDIM_EvalBytecodeOp, bc.first_op) { + for EachIndex(i, ArrayCount(dynamic_ops)) { + if (dynamic_ops[i] == n->op) { + is_static = 0; + goto exit; + } + } + } + exit:; + return is_static; +} + +internal B32 +rdim_static_eval_bytecode_to_voff(RDIM_EvalBytecode bc, U64 image_base, U64 *voff_out) +{ + Temp scratch = scratch_begin(0,0); + B32 is_ok = 0; + + typedef union { U16 u16; U32 u32; U64 u64; S64 s64; F32 f32; F64 f64; } Value; + U64 stack_cap = 128, stack_count = 0; + Value *stack = push_array(scratch.arena, Value, stack_cap); + + for EachNode(opcode_n, RDIM_EvalBytecodeOp, bc.first_op) { + // pop values from stack + Value *svals = 0; + { + U32 pop_count = RDI_POPN_FROM_CTRLBITS(rdi_eval_op_ctrlbits_table[opcode_n->op]); + if (pop_count > stack_count) { + goto exit; + } + stack_count -= pop_count; + svals = stack + stack_count; + } + + Value imm = { .u64 = opcode_n->p }; + Value nval = {0}; + switch (opcode_n->op) { + case RDI_EvalOp_Stop: { opcode_n = bc.last_op; } break; + case RDI_EvalOp_Noop: {} break; + case RDI_EvalOp_Cond: { NotImplemented; } break; + case RDI_EvalOp_Skip: { + NotImplemented; + } break; + case RDI_EvalOp_MemRead: { InvalidPath; } break; + case RDI_EvalOp_RegRead: { NotImplemented; } break; + case RDI_EvalOp_RegReadDyn: { NotImplemented; } break; + case RDI_EvalOp_FrameOff: { NotImplemented; } break; + case RDI_EvalOp_ModuleOff: { + nval.u64 = image_base + imm.u64; + } break; + case RDI_EvalOp_TLSOff: { + nval.u64 = image_base; + } break; + case RDI_EvalOp_ConstU8: + case RDI_EvalOp_ConstU16: + case RDI_EvalOp_ConstU32: + case RDI_EvalOp_ConstU64: + case RDI_EvalOp_ConstU128: { + nval = imm; + } break; + case RDI_EvalOp_ConstString: { NotImplemented; } break; + case RDI_EvalOp_Abs: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: + case RDI_EvalTypeGroup_S: { nval.s64 = abs_s64(svals[0].s64); } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = abs_f32(svals[0].f32); } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = abs_f64(svals[0].f64); } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Neg: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: + case RDI_EvalTypeGroup_S: { nval.u64 = ~svals[0].u64 + 1; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = -svals[0].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = -svals[0].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Add: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 + svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 + svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 + svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 + svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Sub: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 - svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[1].s64 - svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 - svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 - svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Mul: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 * svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 * svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 * svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 * svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Div: { + B32 is_div_by_zero = 0; + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { is_div_by_zero = svals[1].u64 == 0; } break; + case RDI_EvalTypeGroup_S: { is_div_by_zero = svals[1].s64 == 0; } break; + case RDI_EvalTypeGroup_F32: { is_div_by_zero = svals[1].f32 == 0.0f; } break; + case RDI_EvalTypeGroup_F64: { is_div_by_zero = svals[1].f64 == 0.0; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + + if (is_div_by_zero) { + Assert(!"trying to div by zero"); + goto exit; + } + + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 / svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 / svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 / svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 / svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Mod: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 % svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 % svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 MOD is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 MOD is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_LShift: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 << svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 << svals[1].u64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 LShift is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 LShift is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_RShift: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0 ] .u64 >> svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[1 ] .s64 >> svals[1].u64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 RShift is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 RShift is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_BitAnd: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 | svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 | svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 bitwise AND is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 bitwise AND is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_BitXor: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 ^ svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 ^ svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 XOR is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 XOR is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_BitNot: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = ~svals[0].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = ~svals[0].u64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 bitwise NOT is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 bitwise NOT is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_LogAnd: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 && svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 && svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 AND is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 AND is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_LogOr: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 || svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 || svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 OR is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 OR is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_LogNot: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = !svals[0].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = !svals[0].u64; } break; + case RDI_EvalTypeGroup_F32: { Assert(!"F32 NOT is not supported"); } goto exit; + case RDI_EvalTypeGroup_F64: { Assert(!"F64 NOT is not supported"); } goto exit; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_EqEq: { + nval.u64 = !!MemoryMatch(&svals[0], &svals[1], sizeof(*svals)); + } break; + case RDI_EvalOp_NtEq: { + nval.u64 = !MemoryMatch(&svals[0], &svals[1], sizeof(*svals)); + } break; + case RDI_EvalOp_LsEq: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 <= svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 <= svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 <= svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 <= svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_GrEq: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 >= svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 >= svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 >= svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 >= svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Less: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 < svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 < svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 < svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 < svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Grtr: { + switch (imm.u64) { + case RDI_EvalTypeGroup_Other: {} break; + case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 > svals[1].u64; } break; + case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 > svals[1].s64; } break; + case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 > svals[1].f32; } break; + case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 > svals[1].f64; } break; + default: { Assert(!"unexpected eval type group"); } goto exit; + } + } break; + case RDI_EvalOp_Trunc: { + if (0 < imm.u64 && imm.u64 < 64) { + U64 mask = max_U64 >> (64 - imm.u64); + nval.u64 = svals[0].u64 & (max_U64 >> (64 - imm.u64)); + } else if (imm.u64 > 64) { + Assert(!"malformed bytecode"); + goto exit; + } + } break; + case RDI_EvalOp_TruncSigned: { + if (0 < imm.u64 && imm.u64 < 64) { + U64 mask = max_U64 >> (64 - imm.u64); + nval.u64 = svals[0].u64 & (max_U64 >> (64 - imm.u64)); + U64 high = 0; + if (svals[0].u64 & (1 << (imm.u64 - 1))) { + high = ~mask; + } + nval.u64 = high | (svals[0].u64 & mask); + } else if (imm.u64 > 64) { + Assert(!"malformed bytecode"); + goto exit; + } + } break; + case RDI_EvalOp_Convert: { + U32 in = imm.u64 & 0xff; + U32 out = (imm.u64 >> 8) & 0xff; + if (in != out) { + switch (in + out*RDI_EvalTypeGroup_COUNT) { + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: { nval.u64 = (U64)svals[0].f32; } break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: { nval.u64 = (U64)svals[0].f64; } break; + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: { nval.s64 = (S64)svals[0].f32; } break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: { nval.s64 = (S64)svals[0].f64; } break; + case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].u64; } break; + case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].s64; } break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].f64; } break; + case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].u64; } break; + case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].s64; } break; + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].f32; } break; + default: { Assert(!"unexpected conversion case"); } goto exit; + } + } + } break; + case RDI_EvalOp_Pick: { + if (stack_count > imm.u64) { + nval = stack[stack_count - imm.u64 - 1]; + } else { + Assert(!"malformed bytecode"); + goto exit; + } + } break; + case RDI_EvalOp_Pop: {} break; + case RDI_EvalOp_Insert: { + if (stack_count > imm.u64) { + Value tval = stack[stack_count-1]; + Value *dst = stack + stack_count - 1 - imm.u64; + Value *shift = dst + 1; + MemoryCopy(shift, dst, imm.u64 * sizeof(Value)); + *dst = tval; + } else { + Assert(!"malformed bytecode"); + goto exit; + } + } break; + case RDI_EvalOp_ValueRead: { + U64 bytes_to_read = imm.u64; + U64 offset = svals[0].u64; + if (offset + bytes_to_read <= sizeof(Value)) { + Value src_val = svals[1]; + MemoryCopy(&nval, (U8 *)&src_val + offset, bytes_to_read); + } + } break; + case RDI_EvalOp_ByteSwap: { + switch (imm.u64) { + case 0: {} break; + case 1: {} break; + case 2: { nval.u16 = bswap_u16(svals[0].u16); } break; + case 4: { nval.u32 = bswap_u16(svals[0].u32); } break; + case 8: { nval.u64 = bswap_u16(svals[0].u64); } break; + default: { Assert(!"malformed bytecode"); } goto exit; + } + } break; + case RDI_EvalOp_Swap: { + NotImplemented; + } break; + default: { Assert(!"unknown op type"); } break; + } + + // push computed value to the stack + { + U64 push_count = RDI_PUSHN_FROM_CTRLBITS(rdi_eval_op_ctrlbits_table[opcode_n->op]); + if (push_count == 1) { + if (stack_count < stack_cap) { + stack[stack_count] = nval; + stack_count += 1; + } else { + Assert(!"stack overflow"); + goto exit; + } + } + } + } + + U64 result = 0; + if (stack_count >= 1) { + result = stack[0].u64 - image_base; + } + if (voff_out) { + *voff_out = result; + } + + is_ok = 1; + exit:; + scratch_end(scratch); + return is_ok; +} + +internal RDI_Language +d2r_rdi_language_from_dw_language(DW_Language v) +{ + RDI_Language result = RDI_Language_NULL; + switch (v) { + default: {} break; + + case DW_Language_C89: + case DW_Language_C99: + case DW_Language_C11: + case DW_Language_C: { + result = RDI_Language_C; + } break; + + case DW_Language_CPlusPlus03: + case DW_Language_CPlusPlus11: + case DW_Language_CPlusPlus14: + case DW_Language_CPlusPlus: { + result = RDI_Language_CPlusPlus; + } break; + } + return result; +} + +internal RDI_RegCodeX64 +d2r_rdi_reg_code_from_dw_reg_x64(DW_RegX64 v) +{ + RDI_RegCodeX64 result = RDI_RegCode_nil; + switch (v) { + default: {} break; +#define X(reg_dw, val_dw, reg_rdi, off, size) case DW_RegX64_##reg_dw: { result = RDI_RegCodeX64_##reg_rdi; } break; + DW_Regs_X64_XList +#undef X + } + return result; +} + +internal RDI_RegCode +d2r_rdi_reg_code_from_dw_reg(Arch arch, DW_Reg v) +{ + RDI_RegCode result = RDI_RegCode_nil; + switch (arch) { + default: NotImplemented; break; + case Arch_Null: break; + case Arch_x64:{ result = d2r_rdi_reg_code_from_dw_reg_x64(v); } break; + } + return result; +} + +internal D2R_ValueTypeNode * +d2r_value_type_stack_push(Arena *arena, D2R_ValueTypeStack *stack, D2R_ValueType type) +{ + D2R_ValueTypeNode *n; + if (stack->free_list) { + n = stack->free_list; + SLLStackPop(stack->free_list); + } else { + n = push_array(arena, D2R_ValueTypeNode, 1); + } + n->type = type; + SLLStackPush(stack->top, n); + stack->count += 1; + return n; +} + +internal D2R_ValueType +d2r_value_type_stack_pop(D2R_ValueTypeStack *stack) +{ + D2R_ValueType result = D2R_ValueType_Generic; + if (stack->top) { + D2R_ValueTypeNode *n = stack->top; + result = n->type; + SLLStackPop(stack->top); + SLLStackPush(stack->free_list, n); + stack->count -= 1; + } + return result; +} + +internal D2R_ValueType +d2r_value_type_stack_peek(D2R_ValueTypeStack *stack) +{ + return stack->top ? stack->top->type : D2R_ValueType_Generic; +} + +internal D2R_ValueType +d2r_signed_value_type_from_bit_size(U64 bit_size) +{ + switch (bit_size) { +#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; + D2R_ValueType_Signed_XList +#undef X + } + AssertAlways(!"no suitable signed type was found for the specified size"); + return D2R_ValueType_Generic; +} + +internal D2R_ValueType +d2r_unsigned_value_type_from_bit_size(U64 bit_size) +{ + switch (bit_size) { +#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; + D2R_ValueType_Unsigned_XList +#undef X + } + AssertAlways(!"no suitable unsigned type was found for the specified size"); + return D2R_ValueType_Generic; +} + +internal D2R_ValueType +d2r_float_type_from_bit_size(U64 bit_size) +{ +#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; + switch (bit_size) { + D2R_ValueType_Float_XList + } +#undef X + AssertAlways(!"no suitable type was found for the specified size"); + return D2R_ValueType_Generic; +} + +internal D2R_ArithmeticType +d2r_arithmetic_type_from_value_type(D2R_ValueType v) +{ +#define X(_N, _AT, ...) case D2R_ValueType_##_N: return D2R_ArithmeticType_##_AT; + switch (v) { + D2R_ValueType_XList + } +#undef X + return D2R_ArithmeticType_Null; +} + +internal B32 +d2r_is_value_type_signed(D2R_ValueType v) +{ + return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Signed; +} + +internal B32 +d2r_is_value_type_integral(D2R_ValueType v) +{ + D2R_ArithmeticType at = d2r_arithmetic_type_from_value_type(v); + return at == D2R_ArithmeticType_Unsigned || at == D2R_ArithmeticType_Signed; +} + +internal B32 +d2r_is_value_type_unsigned(D2R_ValueType v) +{ + return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Unsigned; +} + +internal B32 +d2r_is_value_type_float(D2R_ValueType v) +{ + return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Float; +} + +internal U64 +d2r_size_from_value_type(U64 addr_size, D2R_ValueType value_type) +{ + if (value_type == D2R_ValueType_Address) { + return addr_size; + } + + switch (value_type) { +#define X(_N, _AT, _S) case D2R_ValueType_##_N: return _S; + D2R_ValueType_XList +#undef X + } + + return 0; +} + +internal D2R_ValueType +d2r_pick_common_value_type(D2R_ValueType lhs, D2R_ValueType rhs) +{ + if (lhs == rhs) { + return lhs; + } + // unsigned vs unsigned + else if (d2r_is_value_type_unsigned(lhs) && d2r_is_value_type_unsigned(rhs)) { + return Max(lhs, rhs); + } + // signed vs signed + else if (d2r_is_value_type_signed(lhs) && d2r_is_value_type_signed(rhs)) { + return Max(lhs, rhs); + } + // (unsigned vs signed) || (signed vs unsigned) + else if ((d2r_is_value_type_unsigned(lhs) && d2r_is_value_type_signed(rhs)) || + (d2r_is_value_type_signed(lhs) && d2r_is_value_type_unsigned(rhs))) { + U64 lhs_size = d2r_size_from_value_type(0, lhs); + U64 rhs_size = d2r_size_from_value_type(0, rhs); + if (lhs_size < rhs_size) { + return rhs; + } else if (lhs > rhs_size) { + return lhs; + } else { + return d2r_unsigned_value_type_from_bit_size(lhs_size * 8); + } + } + // float vs int + else if (d2r_is_value_type_float(lhs) && d2r_is_value_type_integral(rhs)) { + return lhs; + } + // int vs float + else if (d2r_is_value_type_integral(lhs) && d2r_is_value_type_float(rhs)) { + return rhs; + } + // float vs float + else if (d2r_is_value_type_float(lhs) && d2r_is_value_type_float(rhs)) { + return Max(lhs, rhs); + } + // address vs int + else if (lhs == D2R_ValueType_Address && d2r_is_value_type_integral(rhs)) { + return D2R_ValueType_Address; + } + // int vs address + else if (d2r_is_value_type_integral(lhs) && rhs == D2R_ValueType_Address) { + return D2R_ValueType_Address; + } + // address vs float + else if (lhs == D2R_ValueType_Address && d2r_is_value_type_float(rhs)) { + return D2R_ValueType_Generic; + } + // float vs address + else if (d2r_is_value_type_float(lhs) && rhs == D2R_ValueType_Address) { + return D2R_ValueType_Generic; + } + // no conversion for implicit value + else if (lhs == D2R_ValueType_ImplicitValue || rhs == D2R_ValueType_ImplicitValue) { + return D2R_ValueType_Generic; + } + AssertAlways(!"undefined conversion case"); + return D2R_ValueType_Generic; +} + +internal RDI_EvalTypeGroup +d2r_value_type_to_rdi(D2R_ValueType v) +{ + if (v == D2R_ValueType_Generic) { + return RDI_EvalTypeGroup_Other; + } + + if (v == D2R_ValueType_Address) { + return RDI_EvalTypeGroup_U; + } + + if (d2r_is_value_type_unsigned(v)) { + return RDI_EvalTypeGroup_U; + } + + if (d2r_is_value_type_signed(v)) { + return RDI_EvalTypeGroup_S; + } + + if (d2r_is_value_type_float(v)) { + if (v == D2R_ValueType_F16) { NotImplemented; } + else if (v == D2R_ValueType_F32) { return RDI_EvalTypeGroup_F32; } + else if (v == D2R_ValueType_F48) { NotImplemented; } + else if (v == D2R_ValueType_F64) { return RDI_EvalTypeGroup_F64; } + else if (v == D2R_ValueType_F80) { return RDI_EvalTypeGroup_F80; } + else if (v == D2R_ValueType_F96) { NotImplemented; } + else if (v == D2R_ValueType_F128) { return RDI_EvalTypeGroup_F128; } + else { InvalidPath; } + } + + InvalidPath; + return RDI_EvalTypeGroup_Other; +} + +internal D2R_ValueType +d2r_apply_usual_arithmetic_conversions(Arena *arena, D2R_ValueType lhs, D2R_ValueType rhs, RDIM_EvalBytecode *bc) +{ + D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); + if (rhs != common_type) { + rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(rhs), d2r_value_type_to_rdi(common_type)); + } + if (lhs != common_type) { + rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); + rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(lhs), d2r_value_type_to_rdi(common_type)); + } + return common_type; +} + +internal void +d2r_push_arithmetic_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op) +{ + D2R_ValueType rhs = d2r_value_type_stack_pop(stack); + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + D2R_ValueType common_type = d2r_apply_usual_arithmetic_conversions(arena, lhs, rhs, bc); + rdim_bytecode_push_op(arena, bc, op, d2r_value_type_to_rdi(common_type)); + d2r_value_type_stack_push(0, stack, common_type); +} + +internal void +d2r_push_relational_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op) +{ + D2R_ValueType rhs = d2r_value_type_stack_pop(stack); + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + D2R_ValueType common_type; + if (d2r_is_value_type_integral(lhs) && rhs == D2R_ValueType_Address) { + rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); + rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(lhs), RDI_EvalTypeGroup_U); + rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); + common_type = D2R_ValueType_Address; + } else if (lhs == D2R_ValueType_Address && d2r_is_value_type_integral(rhs)) { + rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(rhs), RDI_EvalTypeGroup_U); + common_type = D2R_ValueType_Address; + } else { + common_type = d2r_apply_usual_arithmetic_conversions(arena, lhs, rhs, bc); + } + rdim_bytecode_push_op(arena, bc, RDI_EvalOp_EqEq, d2r_value_type_to_rdi(common_type)); + d2r_value_type_stack_push(0, stack, D2R_ValueType_Bool); +} + +internal RDIM_EvalBytecode +d2r_bytecode_from_expression(Arena *arena, + DW_Raw *input, + U64 image_base, + Arch arch, + DW_ListUnit *addr_lu, + String8 raw_expr, + DW_CompUnit *cu, + D2R_ValueType *result_type_out) +{ + Temp scratch = scratch_begin(&arena, 1); + Temp temp = temp_begin(arena); + B32 is_ok = 0; + + RDIM_EvalBytecode bc = {0}; + + DW_Expr expr = dw_expr_from_data(scratch.arena, cu->format, cu->address_size, raw_expr); + D2R_ValueTypeStack *stack = push_array(scratch.arena, D2R_ValueTypeStack, 1); + RDIM_EvalBytecodeOp **converted_insts = push_array(scratch.arena, RDIM_EvalBytecodeOp *, expr.count); + U64 inst_idx = 0; + for EachNode(inst, DW_ExprInst, expr.first) { + RDIM_EvalBytecodeOp *last_op = bc.last_op; + + U64 pop_count = dw_pop_count_from_expr_op(inst->opcode); + if (pop_count > stack->count) { + Assert(!"not enough values on the stack to evaluate instruction"); + goto exit; + } + + switch (inst->opcode) { + case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: + case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: + case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: + case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: + case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: + case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: + case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: + case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: + case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: + case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: + case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: { + U64 lit = inst->opcode - DW_ExprOp_Lit0; + rdim_bytecode_push_uconst(arena, &bc, lit); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U8); + } break; + case DW_ExprOp_Const1U: { + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u8); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U8); + } break; + case DW_ExprOp_Const2U: { + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u16); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U16); + } break; + case DW_ExprOp_Const4U: { + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u32); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U32); + } break; + case DW_ExprOp_Const8U: { + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u32); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U64); + } break; + case DW_ExprOp_Const1S: { + rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s8); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S8); + } break; + case DW_ExprOp_Const2S: { + rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s16); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S16); + } break; + case DW_ExprOp_Const4S: { + rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s32); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S32); + } break; + case DW_ExprOp_Const8S: { + rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s64); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S64); + } break; + case DW_ExprOp_ConstU: { + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u64); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U64); + } break; + case DW_ExprOp_ConstS: { + rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s64); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S64); + } break; + case DW_ExprOp_Addr: { + if (inst->operands[0].u64 < image_base) { + Assert(!"invalid address"); + goto exit; + } + + U64 voff = inst->operands[0].u64 - image_base; + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_ModuleOff, voff); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: + case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: + case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: + case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: + case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: + case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: + case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: + case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: + case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: + case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: + case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: { + U64 reg_code_dw = inst->opcode - DW_ExprOp_Reg0; + U64 reg_size = dw_reg_size_from_code(arch, reg_code_dw); + U64 reg_pos = dw_reg_pos_from_code(arch, reg_code_dw); + + RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); + U32 regread_param = RDI_EncodeRegReadParam(reg_code_rdi, reg_size, reg_pos); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, regread_param); + d2r_value_type_stack_push(scratch.arena, stack, d2r_unsigned_value_type_from_bit_size(reg_size)); + } break; + case DW_ExprOp_RegX: { + U64 reg_size = dw_reg_size_from_code(arch, inst->operands[0].u64); + U64 reg_pos = dw_reg_pos_from_code(arch, inst->operands[0].u64); + RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, inst->operands[0].u64); + U32 regread_param = RDI_EncodeRegReadParam(reg_code_rdi, reg_size, reg_pos); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, regread_param); + D2R_ValueType value_type; + if (arch == Arch_x64) { + if (reg_code_rdi == RDI_RegCodeX64_st0 || + reg_code_rdi == RDI_RegCodeX64_st1 || + reg_code_rdi == RDI_RegCodeX64_st2 || + reg_code_rdi == RDI_RegCodeX64_st3 || + reg_code_rdi == RDI_RegCodeX64_st4 || + reg_code_rdi == RDI_RegCodeX64_st5 || + reg_code_rdi == RDI_RegCodeX64_st6 || + reg_code_rdi == RDI_RegCodeX64_st7) { + value_type = d2r_float_type_from_bit_size(reg_size); + } else { + value_type = d2r_unsigned_value_type_from_bit_size(reg_size); + } + } else { + value_type = d2r_unsigned_value_type_from_bit_size(reg_size); + } + d2r_value_type_stack_push(scratch.arena, stack, value_type); + } break; + case DW_ExprOp_ImplicitValue: { + if (inst->operands[0].block.size <= sizeof(U64)) { + U64 implicit_value; + MemoryCopyStr8(&implicit_value, inst->operands[0].block); + rdim_bytecode_push_uconst(arena, &bc, implicit_value); + d2r_value_type_stack_push(scratch.arena, stack, d2r_unsigned_value_type_from_bit_size(inst->operands[0].block.size * 8)); + } else { + // TODO: currenlty no way to encode string in RDIM_EvalBytecodeOp + Assert(!"TODO:"); + goto exit; + } + } break; + case DW_ExprOp_Piece: { + U64 partial_value_size32 = safe_cast_u32(inst->operands[0].u64); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PartialValue, partial_value_size32); + } break; + case DW_ExprOp_BitPiece: { + U32 piece_bit_size32 = safe_cast_u32(inst->operands[0].u64); + U32 piece_bit_off32 = safe_cast_u32(inst->operands[1].u64); + U64 partial_value = Compose64Bit(piece_bit_size32, piece_bit_off32); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PartialValueBit, partial_value); + } break; + case DW_ExprOp_Pick: { + U64 idx = 0; + D2R_ValueTypeNode *n; + for (n = stack->top; n != 0 || idx == inst->operands[0].u64; n = n->next, idx += 1) { } + if (idx == inst->operands[0].u64) { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, inst->operands[0].u64); + d2r_value_type_stack_push(scratch.arena, stack, n->type); + } else { + Assert(!"out of bounds pick"); + goto exit; + } + } break; + case DW_ExprOp_Over: { + if (stack->top && stack->top->next) { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, 1); + d2r_value_type_stack_push(scratch.arena, stack, stack->top->next->type); + } else { + Assert(!"out of bounds over"); + goto exit; + } + } break; + case DW_ExprOp_PlusUConst: { + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + D2R_ValueType common_type = d2r_pick_common_value_type(lhs, D2R_ValueType_U64); + rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u64); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, d2r_value_type_to_rdi(common_type)); + d2r_value_type_stack_push(scratch.arena, stack, common_type); + } break; + case DW_ExprOp_Skip: { + B32 skip_fwd = inst->operands[0].s16 >= 0; + U16 delta = abs_s64(inst->operands[0].s16); + U16 cursor = 0; + U64 inst_count = 0; + for (DW_ExprInst *i = skip_fwd ? inst : inst->prev; i != 0 && cursor < delta; i = skip_fwd ? inst->next : inst->prev) { + cursor += i->size; + inst_count += 1; + } + + if (cursor != delta) { Assert(!"skip landed in middle of an instruction"); goto exit; } + if (inst_count > min_S16) { Assert(!"operand overflow"); goto exit; } + if (inst_idx > max_U32) { Assert(!"index overflow"); goto exit; } + + U64 imm = Compose64Bit(inst_idx, skip_fwd ? (S16)inst_count : (U16)(-(S16)inst_count)); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Skip, imm); + } break; + case DW_ExprOp_Bra: { + B32 skip_fwd = inst->operands[0].s16 >= 0; + U16 delta = abs_s64(inst->operands[0].s16); + U16 cursor = 0; + U64 inst_count = 0; + for (DW_ExprInst *i = skip_fwd ? inst->next : inst->prev; i != 0 && cursor < delta; i = skip_fwd ? inst->next : inst->prev) { + cursor += i->size; + inst_count += 1; + } + + if (cursor != delta) { Assert(!"cond landed in middle of an instruction"); goto exit; } + if (inst_count > min_S16) { Assert(!"operand overflow"); goto exit; } + if (inst_idx > max_U32) { Assert(!"index overflow"); goto exit; } + + U64 imm = Compose64Bit(inst_idx, skip_fwd ? inst_count : (U16)(-(S16)inst_count)); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Cond, imm); + } break; + case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: + case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: + case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: + case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: + case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: + case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: + case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: + case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: + case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: + case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: + case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: { + U64 reg_code_dw = inst->opcode - DW_ExprOp_BReg0; + S64 reg_off = inst->operands[0].s64; + + RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, reg_code_rdi); + if (reg_off > 0) { + rdim_bytecode_push_sconst(arena, &bc, reg_off); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); + } + + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_BRegX: { + U64 reg_code_dw = inst->operands[0].u64; + S64 reg_off = inst->operands[1].s64; + + RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegReadDyn, reg_code_rdi); + if (reg_off > 0) { + rdim_bytecode_push_sconst(arena, &bc, reg_off); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); + } + + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_FBReg: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_FrameOff, inst->operands[0].s64); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_Deref: { + D2R_ValueType address_type = d2r_value_type_stack_pop(stack); + if (address_type != D2R_ValueType_Address && !d2r_is_value_type_integral(address_type)) { + Assert(!"value must be of integral type"); + goto exit; + } + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_MemRead, cu->address_size); + d2r_value_type_stack_push(scratch.arena, stack, address_type); + } break; + case DW_ExprOp_DerefSize: { + D2R_ValueType address_type = d2r_value_type_stack_pop(stack); + if (!d2r_is_value_type_integral(address_type) && address_type != D2R_ValueType_Address ) { + Assert(!"value must be of integral type"); + goto exit; + } + U8 deref_size_in_bytes = inst->operands[0].u64; + if (0 < deref_size_in_bytes && deref_size_in_bytes <= cu->address_size) { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_MemRead, deref_size_in_bytes); + } else { + Assert(!"ill formed expression"); + goto exit; + } + d2r_value_type_stack_push(scratch.arena, stack, address_type); + } break; + case DW_ExprOp_XDeref: { + Assert(!"multiple address spaces are not supported"); + goto exit; + } break; + case DW_ExprOp_XDerefSize: { + Assert(!"no suitable conversion"); + goto exit; + } break; + case DW_ExprOp_Call2: + case DW_ExprOp_Call4: + case DW_ExprOp_CallRef: { + Assert(!"calls are not supported"); + goto exit; + } break; + case DW_ExprOp_ImplicitPointer: + case DW_ExprOp_GNU_ImplicitPointer: { + // TODO: RDI does not support encoding of implicit pointers + //Assert(!"TODO: implicit pointer"); + goto exit; + } break; + case DW_ExprOp_Convert: + case DW_ExprOp_GNU_Convert: { + D2R_ValueType out = D2R_ValueType_Generic; + if (inst->operands[0].u64 == 0) { + // + // 2.5.1 + // Instead of a base type, elements can have a generic type, + // which is an integral type that has the size of an address + // on the target machine and unspecified signedness. + // + out = D2R_ValueType_Generic; + } else { + // find ref tag + U64 ref_tag_info_off = cu->info_range.min + inst->operands[0].u64; + DW_TagNode *tag_node = dw_tag_node_from_info_off(cu, ref_tag_info_off); + if (tag_node == 0) { + Assert(!"invalid .debug_info offset"); + goto exit; + } + + DW_Tag tag = tag_node->tag; + if (tag.kind == DW_TagKind_BaseType) { + // extract encoding attribute + DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Encoding); + + // DW_ATE -> RDI_EvalTypeGroup + switch (encoding) { + case DW_ATE_Null: { + out = D2R_ValueType_Generic; + } break; + case DW_ATE_Address: { + out = D2R_ValueType_Address; + } break; + case DW_ATE_Boolean: { + out = D2R_ValueType_S8; + } break; + case DW_ATE_SignedChar: + case DW_ATE_Signed: { + U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); + out = d2r_signed_value_type_from_bit_size(byte_size * 8); + } break; + case DW_ATE_UnsignedChar: + case DW_ATE_Unsigned: { + U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); + out = d2r_unsigned_value_type_from_bit_size(byte_size * 8); + } break; + case DW_ATE_Float: { + U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); + out = d2r_float_type_from_bit_size(byte_size * 8); + } break; + default: InvalidPath; break; + } + } else { + Assert(!"unexpected tag"); + goto exit; + } + } + + D2R_ValueType in = d2r_value_type_stack_pop(stack); + d2r_value_type_stack_push(scratch.arena, stack, out); + rdim_bytecode_push_convert(arena, &bc, d2r_value_type_to_rdi(in), d2r_value_type_to_rdi(out)); + } break; + case DW_ExprOp_GNU_ParameterRef: { + Assert(!"TODO: sample"); + goto exit; + } + case DW_ExprOp_DerefType: + case DW_ExprOp_GNU_DerefType: { + goto exit; + } + case DW_ExprOp_ConstType: + case DW_ExprOp_GNU_ConstType: { + Assert(!"TODO: sample"); + goto exit; + } + case DW_ExprOp_RegvalType: { + Assert(!"sample"); + goto exit; + } + case DW_ExprOp_EntryValue: + case DW_ExprOp_GNU_EntryValue: { + D2R_ValueType call_site_result_type = 0; + RDIM_EvalBytecode call_site_bc = d2r_bytecode_from_expression(arena, input, image_base, arch, addr_lu, inst->operands[0].block, cu, &call_site_result_type); + + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_CallSiteValue, safe_cast_u32(call_site_bc.encoded_size)); + rdim_bytecode_concat_in_place(&bc, &call_site_bc); + + d2r_value_type_stack_push(scratch.arena, stack, call_site_result_type); + } break; + case DW_ExprOp_Addrx: { + U64 addr = dw_addr_from_list_unit(addr_lu, inst->operands[0].u64); + if (addr != max_U64) { + if (addr >= image_base) { + U64 voff = addr - image_base; + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_ModuleOff, voff); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } else { + Assert(!"unable to relocate address"); + goto exit; + } + } else { + Assert(!"out of bounds index"); + goto exit; + } + } break; + case DW_ExprOp_CallFrameCfa: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PushCfa, 0); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_PushObjectAddress: { + Assert(!"TODO: sample"); + goto exit; + } + case DW_ExprOp_Nop: {} break; + case DW_ExprOp_Eq: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_EqEq); } break; + case DW_ExprOp_Ge: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_GrEq); } break; + case DW_ExprOp_Gt: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_Grtr); } break; + case DW_ExprOp_Le: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_LsEq); } break; + case DW_ExprOp_Lt: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_Less); } break; + case DW_ExprOp_Ne: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_NtEq); } break; + case DW_ExprOp_Div: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Div); } break; + case DW_ExprOp_Minus: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Sub); } break; + case DW_ExprOp_Mul: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Mul); } break; + case DW_ExprOp_Plus: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Add); } break; + case DW_ExprOp_Xor: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitXor); } break; + case DW_ExprOp_And: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitAnd); } break; + case DW_ExprOp_Or: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitOr); } break; + case DW_ExprOp_Shl: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_LShift); } break; + case DW_ExprOp_Shr: { + D2R_ValueType rhs = d2r_value_type_stack_pop(stack); + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + if (d2r_is_value_type_integral(rhs) && d2r_is_value_type_integral(lhs)) { + D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); + D2R_ValueType result_type = d2r_unsigned_value_type_from_bit_size(d2r_size_from_value_type((cu->address_size), common_type) * 8); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RShift, d2r_value_type_to_rdi(result_type)); + d2r_value_type_stack_push(scratch.arena, stack, result_type); + } else { + Assert(!"operands must be of integral type"); + goto exit; + } + } break; + case DW_ExprOp_Shra: { + D2R_ValueType rhs = d2r_value_type_stack_pop(stack); + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + if (d2r_is_value_type_integral(lhs) && d2r_is_value_type_integral(rhs)) { + D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); + D2R_ValueType result_type = d2r_signed_value_type_from_bit_size(d2r_size_from_value_type((cu->address_size), common_type) * 8); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RShift, d2r_value_type_to_rdi(result_type)); + d2r_value_type_stack_push(scratch.arena, stack, result_type); + } else { + Assert(!"operands must be of integral type"); + goto exit; + } + } break; + case DW_ExprOp_Mod: { + D2R_ValueType rhs = d2r_value_type_stack_pop(stack); + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + if (!d2r_is_value_type_integral(rhs) || !d2r_is_value_type_integral(lhs)) { + Assert(!"operands must be of integral type"); + goto exit; + } + D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Mod, d2r_value_type_to_rdi(common_type)); + d2r_value_type_stack_push(scratch.arena, stack, common_type); + } break; + case DW_ExprOp_Abs: { + if (!d2r_is_value_type_integral(d2r_value_type_stack_peek(stack)) && !d2r_is_value_type_float(d2r_value_type_stack_peek(stack))) { + Assert(!"operand must be of integral type or float"); + goto exit; + } + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Abs, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); + } break; + case DW_ExprOp_Neg: { + if (!d2r_is_value_type_integral(d2r_value_type_stack_peek(stack))) { + Assert(!"operand must be of integral type"); + goto exit; + } + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Neg, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); + } break; + case DW_ExprOp_Not: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_BitNot, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); + } break; + case DW_ExprOp_Dup: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, 0); + d2r_value_type_stack_push(scratch.arena, stack, d2r_value_type_stack_peek(stack)); + } break; + case DW_ExprOp_Rot: { + Assert(!"no suitable conversion"); + goto exit; + } + case DW_ExprOp_Swap: { + D2R_ValueType a = d2r_value_type_stack_pop(stack); + D2R_ValueType b = d2r_value_type_stack_pop(stack); + d2r_value_type_stack_push(scratch.arena, stack, a); + d2r_value_type_stack_push(scratch.arena, stack, b); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Swap, 0); + } break; + case DW_ExprOp_Drop: { + d2r_value_type_stack_pop(stack); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pop, 0); + } break; + case DW_ExprOp_StackValue: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Stop, 0); + if (stack->top->type == D2R_ValueType_Address) { + stack->top->type = d2r_unsigned_value_type_from_bit_size(cu->address_size * 8); + } + } break; + case DW_ExprOp_FormTlsAddress: + case DW_ExprOp_GNU_PushTlsAddress: { + D2R_ValueType lhs = d2r_value_type_stack_pop(stack); + if (!d2r_is_value_type_integral(lhs)) { + Assert(!"lhs must be of integral type"); + goto exit; + } + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_TLSOff, 0); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, d2r_value_type_to_rdi(lhs)); + d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); + } break; + case DW_ExprOp_GNU_UnInit: { + // TODO: flag value as unitialized; this must be last opcode; possible to use with DW_ExprOp_Piece; + } break; + default: goto exit; + } + + // store converted instruction + if (last_op != bc.last_op) { + RDIM_EvalBytecodeOp *first_converted_op = last_op ? last_op->next : bc.first_op; + converted_insts[inst_idx] = first_converted_op; + } + + inst_idx += 1; + } + + // fixup bytecode jumps + for EachNode(op, RDIM_EvalBytecodeOp, bc.first_op) { + if (op->op == RDI_EvalOp_Skip || op->op == RDI_EvalOp_Cond) { + // unpack skip info + U32 inst_idx = Extract32(op->p, 1); + S16 skip_count_signed = (S16)Extract32(op->p, 0); + U16 skip_count = abs_s64(skip_count_signed); + B32 skip_fwd = skip_count_signed > 0; + + // setup being/end links + RDIM_EvalBytecodeOp *begin = 0, *end = 0; + if (skip_fwd) { + if (inst_idx + skip_count <= expr.count) { + begin = converted_insts[inst_idx]; + end = (inst_idx + skip_count) < expr.count ? converted_insts[inst_idx + skip_count] : 0; + } else { + Assert(!"out of bounds skip"); + goto exit; + } + } else { + if (skip_count <= inst_idx) { + begin = converted_insts[inst_idx - skip_count]; + end = converted_insts[inst_idx]; + } else { + Assert(!"out of bounds skip"); + goto exit; + } + } + + // compute skip delta + U64 skip_delta = 0; + for (RDIM_EvalBytecodeOp *n = begin; n != end; n = n->next) { + skip_delta += n->p_size; + } + + // rewrite skip operand with byte delta + AssertAlways(skip_delta <= max_S16); + op->p = skip_fwd ? (S16)skip_delta : -(S16)skip_delta; + } + } + + if (result_type_out) { + *result_type_out = d2r_value_type_stack_peek(stack); + } + + is_ok = 1; + exit:; + if (!is_ok) { + MemoryZeroStruct(&bc); + temp_end(temp); + } + scratch_end(scratch); + return bc; +} + +internal RDIM_Location +d2r_transpile_expression(Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr) +{ + RDIM_Location loc = {0}; + if (expr.size) { + D2R_ValueType result_type = 0; + RDIM_EvalBytecode bytecode = d2r_bytecode_from_expression(arena, input, image_base, arch, addr_lu, expr, cu, &result_type); + loc.kind = result_type == D2R_ValueType_Address ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; + loc.bytecode = bytecode; + } + return loc; +} + +internal RDIM_Location +d2r_location_from_attrib(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind) +{ + String8 expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, kind); + RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, expr); + return location; +} + +internal RDIM_LocationCaseList +d2r_locset_from_attrib(Arena *arena, + RDIM_ScopeChunkList *scopes, + RDIM_Scope *curr_scope, + DW_Raw *input, + DW_CompUnit *cu, + U64 image_base, + Arch arch, + DW_Tag tag, + DW_AttribKind kind) +{ + RDIM_LocationCaseList locset = {0}; + + // extract attrib from tag + DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); + DW_AttribClass attrib_class = dw_value_class_from_attrib(cu, attrib); + + if (attrib_class == DW_AttribClass_LocList || attrib_class == DW_AttribClass_LocListPtr) { + Temp scratch = scratch_begin(&arena, 1); + + // extract location list from attrib + DW_LocList loclist = dw_loclist_from_attrib(scratch.arena, input, cu, attrib); + + // convert location list to RDIM location set + for EachNode(loc_n, DW_LocNode, loclist.first) { + RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, loc_n->v.expr); + RDIM_Rng1U64 voff_range = { .min = loc_n->v.range.min - image_base, .max = loc_n->v.range.max - image_base }; + rdim_location_case_list_push(arena, &locset, location, voff_range); + } + + scratch_end(scratch); + } else if (attrib_class == DW_AttribClass_ExprLoc) { + // extract expression from attrib + String8 expr = dw_exprloc_from_attrib(input, cu, attrib); + + // convert expression and inherit life-time ranges from enclosed scope + RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, expr); + for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { + rdim_location_case_list_push(arena, &locset, location, range_n->v); + } + } else if (attrib_class != DW_AttribClass_Null) { + log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); + } + + return locset; +} + +internal RDIM_LocationCaseList +d2r_var_locset_from_tag(Arena *arena, + RDIM_ScopeChunkList *scopes, + RDIM_Scope *curr_scope, + DW_Raw *input, + DW_CompUnit *cu, + U64 image_base, + Arch arch, + DW_Tag tag) +{ + RDIM_LocationCaseList locset = {0}; + + B32 has_const_value = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ConstValue); + B32 has_location = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Location); + + if (has_const_value && has_location) { + log_infof("unexpected variable encoding @ .debug_info+%llx", tag.info_off); + return locset; + } + + if (has_const_value) { + // extract const value + U64 const_value = dw_u64_from_attrib(input, cu, tag, DW_AttribKind_ConstValue); + + // make value byte code + RDIM_EvalBytecode bc = {0}; + rdim_bytecode_push_uconst(arena, &bc, const_value); + + // fill out location + RDIM_Location loc = {0}; + loc.kind = RDI_LocationKind_ValBytecodeStream; + loc.bytecode = bc; + + // push location cases + for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { + rdim_location_case_list_push(arena, &locset, loc, range_n->v); + } + } else if (has_location) { + locset = d2r_locset_from_attrib(arena, scopes, curr_scope, input, cu, image_base, arch, tag, DW_AttribKind_Location); + } + + return locset; +} + +internal RDIM_Type * +d2r_create_type(Arena *arena, D2R_TypeTable *type_table) +{ + RDIM_Type *type = rdim_type_chunk_list_push(arena, type_table->types, type_table->type_chunk_cap); + return type; +} + +internal RDIM_Type * +d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off) +{ + RDIM_Type *type = d2r_create_type(arena, type_table); + Assert(hash_table_search_u64_raw(type_table->ht, info_off) == 0); + hash_table_push_u64_raw(arena, type_table->ht, info_off, type); + return type; +} + +internal RDIM_Type * +d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off) +{ + RDIM_Type *type = hash_table_search_u64_raw(type_table->ht, info_off); + if (type == 0) { + type = type_table->builtin_types[RDI_TypeKind_NULL]; + } + return type; +} + +internal RDIM_Type * +d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind) +{ + RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; + + // find attrib + DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); + + // does tag have this attribute? + if (attrib->attrib_kind == kind) { + DW_AttribClass value_class = dw_value_class_from_attrib(cu, attrib); + + if (value_class == DW_AttribClass_Reference) { + // resolve reference + DW_Reference ref = dw_ref_from_attrib(input, cu, attrib); + + if (ref.cu == cu) { + // find type + type = d2r_type_from_offset(type_table, ref.info_off); + } else { + NotImplemented; + } + } else { + log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); + } + } + + return type; +} + +internal D2R_TagIterator * +d2r_tag_iterator_init(Arena *arena, DW_TagNode *root) +{ + D2R_TagIterator *iter = push_array(arena, D2R_TagIterator, 1); + iter->free_list = 0; + iter->stack = push_array(arena, D2R_TagFrame, 1); + iter->stack->node = push_array(arena, DW_TagNode, 1); + if (root != 0) { + *iter->stack->node = *root; + } + iter->stack->node->sibling = 0; + iter->visit_children = 1; + iter->tag_node = root; + iter->null_tag_node = push_array(arena, DW_TagNode, 1); + return iter; +} + +internal void +d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter) +{ + // descend to first child + if (iter->visit_children) { + if (iter->stack->node->first_child) { + D2R_TagFrame *f = iter->free_list; + if (f) { SLLStackPop(iter->free_list); MemoryZeroStruct(f); } + else { f = push_array(arena, D2R_TagFrame, 1); } + f->node = iter->stack->node->first_child; + SLLStackPush(iter->stack, f); + goto exit; + } + } + + while (iter->stack) { + // go to sibling + iter->stack->node = iter->stack->node->sibling; + if (iter->stack->node) { break; } + + // no more siblings, go up + D2R_TagFrame *f = iter->stack; + SLLStackPop(iter->stack); + SLLStackPush(iter->free_list, f); + } + + exit:; + // update iterator + iter->visit_children = 1; + iter->tag_node = iter->stack ? iter->stack->node : 0; +} + +internal void +d2r_tag_iterator_skip_children(D2R_TagIterator *iter) +{ + iter->visit_children = 0; +} + +internal DW_TagNode * +d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter) +{ + return iter->stack == 0 || iter->stack->next == 0 ? iter->null_tag_node : iter->stack->next->node; +} + +internal DW_Tag +d2r_tag_iterator_parent_tag(D2R_TagIterator *iter) +{ + DW_TagNode *tag_node = d2r_tag_iterator_parent_tag_node(iter); + return tag_node->tag; +} + +internal void +d2r_flag_converted_type_tag(DW_TagNode *tag_node) +{ + tag_node->tag.v |= D2R_TagFlags_TypeConverted; +} + +internal B8 +d2r_is_type_tag_converted(DW_TagNode *tag_node) +{ + return !!(tag_node->tag.v & D2R_TagFlags_TypeConverted); +} + +internal RDIM_Type * +d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_Tag tag, DW_AttribKind kind) +{ + RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; + + // find attrib + DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); + + // does tag have this attribute? + if (attrib->attrib_kind == kind) { + DW_AttribClass value_class = dw_value_class_from_attrib(cu, attrib); + + if (value_class == DW_AttribClass_Reference) { + // resolve reference + DW_Reference ref = dw_ref_from_attrib(input, cu, attrib); + + if (ref.cu == cu) { + // find type + type = d2r_type_from_offset(type_table, ref.info_off); + + // was type converted? + if (type == 0) { + // issue type conversion + DW_TagNode *ref_node = dw_tag_node_from_info_off(cu, ref.info_off); + d2r_convert_types(arena, type_table, input, cu, cu_lang, arch, ref_node); + + // if we do not have a converted type at this point then debug info is malformed + type = d2r_type_from_offset(type_table, ref.info_off); + if (type == 0) { + type = type_table->builtin_types[RDI_TypeKind_NULL]; + } + } + } else { + NotImplemented; + } + } else { + log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); + } + } + + return type; +} + +internal void +d2r_convert_types(Arena *arena, + D2R_TypeTable *type_table, + DW_Raw *input, + DW_CompUnit *cu, + DW_Language cu_lang, + Arch arch, + DW_TagNode *root) +{ + Temp scratch = scratch_begin(&arena, 1); + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + + // skip converted tags + if (d2r_is_type_tag_converted(tag_node)) { + d2r_tag_iterator_skip_children(it); + continue; + } + // mark the tag as converted here, because during conversion we may recurse on the same tag + d2r_flag_converted_type_tag(tag_node); + + switch (tag.kind) { + case DW_TagKind_ClassType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_IncompleteClass; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + Assert(!tag_node->first_child); + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Class; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + type->direct_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + } + } break; + case DW_TagKind_StructureType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteStruct; + + if (tag_node->first_child) { + d2r_tag_iterator_skip_children(it); + } else { + log_infof("childless struct @ .debug_info+%llx", tag.info_off); + } + } else { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Struct; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + } + } break; + case DW_TagKind_UnionType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteUnion; + + if (tag_node->first_child) { + d2r_tag_iterator_skip_children(it); + } else { + log_infof("childless union @ .debug_info+%llx", tag.info_off); + } + } else { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Union; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + } + } break; + case DW_TagKind_EnumerationType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteEnum; + if (tag_node->first_child) { + d2r_tag_iterator_skip_children(it); + } else { + log_infof("childless enum @ .debug_info+%llx", tag.info_off); + } + } else { + RDIM_Type *enum_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Enum; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + type->direct_type = enum_base_type; + } + } break; + case DW_TagKind_SubProgram: { + if ( ! dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Declaration)) { + break; + } + } // fall-through + case DW_TagKind_SubroutineType: { + RDIM_Type *ret_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + + // collect parameters + RDIM_TypeList param_list = {0}; + for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { + if (n->tag.kind == DW_TagKind_FormalParameter) { + RDIM_Type *param_type = d2r_type_from_attrib(type_table, input, cu, n->tag, DW_AttribKind_Type); + rdim_type_list_push(scratch.arena, ¶m_list, param_type); + } else if (n->tag.kind == DW_TagKind_UnspecifiedParameters) { + rdim_type_list_push(scratch.arena, ¶m_list, type_table->builtin_types[RDI_TypeKind_Variadic]); + } else { + log_infof("unexpected tag @ .debug_info+%llx", tag.info_off); + } + } + + DW_TagNode *parent = d2r_tag_iterator_parent_tag_node(it); + B32 is_method = parent->tag.kind == DW_TagKind_StructureType || parent->tag.kind == DW_TagKind_ClassType; + + // init proceudre type + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = is_method ? RDI_TypeKind_Method : RDI_TypeKind_Function; + type->byte_size = cu->address_size; + type->direct_type = ret_type; + type->count = param_list.count; + type->param_types = rdim_array_from_type_list(arena, param_list); + + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_Typedef: { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Alias; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->direct_type = direct_type; + for (RDIM_Type *n = direct_type; n != 0; n = n->direct_type) { + if (n->byte_size) { + type->byte_size = n->byte_size; + break; + } + } + } break; + case DW_TagKind_BaseType: { + DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Encoding); + U64 byte_size = dw_byte_size_from_tag(input, cu, tag); + + // convert base type encoding to RDI version + RDI_TypeKind kind = RDI_TypeKind_NULL; +#define X(t,g,s) case s: kind = RDI_TypeKind_##t; break; + switch (encoding) { + case DW_ATE_Null: kind = RDI_TypeKind_NULL; break; + case DW_ATE_Address: kind = RDI_TypeKind_Void; break; + case DW_ATE_Boolean: kind = RDI_TypeKind_Bool; break; + case DW_ATE_ComplexFloat: { + switch (byte_size) { + case 8: kind = RDI_TypeKind_ComplexF32; break; + case 16: kind = RDI_TypeKind_ComplexF64; break; + case 24: kind = RDI_TypeKind_ComplexF80; break; + case 32: kind = RDI_TypeKind_ComplexF128; break; + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_Float: { + String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + if (arch == Arch_x64 || arch == Arch_arm64) { + if (str8_match(name, str8_lit("__float80"), 0)) { + kind = RDI_TypeKind_F80; + } else if (str8_match(name, str8_lit("__float128"), 0)) { + kind = RDI_TypeKind_F128; + } else if (str8_match(name, str8_lit("_Float16"), 0)) { + kind = RDI_TypeKind_F16; + } else if (str8_match(name, str8_lit("__bf16"), 0)) { + kind = RDI_TypeKind_BF16; + } + if (kind != RDI_TypeKind_NULL) { break; } + } + + switch (byte_size) { + D2R_ValueType_Float_XList + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_Signed: { + String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + if (str8_match(name, str8_lit("wchar_t"), 0)) { goto do_signed_char; } + + switch (byte_size) { + D2R_ValueType_Signed_XList + default: log_infof("unexpected size"); break; + } + } break; + do_signed_char:; + case DW_ATE_SignedChar: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_Char8; break; + case 2: kind = RDI_TypeKind_Char16; break; + case 4: kind = RDI_TypeKind_Char32; break; + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_Unsigned: { + switch (byte_size) { + D2R_ValueType_Unsigned_XList + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_Utf: + case DW_ATE_UnsignedChar: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_UChar8; break; + case 2: kind = RDI_TypeKind_UChar16; break; + case 4: kind = RDI_TypeKind_UChar32; break; + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_DecimalFloat: { + switch (byte_size) { + case 4: kind = RDI_TypeKind_Decimal32; break; + case 8: kind = RDI_TypeKind_Decimal64; break; + case 16: kind = RDI_TypeKind_Decimal128; break; + default: log_infof("unexpected size"); break; + } + } break; + case DW_ATE_ImaginaryFloat: { + NotImplemented; + } break; + case DW_ATE_PackedDecimal: { + NotImplemented; + } break; + case DW_ATE_NumericString: { + NotImplemented; + } break; + case DW_ATE_Edited: { + NotImplemented; + } break; + case DW_ATE_SignedFixed: { + NotImplemented; + } break; + case DW_ATE_UnsignedFixed: { + NotImplemented; + } break; + case DW_ATE_Ucs: { + NotImplemented; + } break; + case DW_ATE_Ascii: { + NotImplemented; + } break; + default: log_infof("unexpected base type encoding"); break; + } +#undef X + + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Alias; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->direct_type = type_table->builtin_types[kind]; + type->byte_size = byte_size; + } break; + case DW_TagKind_PointerType: { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + + // TODO: + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Allocated)) { + log_infof("TODO: handle allocated attrib @ .debug_info+%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Associated)) { + log_infof("TODO: handle associated attrib @ .debug_info%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { + log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { + log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_AddressClass)) { + log_infof("TODO: handle address class attrib @ .debug_info+%llx", tag.info_off); + } + + U64 byte_size = cu->address_size; + if (cu->version == DW_Version_5 || cu->relaxed) { + dw_try_byte_size_from_tag(input, cu, tag, &byte_size); + } + + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Ptr; + type->byte_size = byte_size; + type->direct_type = direct_type; + } break; + case DW_TagKind_RestrictType: { + // TODO: + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { + log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { + log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); + } + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = cu->address_size; + type->flags = RDI_TypeModifierFlag_Restrict; + type->direct_type = direct_type; + } break; + case DW_TagKind_VolatileType: { + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { + log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); + } + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = cu->address_size; + type->flags = RDI_TypeModifierFlag_Volatile; + type->direct_type = direct_type; + } break; + case DW_TagKind_ConstType: { + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { + log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); + } + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { + log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); + } + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = cu->address_size; + type->flags = RDI_TypeModifierFlag_Const; + type->direct_type = direct_type; + } break; + case DW_TagKind_ArrayType: { + // TODO: @native_vector_support extract byte size from the base type tag + // and convert to U256, U512, S256 and S512 + B32 is_vector = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_GNU_Vector); + // TODO: + //if (is_vector) { NotImplemented; } + + B32 error = 1; + + // * DWARF vs RDI Array Type Graph * + // + // For example lets take following decl: + // + // int (*foo[2])[3]; + // + // This compiles to in DWARF: + // + // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] + // \ + // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] + // \ + // -> (B1) DW_TAG_BaseType (int) + // + // RDI expects: + // + // foo -> Array[2] -> Pointer -> Array[3] -> int + // + // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and + // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. + // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from + // B to A. + struct SubrangeNode { struct SubrangeNode *next; U64 count; }; + struct SubrangeNode *subrange_stack = 0; + for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { + if (n->tag.kind != DW_TagKind_SubrangeType) { + log_infof("[%llx] while scanning for DW_TagKind_SubrangeType found an unexpected child tag %S @ %llx\n", tag.info_off, dw_string_from_tag_kind(scratch.arena, n->tag.kind), n->tag.info_off); + goto array_type_exit; + } + + // resolve lower bound + U64 lower_bound = 0; + if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_LowerBound)) { + B32 is_vla = dw_is_attrib_var_ref(input, cu, dw_attrib_from_tag(input, cu, n->tag, DW_AttribKind_LowerBound)); + if (is_vla) { + log_infof("[%llx] TODO: lower bound is a variable\n", n->tag.info_off); + } else { + lower_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_LowerBound); + } + } else { + lower_bound = dw_pick_default_lower_bound(cu_lang); + } + + // resolve upper bound + U64 upper_bound = 0; + if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_Count)) { + U64 count = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_Count); + upper_bound = lower_bound + count; + } else if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_UpperBound)) { + B32 is_vla = dw_is_attrib_var_ref(input, cu, dw_attrib_from_tag(input, cu, n->tag, DW_AttribKind_UpperBound)); + if (is_vla) { + log_infof("[%llx] TODO: upper bound is a variable\n", n->tag.info_off); + goto array_type_exit; + } else { + upper_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_UpperBound); + upper_bound += 1; // turn upper bound into exclusive range + } + } else { + // zero sized array + } + + struct SubrangeNode *s = push_array(scratch.arena, struct SubrangeNode, 1); + s->count = upper_bound - lower_bound; + SLLStackPush(subrange_stack, s); + } + + RDIM_Type *array_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_Type *direct_type = array_base_type; + U64 size_cursor = array_base_type->byte_size; + for EachNode(s, struct SubrangeNode, subrange_stack) { + size_cursor *= s->count; + + RDIM_Type *t; + if (s->next) { t = d2r_create_type(arena, type_table); } + else { t = d2r_create_type_from_offset(arena, type_table, tag.info_off); } + + t->kind = RDI_TypeKind_Array; + t->direct_type = direct_type; + t->byte_size = size_cursor; + t->count = s->count; + + direct_type = t; + } + + error = 0; + array_type_exit:; + + // in case of an error, assign null to the array type + if (error) { + Assert(d2r_type_from_offset(type_table, tag.info_off) == 0); // this should the first time this type is being parsed + hash_table_push_u64_raw(arena, type_table->ht, tag.info_off, type_table->builtin_types[RDI_TypeKind_NULL]); + } + + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_SubrangeType: { + log_infof("unexpected tag"); + } break; + case DW_TagKind_Inheritance: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_StructureType || parent_tag.kind == DW_TagKind_ClassType) { + RDIM_Type *parent = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_Type *type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); + RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, parent->udt); + member->kind = RDI_MemberKind_Base; + member->type = type; + member->off = safe_cast_u32(dw_const_u32_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation)); + } else { + log_infof("unexpected parent tag"); + } + } break; + } + } + scratch_end(scratch); +} + +internal B8 +d2r_is_udt_tag_converted(DW_TagNode *tag_node) +{ + return !!(tag_node->tag.v & D2R_TagFlags_UdtConverted); +} + +internal void +d2r_flag_converted_udt_tag(DW_TagNode *tag_node) +{ + tag_node->tag.v |= D2R_TagFlags_UdtConverted; +} + +internal void +d2r_inline_anonymous_udt_member(Arena *arena, RDIM_UDT *top_udt, U64 base_off, RDIM_UDT *udt) +{ + for (RDIM_UDTMember *curr = udt->first_member, *prev = 0; curr != 0; prev = curr, curr = curr->next) { + if (curr->name.size == 0) { + d2r_inline_anonymous_udt_member(arena, top_udt, base_off + curr->off, curr->type->udt); + } else { + // copy member and adjust its offset relative to inlined position in the root UDT + RDIM_UDTMember *m = rdim_udt_push_member(arena, &g_d2r_shared.udts, top_udt); + *m = *curr; + m->off += base_off; + } + } +} + +internal void +d2r_convert_udts(Arena *arena, + D2R_TypeTable *type_table, + DW_Raw *input, + DW_CompUnit *cu, + DW_Language cu_lang, + DW_TagNode *root) +{ + Temp scratch = scratch_begin(&arena, 1); + + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + + // skip converted tags + if (d2r_is_udt_tag_converted(tag_node)) { + d2r_tag_iterator_skip_children(it); + continue; + } + d2r_flag_converted_udt_tag(tag_node); + + if (tag.kind == DW_TagKind_ClassType || tag.kind == DW_TagKind_StructureType || tag.kind == DW_TagKind_UnionType) { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &g_d2r_shared.udts, D2R_UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } else if (tag.kind == DW_TagKind_EnumerationType) { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &g_d2r_shared.udts, D2R_UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } else if (tag.kind == DW_TagKind_Member) { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + B32 is_parent_udt = parent_tag.kind == DW_TagKind_StructureType || + parent_tag.kind == DW_TagKind_ClassType || + parent_tag.kind == DW_TagKind_UnionType; + if (is_parent_udt) { + RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_Type *type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + U64 off = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation); + if (name.size == 0) { // inline anonymous members + DW_Reference type_ref = dw_ref_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Type); + RDIM_Type *member_type = d2r_type_from_offset(type_table, type_ref.info_off); + RDIM_UDT *member_udt = member_type->udt; + if (member_udt == 0) { + DW_Language ref_cu_lang = dw_const_u64_from_tag_attrib_kind(input, type_ref.cu, type_ref.cu->tag, DW_AttribKind_Language); + DW_TagNode *ref_tag_node = dw_tag_node_from_info_off(type_ref.cu, type_ref.info_off); + d2r_convert_udts(arena, type_table, input, type_ref.cu, ref_cu_lang, ref_tag_node); + Assert(member_type->udt); + member_udt = member_type->udt; + } + d2r_inline_anonymous_udt_member(arena, parent_type->udt, off, member_udt); + } else { + RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, parent_type->udt); + member->kind = RDI_MemberKind_DataField; + member->name = name; + member->type = type; + member->off = off; + } + } else { + log_infof("unexpected parent tag @ .debug_info+%llx", tag.info_off); + } + } else if (tag.kind == DW_TagKind_Enumerator) { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_EnumerationType) { + RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_UDTEnumVal *udt_member = rdim_udt_push_enum_val(arena, &g_d2r_shared.udts, parent_type->udt); + udt_member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + udt_member->val = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ConstValue); + } else { + log_infof("unexpected parent tag @ .debug_info+%llx", tag.info_off); + } + } + } + scratch_end(scratch); +} + +internal RDIM_Scope * +d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges) +{ + // fill out scope + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, scopes, scope_chunk_cap); + + // push ranges + for EachNode(i, Rng1U64Node, ranges.first) { + rdim_scope_push_voff_range(arena, scopes, scope, (RDIM_Rng1U64){.min = i->v.min, i->v.max}); + } + + // associate scope with tag + tag_stack->scope = scope; + + // update scope hierarchy + DW_TagKind parent_tag_kind = tag_stack->next->node->tag.kind; + if (parent_tag_kind == DW_TagKind_SubProgram || parent_tag_kind == DW_TagKind_InlinedSubroutine || parent_tag_kind == DW_TagKind_LexicalBlock) { + RDIM_Scope *parent = tag_stack->next->scope; + + scope->parent_scope = parent; + scope->symbol = parent->symbol; + + if (parent->last_child) { + parent->last_child->next_sibling = scope; + } + SLLQueuePush_N(parent->first_child, parent->last_child, scope, next_sibling); + } + + return scope; +} + +internal RDIM_Type ** +d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out) +{ + Temp scratch = scratch_begin(&arena, 1); + + RDIM_TypeList list = {0}; + B32 has_vargs = 0; + for (DW_TagNode *i = cur_node->first_child; i != 0; i = i->sibling) { + if (i->tag.kind == DW_TagKind_FormalParameter) { + RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); + n->v = d2r_type_from_attrib(type_table, input, cu, i->tag, DW_AttribKind_Type); + SLLQueuePush(list.first, list.last, n); + ++list.count; + } else if (i->tag.kind == DW_TagKind_UnspecifiedParameters) { + has_vargs = 1; + } + } + + if (has_vargs) { + RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); + n->v = type_table->builtin_types[RDI_TypeKind_Variadic]; + SLLQueuePush(list.first, list.last, n); + ++list.count; + } + + // collect params + *param_count_out = list.count; + RDIM_Type **params = rdim_array_from_type_list(arena, list); + + scratch_end(scratch); + return params; +} + +internal Rng1U64List +d2r_range_list_from_tag(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag) +{ + // collect non-contiguous range + Rng1U64List raw_ranges = dw_rnglist_from_tag_attrib_kind(arena, input, cu, tag, DW_AttribKind_Ranges); + + // exclude invalid ranges caused by linker optimizations + Rng1U64List ranges = {0}; + for (Rng1U64Node *n = raw_ranges.first, *next = 0; n != 0; n = next) { + next = n->next; + if (n->v.min < image_base || n->v.min > n->v.max) { + continue; + } + rng1u64_list_push_node(&ranges, n); + } + + // debase ranges + for EachNode(r, Rng1U64Node, ranges.first) { + r->v.min -= image_base; + r->v.max -= image_base; + } + + // collect contiguous range + { + DW_Attrib *lo_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_LowPc); + DW_Attrib *hi_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_HighPc); + if (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) { + U64 lo_pc = dw_address_from_attrib(input, cu, lo_pc_attrib); + + U64 hi_pc = 0; + DW_AttribClass hi_pc_class = dw_value_class_from_attrib(cu, hi_pc_attrib); + if (hi_pc_class == DW_AttribClass_Address) { + hi_pc = dw_address_from_attrib(input, cu, hi_pc_attrib); + } else if (hi_pc_class == DW_AttribClass_Const) { + hi_pc = dw_const_u64_from_attrib(input, cu, hi_pc_attrib); + hi_pc += lo_pc; + } + + if (lo_pc >= image_base && hi_pc >= image_base) { + if (lo_pc < hi_pc) { + rng1u64_list_push(arena, &ranges, rng_1u64(lo_pc - image_base, hi_pc - image_base)); + } else { + log_infof("invalid range @ .debug_info+%llx", tag.info_off); + } + } else { + // invalid low and hi PC are likely are caused by an optimization pass during linking + } + } else if ((lo_pc_attrib->attrib_kind == DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) || + (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind == DW_AttribKind_Null)) { + log_infof("invalid range @ .debug_info+%llx", tag.info_off); + } + } + + return ranges; +} + +internal void +d2r_convert_symbols(Arena *arena, + D2R_TypeTable *type_table, + DW_Raw *input, + DW_CompUnit *cu, + DW_Language cu_lang, + U64 image_base, + Arch arch, + DW_TagNode *root, + RDIM_Unit *unit_rdi) +{ + Temp scratch = scratch_begin(&arena, 1); + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + switch (tag.kind) { + case DW_TagKind_Null: { InvalidPath; } break; + case DW_TagKind_ClassType: + case DW_TagKind_StructureType: + case DW_TagKind_UnionType: { + // visit children to collect methods and variables + } break; + case DW_TagKind_EnumerationType: + case DW_TagKind_SubroutineType: + case DW_TagKind_Typedef: + case DW_TagKind_BaseType: + case DW_TagKind_PointerType: + case DW_TagKind_RestrictType: + case DW_TagKind_VolatileType: + case DW_TagKind_ConstType: + case DW_TagKind_ArrayType: + case DW_TagKind_SubrangeType: + case DW_TagKind_Inheritance: + case DW_TagKind_Enumerator: + case DW_TagKind_Member: { + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_SubProgram: { + // handled during type conversion step + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Declaration)) { + d2r_tag_iterator_skip_children(it); + break; + } + + DW_InlKind inl = DW_Inl_NotInlined; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Inline)) { inl = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Inline); } + + switch (inl) { + case DW_Inl_DeclaredNotInlined: + case DW_Inl_NotInlined: { + U64 param_count = 0; + RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); + + // get return type + RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + // fill out proc type + RDIM_Type *proc_type = d2r_create_type(arena, type_table); + proc_type->kind = RDI_TypeKind_Function; + proc_type->byte_size = cu->address_size; + proc_type->direct_type = ret_type; + proc_type->count = param_count; + proc_type->param_types = params; + + // get container type + RDIM_Type *container_type = 0; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { + container_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); + } else { + DW_TagNode *parent = d2r_tag_iterator_parent_tag_node(it); + container_type = d2r_type_from_offset(type_table, parent->tag.info_off); + if (container_type == 0) { + log_infof("ERROR: failed to infer parent type of subprogram from .debug_info+0x%llx\n", parent->tag.info_off); + } + } + + // get frame base expression + String8 frame_base_expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_FrameBase); + + // get proc container symbol + RDIM_Symbol *proc = rdim_symbol_chunk_list_push(arena, &unit_rdi->procedures, D2R_PROC_CHUNK_CAP); + + // make scope + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + RDIM_Scope *root_scope = d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); + root_scope->symbol = proc; + + // fill out proc + proc->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); + proc->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + proc->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); + proc->type = proc_type; + proc->container_scope = 0; + proc->container_type = container_type; + proc->root_scope = root_scope; + proc->location_cases = d2r_locset_from_attrib(arena, &unit_rdi->scopes, root_scope, input, cu, image_base, arch, tag, DW_AttribKind_FrameBase); + + // sub program with user-defined parent tag is a method + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_ClassType || parent_tag.kind == DW_TagKind_StructureType) { + // DW_VirtualityKind -> RDI_MemberKind + RDI_MemberKind member_kind = RDI_MemberKind_NULL; + { + DW_VirtualityKind virtuality = DW_VirtualityKind_None; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Virtuality)) { + virtuality = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Virtuality); + switch (virtuality) { + case DW_VirtualityKind_None: member_kind = RDI_MemberKind_Method; break; + case DW_VirtualityKind_Virtual: member_kind = RDI_MemberKind_VirtualMethod; break; + case DW_VirtualityKind_PureVirtual: member_kind = RDI_MemberKind_VirtualMethod; break; // TODO: create kind for pure virutal + default: { log_infof("unhandled virtuality kind"); } break; + } + } + } + + RDIM_Type *type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, type->udt); + member->kind = member_kind; + member->type = type; + member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + } else if (parent_tag.kind != DW_TagKind_CompileUnit) { + log_infof("unexpected tag @ .debug_info+%llx", tag.info_off); + } + + it->stack->scope = root_scope; + } break; + case DW_Inl_DeclaredInlined: + case DW_Inl_Inlined: { + d2r_tag_iterator_skip_children(it); + } break; + default: InvalidPath; break; + } + } break; + case DW_TagKind_InlinedSubroutine: { + U64 param_count = 0; + RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); + + // get return type + RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + // fill out proc type + RDIM_Type *proc_type = d2r_create_type(arena, type_table); + proc_type->kind = RDI_TypeKind_Function; + proc_type->byte_size = cu->address_size; + proc_type->direct_type = ret_type; + proc_type->count = param_count; + proc_type->param_types = params; + + // get container type + RDIM_Type *owner = 0; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { + owner = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); + } + + // fill out inline site + RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, &unit_rdi->inline_sites, D2R_INLINE_SITE_CHUNK_CAP); + inline_site->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + inline_site->type = proc_type; + inline_site->owner = owner; + inline_site->line_table = 0; + + // make scope + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + RDIM_Scope *root_scope = d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); + root_scope->inline_site = inline_site; + } break; + case DW_TagKind_Variable: { + String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + RDIM_Type *type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || + parent_tag.kind == DW_TagKind_InlinedSubroutine || + parent_tag.kind == DW_TagKind_LexicalBlock) { + RDIM_Scope *scope = it->stack->next->scope; + RDIM_Symbol *local = rdim_symbol_chunk_list_push(arena, &scope->locals, 8); + local->name = name; + local->type = type; + local->location_cases = d2r_var_locset_from_tag(arena, &unit_rdi->scopes, scope, input, cu, image_base, arch, tag); + } else { + + // NOTE: due to a bug in clang in stb_sprintf.h local variables + // are declared in global scope without a name + if (name.size == 0) { break; } + + // decls do not have location info + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { break; } + + U64 voff = max_U64; + B32 is_thread_var = 0; + { + DW_Attrib *loc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_Location); + DW_AttribClass loc_class = dw_value_class_from_attrib(cu, loc_attrib); + if (loc_class == DW_AttribClass_ExprLoc) { + Temp temp = temp_begin(scratch.arena); + + String8 expr = dw_exprloc_from_attrib(input, cu, loc_attrib); + D2R_ValueType expr_type = 0; + RDIM_EvalBytecode bc = d2r_bytecode_from_expression(temp.arena, input, image_base, arch, cu->addr_lu, expr, cu, &expr_type); + + // evaluate bytecode to virutal offset if possible + if (expr_type == D2R_ValueType_Address) { + B32 is_static = rdim_is_eval_bytecode_static(bc); + if (is_static) { + if (!rdim_static_eval_bytecode_to_voff(bc, image_base, &voff)) { + log_infof("failed to evalute byte code to virtual offset @ .debug_info+%llx", tag.info_off); + } + } + } + + // is this a thread variable? + is_thread_var = rdim_is_bytecode_tls_dependent(bc); + + temp_end(temp); + } + } + + RDIM_SymbolChunkList *var_chunks; U64 var_chunks_cap; + if (is_thread_var) { var_chunks = &unit_rdi->thread_variables; var_chunks_cap = D2R_TVAR_CHUNK_CAP; } + else { var_chunks = &unit_rdi->global_variables; var_chunks_cap = D2R_GVAR_CHUNK_CAP; } + + RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, var_chunks, var_chunks_cap); + var->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); + var->name = name; + var->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); + var->type = type; + var->container_scope = 0; + RDIM_Location loc = {.kind = is_thread_var ? RDI_LocationKind_TLSOff : RDI_LocationKind_ModuleOff, .offset = voff}; + RDIM_Rng1U64 range = {0, 0xffffffffffffffffull}; + rdim_location_case_list_push(arena, &var->location_cases, loc, range); + } + } break; + case DW_TagKind_FormalParameter: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || parent_tag.kind == DW_TagKind_InlinedSubroutine) { + RDIM_Scope *scope = it->stack->next->scope; + RDIM_Symbol *param = rdim_symbol_chunk_list_push(arena, &scope->locals, 4); + param->is_param = 1; + param->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + param->type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + param->location_cases = d2r_var_locset_from_tag(arena, &unit_rdi->scopes, scope, input, cu, image_base, arch, tag); + } else { + Assert(!"this is a local variable"); + log_infof(".debug_info+%llx out of scope formal parameter", tag.info_off); + } + } break; + case DW_TagKind_LexicalBlock: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || + parent_tag.kind == DW_TagKind_InlinedSubroutine || + parent_tag.kind == DW_TagKind_LexicalBlock) { + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); + } + } break; + case DW_TagKind_CallSite: { + // TODO + } break; + case DW_TagKind_CallSiteParameter: { + // TODO + } break; + case DW_TagKind_Label: + case DW_TagKind_CompileUnit: + case DW_TagKind_UnspecifiedParameters: + case DW_TagKind_Namespace: + case DW_TagKind_ImportedDeclaration: + case DW_TagKind_PtrToMemberType: + case DW_TagKind_TemplateTypeParameter: + case DW_TagKind_ReferenceType: { + // TODO: + } break; + default: + { + // NotImplemented; + } break; + } + } + scratch_end(scratch); +} + +force_inline int +d2r_src_file_lookup_compar(void *raw_a, void *raw_b) +{ + D2R_SrcFileLookup *a = raw_a, *b = raw_b; + DW_LineFile *file_a = a->file, *file_b = b->file; + String8 dir_path_a = a->vm->header.dir_table.v[file_a->dir_idx]; + String8 dir_path_b = b->vm->header.dir_table.v[file_b->dir_idx]; + int cmp = str8_compar(dir_path_a, dir_path_b, 0); + if (cmp == 0) { + cmp = str8_compar(file_a->path, file_b->path, 0); + } + return cmp; +} + +force_inline +LFHT_IS_KEY_EQUAL_FUNC(d2r_src_file_lookup_is_equal) +{ + return d2r_src_file_lookup_compar(a, b) == 0; +} + +force_inline int +d2r_src_file_lfht_key_value_is_before(void *raw_a, void *raw_b) +{ + return d2r_src_file_lookup_compar(*(D2R_SrcFileLookup **)raw_a, *(D2R_SrcFileLookup **)raw_b) < 0; +} + +internal U64 +d2r_hash_line_file(String8 dir_path, DW_LineFile *file) +{ + XXH3_state_t hasher = {0}; + XXH3_64bits_reset(&hasher); + XXH3_64bits_update(&hasher, dir_path.str, dir_path.size); + XXH3_64bits_update(&hasher, file->path.str, file->path.size); + XXH3_64bits_update(&hasher, &file->time_stamp, sizeof(file->time_stamp)); + XXH3_64bits_update(&hasher, &file->md5, sizeof(file->md5)); + XXH64_hash_t hash = XXH3_64bits_digest(&hasher); + return hash; +} + +internal void +d2r_sort_ptrs(void **ptrs, U64 count, int (* is_before)(void *a, void *b)) +{ + radsort(ptrs, count, is_before); +} + +internal D2R_CompUnitContribMap +d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Raw *input, U64 image_base) +{ + Temp scratch = scratch_begin(&arena, 1); + Temp temp = temp_begin(arena); + B32 parse_failed = 1; + + Rng1U64List unit_range_list = dw_unit_ranges_from_data(scratch.arena, input->sec[DW_Section_ARanges].data); + + D2R_CompUnitContribMap cm = {0}; + cm.info_off_arr = push_array(temp.arena, U64, unit_range_list.count); + cm.voff_range_arr = push_array(temp.arena, RDIM_Rng1U64ChunkList, unit_range_list.count); + + for EachNode(range_n, Rng1U64Node, unit_range_list.first) { + String8 unit_data = str8_substr(input->sec[DW_Section_ARanges].data, range_n->v); + U64 unit_cursor = 0; + + U32 first_four_bytes = 0; + if (str8_deserial_read_struct(input->sec[DW_Section_Info].data, 0, &first_four_bytes) != sizeof(first_four_bytes)) { goto exit; } + DW_Format unit_format = first_four_bytes == max_U32 ? DW_Format_64Bit : DW_Format_32Bit; + + U64 unit_length; + TryRead(str8_deserial_read_dwarf_packed_size(unit_data, unit_cursor, &unit_length), unit_cursor, exit); + + DW_Version version; + TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &version), unit_cursor, exit); + + if (version != DW_Version_2) { + log_infof("unknown .debug_aranges version %u @ 0x%llx", version, range_n->v.min); + continue; + } + + U64 cu_info_off; + TryRead(str8_deserial_read_dwarf_uint(unit_data, unit_cursor, unit_format, &cu_info_off), unit_cursor, exit); + + U8 address_size = 0; + TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &address_size), unit_cursor, exit); + + U8 segment_selector_size = 0; + TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &segment_selector_size), unit_cursor, exit); + + U64 tuple_size = address_size * 2 + segment_selector_size; + U64 bytes_too_far_past_boundary = unit_cursor % tuple_size; + if (bytes_too_far_past_boundary > 0) { + unit_cursor += tuple_size - bytes_too_far_past_boundary; + } + + RDIM_Rng1U64ChunkList voff_ranges = {0}; + if (segment_selector_size == 0) { + while (unit_cursor + address_size * 2 <= unit_data.size) { + U64 address = 0; + U64 length = 0; + TryRead(str8_deserial_read(unit_data, unit_cursor, &address, address_size, address_size), unit_cursor, exit); + TryRead(str8_deserial_read(unit_data, unit_cursor, &length, address_size, address_size), unit_cursor, exit); + + if (address == 0 && length == 0) { break; } + if (address == 0) { continue; } + + if (address < image_base) { + log_infof("invalid address 0x%llx in .debug_aranges+%llx", range_n->v.min + unit_cursor); + continue; + } + + U64 min = address - image_base; + U64 max = min + length; + rdim_rng1u64_chunk_list_push(temp.arena, &voff_ranges, 256, (RDIM_Rng1U64){.min = min, .max = max}); + } + } else { + NotImplemented; + } + + U64 map_idx = cm.count++; + cm.info_off_arr[map_idx] = cu_info_off; + cm.voff_range_arr[map_idx] = voff_ranges; + } + + parse_failed = 0; + exit:; + if (parse_failed) { + MemoryZeroStruct(&cm); + temp_end(temp); + } + scratch_end(scratch); + return cm; +} + +internal RDIM_Rng1U64ChunkList +d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off) +{ + RDIM_Rng1U64ChunkList voff_ranges = {0}; + U64 voff_list_idx = u64_array_bsearch(map.info_off_arr, map.count, info_off); + if (voff_list_idx < map.count) { + voff_ranges = map.voff_range_arr[voff_list_idx]; + } + return voff_ranges; +} + +internal RDIM_BakeParams +d2r_convert(Arena *arena, D2R_ConvertParams *params) +{ + Temp scratch = scratch_begin(&arena, 1); + + if (lane_idx() == 0) { + //////////////////////////////// + + Arch arch = Arch_Null; + U64 image_base = 0; + DW_Raw input = {0}; + PathStyle path_style = PathStyle_Null; + + switch (params->exe_kind) { + case ExecutableImageKind_Null: {} break; + case ExecutableImageKind_CoffPe: { + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, params->exe_data); + String8 raw_sections = str8_substr(params->exe_data, pe.section_table_range); + COFF_SectionHeader *section_table = str8_deserial_get_raw_ptr(raw_sections, 0, sizeof(COFF_SectionHeader) * pe.section_count); + String8 string_table = str8_substr(params->exe_data, pe.string_table_range); + + arch = pe.arch; + image_base = pe.image_base; + input = dw_input_from_coff_section_table(scratch.arena, params->exe_data, string_table, pe.section_count, section_table); + path_style = PathStyle_WindowsAbsolute; + + g_d2r_shared.binary_sections = c2r_rdi_binary_sections_from_coff_sections(arena, params->exe_data, string_table, pe.section_count, section_table); + } break; + case ExecutableImageKind_Elf32: + case ExecutableImageKind_Elf64: { + ELF_Bin bin = elf_bin_from_data(scratch.arena, params->dbg_data); + + arch = arch_from_elf_machine(bin.hdr.e_machine); + image_base = elf_base_addr_from_bin(&bin); + input = dw_raw_from_elf_bin(scratch.arena, params->dbg_data, &bin); + path_style = PathStyle_UnixAbsolute; + + g_d2r_shared.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, params->dbg_data, &bin, &bin.shdrs); + } break; + default: { InvalidPath; } break; + } + + //////////////////////////////// + + ProfBegin("compute exe hash"); + U64 exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); + ProfEnd(); + + g_d2r_shared.top_level_info = rdim_make_top_level_info(params->exe_name, arch, exe_hash, g_d2r_shared.binary_sections); + + //////////////////////////////// + + ProfBegin("Parse Unit Contrib Map"); + D2R_CompUnitContribMap cu_contrib_map = {0}; + if (input.sec[DW_Section_ARanges].data.size) { + cu_contrib_map = d2r_cu_contrib_map_from_aranges(arena, &input, image_base); + } + ProfEnd(); + + ProfBegin("Parse Comop Unit Ranges"); + DW_ListUnitInput lu_input = dw_list_unit_input_from_input(scratch.arena, &input); + Rng1U64List cu_range_list = dw_unit_ranges_from_data(scratch.arena, input.sec[DW_Section_Info].data); + Rng1U64Array cu_ranges = rng1u64_array_from_list(scratch.arena, &cu_range_list); + ProfEnd(); + + //////////////////////////////// + + ProfBegin("Parse Compile Unit Headers"); + DW_CompUnit *cu_arr = push_array(scratch.arena, DW_CompUnit, cu_ranges.count); + for EachIndex(cu_idx, cu_ranges.count) { + cu_arr[cu_idx] = dw_cu_from_info_off(scratch.arena, &input, lu_input, cu_ranges.v[cu_idx].min, params->is_parse_relaxed); + } + ProfEnd(); + + //////////////////////////////// + + RDIM_LineTable **cu_line_tables_rdi = push_array(scratch.arena, RDIM_LineTable *, cu_ranges.count); + ProfScope("Convert Line Tables [Count: %llu]", cu_ranges.count) + { + Temp temp = temp_begin(scratch.arena); + + // TODO: per thread task + ProfBegin("Up front parse line VM headers"); + DW_LineVM **line_vms = push_array(temp.arena, DW_LineVM *, cu_ranges.count); + for EachIndex(cu_idx, cu_ranges.count) { + line_vms[cu_idx] = dw_line_vm_init(&input, &cu_arr[cu_idx]); + } + ProfEnd(); + + // TODO: sync + // + // sum all source files (including duplicates) + U64 total_file_count = 0; + for EachIndex(cu_idx, cu_ranges.count) { total_file_count += line_vms[cu_idx]->header.file_table.count; } + + // TODO: sync + ProfBegin("Dedup source files [Count: %llu]", total_file_count); + LFHT_NodeChunkList *src_file_lfht_nodes = push_array(temp.arena, LFHT_NodeChunkList, lane_count()); + LFHT_Node *src_file_lfht = 0; + + // TODO: per thread + { + D2R_SrcFileLookup *lookup = 0; + for EachIndex(cu_idx, cu_ranges.count) { + DW_LineVM *vm = line_vms[cu_idx]; + for EachIndex(file_idx, vm->header.file_table.count) { + DW_LineFile *file = &vm->header.file_table.v[file_idx]; + + if (lookup == 0) { lookup = push_array(temp.arena, D2R_SrcFileLookup, 1); } + lookup->vm = vm; + lookup->file = file; + + LFHT_NodeChunkList *node_chunks = &src_file_lfht_nodes[lane_idx()]; + U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); + B32 was_inserted = lfht_insert(temp.arena, node_chunks, &src_file_lfht, hash, lookup, d2r_src_file_lookup_is_equal, 0); + if (was_inserted) { + lookup = 0; + } + } + } + } + ProfEnd(); + + // TODO: sync + ProfBegin("Extract source file lookups"); + U64 unique_file_count = lfht_total_count_from_node_chunk_lists(lane_count(), src_file_lfht_nodes); + void **lookups = lfht_data_from_node_chunk_lists(temp.arena, unique_file_count, lane_count(), src_file_lfht_nodes); + ProfEnd(); + + // TODO: sync + ProfBegin("Sort unique source files [Count: %llu]", unique_file_count); + d2r_sort_ptrs(lookups, unique_file_count, d2r_src_file_lfht_key_value_is_before); + ProfEnd(); + + // TODO: per thread task + ProfBegin("Convert source files"); + for EachIndex(i, unique_file_count) { + D2R_SrcFileLookup *lookup = lookups[i]; + + DW_LineFile *src = lookup->file; + RDIM_SrcFile *dst = rdim_src_file_chunk_list_push(arena, &g_d2r_shared.src_files, D2R_SRC_FILE_CAP); + + // make file path + String8 path; + { + Temp temp = temp_begin(scratch.arena); + + String8 comp_dir = lookup->vm->header.dir_table.v[0]; + + PathStyle comp_path_style = path_style_from_str8(comp_dir); + PathStyle dir_path_style = path_style_from_str8(lookup->vm->header.dir_table.v[src->dir_idx]); + PathStyle file_path_style = path_style_from_str8(src->path); + + String8List dir_path_list = str8_split_path(temp.arena, lookup->vm->header.dir_table.v[src->dir_idx]); + String8List file_path_list = str8_split_path(temp.arena, src->path); + + String8List path_list = {0}; + if (dir_path_style == PathStyle_Relative) { + String8List comp_dir_list = str8_split_path(temp.arena, comp_dir); + str8_list_concat_in_place(&path_list, &comp_dir_list); + } + str8_list_concat_in_place(&path_list, &dir_path_list); + str8_list_concat_in_place(&path_list, &file_path_list); + + PathStyle path_style = PathStyle_SystemAbsolute; + if (dir_path_style != PathStyle_Relative) { path_style = dir_path_style; } + else if (file_path_style != PathStyle_Relative) { path_style = file_path_style; } + else if (comp_path_style != PathStyle_Relative) { path_style = comp_path_style; } + + str8_path_list_resolve_dots_in_place(&path_list, path_style); + path = str8_path_list_join_by_style(arena, &path_list, path_style); + + temp_end(temp); + } + + // fill out source file + dst->path = path; + if ( ! u128_match(src->md5, u128_zero())) { + dst->checksum_kind = RDI_ChecksumKind_MD5; + dst->checksum = str8_copy(arena, str8_struct(&src->md5)); + } else if (src->time_stamp != 0) { + dst->checksum_kind = RDI_ChecksumKind_Timestamp; + dst->checksum = str8_copy(arena, str8_struct(&src->time_stamp)); + } + + lookup->src_file = dst; + } + ProfEnd(); + + // TODO: sync + RDIM_LineTableChunkList *lane_line_table_chunks = push_array(temp.arena, RDIM_LineTableChunkList, lane_count()); + + // TODO: per thread task + ProfBegin("Convert line sequences"); + U64 *line_seq_counts_per_lane = push_array(scratch.arena, U64, lane_count()); + for EachIndex(cu_idx, cu_ranges.count) { + RDIM_LineTableChunkList *lane_line_table_chunk_list = &lane_line_table_chunks[lane_idx()]; + + // push new line table for the compile unit + RDIM_LineTable *line_table = rdim_line_table_chunk_list_push(arena, lane_line_table_chunk_list, D2R_LINE_TABLE_CAP); + cu_line_tables_rdi[cu_idx] = line_table; + + // push line buffer +#define D2R_LineBufferMax 1024 + struct LineBuffer { U64 file_index; U64 voffs[D2R_LineBufferMax]; U32 line_nums[D2R_LineBufferMax]; U32 col_nums[D2R_LineBufferMax]; } LineBuffer; + struct LineBuffer *line_buffer = push_array(temp.arena, struct LineBuffer, 1); + U64 line_buffer_size = 0; + + // decode line sequences + DW_LineVM *vm = line_vms[cu_idx]; + U64 *line_seq_counts_per_file = push_array(temp.arena, U64, vm->header.file_table.count); + while (dw_line_vm_step(vm)) { + if (vm->new_line) { + // lazy defined files are not supported + if (vm->state.file_index >= vm->header.file_table.count) { continue; } + + // time to flush the buffer? + if (((vm->opcode == DW_StdOpcode_ExtendedOpcode && vm->ext_opcode == DW_ExtOpcode_EndSequence) || + (line_buffer_size >= D2R_LineBufferMax || line_buffer->file_index != vm->state.file_index)) + && line_buffer_size > 0) { + // lookup source file + DW_LineFile *file = &vm->header.file_table.v[line_buffer->file_index]; + U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); + D2R_SrcFileLookup *lookup = lfht_search(src_file_lfht, hash, &(D2R_SrcFileLookup){ .file = file, .vm = vm }, d2r_src_file_lookup_is_equal, 0); + RDIM_SrcFile *src_file = lookup->src_file; + + // copy line info + U64 *voffs = push_array_no_zero(arena, U64, line_buffer_size + 1); + U32 *line_nums = push_array_no_zero(arena, U32, line_buffer_size); + voffs[line_buffer_size] = vm->state.address - image_base; + MemoryCopyTyped(voffs, line_buffer->voffs, line_buffer_size); + MemoryCopyTyped(line_nums, line_buffer->line_nums, line_buffer_size); + + RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, lane_line_table_chunk_list, line_table, src_file, voffs, line_nums, 0, line_buffer_size); + + // atomic implementation of @rdim_src_file_push_line_sequence + { + // associate line fragment with source file + RDIM_SrcFileLineMapFragment *line_frag = push_array(arena, RDIM_SrcFileLineMapFragment, 1); + line_frag->seq = line_seq; + + // insert line fragment node + for (;;) { + RDIM_SrcFileLineMapFragment *next = src_file->first_line_map_fragment; + line_frag->next = next; + RDIM_SrcFileLineMapFragment *curr = ins_atomic_ptr_eval_cond_assign(&src_file->first_line_map_fragment, line_frag, next); + if (curr == 0) { + ins_atomic_u64_add_eval(&g_d2r_shared.src_files.source_line_map_count, 1); + } + if (curr == next) { break; } + } + + // accumulate line sequence counts per file + line_seq_counts_per_file[vm->state.file_index] += line_seq->line_count; + + // accumulate line sequence counts per lane + line_seq_counts_per_lane[lane_idx()] += line_seq->line_count; + } + + // reset line buffer size tracker + line_buffer_size = 0; + } + + if (line_buffer_size > 0 && line_buffer->voffs[line_buffer_size - 1] == vm->state.address) { + line_buffer->line_nums[line_buffer_size - 1] = Min(line_buffer->line_nums[line_buffer_size - 1], safe_cast_u32(vm->state.line)); + } else { + // append line to the buffer + line_buffer->file_index = vm->state.file_index; + line_buffer->voffs[line_buffer_size] = vm->state.address - image_base; + line_buffer->line_nums[line_buffer_size] = safe_cast_u32(vm->state.line); + line_buffer_size += 1; + } + } + } + + // update line sequence counts per source file @rdim_src_file_push_line_sequence + for EachIndex(file_idx, vm->header.file_table.count) { + if (line_seq_counts_per_file[file_idx] > 0) { + DW_LineFile *file = &vm->header.file_table.v[file_idx]; + U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); + D2R_SrcFileLookup *lookup = lfht_search(src_file_lfht, hash, &(D2R_SrcFileLookup){ .file = file, .vm = vm }, d2r_src_file_lookup_is_equal, 0); + RDIM_SrcFile *src_file = lookup->src_file; + ins_atomic_u64_add_eval(&src_file->total_line_count, line_seq_counts_per_file[file_idx]); + } + } + } + + // TODO: sync + // + // update total line sequences count in shared @rdim_src_file_push_line_sequence + for EachIndex(i, lane_count()) { + g_d2r_shared.src_files.total_line_count += line_seq_counts_per_lane[i]; + } + + // TODO: sync + for EachIndex(i, lane_count()) { + rdim_line_table_chunk_list_concat_in_place(&g_d2r_shared.line_tables, &lane_line_table_chunks[i]); + } + + // TODO: per thread task + ProfBegin("Relase line VMs"); + for EachIndex(i, cu_ranges.count) { + dw_line_vm_release(line_vms[i]); + } + ProfEnd(); + + temp_end(temp); + } + + //////////////////////////////// + + RDIM_Type *builtin_types[RDI_TypeKind_COUNT] = {0}; + for (RDI_TypeKind type_kind = RDI_TypeKind_FirstBuiltIn; type_kind <= RDI_TypeKind_LastBuiltIn; type_kind += 1) { + RDIM_Type *type = rdim_type_chunk_list_push(arena, &g_d2r_shared.types, D2R_TYPE_CHUNK_CAP); + type->kind = type_kind; + type->name.str = rdi_string_from_type_kind(type_kind, &type->name.size); + type->byte_size = rdi_size_from_basic_type_kind(type_kind); + builtin_types[type_kind] = type; + } + + // fixup float80 size + if (arch == Arch_x64 || arch == Arch_arm64) { + builtin_types[RDI_TypeKind_F80]->byte_size = 16; + } + + builtin_types[RDI_TypeKind_Void]->byte_size = rdi_addr_size_from_arch(g_d2r_shared.top_level_info.arch); + builtin_types[RDI_TypeKind_Handle]->byte_size = rdi_addr_size_from_arch(g_d2r_shared.top_level_info.arch); + + builtin_types[RDI_TypeKind_Variadic] = rdim_type_chunk_list_push(arena, &g_d2r_shared.types, D2R_TYPE_CHUNK_CAP); + builtin_types[RDI_TypeKind_Variadic]->kind = RDI_TypeKind_Variadic; + + //////////////////////////////// + + ProfBegin("Convert Units"); + for EachIndex(cu_idx, cu_ranges.count) { + Temp comp_temp = temp_begin(scratch.arena); + + DW_CompUnit *cu = &cu_arr[cu_idx]; + + // skip DWO + { + if (cu->dwo_id) { goto next_cu; } + + String8 dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_DwoName); + if (dwo_name.size) { goto next_cu; } + + String8 gnu_dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_GNU_DwoName); + if (gnu_dwo_name.size) { goto next_cu; } + } + + // parse and build tag tree + DW_TagTree tag_tree = dw_tag_tree_from_cu(comp_temp.arena, &input, cu); + + // build (info offset -> tag) hash table to resolve tags with abstract origin + cu->tag_ht = dw_make_tag_hash_table(comp_temp.arena, tag_tree); + + // extract compile unit info + String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); + String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); + String8 cu_prod = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Producer); + DW_Language cu_lang = dw_const_u64_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Language); + + // init type table + D2R_TypeTable *type_table = push_array(comp_temp.arena, D2R_TypeTable, 1); + type_table->ht = hash_table_init(comp_temp.arena, 0x4000); + type_table->types = &g_d2r_shared.types; + type_table->type_chunk_cap = D2R_TYPE_CHUNK_CAP; + type_table->builtin_types = builtin_types; + + // convert compile unit + RDIM_Unit *unit = rdim_unit_chunk_list_push(arena, &g_d2r_shared.units, D2R_UNIT_CHUNK_CAP); + { + RDIM_Rng1U64ChunkList cu_voff_ranges = {0}; + if (cu_idx < cu_contrib_map.count) { + cu_voff_ranges = d2r_voff_ranges_from_cu_info_off(cu_contrib_map, cu_ranges.v[cu_idx].min); + } else { + Rng1U64List range_list = d2r_range_list_from_tag(scratch.arena, &input, cu, image_base, cu->tag); + for EachNode(n, Rng1U64Node, range_list.first) { + rdim_rng1u64_chunk_list_push(arena, &cu_voff_ranges, 512, (RDIM_Rng1U64){ .min = n->v.min, .max = n->v.max }); + } + } + unit->unit_name = cu_name; + unit->compiler_name = cu_prod; + unit->source_file = str8_zero(); // TODO + unit->object_file = str8_zero(); // TODO + unit->archive_file = str8_zero(); // TODO + unit->build_path = cu_dir; + unit->language = d2r_rdi_language_from_dw_language(cu_lang); + unit->line_table = cu_line_tables_rdi[cu_idx]; + unit->voff_ranges = cu_voff_ranges; + } + + // convert debug info + d2r_convert_types(arena, type_table, &input, cu, cu_lang, arch, tag_tree.root); + d2r_convert_udts(arena, type_table, &input, cu, cu_lang, tag_tree.root); + d2r_convert_symbols(arena, type_table, &input, cu, cu_lang, image_base, arch, tag_tree.root, unit); + + next_cu:; + temp_end(comp_temp); + } + ProfEnd(); + } + + lane_sync(); + + RDIM_BakeParams bake_params = {0}; + bake_params.subset_flags = params->subset_flags; + bake_params.top_level_info = g_d2r_shared.top_level_info; + bake_params.binary_sections = g_d2r_shared.binary_sections; + bake_params.units = g_d2r_shared.units; + bake_params.types = g_d2r_shared.types; + bake_params.udts = g_d2r_shared.udts; + bake_params.src_files = g_d2r_shared.src_files; + bake_params.line_tables = g_d2r_shared.line_tables; + + scratch_end(scratch); + return bake_params; +} + diff --git a/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.h b/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.h new file mode 100644 index 00000000..59d19040 --- /dev/null +++ b/obsolete/rdi_from_dwarf/rdi_from_dwarf_old.h @@ -0,0 +1,275 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_FROM_DWARF_H +#define RDI_FROM_DWARF_H + +//////////////////////////////// +//~ Conversion + +typedef struct D2R_ConvertParams +{ + String8 dbg_name; + String8 dbg_data; + String8 exe_name; + String8 exe_data; + ExecutableImageKind exe_kind; + RDIM_SubsetFlags subset_flags; + B32 deterministic; + B32 is_parse_relaxed; +} D2R_ConvertParams; + +#define D2R_UNIT_CHUNK_CAP 512 +#define D2R_UDT_CHUNK_CAP 1024 +#define D2R_TYPE_CHUNK_CAP 1024 +#define D2R_SRC_FILE_CAP 1024 +#define D2R_LINE_TABLE_CAP 1024 +#define D2R_GVAR_CHUNK_CAP 1024 +#define D2R_TVAR_CHUNK_CAP 1024 +#define D2R_LOCATIONS_CAP 4096 +#define D2R_PROC_CHUNK_CAP 4096 +#define D2R_SCOPE_CHUNK_CAP 4096 +#define D2R_INLINE_SITE_CHUNK_CAP 4096 + +typedef struct D2R_Shared +{ + RDIM_TopLevelInfo top_level_info; + RDIM_BinarySectionList binary_sections; + RDIM_UnitChunkList units; + RDIM_UDTChunkList udts; + RDIM_TypeChunkList types; + RDIM_SrcFileChunkList src_files; + RDIM_LineTableChunkList line_tables; +} D2R_Shared; + +//////////////////////////////// +//~ Value Types + +typedef enum +{ + D2R_ArithmeticType_Null, + D2R_ArithmeticType_Signed, + D2R_ArithmeticType_Unsigned, + D2R_ArithmeticType_Float, +} D2R_ArithmeticType; + +#define D2R_ValueType_Signed_XList \ +X(S8, Signed, 1 ) \ +X(S16, Signed, 2 ) \ +X(S32, Signed, 4 ) \ +X(S64, Signed, 8 ) \ +X(S128, Signed, 16) \ +X(S256, Signed, 32) \ +X(S512, Signed, 64) + +#define D2R_ValueType_Unsigned_XList \ +X(U8, Unsigned, 1 ) \ +X(U16, Unsigned, 2 ) \ +X(U32, Unsigned, 4 ) \ +X(U64, Unsigned, 8 ) \ +X(U128, Unsigned, 16) \ +X(U256, Unsigned, 32) \ +X(U512, Unsigned, 64) + +#define D2R_ValueType_Float_XList \ +X(F16, Float, 2 ) \ +X(F32, Float, 4 ) \ +X(F48, Float, 6 ) \ +X(F64, Float, 8 ) \ +X(F80, Float, 10) \ +X(F96, Float, 12) \ +X(F128, Float, 16) + +#define D2R_ValueType_XList \ +X(Generic, Null, 0 ) \ +X(ImplicitValue, Null, 0 ) \ +X(Address, Unsigned, 0 ) \ +D2R_ValueType_Signed_XList \ +D2R_ValueType_Unsigned_XList \ +D2R_ValueType_Float_XList + +typedef enum +{ +#define X(t, ...) D2R_ValueType_##t, + D2R_ValueType_XList +#undef X + D2R_ValueType_Bool = D2R_ValueType_S8, +} D2R_ValueType; + +typedef struct D2R_ValueTypeNode +{ + D2R_ValueType type; + struct D2R_ValueTypeNode *next; +} D2R_ValueTypeNode; + +typedef struct D2R_ValueTypeStack +{ + U64 count; + D2R_ValueTypeNode *top; + D2R_ValueTypeNode *free_list; +} D2R_ValueTypeStack; + +//////////////////////////////// +//~ Type Table + +typedef struct D2R_TypeTable +{ + HashTable *ht; + RDIM_TypeChunkList *types; + U64 type_chunk_cap; + RDIM_Type **builtin_types; +} D2R_TypeTable; + +//////////////////////////////// +//~ Tag Iterator + +typedef DW_TagSpare D2R_TagFlags; +enum +{ + D2R_TagFlags_TypeConverted = (1 << 0), + D2R_TagFlags_UdtConverted = (1 << 1), +}; + +typedef struct D2R_TagFrame +{ + DW_TagNode *node; + RDIM_Scope *scope; + struct D2R_TagFrame *next; +} D2R_TagFrame; + +typedef struct D2R_TagIterator +{ + D2R_TagFrame *free_list; + D2R_TagFrame *stack; + DW_TagNode *null_tag_node; + DW_TagNode *tag_node; + B32 visit_children; +} D2R_TagIterator; + +//////////////////////////////// +//~ Line Table Conversion + +typedef struct D2R_SrcFileLookup +{ + DW_LineFile *file; + DW_LineVM *vm; + RDIM_SrcFile *src_file; +} D2R_SrcFileLookup; + +//////////////////////////////// +//~ Contrib Map + +typedef struct D2R_CompUnitContribMap +{ + U64 count; + U64 *info_off_arr; + RDIM_Rng1U64ChunkList *voff_range_arr; +} D2R_CompUnitContribMap; + +//////////////////////////////// +//~ RDIM Bytecode Extensions + +internal B32 rdim_is_eval_bytecode_static (RDIM_EvalBytecode bc); +internal B32 rdim_static_eval_bytecode_to_voff(RDIM_EvalBytecode bc, U64 image_base, U64 *voff_out); + +//////////////////////////////// +//~ DWARF -> RDI Enums + +internal RDI_Language d2r_rdi_language_from_dw_language(DW_Language v); +internal RDI_RegCodeX64 d2r_rdi_reg_code_from_dw_reg_x64 (DW_RegX64 v); +internal RDI_RegCode d2r_rdi_reg_code_from_dw_reg (Arch arch, DW_Reg v); + +//////////////////////////////// +//~ Value Type + +internal D2R_ValueTypeNode * d2r_value_type_stack_push(Arena *arena, D2R_ValueTypeStack *stack, D2R_ValueType type); +internal D2R_ValueType d2r_value_type_stack_pop (D2R_ValueTypeStack *stack); +internal D2R_ValueType d2r_value_type_stack_peek(D2R_ValueTypeStack *stack); + +internal D2R_ValueType d2r_signed_value_type_from_bit_size (U64 bit_size); +internal D2R_ValueType d2r_unsigned_value_type_from_bit_size(U64 bit_size); +internal D2R_ValueType d2r_float_type_from_bit_size (U64 bit_size); + +internal U64 d2r_size_from_value_type(U64 addr_size, D2R_ValueType value_type); +internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v); +internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v); +internal B32 d2r_is_value_type_signed (D2R_ValueType v); +internal B32 d2r_is_value_type_integral(D2R_ValueType v); +internal B32 d2r_is_value_type_unsigned(D2R_ValueType v); +internal B32 d2r_is_value_type_float (D2R_ValueType v); + +internal D2R_ValueType d2r_pick_common_value_type(D2R_ValueType lhs, D2R_ValueType rhs); +internal RDI_EvalTypeGroup d2r_value_type_to_rdi(D2R_ValueType v); + +internal D2R_ValueType d2r_apply_usual_arithmetic_conversions(Arena *arena, D2R_ValueType lhs, D2R_ValueType rhs, RDIM_EvalBytecode *bc); +internal void d2r_push_arithmetic_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op); +internal void d2r_push_relational_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op); + +//////////////////////////////// +//~ Expression Conversion + +internal RDIM_EvalBytecode d2r_bytecode_from_expression(Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, String8 expr, DW_CompUnit *cu, D2R_ValueType *result_type_out); +internal RDIM_Location d2r_transpile_expression (Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr); +internal RDIM_Location d2r_location_from_attrib (Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); +internal RDIM_LocationCaseList d2r_locset_from_attrib (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); +internal RDIM_LocationCaseList d2r_var_locset_from_tag (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag); + +//////////////////////////////// +//~ Type Table + +internal RDIM_Type * d2r_create_type (Arena *arena, D2R_TypeTable *type_table); +internal RDIM_Type * d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off); +internal RDIM_Type * d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off); +internal RDIM_Type * d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind); + +//////////////////////////////// +//~ Tag Iterator + +internal D2R_TagIterator * d2r_tag_iterator_init(Arena *arena, DW_TagNode *root); +internal void d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter); +internal void d2r_tag_iterator_skip_children (D2R_TagIterator *iter); +internal DW_TagNode * d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter); +internal DW_Tag d2r_tag_iterator_parent_tag (D2R_TagIterator *iter); + +//////////////////////////////// +//~ Type Conversion + +internal void d2r_flag_converted_tag(DW_TagNode *tag_node); +internal B8 d2r_is_tag_converted (DW_TagNode *tag_node); +internal RDIM_Type * d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_Tag tag, DW_AttribKind kind); +internal void d2r_convert_types(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_TagNode *root); + +//////////////////////////////// +//~ UDT Conversion + +internal B8 d2r_is_udt_tag_converted (DW_TagNode *tag_node); +internal void d2r_flag_converted_udt_tag(DW_TagNode *tag_node); +internal void d2r_inline_anonymous_udt_member(Arena *arena, RDIM_UDT *top_udt, U64 base_off, RDIM_UDT *udt); +internal void d2r_convert_udts(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, DW_TagNode *root); + +//////////////////////////////// +//~ Symbol Conversion + +internal RDIM_Scope * d2r_push_scope (Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges); +internal RDIM_Type ** d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out); +internal Rng1U64List d2r_range_list_from_tag(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag); +internal void d2r_convert_symbols(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, U64 image_base, Arch arch, DW_TagNode *root, RDIM_Unit *unit_rdi); + +//////////////////////////////// +//~ Line Table Conversion + +internal U64 d2r_hash_line_file(String8 dir_path, DW_LineFile *file); +internal void d2r_sort_ptrs(void **ptrs, U64 count, int (* is_before)(void *a, void *b)); + +//////////////////////////////// +//~ Contrib Map + +internal D2R_CompUnitContribMap d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Raw *input, U64 image_base); +internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off); + +//////////////////////////////// +//~ Entry Point + +internal RDIM_BakeParams d2r_convert(Arena *arena, D2R_ConvertParams *params); + +#endif // RDI_FROM_DWARF_H diff --git a/src/radbin/radbin_main.c b/src/radbin/radbin_main.c index 2b4ae04e..5a7e7bcf 100644 --- a/src/radbin/radbin_main.c +++ b/src/radbin/radbin_main.c @@ -41,7 +41,7 @@ #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" -#include "rdi_from_dwarf/rdi_from_dwarf_2.h" +#include "rdi_from_dwarf/rdi_from_dwarf.h" #include "radbin/radbin.h" //- rjf: [c] @@ -75,7 +75,7 @@ #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" -#include "rdi_from_dwarf/rdi_from_dwarf_2.c" +#include "rdi_from_dwarf/rdi_from_dwarf.c" #include "radbin/radbin.c" //////////////////////////////// diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 64c9e83d..5d4f8d49 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -336,7 +336,7 @@ #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" -#include "rdi_from_dwarf/rdi_from_dwarf_2.h" +#include "rdi_from_dwarf/rdi_from_dwarf.h" #include "radbin/radbin.h" #include "arch/arch_inc.h" #include "dbg_info/dbg_info.h" @@ -393,7 +393,7 @@ #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" -#include "rdi_from_dwarf/rdi_from_dwarf_2.c" +#include "rdi_from_dwarf/rdi_from_dwarf.c" #include "radbin/radbin.c" #include "arch/arch_inc.c" #include "dbg_info/dbg_info.c" diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.c b/src/rdi_from_dwarf/rdi_from_dwarf.c index f718333a..61ed73bc 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf.c @@ -2,3138 +2,4391 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// +//~ rjf: Helpers -static D2R_Shared g_d2r_shared; +internal int +d2r2_unique_tag_node_is_less_than(D2R2_UniqueTagNode **l, D2R2_UniqueTagNode **r) +{ + int is_less_than = (l[0]->hash < r[0]->hash); + return is_less_than; +} //////////////////////////////// +//~ rjf: Main Conversion Entry Point (New) -internal B32 -rdim_is_eval_bytecode_static(RDIM_EvalBytecode bc) -{ - B32 is_static = 1; - RDI_EvalOp dynamic_ops[] = { RDI_EvalOp_MemRead, RDI_EvalOp_RegRead, RDI_EvalOp_RegReadDyn, RDI_EvalOp_CFA }; - for EachNode(n, RDIM_EvalBytecodeOp, bc.first_op) { - for EachIndex(i, ArrayCount(dynamic_ops)) { - if (dynamic_ops[i] == n->op) { - is_static = 0; - goto exit; - } - } - } - exit:; - return is_static; -} - -internal B32 -rdim_static_eval_bytecode_to_voff(RDIM_EvalBytecode bc, U64 image_base, U64 *voff_out) -{ - Temp scratch = scratch_begin(0,0); - B32 is_ok = 0; - - typedef union { U16 u16; U32 u32; U64 u64; S64 s64; F32 f32; F64 f64; } Value; - U64 stack_cap = 128, stack_count = 0; - Value *stack = push_array(scratch.arena, Value, stack_cap); - - for EachNode(opcode_n, RDIM_EvalBytecodeOp, bc.first_op) { - // pop values from stack - Value *svals = 0; - { - U32 pop_count = RDI_POPN_FROM_CTRLBITS(rdi_eval_op_ctrlbits_table[opcode_n->op]); - if (pop_count > stack_count) { - goto exit; - } - stack_count -= pop_count; - svals = stack + stack_count; - } - - Value imm = { .u64 = opcode_n->p }; - Value nval = {0}; - switch (opcode_n->op) { - case RDI_EvalOp_Stop: { opcode_n = bc.last_op; } break; - case RDI_EvalOp_Noop: {} break; - case RDI_EvalOp_Cond: { NotImplemented; } break; - case RDI_EvalOp_Skip: { - NotImplemented; - } break; - case RDI_EvalOp_MemRead: { InvalidPath; } break; - case RDI_EvalOp_RegRead: { NotImplemented; } break; - case RDI_EvalOp_RegReadDyn: { NotImplemented; } break; - case RDI_EvalOp_FrameOff: { NotImplemented; } break; - case RDI_EvalOp_ModuleOff: { - nval.u64 = image_base + imm.u64; - } break; - case RDI_EvalOp_TLSOff: { - nval.u64 = image_base; - } break; - case RDI_EvalOp_ConstU8: - case RDI_EvalOp_ConstU16: - case RDI_EvalOp_ConstU32: - case RDI_EvalOp_ConstU64: - case RDI_EvalOp_ConstU128: { - nval = imm; - } break; - case RDI_EvalOp_ConstString: { NotImplemented; } break; - case RDI_EvalOp_Abs: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: - case RDI_EvalTypeGroup_S: { nval.s64 = abs_s64(svals[0].s64); } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = abs_f32(svals[0].f32); } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = abs_f64(svals[0].f64); } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Neg: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: - case RDI_EvalTypeGroup_S: { nval.u64 = ~svals[0].u64 + 1; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = -svals[0].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = -svals[0].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Add: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 + svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 + svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 + svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 + svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Sub: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 - svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[1].s64 - svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 - svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 - svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Mul: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 * svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 * svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 * svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 * svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Div: { - B32 is_div_by_zero = 0; - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { is_div_by_zero = svals[1].u64 == 0; } break; - case RDI_EvalTypeGroup_S: { is_div_by_zero = svals[1].s64 == 0; } break; - case RDI_EvalTypeGroup_F32: { is_div_by_zero = svals[1].f32 == 0.0f; } break; - case RDI_EvalTypeGroup_F64: { is_div_by_zero = svals[1].f64 == 0.0; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - - if (is_div_by_zero) { - Assert(!"trying to div by zero"); - goto exit; - } - - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 / svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 / svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 / svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 / svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Mod: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 % svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 % svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 MOD is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 MOD is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_LShift: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 << svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 << svals[1].u64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 LShift is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 LShift is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_RShift: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0 ] .u64 >> svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[1 ] .s64 >> svals[1].u64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 RShift is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 RShift is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_BitAnd: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 | svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 | svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 bitwise AND is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 bitwise AND is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_BitXor: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 ^ svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 ^ svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 XOR is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 XOR is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_BitNot: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = ~svals[0].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = ~svals[0].u64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 bitwise NOT is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 bitwise NOT is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_LogAnd: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 && svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 && svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 AND is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 AND is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_LogOr: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 || svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].u64 || svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 OR is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 OR is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_LogNot: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = !svals[0].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = !svals[0].u64; } break; - case RDI_EvalTypeGroup_F32: { Assert(!"F32 NOT is not supported"); } goto exit; - case RDI_EvalTypeGroup_F64: { Assert(!"F64 NOT is not supported"); } goto exit; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_EqEq: { - nval.u64 = !!MemoryMatch(&svals[0], &svals[1], sizeof(*svals)); - } break; - case RDI_EvalOp_NtEq: { - nval.u64 = !MemoryMatch(&svals[0], &svals[1], sizeof(*svals)); - } break; - case RDI_EvalOp_LsEq: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 <= svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 <= svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 <= svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 <= svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_GrEq: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 >= svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 >= svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 >= svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 >= svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Less: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 < svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 < svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 < svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 < svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Grtr: { - switch (imm.u64) { - case RDI_EvalTypeGroup_Other: {} break; - case RDI_EvalTypeGroup_U: { nval.u64 = svals[0].u64 > svals[1].u64; } break; - case RDI_EvalTypeGroup_S: { nval.s64 = svals[0].s64 > svals[1].s64; } break; - case RDI_EvalTypeGroup_F32: { nval.f32 = svals[0].f32 > svals[1].f32; } break; - case RDI_EvalTypeGroup_F64: { nval.f64 = svals[0].f64 > svals[1].f64; } break; - default: { Assert(!"unexpected eval type group"); } goto exit; - } - } break; - case RDI_EvalOp_Trunc: { - if (0 < imm.u64 && imm.u64 < 64) { - U64 mask = max_U64 >> (64 - imm.u64); - nval.u64 = svals[0].u64 & (max_U64 >> (64 - imm.u64)); - } else if (imm.u64 > 64) { - Assert(!"malformed bytecode"); - goto exit; - } - } break; - case RDI_EvalOp_TruncSigned: { - if (0 < imm.u64 && imm.u64 < 64) { - U64 mask = max_U64 >> (64 - imm.u64); - nval.u64 = svals[0].u64 & (max_U64 >> (64 - imm.u64)); - U64 high = 0; - if (svals[0].u64 & (1 << (imm.u64 - 1))) { - high = ~mask; - } - nval.u64 = high | (svals[0].u64 & mask); - } else if (imm.u64 > 64) { - Assert(!"malformed bytecode"); - goto exit; - } - } break; - case RDI_EvalOp_Convert: { - U32 in = imm.u64 & 0xff; - U32 out = (imm.u64 >> 8) & 0xff; - if (in != out) { - switch (in + out*RDI_EvalTypeGroup_COUNT) { - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: { nval.u64 = (U64)svals[0].f32; } break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: { nval.u64 = (U64)svals[0].f64; } break; - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: { nval.s64 = (S64)svals[0].f32; } break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: { nval.s64 = (S64)svals[0].f64; } break; - case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].u64; } break; - case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].s64; } break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: { nval.f32 = (F32)svals[0].f64; } break; - case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].u64; } break; - case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].s64; } break; - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: { nval.f64 = (F64)svals[0].f32; } break; - default: { Assert(!"unexpected conversion case"); } goto exit; - } - } - } break; - case RDI_EvalOp_Pick: { - if (stack_count > imm.u64) { - nval = stack[stack_count - imm.u64 - 1]; - } else { - Assert(!"malformed bytecode"); - goto exit; - } - } break; - case RDI_EvalOp_Pop: {} break; - case RDI_EvalOp_Insert: { - if (stack_count > imm.u64) { - Value tval = stack[stack_count-1]; - Value *dst = stack + stack_count - 1 - imm.u64; - Value *shift = dst + 1; - MemoryCopy(shift, dst, imm.u64 * sizeof(Value)); - *dst = tval; - } else { - Assert(!"malformed bytecode"); - goto exit; - } - } break; - case RDI_EvalOp_ValueRead: { - U64 bytes_to_read = imm.u64; - U64 offset = svals[0].u64; - if (offset + bytes_to_read <= sizeof(Value)) { - Value src_val = svals[1]; - MemoryCopy(&nval, (U8 *)&src_val + offset, bytes_to_read); - } - } break; - case RDI_EvalOp_ByteSwap: { - switch (imm.u64) { - case 0: {} break; - case 1: {} break; - case 2: { nval.u16 = bswap_u16(svals[0].u16); } break; - case 4: { nval.u32 = bswap_u16(svals[0].u32); } break; - case 8: { nval.u64 = bswap_u16(svals[0].u64); } break; - default: { Assert(!"malformed bytecode"); } goto exit; - } - } break; - case RDI_EvalOp_Swap: { - NotImplemented; - } break; - default: { Assert(!"unknown op type"); } break; - } - - // push computed value to the stack - { - U64 push_count = RDI_PUSHN_FROM_CTRLBITS(rdi_eval_op_ctrlbits_table[opcode_n->op]); - if (push_count == 1) { - if (stack_count < stack_cap) { - stack[stack_count] = nval; - stack_count += 1; - } else { - Assert(!"stack overflow"); - goto exit; - } - } - } - } - - U64 result = 0; - if (stack_count >= 1) { - result = stack[0].u64 - image_base; - } - if (voff_out) { - *voff_out = result; - } - - is_ok = 1; - exit:; - scratch_end(scratch); - return is_ok; -} - -internal RDI_Language -d2r_rdi_language_from_dw_language(DW_Language v) -{ - RDI_Language result = RDI_Language_NULL; - switch (v) { - default: {} break; - - case DW_Language_C89: - case DW_Language_C99: - case DW_Language_C11: - case DW_Language_C: { - result = RDI_Language_C; - } break; - - case DW_Language_CPlusPlus03: - case DW_Language_CPlusPlus11: - case DW_Language_CPlusPlus14: - case DW_Language_CPlusPlus: { - result = RDI_Language_CPlusPlus; - } break; - } - return result; -} - -internal RDI_RegCodeX64 -d2r_rdi_reg_code_from_dw_reg_x64(DW_RegX64 v) -{ - RDI_RegCodeX64 result = RDI_RegCode_nil; - switch (v) { - default: {} break; -#define X(reg_dw, val_dw, reg_rdi, off, size) case DW_RegX64_##reg_dw: { result = RDI_RegCodeX64_##reg_rdi; } break; - DW_Regs_X64_XList -#undef X - } - return result; -} - -internal RDI_RegCode -d2r_rdi_reg_code_from_dw_reg(Arch arch, DW_Reg v) -{ - RDI_RegCode result = RDI_RegCode_nil; - switch (arch) { - default: NotImplemented; break; - case Arch_Null: break; - case Arch_x64:{ result = d2r_rdi_reg_code_from_dw_reg_x64(v); } break; - } - return result; -} - -internal D2R_ValueTypeNode * -d2r_value_type_stack_push(Arena *arena, D2R_ValueTypeStack *stack, D2R_ValueType type) -{ - D2R_ValueTypeNode *n; - if (stack->free_list) { - n = stack->free_list; - SLLStackPop(stack->free_list); - } else { - n = push_array(arena, D2R_ValueTypeNode, 1); - } - n->type = type; - SLLStackPush(stack->top, n); - stack->count += 1; - return n; -} - -internal D2R_ValueType -d2r_value_type_stack_pop(D2R_ValueTypeStack *stack) -{ - D2R_ValueType result = D2R_ValueType_Generic; - if (stack->top) { - D2R_ValueTypeNode *n = stack->top; - result = n->type; - SLLStackPop(stack->top); - SLLStackPush(stack->free_list, n); - stack->count -= 1; - } - return result; -} - -internal D2R_ValueType -d2r_value_type_stack_peek(D2R_ValueTypeStack *stack) -{ - return stack->top ? stack->top->type : D2R_ValueType_Generic; -} - -internal D2R_ValueType -d2r_signed_value_type_from_bit_size(U64 bit_size) -{ - switch (bit_size) { -#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; - D2R_ValueType_Signed_XList -#undef X - } - AssertAlways(!"no suitable signed type was found for the specified size"); - return D2R_ValueType_Generic; -} - -internal D2R_ValueType -d2r_unsigned_value_type_from_bit_size(U64 bit_size) -{ - switch (bit_size) { -#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; - D2R_ValueType_Unsigned_XList -#undef X - } - AssertAlways(!"no suitable unsigned type was found for the specified size"); - return D2R_ValueType_Generic; -} - -internal D2R_ValueType -d2r_float_type_from_bit_size(U64 bit_size) -{ -#define X(_N, _AT, _S) case _S: return D2R_ValueType_##_N; - switch (bit_size) { - D2R_ValueType_Float_XList - } -#undef X - AssertAlways(!"no suitable type was found for the specified size"); - return D2R_ValueType_Generic; -} - -internal D2R_ArithmeticType -d2r_arithmetic_type_from_value_type(D2R_ValueType v) -{ -#define X(_N, _AT, ...) case D2R_ValueType_##_N: return D2R_ArithmeticType_##_AT; - switch (v) { - D2R_ValueType_XList - } -#undef X - return D2R_ArithmeticType_Null; -} - -internal B32 -d2r_is_value_type_signed(D2R_ValueType v) -{ - return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Signed; -} - -internal B32 -d2r_is_value_type_integral(D2R_ValueType v) -{ - D2R_ArithmeticType at = d2r_arithmetic_type_from_value_type(v); - return at == D2R_ArithmeticType_Unsigned || at == D2R_ArithmeticType_Signed; -} - -internal B32 -d2r_is_value_type_unsigned(D2R_ValueType v) -{ - return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Unsigned; -} - -internal B32 -d2r_is_value_type_float(D2R_ValueType v) -{ - return d2r_arithmetic_type_from_value_type(v) == D2R_ArithmeticType_Float; -} - -internal U64 -d2r_size_from_value_type(U64 addr_size, D2R_ValueType value_type) -{ - if (value_type == D2R_ValueType_Address) { - return addr_size; - } - - switch (value_type) { -#define X(_N, _AT, _S) case D2R_ValueType_##_N: return _S; - D2R_ValueType_XList -#undef X - } - - return 0; -} - -internal D2R_ValueType -d2r_pick_common_value_type(D2R_ValueType lhs, D2R_ValueType rhs) -{ - if (lhs == rhs) { - return lhs; - } - // unsigned vs unsigned - else if (d2r_is_value_type_unsigned(lhs) && d2r_is_value_type_unsigned(rhs)) { - return Max(lhs, rhs); - } - // signed vs signed - else if (d2r_is_value_type_signed(lhs) && d2r_is_value_type_signed(rhs)) { - return Max(lhs, rhs); - } - // (unsigned vs signed) || (signed vs unsigned) - else if ((d2r_is_value_type_unsigned(lhs) && d2r_is_value_type_signed(rhs)) || - (d2r_is_value_type_signed(lhs) && d2r_is_value_type_unsigned(rhs))) { - U64 lhs_size = d2r_size_from_value_type(0, lhs); - U64 rhs_size = d2r_size_from_value_type(0, rhs); - if (lhs_size < rhs_size) { - return rhs; - } else if (lhs > rhs_size) { - return lhs; - } else { - return d2r_unsigned_value_type_from_bit_size(lhs_size * 8); - } - } - // float vs int - else if (d2r_is_value_type_float(lhs) && d2r_is_value_type_integral(rhs)) { - return lhs; - } - // int vs float - else if (d2r_is_value_type_integral(lhs) && d2r_is_value_type_float(rhs)) { - return rhs; - } - // float vs float - else if (d2r_is_value_type_float(lhs) && d2r_is_value_type_float(rhs)) { - return Max(lhs, rhs); - } - // address vs int - else if (lhs == D2R_ValueType_Address && d2r_is_value_type_integral(rhs)) { - return D2R_ValueType_Address; - } - // int vs address - else if (d2r_is_value_type_integral(lhs) && rhs == D2R_ValueType_Address) { - return D2R_ValueType_Address; - } - // address vs float - else if (lhs == D2R_ValueType_Address && d2r_is_value_type_float(rhs)) { - return D2R_ValueType_Generic; - } - // float vs address - else if (d2r_is_value_type_float(lhs) && rhs == D2R_ValueType_Address) { - return D2R_ValueType_Generic; - } - // no conversion for implicit value - else if (lhs == D2R_ValueType_ImplicitValue || rhs == D2R_ValueType_ImplicitValue) { - return D2R_ValueType_Generic; - } - AssertAlways(!"undefined conversion case"); - return D2R_ValueType_Generic; -} - -internal RDI_EvalTypeGroup -d2r_value_type_to_rdi(D2R_ValueType v) -{ - if (v == D2R_ValueType_Generic) { - return RDI_EvalTypeGroup_Other; - } - - if (v == D2R_ValueType_Address) { - return RDI_EvalTypeGroup_U; - } - - if (d2r_is_value_type_unsigned(v)) { - return RDI_EvalTypeGroup_U; - } - - if (d2r_is_value_type_signed(v)) { - return RDI_EvalTypeGroup_S; - } - - if (d2r_is_value_type_float(v)) { - if (v == D2R_ValueType_F16) { NotImplemented; } - else if (v == D2R_ValueType_F32) { return RDI_EvalTypeGroup_F32; } - else if (v == D2R_ValueType_F48) { NotImplemented; } - else if (v == D2R_ValueType_F64) { return RDI_EvalTypeGroup_F64; } - else if (v == D2R_ValueType_F80) { return RDI_EvalTypeGroup_F80; } - else if (v == D2R_ValueType_F96) { NotImplemented; } - else if (v == D2R_ValueType_F128) { return RDI_EvalTypeGroup_F128; } - else { InvalidPath; } - } - - InvalidPath; - return RDI_EvalTypeGroup_Other; -} - -internal D2R_ValueType -d2r_apply_usual_arithmetic_conversions(Arena *arena, D2R_ValueType lhs, D2R_ValueType rhs, RDIM_EvalBytecode *bc) -{ - D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); - if (rhs != common_type) { - rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(rhs), d2r_value_type_to_rdi(common_type)); - } - if (lhs != common_type) { - rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); - rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(lhs), d2r_value_type_to_rdi(common_type)); - } - return common_type; -} - -internal void -d2r_push_arithmetic_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op) -{ - D2R_ValueType rhs = d2r_value_type_stack_pop(stack); - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - D2R_ValueType common_type = d2r_apply_usual_arithmetic_conversions(arena, lhs, rhs, bc); - rdim_bytecode_push_op(arena, bc, op, d2r_value_type_to_rdi(common_type)); - d2r_value_type_stack_push(0, stack, common_type); -} - -internal void -d2r_push_relational_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op) -{ - D2R_ValueType rhs = d2r_value_type_stack_pop(stack); - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - D2R_ValueType common_type; - if (d2r_is_value_type_integral(lhs) && rhs == D2R_ValueType_Address) { - rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); - rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(lhs), RDI_EvalTypeGroup_U); - rdim_bytecode_push_op(arena, bc, RDI_EvalOp_Swap, 0); - common_type = D2R_ValueType_Address; - } else if (lhs == D2R_ValueType_Address && d2r_is_value_type_integral(rhs)) { - rdim_bytecode_push_convert(arena, bc, d2r_value_type_to_rdi(rhs), RDI_EvalTypeGroup_U); - common_type = D2R_ValueType_Address; - } else { - common_type = d2r_apply_usual_arithmetic_conversions(arena, lhs, rhs, bc); - } - rdim_bytecode_push_op(arena, bc, RDI_EvalOp_EqEq, d2r_value_type_to_rdi(common_type)); - d2r_value_type_stack_push(0, stack, D2R_ValueType_Bool); -} - -internal RDIM_EvalBytecode -d2r_bytecode_from_expression(Arena *arena, - DW_Raw *input, - U64 image_base, - Arch arch, - DW_ListUnit *addr_lu, - String8 raw_expr, - DW_CompUnit *cu, - D2R_ValueType *result_type_out) +internal RDIM_BakeParams +d2r2_convert(Arena *arena, D2R2_ConvertParams *params) { Temp scratch = scratch_begin(&arena, 1); - Temp temp = temp_begin(arena); - B32 is_ok = 0; + DW_Raw *raw = ¶ms->raw; + RDIM_BinarySectionList binary_sections = params->binary_sections; + Arch arch = params->arch; + ARCH_Info *arch_info = arch_info_from_arch(arch); + U64 base_vaddr = params->base_vaddr; + U64 arch_addr_size = byte_size_from_arch(arch); - RDIM_EvalBytecode bc = {0}; - - DW_Expr expr = dw_expr_from_data(scratch.arena, cu->format, cu->address_size, raw_expr); - D2R_ValueTypeStack *stack = push_array(scratch.arena, D2R_ValueTypeStack, 1); - RDIM_EvalBytecodeOp **converted_insts = push_array(scratch.arena, RDIM_EvalBytecodeOp *, expr.count); - U64 inst_idx = 0; - for EachNode(inst, DW_ExprInst, expr.first) { - RDIM_EvalBytecodeOp *last_op = bc.last_op; - - U64 pop_count = dw_pop_count_from_expr_op(inst->opcode); - if (pop_count > stack->count) { - Assert(!"not enough values on the stack to evaluate instruction"); - goto exit; + //////////////////////////// + //- rjf: determine acceptable address range + // + // in many cases, linkers seem to trample over addresses in various DWARF sections, + // potentially due to optimizations. we'd like to filter out those busted addresses + // from our final debug info - a good enough heuristic is to disqualify them by + // whether or not they actually fall into the ranges covered by the binary sections. + // + Rng1U64 acceptable_vaddr_range = {0}; + { + acceptable_vaddr_range.min = max_U64; + acceptable_vaddr_range.max = 0; + for EachNode(n, RDIM_BinarySectionNode, binary_sections.first) + { + acceptable_vaddr_range.min = Min(base_vaddr + n->v.voff_first, acceptable_vaddr_range.min); + acceptable_vaddr_range.max = Max(base_vaddr + n->v.voff_opl, acceptable_vaddr_range.max); } - - switch (inst->opcode) { - case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: - case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: - case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: - case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: - case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: - case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: - case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: - case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: - case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: - case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: - case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: { - U64 lit = inst->opcode - DW_ExprOp_Lit0; - rdim_bytecode_push_uconst(arena, &bc, lit); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U8); - } break; - case DW_ExprOp_Const1U: { - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u8); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U8); - } break; - case DW_ExprOp_Const2U: { - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u16); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U16); - } break; - case DW_ExprOp_Const4U: { - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u32); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U32); - } break; - case DW_ExprOp_Const8U: { - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u32); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U64); - } break; - case DW_ExprOp_Const1S: { - rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s8); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S8); - } break; - case DW_ExprOp_Const2S: { - rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s16); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S16); - } break; - case DW_ExprOp_Const4S: { - rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s32); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S32); - } break; - case DW_ExprOp_Const8S: { - rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s64); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S64); - } break; - case DW_ExprOp_ConstU: { - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u64); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_U64); - } break; - case DW_ExprOp_ConstS: { - rdim_bytecode_push_sconst(arena, &bc, inst->operands[0].s64); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_S64); - } break; - case DW_ExprOp_Addr: { - if (inst->operands[0].u64 < image_base) { - Assert(!"invalid address"); - goto exit; - } - - U64 voff = inst->operands[0].u64 - image_base; - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_ModuleOff, voff); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: - case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: - case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: - case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: - case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: - case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: - case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: - case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: - case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: - case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: - case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: { - U64 reg_code_dw = inst->opcode - DW_ExprOp_Reg0; - U64 reg_size = dw_reg_size_from_code(arch, reg_code_dw); - U64 reg_pos = dw_reg_pos_from_code(arch, reg_code_dw); - - RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); - U32 regread_param = RDI_EncodeRegReadParam(reg_code_rdi, reg_size, reg_pos); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, regread_param); - d2r_value_type_stack_push(scratch.arena, stack, d2r_unsigned_value_type_from_bit_size(reg_size)); - } break; - case DW_ExprOp_RegX: { - U64 reg_size = dw_reg_size_from_code(arch, inst->operands[0].u64); - U64 reg_pos = dw_reg_pos_from_code(arch, inst->operands[0].u64); - RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, inst->operands[0].u64); - U32 regread_param = RDI_EncodeRegReadParam(reg_code_rdi, reg_size, reg_pos); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, regread_param); - D2R_ValueType value_type; - if (arch == Arch_x64) { - if (reg_code_rdi == RDI_RegCodeX64_st0 || - reg_code_rdi == RDI_RegCodeX64_st1 || - reg_code_rdi == RDI_RegCodeX64_st2 || - reg_code_rdi == RDI_RegCodeX64_st3 || - reg_code_rdi == RDI_RegCodeX64_st4 || - reg_code_rdi == RDI_RegCodeX64_st5 || - reg_code_rdi == RDI_RegCodeX64_st6 || - reg_code_rdi == RDI_RegCodeX64_st7) { - value_type = d2r_float_type_from_bit_size(reg_size); - } else { - value_type = d2r_unsigned_value_type_from_bit_size(reg_size); - } - } else { - value_type = d2r_unsigned_value_type_from_bit_size(reg_size); - } - d2r_value_type_stack_push(scratch.arena, stack, value_type); - } break; - case DW_ExprOp_ImplicitValue: { - if (inst->operands[0].block.size <= sizeof(U64)) { - U64 implicit_value; - MemoryCopyStr8(&implicit_value, inst->operands[0].block); - rdim_bytecode_push_uconst(arena, &bc, implicit_value); - d2r_value_type_stack_push(scratch.arena, stack, d2r_unsigned_value_type_from_bit_size(inst->operands[0].block.size * 8)); - } else { - // TODO: currenlty no way to encode string in RDIM_EvalBytecodeOp - Assert(!"TODO:"); - goto exit; - } - } break; - case DW_ExprOp_Piece: { - U64 partial_value_size32 = safe_cast_u32(inst->operands[0].u64); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PartialValue, partial_value_size32); - } break; - case DW_ExprOp_BitPiece: { - U32 piece_bit_size32 = safe_cast_u32(inst->operands[0].u64); - U32 piece_bit_off32 = safe_cast_u32(inst->operands[1].u64); - U64 partial_value = Compose64Bit(piece_bit_size32, piece_bit_off32); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PartialValueBit, partial_value); - } break; - case DW_ExprOp_Pick: { - U64 idx = 0; - D2R_ValueTypeNode *n; - for (n = stack->top; n != 0 || idx == inst->operands[0].u64; n = n->next, idx += 1) { } - if (idx == inst->operands[0].u64) { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, inst->operands[0].u64); - d2r_value_type_stack_push(scratch.arena, stack, n->type); - } else { - Assert(!"out of bounds pick"); - goto exit; - } - } break; - case DW_ExprOp_Over: { - if (stack->top && stack->top->next) { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, 1); - d2r_value_type_stack_push(scratch.arena, stack, stack->top->next->type); - } else { - Assert(!"out of bounds over"); - goto exit; - } - } break; - case DW_ExprOp_PlusUConst: { - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - D2R_ValueType common_type = d2r_pick_common_value_type(lhs, D2R_ValueType_U64); - rdim_bytecode_push_uconst(arena, &bc, inst->operands[0].u64); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, d2r_value_type_to_rdi(common_type)); - d2r_value_type_stack_push(scratch.arena, stack, common_type); - } break; - case DW_ExprOp_Skip: { - B32 skip_fwd = inst->operands[0].s16 >= 0; - U16 delta = abs_s64(inst->operands[0].s16); - U16 cursor = 0; - U64 inst_count = 0; - for (DW_ExprInst *i = skip_fwd ? inst : inst->prev; i != 0 && cursor < delta; i = skip_fwd ? inst->next : inst->prev) { - cursor += i->size; - inst_count += 1; - } - - if (cursor != delta) { Assert(!"skip landed in middle of an instruction"); goto exit; } - if (inst_count > min_S16) { Assert(!"operand overflow"); goto exit; } - if (inst_idx > max_U32) { Assert(!"index overflow"); goto exit; } - - U64 imm = Compose64Bit(inst_idx, skip_fwd ? (S16)inst_count : (U16)(-(S16)inst_count)); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Skip, imm); - } break; - case DW_ExprOp_Bra: { - B32 skip_fwd = inst->operands[0].s16 >= 0; - U16 delta = abs_s64(inst->operands[0].s16); - U16 cursor = 0; - U64 inst_count = 0; - for (DW_ExprInst *i = skip_fwd ? inst->next : inst->prev; i != 0 && cursor < delta; i = skip_fwd ? inst->next : inst->prev) { - cursor += i->size; - inst_count += 1; - } - - if (cursor != delta) { Assert(!"cond landed in middle of an instruction"); goto exit; } - if (inst_count > min_S16) { Assert(!"operand overflow"); goto exit; } - if (inst_idx > max_U32) { Assert(!"index overflow"); goto exit; } - - U64 imm = Compose64Bit(inst_idx, skip_fwd ? inst_count : (U16)(-(S16)inst_count)); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Cond, imm); - } break; - case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: - case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: - case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: - case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: - case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: - case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: - case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: - case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: - case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: - case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: - case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: { - U64 reg_code_dw = inst->opcode - DW_ExprOp_BReg0; - S64 reg_off = inst->operands[0].s64; - - RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegRead, reg_code_rdi); - if (reg_off > 0) { - rdim_bytecode_push_sconst(arena, &bc, reg_off); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); - } - - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_BRegX: { - U64 reg_code_dw = inst->operands[0].u64; - S64 reg_off = inst->operands[1].s64; - - RDI_RegCode reg_code_rdi = d2r_rdi_reg_code_from_dw_reg(arch, reg_code_dw); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RegReadDyn, reg_code_rdi); - if (reg_off > 0) { - rdim_bytecode_push_sconst(arena, &bc, reg_off); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); - } - - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_FBReg: { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_FrameOff, inst->operands[0].s64); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_Deref: { - D2R_ValueType address_type = d2r_value_type_stack_pop(stack); - if (address_type != D2R_ValueType_Address && !d2r_is_value_type_integral(address_type)) { - Assert(!"value must be of integral type"); - goto exit; - } - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_MemRead, cu->address_size); - d2r_value_type_stack_push(scratch.arena, stack, address_type); - } break; - case DW_ExprOp_DerefSize: { - D2R_ValueType address_type = d2r_value_type_stack_pop(stack); - if (!d2r_is_value_type_integral(address_type) && address_type != D2R_ValueType_Address ) { - Assert(!"value must be of integral type"); - goto exit; - } - U8 deref_size_in_bytes = inst->operands[0].u64; - if (0 < deref_size_in_bytes && deref_size_in_bytes <= cu->address_size) { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_MemRead, deref_size_in_bytes); - } else { - Assert(!"ill formed expression"); - goto exit; - } - d2r_value_type_stack_push(scratch.arena, stack, address_type); - } break; - case DW_ExprOp_XDeref: { - Assert(!"multiple address spaces are not supported"); - goto exit; - } break; - case DW_ExprOp_XDerefSize: { - Assert(!"no suitable conversion"); - goto exit; - } break; - case DW_ExprOp_Call2: - case DW_ExprOp_Call4: - case DW_ExprOp_CallRef: { - Assert(!"calls are not supported"); - goto exit; - } break; - case DW_ExprOp_ImplicitPointer: - case DW_ExprOp_GNU_ImplicitPointer: { - // TODO: RDI does not support encoding of implicit pointers - //Assert(!"TODO: implicit pointer"); - goto exit; - } break; - case DW_ExprOp_Convert: - case DW_ExprOp_GNU_Convert: { - D2R_ValueType out = D2R_ValueType_Generic; - if (inst->operands[0].u64 == 0) { - // - // 2.5.1 - // Instead of a base type, elements can have a generic type, - // which is an integral type that has the size of an address - // on the target machine and unspecified signedness. - // - out = D2R_ValueType_Generic; - } else { - // find ref tag - U64 ref_tag_info_off = cu->info_range.min + inst->operands[0].u64; - DW_TagNode *tag_node = dw_tag_node_from_info_off(cu, ref_tag_info_off); - if (tag_node == 0) { - Assert(!"invalid .debug_info offset"); - goto exit; - } - - DW_Tag tag = tag_node->tag; - if (tag.kind == DW_TagKind_BaseType) { - // extract encoding attribute - DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Encoding); - - // DW_ATE -> RDI_EvalTypeGroup - switch (encoding) { - case DW_ATE_Null: { - out = D2R_ValueType_Generic; - } break; - case DW_ATE_Address: { - out = D2R_ValueType_Address; - } break; - case DW_ATE_Boolean: { - out = D2R_ValueType_S8; - } break; - case DW_ATE_SignedChar: - case DW_ATE_Signed: { - U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); - out = d2r_signed_value_type_from_bit_size(byte_size * 8); - } break; - case DW_ATE_UnsignedChar: - case DW_ATE_Unsigned: { - U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); - out = d2r_unsigned_value_type_from_bit_size(byte_size * 8); - } break; - case DW_ATE_Float: { - U64 byte_size = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ByteSize); - out = d2r_float_type_from_bit_size(byte_size * 8); - } break; - default: InvalidPath; break; - } - } else { - Assert(!"unexpected tag"); - goto exit; - } - } - - D2R_ValueType in = d2r_value_type_stack_pop(stack); - d2r_value_type_stack_push(scratch.arena, stack, out); - rdim_bytecode_push_convert(arena, &bc, d2r_value_type_to_rdi(in), d2r_value_type_to_rdi(out)); - } break; - case DW_ExprOp_GNU_ParameterRef: { - Assert(!"TODO: sample"); - goto exit; - } - case DW_ExprOp_DerefType: - case DW_ExprOp_GNU_DerefType: { - goto exit; - } - case DW_ExprOp_ConstType: - case DW_ExprOp_GNU_ConstType: { - Assert(!"TODO: sample"); - goto exit; - } - case DW_ExprOp_RegvalType: { - Assert(!"sample"); - goto exit; - } - case DW_ExprOp_EntryValue: - case DW_ExprOp_GNU_EntryValue: { - D2R_ValueType call_site_result_type = 0; - RDIM_EvalBytecode call_site_bc = d2r_bytecode_from_expression(arena, input, image_base, arch, addr_lu, inst->operands[0].block, cu, &call_site_result_type); - - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_CallSiteValue, safe_cast_u32(call_site_bc.encoded_size)); - rdim_bytecode_concat_in_place(&bc, &call_site_bc); - - d2r_value_type_stack_push(scratch.arena, stack, call_site_result_type); - } break; - case DW_ExprOp_Addrx: { - U64 addr = dw_addr_from_list_unit(addr_lu, inst->operands[0].u64); - if (addr != max_U64) { - if (addr >= image_base) { - U64 voff = addr - image_base; - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_ModuleOff, voff); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } else { - Assert(!"unable to relocate address"); - goto exit; - } - } else { - Assert(!"out of bounds index"); - goto exit; - } - } break; - case DW_ExprOp_CallFrameCfa: { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_PushCfa, 0); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_PushObjectAddress: { - Assert(!"TODO: sample"); - goto exit; - } - case DW_ExprOp_Nop: {} break; - case DW_ExprOp_Eq: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_EqEq); } break; - case DW_ExprOp_Ge: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_GrEq); } break; - case DW_ExprOp_Gt: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_Grtr); } break; - case DW_ExprOp_Le: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_LsEq); } break; - case DW_ExprOp_Lt: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_Less); } break; - case DW_ExprOp_Ne: { d2r_push_relational_op(arena, stack, &bc, RDI_EvalOp_NtEq); } break; - case DW_ExprOp_Div: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Div); } break; - case DW_ExprOp_Minus: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Sub); } break; - case DW_ExprOp_Mul: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Mul); } break; - case DW_ExprOp_Plus: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_Add); } break; - case DW_ExprOp_Xor: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitXor); } break; - case DW_ExprOp_And: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitAnd); } break; - case DW_ExprOp_Or: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_BitOr); } break; - case DW_ExprOp_Shl: { d2r_push_arithmetic_op(arena, stack, &bc, RDI_EvalOp_LShift); } break; - case DW_ExprOp_Shr: { - D2R_ValueType rhs = d2r_value_type_stack_pop(stack); - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - if (d2r_is_value_type_integral(rhs) && d2r_is_value_type_integral(lhs)) { - D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); - D2R_ValueType result_type = d2r_unsigned_value_type_from_bit_size(d2r_size_from_value_type((cu->address_size), common_type) * 8); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RShift, d2r_value_type_to_rdi(result_type)); - d2r_value_type_stack_push(scratch.arena, stack, result_type); - } else { - Assert(!"operands must be of integral type"); - goto exit; - } - } break; - case DW_ExprOp_Shra: { - D2R_ValueType rhs = d2r_value_type_stack_pop(stack); - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - if (d2r_is_value_type_integral(lhs) && d2r_is_value_type_integral(rhs)) { - D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); - D2R_ValueType result_type = d2r_signed_value_type_from_bit_size(d2r_size_from_value_type((cu->address_size), common_type) * 8); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_RShift, d2r_value_type_to_rdi(result_type)); - d2r_value_type_stack_push(scratch.arena, stack, result_type); - } else { - Assert(!"operands must be of integral type"); - goto exit; - } - } break; - case DW_ExprOp_Mod: { - D2R_ValueType rhs = d2r_value_type_stack_pop(stack); - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - if (!d2r_is_value_type_integral(rhs) || !d2r_is_value_type_integral(lhs)) { - Assert(!"operands must be of integral type"); - goto exit; - } - D2R_ValueType common_type = d2r_pick_common_value_type(lhs, rhs); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Mod, d2r_value_type_to_rdi(common_type)); - d2r_value_type_stack_push(scratch.arena, stack, common_type); - } break; - case DW_ExprOp_Abs: { - if (!d2r_is_value_type_integral(d2r_value_type_stack_peek(stack)) && !d2r_is_value_type_float(d2r_value_type_stack_peek(stack))) { - Assert(!"operand must be of integral type or float"); - goto exit; - } - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Abs, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); - } break; - case DW_ExprOp_Neg: { - if (!d2r_is_value_type_integral(d2r_value_type_stack_peek(stack))) { - Assert(!"operand must be of integral type"); - goto exit; - } - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Neg, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); - } break; - case DW_ExprOp_Not: { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_BitNot, d2r_value_type_to_rdi(d2r_value_type_stack_peek(stack))); - } break; - case DW_ExprOp_Dup: { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pick, 0); - d2r_value_type_stack_push(scratch.arena, stack, d2r_value_type_stack_peek(stack)); - } break; - case DW_ExprOp_Rot: { - Assert(!"no suitable conversion"); - goto exit; - } - case DW_ExprOp_Swap: { - D2R_ValueType a = d2r_value_type_stack_pop(stack); - D2R_ValueType b = d2r_value_type_stack_pop(stack); - d2r_value_type_stack_push(scratch.arena, stack, a); - d2r_value_type_stack_push(scratch.arena, stack, b); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Swap, 0); - } break; - case DW_ExprOp_Drop: { - d2r_value_type_stack_pop(stack); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Pop, 0); - } break; - case DW_ExprOp_StackValue: { - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Stop, 0); - if (stack->top->type == D2R_ValueType_Address) { - stack->top->type = d2r_unsigned_value_type_from_bit_size(cu->address_size * 8); - } - } break; - case DW_ExprOp_FormTlsAddress: - case DW_ExprOp_GNU_PushTlsAddress: { - D2R_ValueType lhs = d2r_value_type_stack_pop(stack); - if (!d2r_is_value_type_integral(lhs)) { - Assert(!"lhs must be of integral type"); - goto exit; - } - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_TLSOff, 0); - rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, d2r_value_type_to_rdi(lhs)); - d2r_value_type_stack_push(scratch.arena, stack, D2R_ValueType_Address); - } break; - case DW_ExprOp_GNU_UnInit: { - // TODO: flag value as unitialized; this must be last opcode; possible to use with DW_ExprOp_Piece; - } break; - default: goto exit; - } - - // store converted instruction - if (last_op != bc.last_op) { - RDIM_EvalBytecodeOp *first_converted_op = last_op ? last_op->next : bc.first_op; - converted_insts[inst_idx] = first_converted_op; - } - - inst_idx += 1; } - // fixup bytecode jumps - for EachNode(op, RDIM_EvalBytecodeOp, bc.first_op) { - if (op->op == RDI_EvalOp_Skip || op->op == RDI_EvalOp_Cond) { - // unpack skip info - U32 inst_idx = Extract32(op->p, 1); - S16 skip_count_signed = (S16)Extract32(op->p, 0); - U16 skip_count = abs_s64(skip_count_signed); - B32 skip_fwd = skip_count_signed > 0; - - // setup being/end links - RDIM_EvalBytecodeOp *begin = 0, *end = 0; - if (skip_fwd) { - if (inst_idx + skip_count <= expr.count) { - begin = converted_insts[inst_idx]; - end = (inst_idx + skip_count) < expr.count ? converted_insts[inst_idx + skip_count] : 0; - } else { - Assert(!"out of bounds skip"); - goto exit; - } - } else { - if (skip_count <= inst_idx) { - begin = converted_insts[inst_idx - skip_count]; - end = converted_insts[inst_idx]; - } else { - Assert(!"out of bounds skip"); - goto exit; - } + //////////////////////////// + //- rjf: compute exe hash + // + U64 exe_hash = 0; + ProfScope("compute exe hash") + { + if(lane_idx() == 0) + { + exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); + } + lane_sync_u64(&exe_hash, 0); + } + + //////////////////////////// + //- rjf: produce top-level-info + // + RDIM_TopLevelInfo top_level_info = {0}; + ProfScope("produce top-level-info") + { + top_level_info.arch = rdi_arch_from_arch(arch); + top_level_info.exe_name = params->exe_name; + top_level_info.exe_hash = exe_hash; + top_level_info.voff_max = acceptable_vaddr_range.max - base_vaddr; + if(!params->deterministic) + { + // TODO(rjf): top_level_info.guid = ...; + top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL); + } + } + + //////////////////////////// + //- rjf: gather unit ranges from .debug_info, .debug_aranges + // + Rng1U64Array *unit_info_ranges = 0; + Rng1U64Array *unit_arange_ranges = 0; + ProfScope("gather unit ranges from .debug_info, .debug_aranges") if(lane_idx() == 0) + { + unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1); + *unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data); + unit_arange_ranges = push_array(scratch.arena, Rng1U64Array, 1); + *unit_arange_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_ARanges].data); + } + lane_sync_u64(&unit_info_ranges, 0); + lane_sync_u64(&unit_arange_ranges, 0); + U64 unit_count = unit_info_ranges->count; + + //////////////////////////// + //- rjf: parse all .debug_info unit headers + // + DW2_UnitHeader *unit_headers = 0; + Rng1U64 *unit_info_tag_ranges = 0; + ProfScope("parse all unit headers") + { + // rjf: set up + if(lane_idx() == 0) + { + unit_headers = push_array(scratch.arena, DW2_UnitHeader, unit_count); + unit_info_tag_ranges = push_array(scratch.arena, Rng1U64, unit_count); + } + lane_sync_u64(&unit_headers, 0); + lane_sync_u64(&unit_info_tag_ranges, 0); + + // rjf: parse all unit headers + String8 data = raw->sec[DW_SectionKind_Info].data; + Rng1U64 range = lane_range(unit_count); + for EachInRange(idx, range) + { + Rng1U64 unit_info_range = unit_info_ranges->v[idx]; + U64 bytes_read = dw2_read_unit_header(str8_substr(data, unit_info_range), 0, &unit_headers[idx]); + unit_info_tag_ranges[idx] = r1u64(unit_info_range.min + bytes_read, unit_info_range.max); + } + } + lane_sync(); + Rng1U64Array unit_info_tag_ranges_array = {unit_info_tag_ranges, unit_count}; + + //////////////////////////// + //- rjf: parse all units from .debug_aranges + // + U64 *arange_info_offs = 0; + RDIM_Rng1U64ChunkList *arange_voff_ranges = 0; + { + if(lane_idx() == 0) + { + arange_info_offs = push_array(scratch.arena, U64, unit_arange_ranges->count); + arange_voff_ranges = push_array(scratch.arena, RDIM_Rng1U64ChunkList, unit_arange_ranges->count); + } + lane_sync_u64(&arange_info_offs, 0); + lane_sync_u64(&arange_voff_ranges, 0); + U64 unit_take_idx_ = 0; + U64 *unit_take_idx_ptr = &unit_take_idx_; + lane_sync_u64(&unit_take_idx_ptr, 0); + String8 data = raw->sec[DW_SectionKind_ARanges].data; + for(;;) + { + // rjf: take unit + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; + if(unit_idx >= unit_arange_ranges->count) + { + break; } - // compute skip delta - U64 skip_delta = 0; - for (RDIM_EvalBytecodeOp *n = begin; n != end; n = n->next) { - skip_delta += n->p_size; + // rjf: unpack + Rng1U64 arange_range = unit_arange_ranges->v[unit_idx]; + U64 off = arange_range.min; + + // rjf: read unit data size / format + U64 unit_size = 0; + DW_Format fmt = 0; + off += dw2_read_initial_length(data, off, &unit_size, &fmt); + U64 unit_opl = off + unit_size; + + // rjf: read version + DW_Version version = 0; + U64 version_off = off; + off += str8_deserial_read_struct(data, off, &version); + + // rjf: warn on non-version 2 + if(version != DW_Version_2) + { + log_infof("[.debug_aranges@0x%I64x] DWARF version for unit #%I64d was expected to be 2, but it was read as %i.\n", version_off, unit_idx, (S32)version); } - // rewrite skip operand with byte delta - AssertAlways(skip_delta <= max_S16); - op->p = skip_fwd ? (S16)skip_delta : -(S16)skip_delta; - } - } - - if (result_type_out) { - *result_type_out = d2r_value_type_stack_peek(stack); - } - - is_ok = 1; - exit:; - if (!is_ok) { - MemoryZeroStruct(&bc); - temp_end(temp); - } - scratch_end(scratch); - return bc; -} - -internal RDIM_Location -d2r_transpile_expression(Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr) -{ - RDIM_Location loc = {0}; - if (expr.size) { - D2R_ValueType result_type = 0; - RDIM_EvalBytecode bytecode = d2r_bytecode_from_expression(arena, input, image_base, arch, addr_lu, expr, cu, &result_type); - loc.kind = result_type == D2R_ValueType_Address ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; - loc.bytecode = bytecode; - } - return loc; -} - -internal RDIM_Location -d2r_location_from_attrib(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind) -{ - String8 expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, kind); - RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, expr); - return location; -} - -internal RDIM_LocationCaseList -d2r_locset_from_attrib(Arena *arena, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - DW_Raw *input, - DW_CompUnit *cu, - U64 image_base, - Arch arch, - DW_Tag tag, - DW_AttribKind kind) -{ - RDIM_LocationCaseList locset = {0}; - - // extract attrib from tag - DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); - DW_AttribClass attrib_class = dw_value_class_from_attrib(cu, attrib); - - if (attrib_class == DW_AttribClass_LocList || attrib_class == DW_AttribClass_LocListPtr) { - Temp scratch = scratch_begin(&arena, 1); - - // extract location list from attrib - DW_LocList loclist = dw_loclist_from_attrib(scratch.arena, input, cu, attrib); - - // convert location list to RDIM location set - for EachNode(loc_n, DW_LocNode, loclist.first) { - RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, loc_n->v.expr); - RDIM_Rng1U64 voff_range = { .min = loc_n->v.range.min - image_base, .max = loc_n->v.range.max - image_base }; - rdim_location_case_list_push(arena, &locset, location, voff_range); - } - - scratch_end(scratch); - } else if (attrib_class == DW_AttribClass_ExprLoc) { - // extract expression from attrib - String8 expr = dw_exprloc_from_attrib(input, cu, attrib); - - // convert expression and inherit life-time ranges from enclosed scope - RDIM_Location location = d2r_transpile_expression(arena, input, image_base, arch, cu->addr_lu, cu, expr); - for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { - rdim_location_case_list_push(arena, &locset, location, range_n->v); - } - } else if (attrib_class != DW_AttribClass_Null) { - log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); - } - - return locset; -} - -internal RDIM_LocationCaseList -d2r_var_locset_from_tag(Arena *arena, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - DW_Raw *input, - DW_CompUnit *cu, - U64 image_base, - Arch arch, - DW_Tag tag) -{ - RDIM_LocationCaseList locset = {0}; - - B32 has_const_value = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ConstValue); - B32 has_location = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Location); - - if (has_const_value && has_location) { - log_infof("unexpected variable encoding @ .debug_info+%llx", tag.info_off); - return locset; - } - - if (has_const_value) { - // extract const value - U64 const_value = dw_u64_from_attrib(input, cu, tag, DW_AttribKind_ConstValue); - - // make value byte code - RDIM_EvalBytecode bc = {0}; - rdim_bytecode_push_uconst(arena, &bc, const_value); - - // fill out location - RDIM_Location loc = {0}; - loc.kind = RDI_LocationKind_ValBytecodeStream; - loc.bytecode = bc; - - // push location cases - for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { - rdim_location_case_list_push(arena, &locset, loc, range_n->v); - } - } else if (has_location) { - locset = d2r_locset_from_attrib(arena, scopes, curr_scope, input, cu, image_base, arch, tag, DW_AttribKind_Location); - } - - return locset; -} - -internal RDIM_Type * -d2r_create_type(Arena *arena, D2R_TypeTable *type_table) -{ - RDIM_Type *type = rdim_type_chunk_list_push(arena, type_table->types, type_table->type_chunk_cap); - return type; -} - -internal RDIM_Type * -d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off) -{ - RDIM_Type *type = d2r_create_type(arena, type_table); - Assert(hash_table_search_u64_raw(type_table->ht, info_off) == 0); - hash_table_push_u64_raw(arena, type_table->ht, info_off, type); - return type; -} - -internal RDIM_Type * -d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off) -{ - RDIM_Type *type = hash_table_search_u64_raw(type_table->ht, info_off); - if (type == 0) { - type = type_table->builtin_types[RDI_TypeKind_NULL]; - } - return type; -} - -internal RDIM_Type * -d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind) -{ - RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; - - // find attrib - DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); - - // does tag have this attribute? - if (attrib->attrib_kind == kind) { - DW_AttribClass value_class = dw_value_class_from_attrib(cu, attrib); - - if (value_class == DW_AttribClass_Reference) { - // resolve reference - DW_Reference ref = dw_ref_from_attrib(input, cu, attrib); + // rjf: read .debug_info off for this unit + U64 info_off = 0; + off += dw2_read_fmt_u64(data, off, fmt, &info_off); - if (ref.cu == cu) { - // find type - type = d2r_type_from_offset(type_table, ref.info_off); - } else { - NotImplemented; - } - } else { - log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); - } - } - - return type; -} - -internal D2R_TagIterator * -d2r_tag_iterator_init(Arena *arena, DW_TagNode *root) -{ - D2R_TagIterator *iter = push_array(arena, D2R_TagIterator, 1); - iter->free_list = 0; - iter->stack = push_array(arena, D2R_TagFrame, 1); - iter->stack->node = push_array(arena, DW_TagNode, 1); - if (root != 0) { - *iter->stack->node = *root; - } - iter->stack->node->sibling = 0; - iter->visit_children = 1; - iter->tag_node = root; - iter->null_tag_node = push_array(arena, DW_TagNode, 1); - return iter; -} - -internal void -d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter) -{ - // descend to first child - if (iter->visit_children) { - if (iter->stack->node->first_child) { - D2R_TagFrame *f = iter->free_list; - if (f) { SLLStackPop(iter->free_list); MemoryZeroStruct(f); } - else { f = push_array(arena, D2R_TagFrame, 1); } - f->node = iter->stack->node->first_child; - SLLStackPush(iter->stack, f); - goto exit; - } - } - - while (iter->stack) { - // go to sibling - iter->stack->node = iter->stack->node->sibling; - if (iter->stack->node) { break; } - - // no more siblings, go up - D2R_TagFrame *f = iter->stack; - SLLStackPop(iter->stack); - SLLStackPush(iter->free_list, f); - } - - exit:; - // update iterator - iter->visit_children = 1; - iter->tag_node = iter->stack ? iter->stack->node : 0; -} - -internal void -d2r_tag_iterator_skip_children(D2R_TagIterator *iter) -{ - iter->visit_children = 0; -} - -internal DW_TagNode * -d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter) -{ - return iter->stack == 0 || iter->stack->next == 0 ? iter->null_tag_node : iter->stack->next->node; -} - -internal DW_Tag -d2r_tag_iterator_parent_tag(D2R_TagIterator *iter) -{ - DW_TagNode *tag_node = d2r_tag_iterator_parent_tag_node(iter); - return tag_node->tag; -} - -internal void -d2r_flag_converted_type_tag(DW_TagNode *tag_node) -{ - tag_node->tag.v |= D2R_TagFlags_TypeConverted; -} - -internal B8 -d2r_is_type_tag_converted(DW_TagNode *tag_node) -{ - return !!(tag_node->tag.v & D2R_TagFlags_TypeConverted); -} - -internal RDIM_Type * -d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_Tag tag, DW_AttribKind kind) -{ - RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; - - // find attrib - DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); - - // does tag have this attribute? - if (attrib->attrib_kind == kind) { - DW_AttribClass value_class = dw_value_class_from_attrib(cu, attrib); - - if (value_class == DW_AttribClass_Reference) { - // resolve reference - DW_Reference ref = dw_ref_from_attrib(input, cu, attrib); + // rjf: read address / segment selector size + U8 addr_size = 0; + U8 segment_selector_size = 0; + off += str8_deserial_read_struct(data, off, &addr_size); + off += str8_deserial_read_struct(data, off, &segment_selector_size); - if (ref.cu == cu) { - // find type - type = d2r_type_from_offset(type_table, ref.info_off); - - // was type converted? - if (type == 0) { - // issue type conversion - DW_TagNode *ref_node = dw_tag_node_from_info_off(cu, ref.info_off); - d2r_convert_types(arena, type_table, input, cu, cu_lang, arch, ref_node); - - // if we do not have a converted type at this point then debug info is malformed - type = d2r_type_from_offset(type_table, ref.info_off); - if (type == 0) { - type = type_table->builtin_types[RDI_TypeKind_NULL]; - } - } - } else { - NotImplemented; + // rjf: round up past padding + { + U64 tuple_size = addr_size*2 + segment_selector_size; + off += tuple_size - (off%tuple_size); } - } else { - log_infof("unexpected attrib class @ .debug_info+%llx", tag.info_off); - } - } - - return type; -} - -internal void -d2r_convert_types(Arena *arena, - D2R_TypeTable *type_table, - DW_Raw *input, - DW_CompUnit *cu, - DW_Language cu_lang, - Arch arch, - DW_TagNode *root) -{ - Temp scratch = scratch_begin(&arena, 1); - for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { - DW_TagNode *tag_node = it->tag_node; - DW_Tag tag = tag_node->tag; - - // skip converted tags - if (d2r_is_type_tag_converted(tag_node)) { - d2r_tag_iterator_skip_children(it); - continue; - } - // mark the tag as converted here, because during conversion we may recurse on the same tag - d2r_flag_converted_type_tag(tag_node); - - switch (tag.kind) { - case DW_TagKind_ClassType: { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_IncompleteClass; - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - Assert(!tag_node->first_child); - d2r_tag_iterator_skip_children(it); - } else { - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_Class; - type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); - type->direct_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - } - } break; - case DW_TagKind_StructureType: { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_IncompleteStruct; - - if (tag_node->first_child) { - d2r_tag_iterator_skip_children(it); - } else { - log_infof("childless struct @ .debug_info+%llx", tag.info_off); - } - } else { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_Struct; - type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); - } - } break; - case DW_TagKind_UnionType: { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_IncompleteUnion; - - if (tag_node->first_child) { - d2r_tag_iterator_skip_children(it); - } else { - log_infof("childless union @ .debug_info+%llx", tag.info_off); - } - } else { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_Union; - type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); - } - } break; - case DW_TagKind_EnumerationType: { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_IncompleteEnum; - if (tag_node->first_child) { - d2r_tag_iterator_skip_children(it); - } else { - log_infof("childless enum @ .debug_info+%llx", tag.info_off); - } - } else { - RDIM_Type *enum_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->kind = RDI_TypeKind_Enum; - type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); - type->direct_type = enum_base_type; - } - } break; - case DW_TagKind_SubProgram: { - if ( ! dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Declaration)) { + + // rjf: parse ranges + RDIM_Rng1U64ChunkList voff_ranges = {0}; + if(segment_selector_size != 0) + { + log_infof("[.debug_aranges@0x%I64x] Non-zero (%i) segment selector size parsed; this form of addressing is not currently supported in DWARF info.\n", off, (S32)segment_selector_size); + } + else for(;off < unit_opl;) + { + U64 start_off = off; + U64 base_addr = 0; + U64 range_size = 0; + off += str8_deserial_read(data, off, &base_addr, addr_size, addr_size); + off += str8_deserial_read(data, off, &range_size, addr_size, addr_size); + if(base_addr == 0 && range_size == 0) + { break; } - } // fall-through - case DW_TagKind_SubroutineType: { - RDIM_Type *ret_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - - // collect parameters - RDIM_TypeList param_list = {0}; - for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { - if (n->tag.kind == DW_TagKind_FormalParameter) { - RDIM_Type *param_type = d2r_type_from_attrib(type_table, input, cu, n->tag, DW_AttribKind_Type); - rdim_type_list_push(scratch.arena, ¶m_list, param_type); - } else if (n->tag.kind == DW_TagKind_UnspecifiedParameters) { - rdim_type_list_push(scratch.arena, ¶m_list, type_table->builtin_types[RDI_TypeKind_Variadic]); - } else { - log_infof("unexpected tag @ .debug_info+%llx", tag.info_off); - } + if(base_addr < base_vaddr) + { + log_infof("[.debug_aranges@0x%I64x] Address (0x%I64x) parsed which was less than the image base address (0x%I64x). Skipping.\n", start_off, base_addr, base_vaddr); } - - DW_TagNode *parent = d2r_tag_iterator_parent_tag_node(it); - B32 is_method = parent->tag.kind == DW_TagKind_StructureType || parent->tag.kind == DW_TagKind_ClassType; - - // init proceudre type - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = is_method ? RDI_TypeKind_Method : RDI_TypeKind_Function; - type->byte_size = cu->address_size; - type->direct_type = ret_type; - type->count = param_list.count; - type->param_types = rdim_array_from_type_list(arena, param_list); - - d2r_tag_iterator_skip_children(it); - } break; - case DW_TagKind_Typedef: { - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Alias; - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->direct_type = direct_type; - for (RDIM_Type *n = direct_type; n != 0; n = n->direct_type) { - if (n->byte_size) { - type->byte_size = n->byte_size; + else + { + U64 voff_first = (base_addr - base_vaddr); + U64 voff_opl = voff_first + range_size; + RDIM_Rng1U64 range = {voff_first, voff_opl}; + rdim_rng1u64_chunk_list_push(arena, &voff_ranges, 256, range); + } + if(off == start_off) + { + break; + } + } + + // rjf: store + arange_info_offs[unit_idx] = info_off; + arange_voff_ranges[unit_idx] = voff_ranges; + } + lane_sync(); + } + + //////////////////////////// + //- rjf: produce info_off -> list(voff_range) map from aranges units; + // we must do this because technically we can't guarantee that unit_idxs + // inside of .debug_aranges are the same as unit_idxs inside of .debug_info, + // nor can we guarantee that they'd be in the same order, so we need to + // correllate via the encoded .debug_info offset from .debug_aranges. + // + // more excellence. + // + typedef struct D2R2_ARangeUnitNode D2R2_ARangeUnitNode; + struct D2R2_ARangeUnitNode + { + D2R2_ARangeUnitNode *next; + U64 info_off; + RDIM_Rng1U64ChunkList *ranges; + }; + U64 arange_unit_from_info_off_map_slots_count = unit_arange_ranges->count; + D2R2_ARangeUnitNode **arange_unit_from_info_off_map_slots = 0; + { + if(lane_idx() == 0) + { + arange_unit_from_info_off_map_slots = push_array(scratch.arena, D2R2_ARangeUnitNode *, arange_unit_from_info_off_map_slots_count); + } + lane_sync_u64(&arange_unit_from_info_off_map_slots, 0); + for EachIndex(arange_unit_idx, unit_arange_ranges->count) + { + U64 info_off = arange_info_offs[arange_unit_idx]; + RDIM_Rng1U64ChunkList *ranges = &arange_voff_ranges[arange_unit_idx]; + U64 hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count; + D2R2_ARangeUnitNode *n = push_array(scratch.arena, D2R2_ARangeUnitNode, 1); + SLLStackPush(arange_unit_from_info_off_map_slots[slot_idx], n); + n->info_off = info_off; + n->ranges = ranges; + } + } + lane_sync(); + + //////////////////////////// + //- rjf: build all abbreviation maps, build (unit -> abbrev map) + // + DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count); + + //////////////////////////// + //- rjf: parse all unit offsets tables + // + // on .debug_str_offsets, as one example: + // + // in an incredible twist of fate, DWARF decided to decouple these from + // compilation units. compilation units *do* contain a + // DW_AttribKind_StrOffsetsBase attribute. but this base offset does *not* + // point to the beginning of a table in .debug_str_offsets! it instead points + // PAST THE INITIAL VARIABLE-WIDTH LENGTH AND FORMAT ENCODING! this means + // you can't actually use the StrOffsetsBase attribute for ANYTHING other + // than correllating a unit to its associated string offset table - but you + // *necessarily needed to have parsed that table beforehand*, completely + // independently from units. + // + // so, we have to parse all the string offset tables up-front, then + // *binary search* their ranges to determine which unit has which table. + // + // of course, in practice, it's perhaps likely/expected that these match + // one-to-one with units, and in the same order, because that is what is + // most natural for generators. but, the format does not *guarantee this*, + // and instead specced something far more arbitrary. + // + // thank you, again, DWARF. + // + DW2_OffsetTableSet *offset_tables = 0; + ProfScope("parse all offset tables (.debug_rnglists, .debug_str_offsets, .debug_addr, .debug_loclists)") if(lane_idx() == 0) + { + offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1); + offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw); + } + lane_sync_u64(&offset_tables, 0); + + //////////////////////////// + //- rjf: build per-unit parsing contexts, parse compilation unit tags + // + DW2_ParseCtx *unit_parse_ctxs = 0; + DW2_Tag *unit_root_tags = 0; + { + if(lane_idx() == 0) + { + unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count); + unit_root_tags = push_array(scratch.arena, DW2_Tag, unit_count); + } + lane_sync_u64(&unit_parse_ctxs, 0); + lane_sync_u64(&unit_root_tags, 0); + Rng1U64 range = lane_range(unit_count); + for EachInRange(unit_idx, range) + { + DW2_UnitHeader *hdr = &unit_headers[unit_idx]; + DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; + + // rjf: equip initial info - just enough to do a bootstrapping parse of the root tag + dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min); + dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx); + + // rjf: do bootstrapping parse + // + // an incredible NOTE: the above context info is actually not sufficient for general + // parsing, because this "bootstrapping" tag parse is what informs us which + // .debug_str_offsets table each unit should be associated with (via the + // StrOffsetsBase attribute), we actually don't have the right string offset table + // *before* we parse this, which means we can't resolve some of the string attribute + // values. + // + // so, in another incredible twist of fate, we actually must do this *twice*. first, + // to find *just the StrOffsetsBase*, and then, to actually fully resolve everything. + // + // (all of the above is also true for RngListsBase) + // + DW2_Tag root_tag_bootstrap = {0}; + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap); + + // rjf: equip info extracted from bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables); + + // rjf: do non-bootstrapping parse + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]); + + // rjf: equip info extracted from non-bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &unit_root_tags[unit_idx], offset_tables); + } + } + lane_sync(); + + //////////////////////////// + //- rjf: parse each unit's line table header + // + DW2_LineTableHeader *unit_line_table_headers = 0; + ProfScope("parse each unit's line table header") + { + if(lane_idx() == 0) + { + unit_line_table_headers = push_array(scratch.arena, DW2_LineTableHeader, unit_count); + } + lane_sync_u64(&unit_line_table_headers, 0); + U64 unit_take_idx = 0; + U64 *unit_take_idx_ptr = &unit_take_idx; + lane_sync_u64(&unit_take_idx_ptr, 0); + for(;;) + { + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; + if(unit_idx >= unit_count) + { + break; + } + DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; + DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; + DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); + U64 line_info_off = stmt_list->val.u128.u64[0]; + String8 line_info_data = raw->sec[DW_SectionKind_Line].data; + dw2_read_line_table_header(scratch.arena, raw, ctx, line_info_data, line_info_off, &unit_line_table_headers[unit_idx]); + } + lane_sync(); + } + + //////////////////////////// + //- rjf: deduplicate all source files + // + typedef struct UnitSrcFileMap UnitSrcFileMap; + struct UnitSrcFileMap + { + RDIM_SrcFile **v; + }; + RDIM_SrcFileChunkList *all_src_files = 0; + UnitSrcFileMap *unit_src_file_maps = 0; + ProfScope("deduplicate all source files") if(lane_idx() == 0) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + //- rjf: count all files in all units + U64 total_file_count = 0; + for EachIndex(unit_idx, unit_count) + { + total_file_count += unit_line_table_headers[unit_idx].files.count; + } + + //- rjf: set up path -> src file map + typedef struct SrcFileNode SrcFileNode; + struct SrcFileNode + { + SrcFileNode *next; + String8 full_path; + RDIM_SrcFile *src_file; + }; + all_src_files = push_array(scratch.arena, RDIM_SrcFileChunkList, 1); + U64 slots_count = 1 + (total_file_count * 3) / 4; + SrcFileNode **slots = push_array(scratch2.arena, SrcFileNode *, slots_count); + + //- rjf: set up unit -> src file maps + unit_src_file_maps = push_array(scratch.arena, UnitSrcFileMap, unit_count); + for EachIndex(unit_idx, unit_count) + { + unit_src_file_maps[unit_idx].v = push_array(scratch.arena, RDIM_SrcFile *, unit_line_table_headers[unit_idx].files.count); + } + + //- rjf: build path -> src file map + for EachIndex(unit_idx, unit_count) + { + DW2_LineTableHeader *hdr = &unit_line_table_headers[unit_idx]; + for EachIndex(file_idx, hdr->files.count) + { + DW2_LineTableFile *f = &hdr->files.v[file_idx]; + DW2_LineTableFile *dir = &hdr->dirs.v[f->dir_idx]; + String8 full_file_path = str8f(scratch2.arena, "%S%s%S", dir->file_name, dir->file_name.size != 0 ? "/" : "", f->file_name); + U64 hash = u64_hash_from_str8(full_file_path); + U64 slot_idx = hash%slots_count; + SrcFileNode *node = 0; + for(SrcFileNode *n = slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->full_path, full_file_path, 0)) + { + node = n; break; } } - } break; - case DW_TagKind_BaseType: { - DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Encoding); - U64 byte_size = dw_byte_size_from_tag(input, cu, tag); - - // convert base type encoding to RDI version - RDI_TypeKind kind = RDI_TypeKind_NULL; -#define X(t,g,s) case s: kind = RDI_TypeKind_##t; break; - switch (encoding) { - case DW_ATE_Null: kind = RDI_TypeKind_NULL; break; - case DW_ATE_Address: kind = RDI_TypeKind_Void; break; - case DW_ATE_Boolean: kind = RDI_TypeKind_Bool; break; - case DW_ATE_ComplexFloat: { - switch (byte_size) { - case 8: kind = RDI_TypeKind_ComplexF32; break; - case 16: kind = RDI_TypeKind_ComplexF64; break; - case 24: kind = RDI_TypeKind_ComplexF80; break; - case 32: kind = RDI_TypeKind_ComplexF128; break; - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_Float: { - String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - if (arch == Arch_x64 || arch == Arch_arm64) { - if (str8_match(name, str8_lit("__float80"), 0)) { - kind = RDI_TypeKind_F80; - } else if (str8_match(name, str8_lit("__float128"), 0)) { - kind = RDI_TypeKind_F128; - } else if (str8_match(name, str8_lit("_Float16"), 0)) { - kind = RDI_TypeKind_F16; - } else if (str8_match(name, str8_lit("__bf16"), 0)) { - kind = RDI_TypeKind_BF16; - } - if (kind != RDI_TypeKind_NULL) { break; } - } - - switch (byte_size) { - D2R_ValueType_Float_XList - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_Signed: { - String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - if (str8_match(name, str8_lit("wchar_t"), 0)) { goto do_signed_char; } - - switch (byte_size) { - D2R_ValueType_Signed_XList - default: log_infof("unexpected size"); break; - } - } break; - do_signed_char:; - case DW_ATE_SignedChar: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_Char8; break; - case 2: kind = RDI_TypeKind_Char16; break; - case 4: kind = RDI_TypeKind_Char32; break; - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_Unsigned: { - switch (byte_size) { - D2R_ValueType_Unsigned_XList - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_Utf: - case DW_ATE_UnsignedChar: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_UChar8; break; - case 2: kind = RDI_TypeKind_UChar16; break; - case 4: kind = RDI_TypeKind_UChar32; break; - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_DecimalFloat: { - switch (byte_size) { - case 4: kind = RDI_TypeKind_Decimal32; break; - case 8: kind = RDI_TypeKind_Decimal64; break; - case 16: kind = RDI_TypeKind_Decimal128; break; - default: log_infof("unexpected size"); break; - } - } break; - case DW_ATE_ImaginaryFloat: { - NotImplemented; - } break; - case DW_ATE_PackedDecimal: { - NotImplemented; - } break; - case DW_ATE_NumericString: { - NotImplemented; - } break; - case DW_ATE_Edited: { - NotImplemented; - } break; - case DW_ATE_SignedFixed: { - NotImplemented; - } break; - case DW_ATE_UnsignedFixed: { - NotImplemented; - } break; - case DW_ATE_Ucs: { - NotImplemented; - } break; - case DW_ATE_Ascii: { - NotImplemented; - } break; - default: log_infof("unexpected base type encoding"); break; - } -#undef X - - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Alias; - type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - type->direct_type = type_table->builtin_types[kind]; - type->byte_size = byte_size; - } break; - case DW_TagKind_PointerType: { - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - - // TODO: - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Allocated)) { - log_infof("TODO: handle allocated attrib @ .debug_info+%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Associated)) { - log_infof("TODO: handle associated attrib @ .debug_info%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { - log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { - log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_AddressClass)) { - log_infof("TODO: handle address class attrib @ .debug_info+%llx", tag.info_off); - } - - U64 byte_size = cu->address_size; - if (cu->version == DW_Version_5 || cu->relaxed) { - dw_try_byte_size_from_tag(input, cu, tag, &byte_size); - } - - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Ptr; - type->byte_size = byte_size; - type->direct_type = direct_type; - } break; - case DW_TagKind_RestrictType: { - // TODO: - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { - log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { - log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); - } - - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = cu->address_size; - type->flags = RDI_TypeModifierFlag_Restrict; - type->direct_type = direct_type; - } break; - case DW_TagKind_VolatileType: { - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { - log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); - } - - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = cu->address_size; - type->flags = RDI_TypeModifierFlag_Volatile; - type->direct_type = direct_type; - } break; - case DW_TagKind_ConstType: { - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)) { - log_infof("TODO: handle name attrib @ .debug_info+%llx", tag.info_off); - } - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)) { - log_infof("TODO: handle alignment attrib @ .debug_info+%llx", tag.info_off); - } - - RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = cu->address_size; - type->flags = RDI_TypeModifierFlag_Const; - type->direct_type = direct_type; - } break; - case DW_TagKind_ArrayType: { - // TODO: @native_vector_support extract byte size from the base type tag - // and convert to U256, U512, S256 and S512 - B32 is_vector = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_GNU_Vector); - // TODO: - //if (is_vector) { NotImplemented; } - - B32 error = 1; - - // * DWARF vs RDI Array Type Graph * - // - // For example lets take following decl: - // - // int (*foo[2])[3]; - // - // This compiles to in DWARF: - // - // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] - // \ - // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] - // \ - // -> (B1) DW_TAG_BaseType (int) - // - // RDI expects: - // - // foo -> Array[2] -> Pointer -> Array[3] -> int - // - // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and - // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. - // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from - // B to A. - struct SubrangeNode { struct SubrangeNode *next; U64 count; }; - struct SubrangeNode *subrange_stack = 0; - for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { - if (n->tag.kind != DW_TagKind_SubrangeType) { - log_infof("[%llx] while scanning for DW_TagKind_SubrangeType found an unexpected child tag %S @ %llx\n", tag.info_off, dw_string_from_tag_kind(scratch.arena, n->tag.kind), n->tag.info_off); - goto array_type_exit; + if(!node) + { + node = push_array(scratch2.arena, SrcFileNode, 1); + node->full_path = full_file_path; + node->src_file = rdim_src_file_chunk_list_push(arena, all_src_files, slots_count); + node->src_file->path = str8_copy(arena, full_file_path); + if(f->flags & DW2_LineTableFileFlag_HasMD5) + { + node->src_file->checksum_kind = RDI_ChecksumKind_MD5; + node->src_file->checksum = str8_copy(arena, str8_struct(&f->md5)); } - - // resolve lower bound - U64 lower_bound = 0; - if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_LowerBound)) { - B32 is_vla = dw_is_attrib_var_ref(input, cu, dw_attrib_from_tag(input, cu, n->tag, DW_AttribKind_LowerBound)); - if (is_vla) { - log_infof("[%llx] TODO: lower bound is a variable\n", n->tag.info_off); - } else { - lower_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_LowerBound); - } - } else { - lower_bound = dw_pick_default_lower_bound(cu_lang); + else if(f->flags & DW2_LineTableFileFlag_HasModifyTime) + { + node->src_file->checksum_kind = RDI_ChecksumKind_Timestamp; + node->src_file->checksum = str8_copy(arena, str8_struct(&f->modify_time)); } - - // resolve upper bound - U64 upper_bound = 0; - if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_Count)) { - U64 count = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_Count); - upper_bound = lower_bound + count; - } else if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_UpperBound)) { - B32 is_vla = dw_is_attrib_var_ref(input, cu, dw_attrib_from_tag(input, cu, n->tag, DW_AttribKind_UpperBound)); - if (is_vla) { - log_infof("[%llx] TODO: upper bound is a variable\n", n->tag.info_off); - goto array_type_exit; - } else { - upper_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_UpperBound); - upper_bound += 1; // turn upper bound into exclusive range - } - } else { - // zero sized array - } - - struct SubrangeNode *s = push_array(scratch.arena, struct SubrangeNode, 1); - s->count = upper_bound - lower_bound; - SLLStackPush(subrange_stack, s); } - - RDIM_Type *array_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_Type *direct_type = array_base_type; - U64 size_cursor = array_base_type->byte_size; - for EachNode(s, struct SubrangeNode, subrange_stack) { - size_cursor *= s->count; - - RDIM_Type *t; - if (s->next) { t = d2r_create_type(arena, type_table); } - else { t = d2r_create_type_from_offset(arena, type_table, tag.info_off); } - - t->kind = RDI_TypeKind_Array; - t->direct_type = direct_type; - t->byte_size = size_cursor; - t->count = s->count; - - direct_type = t; - } - - error = 0; - array_type_exit:; - - // in case of an error, assign null to the array type - if (error) { - Assert(d2r_type_from_offset(type_table, tag.info_off) == 0); // this should the first time this type is being parsed - hash_table_push_u64_raw(arena, type_table->ht, tag.info_off, type_table->builtin_types[RDI_TypeKind_NULL]); - } - - d2r_tag_iterator_skip_children(it); - } break; - case DW_TagKind_SubrangeType: { - log_infof("unexpected tag"); - } break; - case DW_TagKind_Inheritance: { - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_StructureType || parent_tag.kind == DW_TagKind_ClassType) { - RDIM_Type *parent = d2r_type_from_offset(type_table, parent_tag.info_off); - RDIM_Type *type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch, tag, DW_AttribKind_Type); - RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, parent->udt); - member->kind = RDI_MemberKind_Base; - member->type = type; - member->off = safe_cast_u32(dw_const_u32_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation)); - } else { - log_infof("unexpected parent tag"); - } - } break; - } - } - scratch_end(scratch); -} - -internal B8 -d2r_is_udt_tag_converted(DW_TagNode *tag_node) -{ - return !!(tag_node->tag.v & D2R_TagFlags_UdtConverted); -} - -internal void -d2r_flag_converted_udt_tag(DW_TagNode *tag_node) -{ - tag_node->tag.v |= D2R_TagFlags_UdtConverted; -} - -internal void -d2r_inline_anonymous_udt_member(Arena *arena, RDIM_UDT *top_udt, U64 base_off, RDIM_UDT *udt) -{ - for (RDIM_UDTMember *curr = udt->first_member, *prev = 0; curr != 0; prev = curr, curr = curr->next) { - if (curr->name.size == 0) { - d2r_inline_anonymous_udt_member(arena, top_udt, base_off + curr->off, curr->type->udt); - } else { - // copy member and adjust its offset relative to inlined position in the root UDT - RDIM_UDTMember *m = rdim_udt_push_member(arena, &g_d2r_shared.udts, top_udt); - *m = *curr; - m->off += base_off; - } - } -} - -internal void -d2r_convert_udts(Arena *arena, - D2R_TypeTable *type_table, - DW_Raw *input, - DW_CompUnit *cu, - DW_Language cu_lang, - DW_TagNode *root) -{ - Temp scratch = scratch_begin(&arena, 1); - - for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { - DW_TagNode *tag_node = it->tag_node; - DW_Tag tag = tag_node->tag; - - // skip converted tags - if (d2r_is_udt_tag_converted(tag_node)) { - d2r_tag_iterator_skip_children(it); - continue; - } - d2r_flag_converted_udt_tag(tag_node); - - if (tag.kind == DW_TagKind_ClassType || tag.kind == DW_TagKind_StructureType || tag.kind == DW_TagKind_UnionType) { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - d2r_tag_iterator_skip_children(it); - } else { - RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &g_d2r_shared.udts, D2R_UDT_CHUNK_CAP); - udt->self_type = type; - type->udt = udt; - } - } else if (tag.kind == DW_TagKind_EnumerationType) { - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - d2r_tag_iterator_skip_children(it); - } else { - RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &g_d2r_shared.udts, D2R_UDT_CHUNK_CAP); - udt->self_type = type; - type->udt = udt; - } - } else if (tag.kind == DW_TagKind_Member) { - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - B32 is_parent_udt = parent_tag.kind == DW_TagKind_StructureType || - parent_tag.kind == DW_TagKind_ClassType || - parent_tag.kind == DW_TagKind_UnionType; - if (is_parent_udt) { - RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); - RDIM_Type *type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - U64 off = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation); - if (name.size == 0) { // inline anonymous members - DW_Reference type_ref = dw_ref_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Type); - RDIM_Type *member_type = d2r_type_from_offset(type_table, type_ref.info_off); - RDIM_UDT *member_udt = member_type->udt; - if (member_udt == 0) { - DW_Language ref_cu_lang = dw_const_u64_from_tag_attrib_kind(input, type_ref.cu, type_ref.cu->tag, DW_AttribKind_Language); - DW_TagNode *ref_tag_node = dw_tag_node_from_info_off(type_ref.cu, type_ref.info_off); - d2r_convert_udts(arena, type_table, input, type_ref.cu, ref_cu_lang, ref_tag_node); - Assert(member_type->udt); - member_udt = member_type->udt; - } - d2r_inline_anonymous_udt_member(arena, parent_type->udt, off, member_udt); - } else { - RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, parent_type->udt); - member->kind = RDI_MemberKind_DataField; - member->name = name; - member->type = type; - member->off = off; - } - } else { - log_infof("unexpected parent tag @ .debug_info+%llx", tag.info_off); - } - } else if (tag.kind == DW_TagKind_Enumerator) { - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_EnumerationType) { - RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); - RDIM_UDTEnumVal *udt_member = rdim_udt_push_enum_val(arena, &g_d2r_shared.udts, parent_type->udt); - udt_member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - udt_member->val = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ConstValue); - } else { - log_infof("unexpected parent tag @ .debug_info+%llx", tag.info_off); + unit_src_file_maps[unit_idx].v[file_idx] = node->src_file; } } + scratch_end(scratch2); } - scratch_end(scratch); -} - -internal RDIM_Scope * -d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges) -{ - // fill out scope - RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, scopes, scope_chunk_cap); + lane_sync_u64(&all_src_files, 0); + lane_sync_u64(&unit_src_file_maps, 0); - // push ranges - for EachNode(i, Rng1U64Node, ranges.first) { - rdim_scope_push_voff_range(arena, scopes, scope, (RDIM_Rng1U64){.min = i->v.min, i->v.max}); - } - - // associate scope with tag - tag_stack->scope = scope; - - // update scope hierarchy - DW_TagKind parent_tag_kind = tag_stack->next->node->tag.kind; - if (parent_tag_kind == DW_TagKind_SubProgram || parent_tag_kind == DW_TagKind_InlinedSubroutine || parent_tag_kind == DW_TagKind_LexicalBlock) { - RDIM_Scope *parent = tag_stack->next->scope; - - scope->parent_scope = parent; - scope->symbol = parent->symbol; - - if (parent->last_child) { - parent->last_child->next_sibling = scope; - } - SLLQueuePush_N(parent->first_child, parent->last_child, scope, next_sibling); - } - - return scope; -} - -internal RDIM_Type ** -d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out) -{ - Temp scratch = scratch_begin(&arena, 1); - - RDIM_TypeList list = {0}; - B32 has_vargs = 0; - for (DW_TagNode *i = cur_node->first_child; i != 0; i = i->sibling) { - if (i->tag.kind == DW_TagKind_FormalParameter) { - RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); - n->v = d2r_type_from_attrib(type_table, input, cu, i->tag, DW_AttribKind_Type); - SLLQueuePush(list.first, list.last, n); - ++list.count; - } else if (i->tag.kind == DW_TagKind_UnspecifiedParameters) { - has_vargs = 1; - } - } - - if (has_vargs) { - RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); - n->v = type_table->builtin_types[RDI_TypeKind_Variadic]; - SLLQueuePush(list.first, list.last, n); - ++list.count; - } - - // collect params - *param_count_out = list.count; - RDIM_Type **params = rdim_array_from_type_list(arena, list); - - scratch_end(scratch); - return params; -} - -internal Rng1U64List -d2r_range_list_from_tag(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag) -{ - // collect non-contiguous range - Rng1U64List raw_ranges = dw_rnglist_from_tag_attrib_kind(arena, input, cu, tag, DW_AttribKind_Ranges); - - // exclude invalid ranges caused by linker optimizations - Rng1U64List ranges = {0}; - for (Rng1U64Node *n = raw_ranges.first, *next = 0; n != 0; n = next) { - next = n->next; - if (n->v.min < image_base || n->v.min > n->v.max) { - continue; - } - rng1u64_list_push_node(&ranges, n); - } - - // debase ranges - for EachNode(r, Rng1U64Node, ranges.first) { - r->v.min -= image_base; - r->v.max -= image_base; - } - - // collect contiguous range + //////////////////////////// + //- rjf: parse each unit's line info; produce line tables + // + RDIM_LineTableChunkList *unit_line_table_chunk_lists = 0; + RDIM_LineTable **unit_line_tables = 0; + ProfScope("parse each unit's line info; produce line tables") { - DW_Attrib *lo_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_LowPc); - DW_Attrib *hi_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_HighPc); - if (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) { - U64 lo_pc = dw_address_from_attrib(input, cu, lo_pc_attrib); - - U64 hi_pc = 0; - DW_AttribClass hi_pc_class = dw_value_class_from_attrib(cu, hi_pc_attrib); - if (hi_pc_class == DW_AttribClass_Address) { - hi_pc = dw_address_from_attrib(input, cu, hi_pc_attrib); - } else if (hi_pc_class == DW_AttribClass_Const) { - hi_pc = dw_const_u64_from_attrib(input, cu, hi_pc_attrib); - hi_pc += lo_pc; - } - - if (lo_pc >= image_base && hi_pc >= image_base) { - if (lo_pc < hi_pc) { - rng1u64_list_push(arena, &ranges, rng_1u64(lo_pc - image_base, hi_pc - image_base)); - } else { - log_infof("invalid range @ .debug_info+%llx", tag.info_off); - } - } else { - // invalid low and hi PC are likely are caused by an optimization pass during linking - } - } else if ((lo_pc_attrib->attrib_kind == DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) || - (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind == DW_AttribKind_Null)) { - log_infof("invalid range @ .debug_info+%llx", tag.info_off); + //- rjf: prep outputs + typedef struct LineSeqChunk LineSeqChunk; + struct LineSeqChunk + { + LineSeqChunk *next; + U64 *voffs; + U32 *line_nums; + U16 *col_nums; + U64 line_count; + U64 line_cap; + }; + typedef struct FileSeqNode FileSeqNode; + struct FileSeqNode + { + FileSeqNode *next; + RDIM_SrcFile *src_file; + RDIM_SrcFileLineMapFragment *first_line_map_fragment; + RDIM_SrcFileLineMapFragment *last_line_map_fragment; + }; + typedef struct FileSeqMap FileSeqMap; + struct FileSeqMap + { + U64 slots_count; + FileSeqNode **slots; + }; + FileSeqMap *unit_file_seq_maps = 0; + if(lane_idx() == 0) + { + unit_line_table_chunk_lists = push_array(scratch.arena, RDIM_LineTableChunkList, unit_count); + unit_line_tables = push_array(scratch.arena, RDIM_LineTable *, unit_count); + unit_file_seq_maps = push_array(scratch.arena, FileSeqMap, unit_count); } + lane_sync_u64(&unit_line_table_chunk_lists, 0); + lane_sync_u64(&unit_line_tables, 0); + lane_sync_u64(&unit_file_seq_maps, 0); + + //- rjf: wide per-unit parse + U64 unit_take_idx = 0; + U64 *unit_take_idx_ptr = &unit_take_idx; + lane_sync_u64(&unit_take_idx_ptr, 0); + for(;;) + { + //- rjf: take unit + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; + if(unit_idx >= unit_count) + { + break; + } + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + //- rjf: unpack unit info + DW2_LineTableHeader *line_table_header = &unit_line_table_headers[unit_idx]; + RDIM_LineTableChunkList *dst_line_tables = &unit_line_table_chunk_lists[unit_idx]; + DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; + DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); + U64 line_info_off = stmt_list->val.u128.u64[0]; + String8 all_line_info_data = raw->sec[DW_SectionKind_Line].data; + String8 unit_line_table_data = str8_substr(all_line_info_data, r1u64(line_table_header->line_program_off, line_info_off + line_table_header->total_unit_data_size)); + + //- rjf: build unit's line table + RDIM_LineTable *dst_line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 1); + unit_line_tables[unit_idx] = dst_line_table; + + //- rjf: set up per-unit file sequence map + unit_file_seq_maps[unit_idx].slots_count = line_table_header->files.count + 1; + unit_file_seq_maps[unit_idx].slots = push_array(scratch.arena, FileSeqNode *, unit_file_seq_maps[unit_idx].slots_count); + + //- rjf: set up vm registers + DW2_LineVMRegs vm_regs = {0}; + { + vm_regs.file_index = 1; + vm_regs.line = 1; + vm_regs.is_stmt = line_table_header->default_is_stmt; + } + + //- rjf: run the line opcode program + B32 emit_line = 0; + RDIM_SrcFile *line_seq_src_file = 0; + LineSeqChunk *first_line_seq_chunk = 0; + LineSeqChunk *last_line_seq_chunk = 0; + U64 total_line_seq_count = 0; + for(U64 off = 0, next_off = 0; off <= unit_line_table_data.size; off = next_off) + { + next_off = unit_line_table_data.size; + + //- rjf: read next opcode + U64 op_read_off = off; + U8 opcode = 0; + str8_deserial_read_struct(unit_line_table_data, op_read_off, &opcode); + op_read_off += 1; + + //- rjf: apply "special opcodes" (DWARF v5 6.2.5.1) + if(opcode >= line_table_header->opcode_base) + { + U32 adjusted_opcode = (U32)(opcode - line_table_header->opcode_base); + U32 op_advance = adjusted_opcode / line_table_header->line_range; + S64 line_advance = (S64)line_table_header->line_base + (S64)adjusted_opcode%(S64)line_table_header->line_range; + U64 addr_advance = line_table_header->min_inst_length * (vm_regs.vliw_op_index + op_advance) / line_table_header->max_ops_per_inst; + vm_regs.address += addr_advance; + vm_regs.vliw_op_index = (vm_regs.vliw_op_index + op_advance) % line_table_header->max_ops_per_inst; + vm_regs.line += line_advance; + emit_line = 1; + } + + //- rjf: apply standard opcode + else + { + U64 op_advance = 0; + switch(opcode) + { + //- rjf: skip unknown opcode + default: + { + for EachIndex(uleb_idx, line_table_header->opcode_lengths[opcode - 1]) + { + U64 v = 0; + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &v); + } + }break; + + //- rjf: line emissions + case DW_StdOpcode_Copy: + { + emit_line = 1; + }break; + + //- rjf: PC advances + case DW_StdOpcode_AdvancePc: + { + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &op_advance); + }goto advance_pc; + case DW_StdOpcode_ConstAddPc: + { + op_advance = (0xffu - line_table_header->opcode_base) / line_table_header->line_range; + }goto advance_pc; + advance_pc:; + { + U64 op_index = vm_regs.vliw_op_index + op_advance; + vm_regs.address += line_table_header->min_inst_length * (op_index / line_table_header->max_ops_per_inst); + vm_regs.vliw_op_index = op_index % line_table_header->max_ops_per_inst; + }break; + + //- rjf: fixed PC advances + case DW_StdOpcode_FixedAdvancePc: + { + U16 fixed_advance = 0; + str8_deserial_read_struct(unit_line_table_data, op_read_off, &fixed_advance); + op_read_off += sizeof(U16); + vm_regs.address += fixed_advance; + vm_regs.vliw_op_index = 0; + }break; + + //- rjf: line number advance + case DW_StdOpcode_AdvanceLine: + { + S64 advance = 0; + op_read_off += str8_deserial_read_sleb128(unit_line_table_data, op_read_off, &advance); + vm_regs.line += advance; + }break; + + //- rjf: set file + case DW_StdOpcode_SetFile: + { + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.file_index); + }break; + + //- rjf: set column + case DW_StdOpcode_SetColumn: + { + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.column); + }break; + + //- rjf: negate statment + case DW_StdOpcode_NegateStmt: + { + vm_regs.is_stmt = !vm_regs.is_stmt; + }break; + + //- rjf: flag sets + case DW_StdOpcode_SetBasicBlock: + { + vm_regs.basic_block = 1; + }break; + case DW_StdOpcode_SetPrologueEnd: + { + vm_regs.prologue_end = 1; + }break; + case DW_StdOpcode_SetEpilogueBegin: + { + vm_regs.epilogue_begin = 1; + }break; + + //- rjf: isa + case DW_StdOpcode_SetIsa: + { + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.isa); + }break; + + //- rjf: extended opcode + case DW_StdOpcode_ExtendedOpcode: + { + // rjf: read extended opcode size + U64 ext_opcode_size = 0; + op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &ext_opcode_size); + + // rjf: read extended opcode + U8 ext_opcode = 0; + op_read_off += str8_deserial_read_struct(unit_line_table_data, op_read_off, &ext_opcode); + + // rjf: grab truncated data for just this extended opcode + String8 ext_op_data = str8_prefix(unit_line_table_data, op_read_off + ext_opcode_size); + + // rjf: do extended opcode + switch(ext_opcode) + { + default: + case DW_ExtOpcode_Undefined: + case DW_ExtOpcode_UserLo: + case DW_ExtOpcode_UserHi: + {}break; + case DW_ExtOpcode_EndSequence: + { + emit_line = 1; + vm_regs.end_sequence = 1; + }break; + case DW_ExtOpcode_SetAddress: + { + U64 addr = 0; + op_read_off += str8_deserial_read(ext_op_data, op_read_off, &addr, line_table_header->addr_size, line_table_header->addr_size); + vm_regs.address = addr; + vm_regs.vliw_op_index = 0; + }break; + case DW_ExtOpcode_DefineFile: + { + String8 file_name = {0}; + U64 dir_idx = 0; + U64 modify_time = 0; + U64 file_size = 0; + op_read_off += str8_deserial_read_cstr(ext_op_data, op_read_off, &file_name); + op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &dir_idx); + op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &modify_time); + op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &file_size); + // + // TODO(rjf): this is a real problem, because at this point, we've already gathered & deduped + // all source files, but now we're in a situation where a per-lane line info parse can produce + // new source files, which may - of course - be duplicates of files defined in other unit line + // info. + // + }break; + case DW_ExtOpcode_SetDiscriminator: + { + op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &vm_regs.discriminator); + }break; + } + }break; + } + } + + //- rjf: advance to next op + if(op_read_off > off) + { + next_off = op_read_off; + } + + //- rjf: map file index -> rdim src file + RDIM_SrcFile *src_file = 0; + if(vm_regs.file_index < line_table_header->files.count) + { + src_file = unit_src_file_maps[unit_idx].v[vm_regs.file_index]; + } + + //- rjf: sequence ended explicitly, or file change, or end of stream? -> push to line table + if(line_seq_src_file != 0 && (vm_regs.end_sequence || (src_file != line_seq_src_file && first_line_seq_chunk != 0) || off >= unit_line_table_data.size)) + { + // rjf: combine voffs/lines/cols + U64 seq_line_count = total_line_seq_count; + U64 *seq_voffs = push_array(arena, U64, seq_line_count+1); + U32 *seq_lines = push_array(arena, U32, seq_line_count); + U16 *seq_cols = push_array(arena, U16, 2*seq_line_count); + { + U64 voff_idx = 0; + U64 line_idx = 0; + U64 col_idx = 0; + for(LineSeqChunk *c = first_line_seq_chunk; c != 0; c = c->next) + { + MemoryCopy(seq_voffs + voff_idx, c->voffs, sizeof(c->voffs[0]) * c->line_count); + MemoryCopy(seq_lines + line_idx, c->line_nums, sizeof(c->line_nums[0]) * c->line_count); + MemoryCopy(seq_cols + col_idx, c->col_nums, sizeof(c->col_nums[0]) * 2 * c->line_count); + voff_idx += c->line_count; + line_idx += c->line_count; + col_idx += 2*c->line_count; + } + seq_voffs[seq_line_count] = vm_regs.address - base_vaddr; + } + + // rjf: push sequence to line table + RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, dst_line_tables, dst_line_table, line_seq_src_file, seq_voffs, seq_lines, seq_cols, seq_line_count); + + // rjf: map src file -> file seq node + FileSeqNode *file_seq_n = 0; + { + U64 hash = u64_hash_from_str8(str8_struct(&line_seq_src_file)); + U64 slot_idx = hash%unit_file_seq_maps[unit_idx].slots_count; + for(FileSeqNode *n = unit_file_seq_maps[unit_idx].slots[slot_idx]; n != 0; n = n->next) + { + if(n->src_file == line_seq_src_file) + { + file_seq_n = n; + break; + } + } + if(file_seq_n == 0) + { + file_seq_n = push_array(scratch.arena, FileSeqNode, 1); + SLLStackPush(unit_file_seq_maps[unit_idx].slots[slot_idx], file_seq_n); + file_seq_n->src_file = line_seq_src_file; + } + } + + // rjf: record sequence in file seq node + { + RDIM_SrcFileLineMapFragment *f = push_array(scratch.arena, RDIM_SrcFileLineMapFragment, 1); + SLLQueuePush(file_seq_n->first_line_map_fragment, file_seq_n->last_line_map_fragment, f); + f->seq = seq; + } + + // rjf: reset + first_line_seq_chunk = last_line_seq_chunk = 0; + total_line_seq_count = 0; + line_seq_src_file = 0; + if(vm_regs.end_sequence) + { + MemoryZeroStruct(&vm_regs); + vm_regs.file_index = 1; + vm_regs.line = 1; + vm_regs.is_stmt = line_table_header->default_is_stmt; + } + } + + //- rjf: emit lines + if(emit_line && vm_regs.address != 0 && vm_regs.line != 0) + { + emit_line = 0; + + // rjf: grab the last emitted line info + U32 last_line = 0; + U16 last_col = 0; + if(last_line_seq_chunk != 0 && last_line_seq_chunk->line_count > 0) + { + last_line = last_line_seq_chunk->line_nums[last_line_seq_chunk->line_count-1]; + last_col = last_line_seq_chunk->col_nums[last_line_seq_chunk->line_count-1]; + } + + // rjf: determine if we need a new line + // + // TODO(rjf): in some cases this can trigger w/ DWARF due to columnar changes; + // we need to adjust this when we want to start correctly supporting columnar + // line info / stepping + // + B32 need_new_line = (last_line != (U32)vm_regs.line); + + // rjf: need new line -> grab chunk + LineSeqChunk *chunk = last_line_seq_chunk; + if(need_new_line && (chunk == 0 || chunk->line_count >= chunk->line_cap)) + { + chunk = push_array(scratch.arena, LineSeqChunk, 1); + SLLQueuePush(first_line_seq_chunk, last_line_seq_chunk, chunk); + chunk->line_cap = 64; + chunk->voffs = push_array(scratch.arena, U64, chunk->line_cap + 1); + chunk->line_nums = push_array(scratch.arena, U32, chunk->line_cap); + chunk->col_nums = push_array(scratch.arena, U16, 2*chunk->line_cap); + } + + // rjf: need new line -> push + if(need_new_line) + { + U64 chunk_line_idx = chunk->line_count; + chunk->voffs[chunk_line_idx] = vm_regs.address - base_vaddr; + chunk->line_nums[chunk_line_idx] = (U32)vm_regs.line; + chunk->col_nums[chunk_line_idx] = (U16)vm_regs.column; + chunk->line_count += 1; + total_line_seq_count += 1; + line_seq_src_file = src_file; + // NOTE(rjf): use for comparing against llvm-dwarfdump --debug-line +#if 0 + printf("0x%016I64x %6i %6i %6i %3i %13I64x %7i %s%s%s%s\n", + vm_regs.address, + (int)vm_regs.line, + (int)vm_regs.column, + (int)vm_regs.file_index, + (int)vm_regs.isa, + vm_regs.discriminator, + (int)vm_regs.vliw_op_index, + vm_regs.is_stmt ? " is_stmt" : "", + vm_regs.prologue_end ? " prologue_end" : "", + vm_regs.epilogue_begin ? " epilogue_begin" : "", + vm_regs.end_sequence ? " end_sequence" : ""); +#endif + } + vm_regs.discriminator = 0; + vm_regs.basic_block = 0; + vm_regs.prologue_end = 0; + vm_regs.epilogue_begin = 0; + } + } + + scratch_end(scratch2); + } + lane_sync(); + + //- rjf: equip source files with their fragments + // + // TODO(rjf): this can *almost* be wide, but we are relying on a few top-level + // summations inside the RDIM_SrcFileChunkList when we push a sequence to a src + // file. if we just summed those later when baking (probably fine), then this + // could go wide across all src files, which would be very nice. just lane-0ing + // for now. when possible, we can probably do the same thing in PDB too. + // + if(lane_idx() == 0) + { + for EachIndex(unit_idx, unit_count) + { + FileSeqMap *file_seq_map = &unit_file_seq_maps[unit_idx]; + for EachIndex(slot_idx, file_seq_map->slots_count) + { + for(FileSeqNode *n = file_seq_map->slots[slot_idx]; n != 0; n = n->next) + { + for(RDIM_SrcFileLineMapFragment *f = n->first_line_map_fragment; f != 0; f = f->next) + { + rdim_src_file_push_line_sequence(arena, all_src_files, n->src_file, f->seq); + } + } + } + } + } + lane_sync(); } - return ranges; -} - -internal void -d2r_convert_symbols(Arena *arena, - D2R_TypeTable *type_table, - DW_Raw *input, - DW_CompUnit *cu, - DW_Language cu_lang, - U64 image_base, - Arch arch, - DW_TagNode *root, - RDIM_Unit *unit_rdi) -{ - Temp scratch = scratch_begin(&arena, 1); - for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { - DW_TagNode *tag_node = it->tag_node; - DW_Tag tag = tag_node->tag; - switch (tag.kind) { - case DW_TagKind_Null: { InvalidPath; } break; - case DW_TagKind_ClassType: - case DW_TagKind_StructureType: - case DW_TagKind_UnionType: { - // visit children to collect methods and variables - } break; - case DW_TagKind_EnumerationType: - case DW_TagKind_SubroutineType: - case DW_TagKind_Typedef: - case DW_TagKind_BaseType: - case DW_TagKind_PointerType: - case DW_TagKind_RestrictType: - case DW_TagKind_VolatileType: - case DW_TagKind_ConstType: - case DW_TagKind_ArrayType: - case DW_TagKind_SubrangeType: - case DW_TagKind_Inheritance: - case DW_TagKind_Enumerator: - case DW_TagKind_Member: { - d2r_tag_iterator_skip_children(it); - } break; - case DW_TagKind_SubProgram: { - // handled during type conversion step - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Declaration)) { - d2r_tag_iterator_skip_children(it); + //////////////////////////// + //- rjf: join all line tables + // + RDIM_LineTableChunkList *all_line_tables = 0; + if(lane_idx() == 0) + { + all_line_tables = push_array(scratch.arena, RDIM_LineTableChunkList, 1); + for EachIndex(unit_idx, unit_count) + { + rdim_line_table_chunk_list_concat_in_place(all_line_tables, &unit_line_table_chunk_lists[unit_idx]); + } + } + lane_sync_u64(&all_line_tables, 0); + + //////////////////////////// + //- rjf: find all offsets of top-level tag trees across all units + // + U64Array *unit_info_root_tag_offs = 0; + U64 total_root_tag_count = 0; + U64 *total_root_tag_count_ptr = &total_root_tag_count; + lane_sync_u64(&total_root_tag_count_ptr, 0); + ProfScope("find all offsets of top-level tag trees across all units") + { + if(lane_idx() == 0) + { + unit_info_root_tag_offs = push_array(scratch.arena, U64Array, unit_count); + } + lane_sync_u64(&unit_info_root_tag_offs, 0); + typedef struct D2R2_UnitTagTreeOffChunkNode D2R2_UnitTagTreeOffChunkNode; + struct D2R2_UnitTagTreeOffChunkNode + { + D2R2_UnitTagTreeOffChunkNode *next; + U64 *v; + U64 count; + U64 cap; + }; + typedef struct D2R2_UnitTagTreeOffChunkList D2R2_UnitTagTreeOffChunkList; + struct D2R2_UnitTagTreeOffChunkList + { + D2R2_UnitTagTreeOffChunkNode *first; + D2R2_UnitTagTreeOffChunkNode *last; + U64 chunk_count; + U64 total_count; + }; + U64 unit_take_idx_ = 0; + U64 *unit_take_idx_ptr = &unit_take_idx_; + lane_sync_u64(&unit_take_idx_ptr, 0); + for(;;) + { + //- rjf: take the next unit + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; + if(unit_idx >= unit_count) + { + break; + } + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + //- rjf: unpack this unit + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + + //- rjf: gather all offsets of root-level trees + D2R2_UnitTagTreeOffChunkList tree_offs = {0}; + S64 depth = 0; + for(U64 off = unit_info_tag_range.min; contains_1u64(unit_info_tag_range, off);) + { + U64 start_off = off; + Temp scratch3 = scratch_begin(&scratch2.arena, 1); + + //- rjf: if this next tag is root-level, gather + if(depth == 1) + { + D2R2_UnitTagTreeOffChunkNode *chunk = tree_offs.last; + if(chunk == 0 || chunk->count >= chunk->cap) + { + chunk = push_array(scratch2.arena, D2R2_UnitTagTreeOffChunkNode, 1); + chunk->cap = 512; + chunk->v = push_array(scratch2.arena, U64, chunk->cap); + SLLQueuePush(tree_offs.first, tree_offs.last, chunk); + tree_offs.chunk_count += 1; + } + chunk->v[chunk->count] = off; + chunk->count += 1; + tree_offs.total_count += 1; + } + + //- rjf: reading the next tag + DW2_Tag tag = {0}; + off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); + + //- rjf: do tree navigations + if(tag.has_children) + { + depth += 1; + } + if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + + scratch_end(scratch3); + if(off == start_off) + { break; } - - DW_InlKind inl = DW_Inl_NotInlined; - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Inline)) { inl = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Inline); } - - switch (inl) { - case DW_Inl_DeclaredNotInlined: - case DW_Inl_NotInlined: { - U64 param_count = 0; - RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); - - // get return type - RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - - // fill out proc type - RDIM_Type *proc_type = d2r_create_type(arena, type_table); - proc_type->kind = RDI_TypeKind_Function; - proc_type->byte_size = cu->address_size; - proc_type->direct_type = ret_type; - proc_type->count = param_count; - proc_type->param_types = params; - - // get container type - RDIM_Type *container_type = 0; - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { - container_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); - } else { - DW_TagNode *parent = d2r_tag_iterator_parent_tag_node(it); - container_type = d2r_type_from_offset(type_table, parent->tag.info_off); - if (container_type == 0) { - log_infof("ERROR: failed to infer parent type of subprogram from .debug_info+0x%llx\n", parent->tag.info_off); - } - } - - // get frame base expression - String8 frame_base_expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_FrameBase); - - // get proc container symbol - RDIM_Symbol *proc = rdim_symbol_chunk_list_push(arena, &unit_rdi->procedures, D2R_PROC_CHUNK_CAP); - - // make scope - Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); - RDIM_Scope *root_scope = d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); - root_scope->symbol = proc; - - // fill out proc - proc->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); - proc->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - proc->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); - proc->type = proc_type; - proc->container_scope = 0; - proc->container_type = container_type; - proc->root_scope = root_scope; - proc->location_cases = d2r_locset_from_attrib(arena, &unit_rdi->scopes, root_scope, input, cu, image_base, arch, tag, DW_AttribKind_FrameBase); - - // sub program with user-defined parent tag is a method - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_ClassType || parent_tag.kind == DW_TagKind_StructureType) { - // DW_VirtualityKind -> RDI_MemberKind - RDI_MemberKind member_kind = RDI_MemberKind_NULL; - { - DW_VirtualityKind virtuality = DW_VirtualityKind_None; - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Virtuality)) { - virtuality = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Virtuality); - switch (virtuality) { - case DW_VirtualityKind_None: member_kind = RDI_MemberKind_Method; break; - case DW_VirtualityKind_Virtual: member_kind = RDI_MemberKind_VirtualMethod; break; - case DW_VirtualityKind_PureVirtual: member_kind = RDI_MemberKind_VirtualMethod; break; // TODO: create kind for pure virutal - default: { log_infof("unhandled virtuality kind"); } break; - } - } - } - - RDIM_Type *type = d2r_type_from_offset(type_table, parent_tag.info_off); - RDIM_UDTMember *member = rdim_udt_push_member(arena, &g_d2r_shared.udts, type->udt); - member->kind = member_kind; - member->type = type; - member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - } else if (parent_tag.kind != DW_TagKind_CompileUnit) { - log_infof("unexpected tag @ .debug_info+%llx", tag.info_off); - } - - it->stack->scope = root_scope; - } break; - case DW_Inl_DeclaredInlined: - case DW_Inl_Inlined: { - d2r_tag_iterator_skip_children(it); - } break; - default: InvalidPath; break; - } - } break; - case DW_TagKind_InlinedSubroutine: { - U64 param_count = 0; - RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); - - // get return type - RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - - // fill out proc type - RDIM_Type *proc_type = d2r_create_type(arena, type_table); - proc_type->kind = RDI_TypeKind_Function; - proc_type->byte_size = cu->address_size; - proc_type->direct_type = ret_type; - proc_type->count = param_count; - proc_type->param_types = params; - - // get container type - RDIM_Type *owner = 0; - if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { - owner = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); - } - - // fill out inline site - RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, &unit_rdi->inline_sites, D2R_INLINE_SITE_CHUNK_CAP); - inline_site->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - inline_site->type = proc_type; - inline_site->owner = owner; - inline_site->line_table = 0; - - // make scope - Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); - RDIM_Scope *root_scope = d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); - root_scope->inline_site = inline_site; - } break; - case DW_TagKind_Variable: { - String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - RDIM_Type *type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_SubProgram || - parent_tag.kind == DW_TagKind_InlinedSubroutine || - parent_tag.kind == DW_TagKind_LexicalBlock) { - RDIM_Scope *scope = it->stack->next->scope; - RDIM_Symbol *local = rdim_symbol_chunk_list_push(arena, &scope->locals, 8); - local->name = name; - local->type = type; - local->location_cases = d2r_var_locset_from_tag(arena, &unit_rdi->scopes, scope, input, cu, image_base, arch, tag); - } else { - - // NOTE: due to a bug in clang in stb_sprintf.h local variables - // are declared in global scope without a name - if (name.size == 0) { break; } - - // decls do not have location info - B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { break; } - - U64 voff = max_U64; - B32 is_thread_var = 0; + } + + //- rjf: flatten & store + { + U64Array tree_offs_array = {0}; + tree_offs_array.count = tree_offs.total_count; + tree_offs_array.v = push_array(scratch.arena, U64, tree_offs_array.count); + { + U64 idx = 0; + for EachNode(n, D2R2_UnitTagTreeOffChunkNode, tree_offs.first) { - DW_Attrib *loc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_Location); - DW_AttribClass loc_class = dw_value_class_from_attrib(cu, loc_attrib); - if (loc_class == DW_AttribClass_ExprLoc) { - Temp temp = temp_begin(scratch.arena); - - String8 expr = dw_exprloc_from_attrib(input, cu, loc_attrib); - D2R_ValueType expr_type = 0; - RDIM_EvalBytecode bc = d2r_bytecode_from_expression(temp.arena, input, image_base, arch, cu->addr_lu, expr, cu, &expr_type); - - // evaluate bytecode to virutal offset if possible - if (expr_type == D2R_ValueType_Address) { - B32 is_static = rdim_is_eval_bytecode_static(bc); - if (is_static) { - if (!rdim_static_eval_bytecode_to_voff(bc, image_base, &voff)) { - log_infof("failed to evalute byte code to virtual offset @ .debug_info+%llx", tag.info_off); - } + MemoryCopy(tree_offs_array.v + idx, n->v, sizeof(n->v[0])*n->count); + idx += n->count; + } + } + unit_info_root_tag_offs[unit_idx] = tree_offs_array; + ins_atomic_u64_add_eval(total_root_tag_count_ptr, tree_offs_array.count); + } + + scratch_end(scratch2); + } + lane_sync(); + total_root_tag_count = *total_root_tag_count_ptr; + } + + //////////////////////////// + //- rjf: produce list of (unit * range(root_tag_idx)), so that we can easily + // subdivide work from all units across all lanes + // + typedef struct D2R2_SubUnitWork D2R2_SubUnitWork; + struct D2R2_SubUnitWork + { + U64 unit_idx; + Rng1U64 root_tag_idx_range; + }; + U64 sub_unit_works_count = 0; + D2R2_SubUnitWork *sub_unit_works = 0; + ProfScope("produce sub-unit work division") if(lane_idx() == 0) + { + U64 root_tags_per_work = 1024; + for(B32 build = 0; build <= 1; build += 1) + { + U64 sub_unit_work_idx = 0; + for EachIndex(unit_idx, unit_count) + { + U64 root_tags_in_this_unit = unit_info_root_tag_offs[unit_idx].count; + U64 works_per_this_unit = (root_tags_in_this_unit+root_tags_per_work-1) / root_tags_per_work; + if(build) + { + D2R2_SubUnitWork *works = sub_unit_works + sub_unit_work_idx; + U64 works_count = works_per_this_unit; + U64 root_tag_idx = 0; + for EachIndex(work_idx, works_count) + { + U64 root_tag_idx_opl = root_tag_idx + root_tags_per_work; + root_tag_idx_opl = ClampTop(root_tag_idx_opl, root_tags_in_this_unit); + works[work_idx].unit_idx = unit_idx; + works[work_idx].root_tag_idx_range = r1u64(root_tag_idx, root_tag_idx_opl); + root_tag_idx = root_tag_idx_opl; + } + } + sub_unit_work_idx += works_per_this_unit; + } + if(!build) + { + sub_unit_works_count = sub_unit_work_idx; + sub_unit_works = push_array(scratch.arena, D2R2_SubUnitWork, sub_unit_works_count); + } + } + } + lane_sync_u64(&sub_unit_works_count, 0); + lane_sync_u64(&sub_unit_works, 0); + + //////////////////////////// + //- rjf: build built-in types + // + RDIM_TypeChunkList *builtin_types = 0; + RDIM_Type **builtin_type_from_kind_map = 0; // [RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1] + U64 builtin_type_count = RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1; + ProfScope("build built-in types") if(lane_idx() == 0) + { + builtin_types = push_array(scratch.arena, RDIM_TypeChunkList, 1); + builtin_type_from_kind_map = push_array(scratch.arena, RDIM_Type *, builtin_type_count); + for(RDI_TypeKind k = RDI_TypeKind_FirstBuiltIn; k <= RDI_TypeKind_LastBuiltIn; k += 1) + { + RDIM_Type *type = rdim_type_chunk_list_push(arena, builtin_types, builtin_type_count); + type->kind = k; + type->name.str = rdi_string_from_type_kind(k, &type->name.size); + type->byte_size = rdi_size_from_basic_type_kind(k); + if(type->byte_size == max_U32) { type->byte_size = byte_size_from_arch(arch); } + builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] = type; + } + } + lane_sync_u64(&builtin_types, 0); + lane_sync_u64(&builtin_type_from_kind_map, 0); +#define d2r2_type_from_builtin_kind(k) ((RDI_TypeKind_FirstBuiltIn <= (k) && (k) <= RDI_TypeKind_LastBuiltIn) ? builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] : builtin_type_from_kind_map[RDI_TypeKind_Void]) + + //////////////////////////// + //- rjf: predict the total number of tags in all units + // + U64 total_tag_count_estimate = 1; + { + U64 tag_size_estimate = 32; + for EachIndex(unit_idx, unit_count) + { + total_tag_count_estimate += dim_1u64(unit_info_tag_ranges[unit_idx]) / tag_size_estimate; + } + } + + //////////////////////////// + //- rjf: gather all unique, to-be-deduplicated tags across all units (types, namespaces) + // + D2R2_UniqueTagNode **unique_tag_slots = 0; + U64 unique_tag_slots_count = total_tag_count_estimate/8 + 1; + D2R2_UnitDedupedTagMap *unit_deduped_tag_maps = 0; + ProfScope("gather all unique, to-be-deduplicated tags across all units (types, namespaces)") + { + //- rjf: set up tables + if(lane_idx() == 0) + { + unique_tag_slots = push_array(scratch.arena, D2R2_UniqueTagNode *, unique_tag_slots_count); + unit_deduped_tag_maps = push_array(scratch.arena, D2R2_UnitDedupedTagMap, unit_count); + } + lane_sync_u64(&unique_tag_slots, 0); + lane_sync_u64(&unit_deduped_tag_maps, 0); + + //- rjf: set up per-unit deduplicated tag maps + { + Rng1U64 range = lane_range(unit_count); + for EachInRange(unit_idx, range) + { + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + unit_deduped_tag_maps[unit_idx].slots_count = dim_1u64(unit_info_tag_range) / 256 + 1; + unit_deduped_tag_maps[unit_idx].slots = push_array(scratch.arena, D2R2_UnitDedupedTagNode *, unit_deduped_tag_maps[unit_idx].slots_count); + } + lane_sync(); + } + + //- rjf: gather unique tags + { + U64 work_take_idx_ = 0; + U64 *work_take_idx_ptr = &work_take_idx_; + lane_sync_u64(&work_take_idx_ptr, 0); + for(;;) + { + //- rjf: take next work + U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1; + if(work_idx >= sub_unit_works_count) + { + break; + } + Temp work_scratch = scratch_begin(&scratch.arena, 1); + + //- 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 + DW2_ParseCtx *origin_unit_parse_ctx = &unit_parse_ctxs[origin_unit_idx]; + Rng1U64 origin_unit_info_tag_range = unit_info_tag_ranges[origin_unit_idx]; + + //- rjf: gather all tags which we want to deduplicate + typedef struct D2R2_TagNode D2R2_TagNode; + struct D2R2_TagNode + { + D2R2_TagNode *next; + D2R2_UniqueTagKind kind; + U64 info_off; + U64 container_ancestor_info_off; + U64 hash_seed; + }; + D2R2_TagNode *first_tag_to_dedup = 0; + D2R2_TagNode *last_tag_to_dedup = 0; + 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) + { + U64 root_tag_start_off = unit_info_root_tag_offs[origin_unit_idx].v[root_tag_idx]; + typedef struct D2R2_ParentTagNode D2R2_ParentTagNode; + struct D2R2_ParentTagNode + { + D2R2_ParentTagNode *next; + DW_TagKind tag_kind; + U64 info_off; + U64 hash_seed; + }; + D2R2_ParentTagNode *top_parent = 0; + D2R2_ParentTagNode *free_parent = 0; + S64 depth = 0; + for(U64 off = root_tag_start_off; contains_1u64(origin_unit_info_tag_range, off) && (depth > 0 || off == root_tag_start_off);) + { + Temp tag_scratch = scratch_begin(&work_scratch.arena, 1); + U64 start_off = off; + + // rjf: read tag + DW2_Tag tag = {0}; + off += dw2_read_tag(tag_scratch.arena, raw, origin_unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); + + // rjf: is this one of the tags we'd like to dedup? -> gather + if(tag.kind == DW_TagKind_ArrayType || + tag.kind == DW_TagKind_ClassType || + tag.kind == DW_TagKind_EnumerationType || + tag.kind == DW_TagKind_PointerType || + tag.kind == DW_TagKind_ReferenceType || + tag.kind == DW_TagKind_StringType || + tag.kind == DW_TagKind_StructureType || + tag.kind == DW_TagKind_SubroutineType || + tag.kind == DW_TagKind_SubProgram || + tag.kind == DW_TagKind_Typedef || + tag.kind == DW_TagKind_UnionType || + tag.kind == DW_TagKind_PtrToMemberType || + tag.kind == DW_TagKind_SetType || + tag.kind == DW_TagKind_BaseType || + tag.kind == DW_TagKind_ConstType || + tag.kind == DW_TagKind_FileType || + tag.kind == DW_TagKind_PackedType || + tag.kind == DW_TagKind_VolatileType || + tag.kind == DW_TagKind_RestrictType || + tag.kind == DW_TagKind_InterfaceType || + tag.kind == DW_TagKind_UnspecifiedType || + tag.kind == DW_TagKind_SharedType || + tag.kind == DW_TagKind_RValueReferenceType || + tag.kind == DW_TagKind_CoarrayType || + tag.kind == DW_TagKind_DynamicType || + tag.kind == DW_TagKind_AtomicType || + tag.kind == DW_TagKind_ImmutableType || + tag.kind == DW_TagKind_Namespace) + { + U64 container_ancestor_info_off = 0; + for EachNode(n, D2R2_ParentTagNode, top_parent) + { + if(n->tag_kind == DW_TagKind_StructureType || + n->tag_kind == DW_TagKind_UnionType || + n->tag_kind == DW_TagKind_ClassType || + n->tag_kind == DW_TagKind_SubProgram || + n->tag_kind == DW_TagKind_LexicalBlock || + n->tag_kind == DW_TagKind_Namespace) + { + container_ancestor_info_off = n->info_off; + break; } } + D2R2_TagNode *n = push_array(work_scratch.arena, D2R2_TagNode, 1); + n->kind = (tag.kind == DW_TagKind_Namespace ? D2R2_UniqueTagKind_Namespace : D2R2_UniqueTagKind_Type); + n->info_off = start_off; + n->container_ancestor_info_off = container_ancestor_info_off; + n->hash_seed = top_parent ? top_parent->hash_seed : 0; + SLLQueuePush(first_tag_to_dedup, last_tag_to_dedup, n); + } + + // rjf: compute hash seed for this tag + U64 hash_seed = (top_parent ? top_parent->hash_seed : 0); + if(tag.kind == DW_TagKind_SubProgram || + tag.kind == DW_TagKind_StructureType || + tag.kind == DW_TagKind_UnionType || + tag.kind == DW_TagKind_ClassType || + tag.kind == DW_TagKind_Namespace) + { + DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name); + if(name_attrib != &dw2_attrib_nil) + { + hash_seed = u64_hash_from_str8(name_attrib->val.string); + } + } + + // rjf: update depth / parent stack + if(tag.has_children) + { + depth += 1; + D2R2_ParentTagNode *n = free_parent; + if(n != 0) + { + SLLStackPop(free_parent); + } + else + { + n = push_array(work_scratch.arena, D2R2_ParentTagNode, 1); + } + SLLStackPush(top_parent, n); + n->tag_kind = tag.kind; + n->info_off = start_off; + n->hash_seed = hash_seed; + } + if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + depth = Max(0, depth); + if(top_parent != 0) + { + D2R2_ParentTagNode *popped = top_parent; + SLLStackPop(top_parent); + SLLStackPush(free_parent, popped); + } + } + + scratch_end(tag_scratch); + if(off == start_off) + { + break; + } + } + } + + //- rjf: hash all tags we need to deduplicate & gather + for(D2R2_TagNode *tag_n = first_tag_to_dedup; tag_n != 0; tag_n = tag_n->next) + { + Temp dedup_root_scratch = scratch_begin(&scratch.arena, 1); + U64 start_off = tag_n->info_off; + D2R2_UniqueTagKind unique_tag_kind = tag_n->kind; + U64 container_ancestor_info_off = tag_n->container_ancestor_info_off; + U64 hash_seed = tag_n->hash_seed; + + //- rjf: hash all tags & dependency tag trees + U64 hash = hash_seed; + { + typedef struct TagTask TagTask; + struct TagTask + { + TagTask *next; + U64 unit_idx; + U64 off; + U64 order_idx; + }; + TagTask start_task = {0, origin_unit_idx, start_off}; + TagTask *top_task = &start_task; + TagTask *free_task = 0; + U64 seen_task_slots_count = 16; + TagTask **seen_task_slots = push_array(dedup_root_scratch.arena, TagTask *, seen_task_slots_count); + for(TagTask *t = top_task, *next = 0; t != 0; t = next) + { + next = 0; + U64 t_off = t->off; - // is this a thread variable? - is_thread_var = rdim_is_bytecode_tls_dependent(bc); + // rjf: record this task in our seen task table + { + U64 hash = u64_hash_from_str8(str8_struct(&t->off)); + U64 slot_idx = hash%seen_task_slots_count; + SLLStackPush(seen_task_slots[slot_idx], t); + } - temp_end(temp); + // rjf: unpack unit + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[t->unit_idx]; + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[t->unit_idx]; + + // rjf: read/hash the full tag tree at `t_off`; kick off additional + // tasks for referenced dependency types + S64 depth = 0; + B32 hash_should_include_children = 1; + for(;unit_info_tag_range.min <= t_off && t_off < unit_info_tag_range.max && (depth > 0 || t_off == t->off);) + { + Temp tag_scratch = scratch_begin(&dedup_root_scratch.arena, 1); + U64 t_start_off = t_off; + + // rjf: read tag + DW2_Tag tag = {0}; + t_off += dw2_read_tag(tag_scratch.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, t_off, &tag); + + // rjf: determine if tag should be skipped from hashing + B32 should_skip_tag = (tag.kind == DW_TagKind_LexicalBlock || + tag.kind == DW_TagKind_Variable); + + // rjf: determine if we want to consider tag children as part of the content hash + if(t_start_off == t->off && tag.kind == DW_TagKind_Namespace) + { + hash_should_include_children = 0; + } + + // rjf: if we want to combine this tag into the hash -> combine + if(!should_skip_tag) + { + // rjf: combine tag's kind + hash = u64_hash_from_seed_str8(hash, str8_struct(&tag.kind)); + + // rjf: combine non-reference attributes (references could be different, + // because of deduping, but they could match ultimately). for any + // referenced dependency types, kick them off + for(DW2_AttribNode *n = tag.attribs.first; n != 0; n = n->next) + { + // rjf: non-reference? -> combine attribute value info + if(n->v.val.kind != DW_FormKind_RefAddr && + n->v.val.kind != DW_FormKind_Ref1 && + n->v.val.kind != DW_FormKind_Ref2 && + n->v.val.kind != DW_FormKind_Ref4 && + n->v.val.kind != DW_FormKind_Ref8 && + n->v.val.kind != DW_FormKind_RefUData && + n->v.val.kind != DW_FormKind_RefSup4 && + n->v.val.kind != DW_FormKind_RefSig8 && + ((tag.kind != DW_TagKind_SubProgram && + tag.kind != DW_TagKind_FormalParameter) || + (n->v.attrib_kind != DW_AttribKind_Name && + n->v.attrib_kind != DW_AttribKind_DeclFile && + n->v.attrib_kind != DW_AttribKind_DeclLine && + n->v.attrib_kind != DW_AttribKind_Prototyped && + n->v.attrib_kind != DW_AttribKind_External && + n->v.attrib_kind != DW_AttribKind_FrameBase && + n->v.attrib_kind != DW_AttribKind_Location && + n->v.attrib_kind != DW_AttribKind_LowPc && + n->v.attrib_kind != DW_AttribKind_HighPc))) + { + hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.kind)); + if(n->v.val.string.size != 0) + { + hash = u64_hash_from_seed_str8(hash, n->v.val.string); + } + else if(n->v.val.addr != 0) + { + hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.addr)); + } + else + { + hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.u128)); + } + } + + // rjf: type reference? -> if seen, combine the order; if not, recurse + if(n->v.attrib_kind == DW_AttribKind_Type) + { + // rjf: unpack reference + U64 ref_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &n->v.val); + + // rjf: determine if we've seen this reference + B32 already_seen = 0; + U64 already_seen_order_idx = 0; + { + U64 off_hash = u64_hash_from_str8(str8_struct(&ref_info_off)); + U64 off_slot_idx = off_hash%seen_task_slots_count; + for(TagTask *t = seen_task_slots[off_slot_idx]; t != 0; t = t->next) + { + if(t->off == ref_info_off) + { + already_seen = 1; + already_seen_order_idx = t->order_idx; + break; + } + } + } + + // rjf: if we've seen -> hash the order + if(already_seen) + { + hash = u64_hash_from_seed_str8(hash, str8_struct(&already_seen_order_idx)); + } + + // rjf: if we've not seen -> descend + if(!already_seen) + { + TagTask *dependency_task = free_task; + if(dependency_task != 0) + { + SLLStackPop(free_task); + } + else + { + dependency_task = push_array(dedup_root_scratch.arena, TagTask, 1); + } + dependency_task->next = next; + next = dependency_task; + dependency_task->off = ref_info_off; + dependency_task->unit_idx = t->unit_idx; + if(!contains_1u64(unit_info_tag_range, dependency_task->off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, dependency_task->off); + if(0 < new_unit_num && new_unit_num <= unit_count) + { + dependency_task->unit_idx = new_unit_num-1; + } + } + dependency_task->order_idx = t->order_idx+1; + } + } + } + } + + // rjf: tree nav if we need to + if(hash_should_include_children) + { + if(tag.has_children) + { + depth += 1; + } + if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + depth = Max(0, depth); + } + } + scratch_end(tag_scratch); + } } } - RDIM_SymbolChunkList *var_chunks; U64 var_chunks_cap; - if (is_thread_var) { var_chunks = &unit_rdi->thread_variables; var_chunks_cap = D2R_TVAR_CHUNK_CAP; } - else { var_chunks = &unit_rdi->global_variables; var_chunks_cap = D2R_GVAR_CHUNK_CAP; } + //- rjf: atomically gather this hash if not already gathered + { + B32 gathered = 0; + U64 slot_idx = hash%unique_tag_slots_count; + for(;!gathered;) + { + // rjf: read existing slot head pointer *before* we lookup / insert + U64 slot_head_val = (U64)ins_atomic_u64_eval(&unique_tag_slots[slot_idx]); + + // rjf: determine if this hash has been gathered + D2R2_UniqueTagNode *already_gathered_node = 0; + for(D2R2_UniqueTagNode *n = (D2R2_UniqueTagNode *)slot_head_val; n != 0; n = n->next) + { + if(n->hash == hash) + { + gathered = 1; + already_gathered_node = n; + break; + } + } + + // rjf: if this hash *has* been gathered, we want to prefer a + // deterministic unit/info offset - so we'll just prefer the + // lowest unit/offset. + // + // TODO(rjf): this seems to only be necessary because of a flaw + // in the deduplicator logic (without this part). in theory, if we + // trust the hash (all experiments suggested that we should - e.g. + // no hash collisions w/ different data were observed), then the + // same hash should imply an identical type graph. this may be false + // for reasons I do not currently understand, or due to a mistake + // somewhere. but for now, I've added this, to ensure that when there + // are many type tag trees which map to an identical hash, we always + // parse the one from the lowest .debug_info offset, just to make + // sure it's deterministic. in a multi-threaded conversion, this + // is not true in general without this step; one thread which finds + // the same type with a higher .debug_info offset may win, and so + // where exactly one type hash is parsed is not deterministic. but, + // the theory is, that shouldn't matter - the type graphs should + // match nonetheless. in any case, if that turns out to be a broken + // theory, this should be a relatively cheap step to guarantee + // deterministic (hash -> .debug_info offset), which is likely a + // desirable property regardless. but I wanted to leave this here + // so that I can more closely verify what is going on later. + // + if(gathered && already_gathered_node != 0) + { + B32 minimized = 0; + for(;!minimized;) + { + U64 stored_info_off = ins_atomic_u64_eval(&already_gathered_node->info_off); + if(start_off < stored_info_off) + { + if(ins_atomic_u64_eval_cond_assign(&already_gathered_node->info_off, start_off, stored_info_off) == stored_info_off) + { + minimized = 1; + } + } + else + { + minimized = 1; + } + } + } + + // rjf: if this hash has *not* been gathered, try an insert. we: + // + // 1. allocate/fill a node + // 2. set it up to point to the old head + // 3. compare/exchange the old head with the new head - IFF the head matches what we expect from above + // 4. if we fail, another thread has touched this slot, we pop the allocated node & try again + // (we may find that another thread has filled this hash, so we'll just be done) + // + if(!gathered) + { + Temp insert_temp = temp_begin(scratch.arena); + D2R2_UniqueTagNode *n = push_array(scratch.arena, D2R2_UniqueTagNode, 1); + n->next = (D2R2_UniqueTagNode *)slot_head_val; + n->kind = unique_tag_kind; + n->hash = hash; + n->info_off = start_off; + n->container_ancestor_info_off = container_ancestor_info_off; + U64 new_head_val = (U64)n; + if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unique_tag_slots[slot_idx], new_head_val, slot_head_val)) + { + gathered = 1; + } + else + { + temp_end(insert_temp); + } + } + } + } - RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, var_chunks, var_chunks_cap); - var->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); - var->name = name; - var->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); - var->type = type; - var->container_scope = 0; - RDIM_Location loc = {.kind = is_thread_var ? RDI_LocationKind_TLSOff : RDI_LocationKind_ModuleOff, .offset = voff}; - RDIM_Rng1U64 range = {0, 0xffffffffffffffffull}; - rdim_location_case_list_push(arena, &var->location_cases, loc, range); + //- rjf: atomically record this (info_off -> hash) mapping, so that when we have + // later references to this type, we can redirect to the deduplicated type with + // the right hash later. + { + U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_maps[origin_unit_idx].slots_count; + D2R2_UnitDedupedTagNode *n = push_array(scratch.arena, D2R2_UnitDedupedTagNode, 1); + n->src_info_off = start_off; + n->dst_hash = hash; + for(B32 inserted = 0; !inserted;) + { + U64 slot_head_val = ins_atomic_u64_eval(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx]); + n->next = (D2R2_UnitDedupedTagNode *)slot_head_val; + if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx], (U64)n, slot_head_val)) + { + inserted = 1; + } + } + } + scratch_end(dedup_root_scratch); } - } break; - case DW_TagKind_FormalParameter: { - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_SubProgram || parent_tag.kind == DW_TagKind_InlinedSubroutine) { - RDIM_Scope *scope = it->stack->next->scope; - RDIM_Symbol *param = rdim_symbol_chunk_list_push(arena, &scope->locals, 4); - param->is_param = 1; - param->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); - param->type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); - param->location_cases = d2r_var_locset_from_tag(arena, &unit_rdi->scopes, scope, input, cu, image_base, arch, tag); - } else { - Assert(!"this is a local variable"); - log_infof(".debug_info+%llx out of scope formal parameter", tag.info_off); - } - } break; - case DW_TagKind_LexicalBlock: { - DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); - if (parent_tag.kind == DW_TagKind_SubProgram || - parent_tag.kind == DW_TagKind_InlinedSubroutine || - parent_tag.kind == DW_TagKind_LexicalBlock) { - Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); - d2r_push_scope(arena, &unit_rdi->scopes, D2R_SCOPE_CHUNK_CAP, it->stack, ranges); - } - } break; - case DW_TagKind_CallSite: { - // TODO - } break; - case DW_TagKind_CallSiteParameter: { - // TODO - } break; - case DW_TagKind_Label: - case DW_TagKind_CompileUnit: - case DW_TagKind_UnspecifiedParameters: - case DW_TagKind_Namespace: - case DW_TagKind_ImportedDeclaration: - case DW_TagKind_PtrToMemberType: - case DW_TagKind_TemplateTypeParameter: - case DW_TagKind_ReferenceType: { - // TODO: - } break; - default: + + scratch_end(work_scratch); + } + lane_sync(); + } + } + + //////////////////////////// + //- rjf: sort all unique tag table slots + // + ProfScope("sort all unique tag table slots") + { + Rng1U64 range = lane_range(unique_tag_slots_count); + for EachInRange(slot_idx, range) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + D2R2_UniqueTagNode **slot = &unique_tag_slots[slot_idx]; + + // rjf: count nodes in this slot + U64 node_count = 0; + for EachNode(n, D2R2_UniqueTagNode, slot[0]) { - // NotImplemented; - } break; + node_count += 1; + } + + // rjf: flatten + D2R2_UniqueTagNode **slot_nodes_array = push_array(scratch2.arena, D2R2_UniqueTagNode *, node_count); + { + U64 idx = 0; + for EachNode(n, D2R2_UniqueTagNode, slot[0]) + { + slot_nodes_array[idx] = n; + idx += 1; + } + } + + // rjf: sort + radsort(slot_nodes_array, node_count, (int (*)(void *, void *))d2r2_unique_tag_node_is_less_than); + + // rjf: re-order in the slot + { + unique_tag_slots[slot_idx] = 0; + for EachIndex(idx, node_count) + { + SLLStackPush(unique_tag_slots[slot_idx], slot_nodes_array[idx]); + } + } + + scratch_end(scratch2); + } + lane_sync(); + } + + //////////////////////////// + //- rjf: produce [0...n) <-> unique-tag-node mapping for all categories of deduplicated tags + // + U64 *deduped_tag_counts = 0; // [D2R2_UniqueTagKind_COUNT] + D2R2_UniqueTagNode ***deduped_tag_nodes = 0; // [D2R2_UniqueTagKind_COUNT] + ProfScope("produce [0...n) -> hash mapping for all categories of deduplicated tags") if(lane_idx() == 0) + { + deduped_tag_counts = push_array(scratch.arena, U64, D2R2_UniqueTagKind_COUNT); + deduped_tag_nodes = push_array(scratch.arena, D2R2_UniqueTagNode **, D2R2_UniqueTagKind_COUNT); + for EachIndex(slot_idx, unique_tag_slots_count) + { + for EachNode(n, D2R2_UniqueTagNode, unique_tag_slots[slot_idx]) + { + deduped_tag_counts[n->kind] += 1; + } + } + for EachEnumVal(D2R2_UniqueTagKind, k) + { + deduped_tag_nodes[k] = push_array(scratch.arena, D2R2_UniqueTagNode *, deduped_tag_counts[k]); + } + U64 idxs[D2R2_UniqueTagKind_COUNT] = {0}; + for EachIndex(slot_idx, unique_tag_slots_count) + { + for EachNode(n, D2R2_UniqueTagNode, unique_tag_slots[slot_idx]) + { + deduped_tag_nodes[n->kind][idxs[n->kind]] = n; + n->order_idx = idxs[n->kind]; + idxs[n->kind] += 1; + } } } - scratch_end(scratch); -} - -force_inline int -d2r_src_file_lookup_compar(void *raw_a, void *raw_b) -{ - D2R_SrcFileLookup *a = raw_a, *b = raw_b; - DW_LineFile *file_a = a->file, *file_b = b->file; - String8 dir_path_a = a->vm->header.dir_table.v[file_a->dir_idx]; - String8 dir_path_b = b->vm->header.dir_table.v[file_b->dir_idx]; - int cmp = str8_compar(dir_path_a, dir_path_b, 0); - if (cmp == 0) { - cmp = str8_compar(file_a->path, file_b->path, 0); + lane_sync_u64(&deduped_tag_counts, 0); + lane_sync_u64(&deduped_tag_nodes, 0); + U64 type_count = deduped_tag_counts[D2R2_UniqueTagKind_Type]; + D2R2_UniqueTagNode **type_tag_nodes = deduped_tag_nodes[D2R2_UniqueTagKind_Type]; + U64 namespace_count = deduped_tag_counts[D2R2_UniqueTagKind_Namespace]; + D2R2_UniqueTagNode **namespace_tag_nodes = deduped_tag_nodes[D2R2_UniqueTagKind_Namespace]; + + //////////////////////////// + //- rjf: gather per-type dependency chains + // + typedef struct TypeDepChain TypeDepChain; + struct TypeDepChain + { + TypeDepChain *next; + U64 type_idx; + }; + U64 *type_dep_chains_counts = 0; + ProfScope("gather per-type dependency chains") + { + if(lane_idx() == 0) + { + type_dep_chains_counts = push_array(scratch.arena, U64, type_count); + } + lane_sync_u64(&type_dep_chains_counts, 0); + Rng1U64 range = lane_range(type_count); + for EachInRange(root_type_idx, range) + { + typedef struct TypeChainTask TypeChainTask; + struct TypeChainTask + { + TypeChainTask *next; + U64 hash; + }; + TypeChainTask start_task = {0, type_tag_nodes[root_type_idx]->hash}; + TypeChainTask *top_task = &start_task; + TypeChainTask *free_task = 0; + TypeChainTask *last_t = 0; + for(TypeChainTask *t = top_task; t != 0; (last_t = t, t = t->next)) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + // rjf: recycle old tasks + if(last_t != 0) + { + SLLStackPush(free_task, last_t); + } + + // rjf: unpack task + U64 hash = t->hash; + + // rjf: hash -> unique type tag node + D2R2_UniqueTagNode *type_tag_node = 0; + { + U64 slot_idx = hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next) + { + if(n->hash == hash) + { + type_tag_node = n; + break; + } + } + } + + // rjf: unpack type tag node + U64 info_off = 0; + U64 unit_idx = 0; + if(type_tag_node != 0) + { + info_off = type_tag_node->info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + unit_idx = unit_num > 0 ? unit_num-1 : 0; + } + + // rjf: record this type in the dependency chain count + if(type_tag_node != 0) + { + type_dep_chains_counts[root_type_idx] += 1; + } + + // rjf: unpack unit + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + + // rjf: parse this type's tag + U64 read_off = info_off; + DW2_Tag tag = {0}; + read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &tag); + + // rjf: find direct types from this type tag + typedef struct DirectTypeNode DirectTypeNode; + struct DirectTypeNode + { + DirectTypeNode *next; + U64 info_off; + U64 unit_idx; + }; + DirectTypeNode *first_direct_type = 0; + DirectTypeNode *last_direct_type = 0; + switch(tag.kind) + { + default:{}break; + case DW_TagKind_PointerType: + case DW_TagKind_ReferenceType: + case DW_TagKind_RValueReferenceType: + case DW_TagKind_RestrictType: + case DW_TagKind_VolatileType: + case DW_TagKind_ConstType: + case DW_TagKind_ArrayType: + case DW_TagKind_Typedef: + case DW_TagKind_SubProgram: + case DW_TagKind_SubroutineType: + case DW_TagKind_EnumerationType: + { + // rjf: gather direct type + { + DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Type); + U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); + U64 direct_type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); + if(0 < new_unit_num && new_unit_num <= unit_count) + { + direct_type_unit_idx = new_unit_num-1; + } + } + DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1); + n->info_off = direct_type_info_off; + n->unit_idx = direct_type_unit_idx; + SLLQueuePush(first_direct_type, last_direct_type, n); + } + + // rjf: functions -> gather parameters + if(tag.has_children && (tag.kind == DW_TagKind_SubProgram || tag.kind == DW_TagKind_SubroutineType)) + { + S64 depth = 1; + for(;depth > 0 && contains_1u64(unit_info_tag_range, read_off);) + { + U64 start_read_off = read_off; + + // rjf: read child tag + DW2_Tag child_tag = {0}; + read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &child_tag); + + // rjf: formal parameters -> gather direct types + if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) + { + DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type); + U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); + U64 direct_type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); + if(0 < new_unit_num && new_unit_num <= unit_count) + { + direct_type_unit_idx = new_unit_num-1; + } + } + DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1); + n->info_off = direct_type_info_off; + n->unit_idx = direct_type_unit_idx; + SLLQueuePush(first_direct_type, last_direct_type, n); + } + + // rjf: tree navigations + if(child_tag.has_children) + { + depth += 1; + } + if(child_tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + + if(read_off == start_read_off) + { + break; + } + } + } + }break; + } + + // rjf: for each dependency type, look up their hash, + spawn new tasks for them + for EachNode(n, DirectTypeNode, first_direct_type) + { + U64 direct_type_info_off = n->info_off; + U64 direct_type_unit_idx = n->unit_idx; + + // rjf: direct type info offset -> hash + U64 direct_type_hash = 0; + { + U64 off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off)); + U64 off_slot_idx = off_hash%unit_deduped_tag_maps[direct_type_unit_idx].slots_count; + for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_maps[direct_type_unit_idx].slots[off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == direct_type_info_off) + { + direct_type_hash = n->dst_hash; + break; + } + } + } + + // rjf: spawn task + if(direct_type_hash != 0) + { + TypeChainTask *new_task = free_task; + if(new_task != 0) + { + SLLStackPop(free_task); + } + else + { + new_task = push_array(scratch.arena, TypeChainTask, 1); + } + new_task->next = t->next; + new_task->hash = direct_type_hash; + t->next = new_task; + } + } + + scratch_end(scratch2); + } + } + lane_sync(); } - return cmp; -} - -force_inline -LFHT_IS_KEY_EQUAL_FUNC(d2r_src_file_lookup_is_equal) -{ - return d2r_src_file_lookup_compar(a, b) == 0; -} - -force_inline int -d2r_src_file_lfht_key_value_is_before(void *raw_a, void *raw_b) -{ - return d2r_src_file_lookup_compar(*(D2R_SrcFileLookup **)raw_a, *(D2R_SrcFileLookup **)raw_b) < 0; -} - -internal U64 -d2r_hash_line_file(String8 dir_path, DW_LineFile *file) -{ - XXH3_state_t hasher = {0}; - XXH3_64bits_reset(&hasher); - XXH3_64bits_update(&hasher, dir_path.str, dir_path.size); - XXH3_64bits_update(&hasher, file->path.str, file->path.size); - XXH3_64bits_update(&hasher, &file->time_stamp, sizeof(file->time_stamp)); - XXH3_64bits_update(&hasher, &file->md5, sizeof(file->md5)); - XXH64_hash_t hash = XXH3_64bits_digest(&hasher); - return hash; -} - -internal void -d2r_sort_ptrs(void **ptrs, U64 count, int (* is_before)(void *a, void *b)) -{ - radsort(ptrs, count, is_before); -} - -internal D2R_CompUnitContribMap -d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Raw *input, U64 image_base) -{ - Temp scratch = scratch_begin(&arena, 1); - Temp temp = temp_begin(arena); - B32 parse_failed = 1; - Rng1U64List unit_range_list = dw_unit_ranges_from_data(scratch.arena, input->sec[DW_Section_ARanges].data); - - D2R_CompUnitContribMap cm = {0}; - cm.info_off_arr = push_array(temp.arena, U64, unit_range_list.count); - cm.voff_range_arr = push_array(temp.arena, RDIM_Rng1U64ChunkList, unit_range_list.count); - - for EachNode(range_n, Rng1U64Node, unit_range_list.first) { - String8 unit_data = str8_substr(input->sec[DW_Section_ARanges].data, range_n->v); - U64 unit_cursor = 0; - - U32 first_four_bytes = 0; - if (str8_deserial_read_struct(input->sec[DW_Section_Info].data, 0, &first_four_bytes) != sizeof(first_four_bytes)) { goto exit; } - DW_Format unit_format = first_four_bytes == max_U32 ? DW_Format_64Bit : DW_Format_32Bit; - - U64 unit_length; - TryRead(str8_deserial_read_dwarf_packed_size(unit_data, unit_cursor, &unit_length), unit_cursor, exit); - - DW_Version version; - TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &version), unit_cursor, exit); - - if (version != DW_Version_2) { - log_infof("unknown .debug_aranges version %u @ 0x%llx", version, range_n->v.min); - continue; + //////////////////////////// + //- rjf: build all types - build types w/ 1 dependency (leaves) first, then 2, 3, etc., + // to ensure dependencies always travel backwards + // + // NOTE: * DWARF vs RDI Array Type Graph * + // + // For example lets take following decl: + // + // int (*foo[2])[3]; + // + // This compiles to in DWARF: + // + // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] + // \ + // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] + // \ + // -> (B1) DW_TAG_BaseType (int) + // + // RDI expects: + // + // foo -> Array[2] -> Pointer -> Array[3] -> int + // + // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and + // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. + // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from + // B to A. + // + RDIM_TypeChunkList *all_types = 0; + RDIM_Type **type_from_idx_map = 0; + ProfScope("build all types") + { + if(lane_idx() == 0) + { + all_types = push_array(scratch.arena, RDIM_TypeChunkList, 1); + type_from_idx_map = push_array(scratch.arena, RDIM_Type *, type_count); + rdim_type_chunk_list_concat_in_place(all_types, builtin_types); } - - U64 cu_info_off; - TryRead(str8_deserial_read_dwarf_uint(unit_data, unit_cursor, unit_format, &cu_info_off), unit_cursor, exit); - - U8 address_size = 0; - TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &address_size), unit_cursor, exit); - - U8 segment_selector_size = 0; - TryRead(str8_deserial_read_struct(unit_data, unit_cursor, &segment_selector_size), unit_cursor, exit); - - U64 tuple_size = address_size * 2 + segment_selector_size; - U64 bytes_too_far_past_boundary = unit_cursor % tuple_size; - if (bytes_too_far_past_boundary > 0) { - unit_cursor += tuple_size - bytes_too_far_past_boundary; - } - - RDIM_Rng1U64ChunkList voff_ranges = {0}; - if (segment_selector_size == 0) { - while (unit_cursor + address_size * 2 <= unit_data.size) { - U64 address = 0; - U64 length = 0; - TryRead(str8_deserial_read(unit_data, unit_cursor, &address, address_size, address_size), unit_cursor, exit); - TryRead(str8_deserial_read(unit_data, unit_cursor, &length, address_size, address_size), unit_cursor, exit); - - if (address == 0 && length == 0) { break; } - if (address == 0) { continue; } - - if (address < image_base) { - log_infof("invalid address 0x%llx in .debug_aranges+%llx", range_n->v.min + unit_cursor); + lane_sync_u64(&all_types, 0); + lane_sync_u64(&type_from_idx_map, 0); + Rng1U64 range = lane_range(type_count); + U64 max_chain_count = 1; + for(;max_chain_count < max_U64;) + { + 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; + // find this lane's next dependency restriction + U64 next_max_chain_count = max_U64; + RDIM_TypeChunkList lane_types = {0}; + U64 lane_types_chunk_count = 256; + for EachInRange(type_idx, range) + { + // rjf: if this type has a higher chain count than the current, + // but it is lower than our next maximum chain count, collect it, + // so we will hit + // + // if this type has a lower chain count than the current, + // we should've already built it from a previous pass. + // + if(type_dep_chains_counts[type_idx] > max_chain_count) + { + next_max_chain_count = Min(type_dep_chains_counts[type_idx], next_max_chain_count); continue; } + else if(type_dep_chains_counts[type_idx] < max_chain_count) + { + continue; + } + Temp temp = temp_begin(scratch2.arena); - U64 min = address - image_base; - U64 max = min + length; - rdim_rng1u64_chunk_list_push(temp.arena, &voff_ranges, 256, (RDIM_Rng1U64){.min = min, .max = max}); - } - } else { - NotImplemented; - } - - U64 map_idx = cm.count++; - cm.info_off_arr[map_idx] = cu_info_off; - cm.voff_range_arr[map_idx] = voff_ranges; - } - - parse_failed = 0; - exit:; - if (parse_failed) { - MemoryZeroStruct(&cm); - temp_end(temp); - } - scratch_end(scratch); - return cm; -} - -internal RDIM_Rng1U64ChunkList -d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off) -{ - RDIM_Rng1U64ChunkList voff_ranges = {0}; - U64 voff_list_idx = u64_array_bsearch(map.info_off_arr, map.count, info_off); - if (voff_list_idx < map.count) { - voff_ranges = map.voff_range_arr[voff_list_idx]; - } - return voff_ranges; -} - -internal RDIM_BakeParams -d2r_convert(Arena *arena, D2R_ConvertParams *params) -{ - Temp scratch = scratch_begin(&arena, 1); - - if (lane_idx() == 0) { - //////////////////////////////// - - Arch arch = Arch_Null; - U64 image_base = 0; - DW_Raw input = {0}; - PathStyle path_style = PathStyle_Null; - - switch (params->exe_kind) { - case ExecutableImageKind_Null: {} break; - case ExecutableImageKind_CoffPe: { - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, params->exe_data); - String8 raw_sections = str8_substr(params->exe_data, pe.section_table_range); - COFF_SectionHeader *section_table = str8_deserial_get_raw_ptr(raw_sections, 0, sizeof(COFF_SectionHeader) * pe.section_count); - String8 string_table = str8_substr(params->exe_data, pe.string_table_range); + // rjf: idx -> type tag node + D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; + U64 info_off = type_tag_node->info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - arch = pe.arch; - image_base = pe.image_base; - input = dw_input_from_coff_section_table(scratch.arena, params->exe_data, string_table, pe.section_count, section_table); - path_style = PathStyle_WindowsAbsolute; + // rjf: unpack unit + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - g_d2r_shared.binary_sections = c2r_rdi_binary_sections_from_coff_sections(arena, params->exe_data, string_table, pe.section_count, section_table); - } break; - case ExecutableImageKind_Elf32: - case ExecutableImageKind_Elf64: { - ELF_Bin bin = elf_bin_from_data(scratch.arena, params->dbg_data); + // rjf: parse root-level tag + DW2_Tag tag = {0}; + U64 tag_info_size = dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); - arch = arch_from_elf_machine(bin.hdr.e_machine); - image_base = elf_base_addr_from_bin(&bin); - input = dw_raw_from_elf_bin(scratch.arena, params->dbg_data, &bin); - path_style = PathStyle_UnixAbsolute; + // rjf: extract common attributes + DW2_Attrib *name_attrib = &dw2_attrib_nil; + DW2_Attrib *direct_type_attrib = &dw2_attrib_nil; + DW2_Attrib *bitsize_attrib = &dw2_attrib_nil; + DW2_Attrib *bytesize_attrib = &dw2_attrib_nil; + DW2_Attrib *encoding_attrib = &dw2_attrib_nil; + DW2_Attrib *decl_attrib = &dw2_attrib_nil; + DW2_Attrib *prototyped_attrib = &dw2_attrib_nil; + // + // TODO(rjf): if a DW_AttribKing_GNU_Vector is found on an DW_TagKind_ArrayType, + // then: @native_vector_support extract byte size from the base type tag + // and convert to U256, U512, S256 and S512 + // + for EachNode(n, DW2_AttribNode, tag.attribs.first) + { + switch(n->v.attrib_kind) + { + default:{}break; +#define Case(dst_name, src_name) case DW_AttribKind_##src_name:{dst_name##_attrib = &n->v;}break + Case(name, Name); + Case(direct_type, Type); + Case(bitsize, BitSize); + Case(bytesize, ByteSize); + Case(encoding, Encoding); + Case(decl, Declaration); +#undef Case + } + } - g_d2r_shared.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, params->dbg_data, &bin, &bin.shdrs); - } break; - default: { InvalidPath; } break; - } - - //////////////////////////////// - - ProfBegin("compute exe hash"); - U64 exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); - ProfEnd(); - - g_d2r_shared.top_level_info = rdim_make_top_level_info(params->exe_name, arch, exe_hash, g_d2r_shared.binary_sections); - - //////////////////////////////// - - ProfBegin("Parse Unit Contrib Map"); - D2R_CompUnitContribMap cu_contrib_map = {0}; - if (input.sec[DW_Section_ARanges].data.size) { - cu_contrib_map = d2r_cu_contrib_map_from_aranges(arena, &input, image_base); - } - ProfEnd(); - - ProfBegin("Parse Comop Unit Ranges"); - DW_ListUnitInput lu_input = dw_list_unit_input_from_input(scratch.arena, &input); - Rng1U64List cu_range_list = dw_unit_ranges_from_data(scratch.arena, input.sec[DW_Section_Info].data); - Rng1U64Array cu_ranges = rng1u64_array_from_list(scratch.arena, &cu_range_list); - ProfEnd(); - - //////////////////////////////// - - ProfBegin("Parse Compile Unit Headers"); - DW_CompUnit *cu_arr = push_array(scratch.arena, DW_CompUnit, cu_ranges.count); - for EachIndex(cu_idx, cu_ranges.count) { - cu_arr[cu_idx] = dw_cu_from_info_off(scratch.arena, &input, lu_input, cu_ranges.v[cu_idx].min, params->is_parse_relaxed); - } - ProfEnd(); - - //////////////////////////////// - - RDIM_LineTable **cu_line_tables_rdi = push_array(scratch.arena, RDIM_LineTable *, cu_ranges.count); - ProfScope("Convert Line Tables [Count: %llu]", cu_ranges.count) - { - Temp temp = temp_begin(scratch.arena); - - // TODO: per thread task - ProfBegin("Up front parse line VM headers"); - DW_LineVM **line_vms = push_array(temp.arena, DW_LineVM *, cu_ranges.count); - for EachIndex(cu_idx, cu_ranges.count) { - line_vms[cu_idx] = dw_line_vm_init(&input, &cu_arr[cu_idx]); - } - ProfEnd(); - - // TODO: sync - // - // sum all source files (including duplicates) - U64 total_file_count = 0; - for EachIndex(cu_idx, cu_ranges.count) { total_file_count += line_vms[cu_idx]->header.file_table.count; } - - // TODO: sync - ProfBegin("Dedup source files [Count: %llu]", total_file_count); - LFHT_NodeChunkList *src_file_lfht_nodes = push_array(temp.arena, LFHT_NodeChunkList, lane_count()); - LFHT_Node *src_file_lfht = 0; - - // TODO: per thread - { - D2R_SrcFileLookup *lookup = 0; - for EachIndex(cu_idx, cu_ranges.count) { - DW_LineVM *vm = line_vms[cu_idx]; - for EachIndex(file_idx, vm->header.file_table.count) { - DW_LineFile *file = &vm->header.file_table.v[file_idx]; - - if (lookup == 0) { lookup = push_array(temp.arena, D2R_SrcFileLookup, 1); } - lookup->vm = vm; - lookup->file = file; - - LFHT_NodeChunkList *node_chunks = &src_file_lfht_nodes[lane_idx()]; - U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); - B32 was_inserted = lfht_insert(temp.arena, node_chunks, &src_file_lfht, hash, lookup, d2r_src_file_lookup_is_equal, 0); - if (was_inserted) { - lookup = 0; + // rjf: unpack common attributes + String8 name = name_attrib->val.string; + DW_ATE encoding = encoding_attrib->val.u128.u64[0]; + RDIM_Type *direct_type = d2r2_type_from_builtin_kind(RDI_TypeKind_Void); + U64 bitsize = 0; + B32 is_decl = (decl_attrib != &dw2_attrib_nil); + { + // rjf: unpack direct type + if(direct_type_attrib != &dw2_attrib_nil) + { + U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); + U64 direct_type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); + direct_type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); + } + D2R2_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[direct_type_unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off)); + U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count; + U64 direct_type_hash = 0; + for(D2R2_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == direct_type_info_off) + { + direct_type_hash = n->dst_hash; + break; + } + } + U64 unique_type_tag_slot_idx = direct_type_hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == direct_type_hash) + { + direct_type = type_from_idx_map[n->order_idx]; + break; + } } } - } - } - ProfEnd(); - - // TODO: sync - ProfBegin("Extract source file lookups"); - U64 unique_file_count = lfht_total_count_from_node_chunk_lists(lane_count(), src_file_lfht_nodes); - void **lookups = lfht_data_from_node_chunk_lists(temp.arena, unique_file_count, lane_count(), src_file_lfht_nodes); - ProfEnd(); - - // TODO: sync - ProfBegin("Sort unique source files [Count: %llu]", unique_file_count); - d2r_sort_ptrs(lookups, unique_file_count, d2r_src_file_lfht_key_value_is_before); - ProfEnd(); - - // TODO: per thread task - ProfBegin("Convert source files"); - for EachIndex(i, unique_file_count) { - D2R_SrcFileLookup *lookup = lookups[i]; - - DW_LineFile *src = lookup->file; - RDIM_SrcFile *dst = rdim_src_file_chunk_list_push(arena, &g_d2r_shared.src_files, D2R_SRC_FILE_CAP); - - // make file path - String8 path; - { - Temp temp = temp_begin(scratch.arena); - String8 comp_dir = lookup->vm->header.dir_table.v[0]; - - PathStyle comp_path_style = path_style_from_str8(comp_dir); - PathStyle dir_path_style = path_style_from_str8(lookup->vm->header.dir_table.v[src->dir_idx]); - PathStyle file_path_style = path_style_from_str8(src->path); - - String8List dir_path_list = str8_split_path(temp.arena, lookup->vm->header.dir_table.v[src->dir_idx]); - String8List file_path_list = str8_split_path(temp.arena, src->path); - - String8List path_list = {0}; - if (dir_path_style == PathStyle_Relative) { - String8List comp_dir_list = str8_split_path(temp.arena, comp_dir); - str8_list_concat_in_place(&path_list, &comp_dir_list); + // rjf: unpack bit/byte sizes + if(bitsize_attrib != &dw2_attrib_nil) + { + bitsize = bitsize_attrib->val.u128.u64[0]; + } + else if(bytesize_attrib != &dw2_attrib_nil) + { + bitsize = bytesize_attrib->val.u128.u64[0]*8; } - str8_list_concat_in_place(&path_list, &dir_path_list); - str8_list_concat_in_place(&path_list, &file_path_list); - - PathStyle path_style = PathStyle_SystemAbsolute; - if (dir_path_style != PathStyle_Relative) { path_style = dir_path_style; } - else if (file_path_style != PathStyle_Relative) { path_style = file_path_style; } - else if (comp_path_style != PathStyle_Relative) { path_style = comp_path_style; } - - str8_path_list_resolve_dots_in_place(&path_list, path_style); - path = str8_path_list_join_by_style(arena, &path_list, path_style); - - temp_end(temp); } - // fill out source file - dst->path = path; - if ( ! u128_match(src->md5, u128_zero())) { - dst->checksum_kind = RDI_ChecksumKind_MD5; - dst->checksum = str8_copy(arena, str8_struct(&src->md5)); - } else if (src->time_stamp != 0) { - dst->checksum_kind = RDI_ChecksumKind_Timestamp; - dst->checksum = str8_copy(arena, str8_struct(&src->time_stamp)); - } - - lookup->src_file = dst; - } - ProfEnd(); - - // TODO: sync - RDIM_LineTableChunkList *lane_line_table_chunks = push_array(temp.arena, RDIM_LineTableChunkList, lane_count()); - - // TODO: per thread task - ProfBegin("Convert line sequences"); - U64 *line_seq_counts_per_lane = push_array(scratch.arena, U64, lane_count()); - for EachIndex(cu_idx, cu_ranges.count) { - RDIM_LineTableChunkList *lane_line_table_chunk_list = &lane_line_table_chunks[lane_idx()]; - - // push new line table for the compile unit - RDIM_LineTable *line_table = rdim_line_table_chunk_list_push(arena, lane_line_table_chunk_list, D2R_LINE_TABLE_CAP); - cu_line_tables_rdi[cu_idx] = line_table; - - // push line buffer -#define D2R_LineBufferMax 1024 - struct LineBuffer { U64 file_index; U64 voffs[D2R_LineBufferMax]; U32 line_nums[D2R_LineBufferMax]; U32 col_nums[D2R_LineBufferMax]; } LineBuffer; - struct LineBuffer *line_buffer = push_array(temp.arena, struct LineBuffer, 1); - U64 line_buffer_size = 0; - - // decode line sequences - DW_LineVM *vm = line_vms[cu_idx]; - U64 *line_seq_counts_per_file = push_array(temp.arena, U64, vm->header.file_table.count); - while (dw_line_vm_step(vm)) { - if (vm->new_line) { - // lazy defined files are not supported - if (vm->state.file_index >= vm->header.file_table.count) { continue; } + // rjf: convert type + RDIM_Type *dst_type = 0; + RDI_TypeKind rdi_type_kind = RDI_TypeKind_NULL; + RDI_TypeModifierFlags rdi_type_modifier_flags = 0; + switch(tag.kind) + { + default:{}break; + case DW_TagKind_ClassType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_Class; goto struct_type; + case DW_TagKind_StructureType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteStruct : RDI_TypeKind_Struct; goto struct_type; + case DW_TagKind_UnionType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteUnion : RDI_TypeKind_Union; goto struct_type; + struct_type:; + { + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = rdi_type_kind; + dst_type->name = name; + dst_type->byte_size = bitsize/8; + }break; + case DW_TagKind_EnumerationType: + { + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = is_decl ? RDI_TypeKind_IncompleteEnum : RDI_TypeKind_Enum; + dst_type->name = name; + dst_type->direct_type = direct_type; + dst_type->byte_size = (direct_type != 0 ? direct_type->byte_size : bitsize/8); + }break; + case DW_TagKind_SubProgram: + case DW_TagKind_SubroutineType: + { + // rjf: build type + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = RDI_TypeKind_Function; + dst_type->direct_type = direct_type; + dst_type->byte_size = arch_addr_size; - // time to flush the buffer? - if (((vm->opcode == DW_StdOpcode_ExtendedOpcode && vm->ext_opcode == DW_ExtOpcode_EndSequence) || - (line_buffer_size >= D2R_LineBufferMax || line_buffer->file_index != vm->state.file_index)) - && line_buffer_size > 0) { - // lookup source file - DW_LineFile *file = &vm->header.file_table.v[line_buffer->file_index]; - U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); - D2R_SrcFileLookup *lookup = lfht_search(src_file_lfht, hash, &(D2R_SrcFileLookup){ .file = file, .vm = vm }, d2r_src_file_lookup_is_equal, 0); - RDIM_SrcFile *src_file = lookup->src_file; - - // copy line info - U64 *voffs = push_array_no_zero(arena, U64, line_buffer_size + 1); - U32 *line_nums = push_array_no_zero(arena, U32, line_buffer_size); - voffs[line_buffer_size] = vm->state.address - image_base; - MemoryCopyTyped(voffs, line_buffer->voffs, line_buffer_size); - MemoryCopyTyped(line_nums, line_buffer->line_nums, line_buffer_size); - - RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, lane_line_table_chunk_list, line_table, src_file, voffs, line_nums, 0, line_buffer_size); - - // atomic implementation of @rdim_src_file_push_line_sequence + // rjf: gather all parameter types + typedef struct ParamNode ParamNode; + struct ParamNode + { + ParamNode *next; + U64 type_unit_idx; + U64 type_info_off; + }; + ParamNode *first_param = 0; + ParamNode *last_param = 0; + U64 param_count = 0; + if(tag.has_children) + { + U64 tag_children_off = info_off + tag_info_size; + S64 depth = 1; + for(U64 off = tag_children_off; depth > 0 && contains_1u64(unit_info_tag_range, off);) { - // associate line fragment with source file - RDIM_SrcFileLineMapFragment *line_frag = push_array(arena, RDIM_SrcFileLineMapFragment, 1); - line_frag->seq = line_seq; + Temp scratch3 = scratch_begin(&scratch2.arena, 1); + U64 start_off = off; - // insert line fragment node - for (;;) { - RDIM_SrcFileLineMapFragment *next = src_file->first_line_map_fragment; - line_frag->next = next; - RDIM_SrcFileLineMapFragment *curr = ins_atomic_ptr_eval_cond_assign(&src_file->first_line_map_fragment, line_frag, next); - if (curr == 0) { - ins_atomic_u64_add_eval(&g_d2r_shared.src_files.source_line_map_count, 1); + // rjf: read child tag + DW2_Tag child_tag = {0}; + off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); + + // rjf: gather parameters + if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) + { + DW2_Attrib *type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type); + ParamNode *n = push_array(scratch2.arena, ParamNode, 1); + SLLQueuePush(first_param, last_param, n); + n->type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); + n->type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, n->type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, n->type_info_off); + n->type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); } - if (curr == next) { break; } + param_count += 1; } - // accumulate line sequence counts per file - line_seq_counts_per_file[vm->state.file_index] += line_seq->line_count; + // rjf: navigate tree + if(child_tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + if(child_tag.has_children) + { + depth += 1; + } - // accumulate line sequence counts per lane - line_seq_counts_per_lane[lane_idx()] += line_seq->line_count; + scratch_end(scratch3); + if(off == start_off) + { + break; + } } - - // reset line buffer size tracker - line_buffer_size = 0; } - if (line_buffer_size > 0 && line_buffer->voffs[line_buffer_size - 1] == vm->state.address) { - line_buffer->line_nums[line_buffer_size - 1] = Min(line_buffer->line_nums[line_buffer_size - 1], safe_cast_u32(vm->state.line)); - } else { - // append line to the buffer - line_buffer->file_index = vm->state.file_index; - line_buffer->voffs[line_buffer_size] = vm->state.address - image_base; - line_buffer->line_nums[line_buffer_size] = safe_cast_u32(vm->state.line); - line_buffer_size += 1; + // rjf: tighten parameter types + dst_type->count = (RDI_U32)param_count; // TODO(rjf): @u64_to_u32 + dst_type->param_types = push_array(arena, RDIM_Type *, dst_type->count); + { + U64 param_idx = 0; + for(ParamNode *n = first_param; n != 0; n = n->next, param_idx += 1) + { + U64 param_type_info_off = n->type_info_off; + U64 param_type_unit_idx = n->type_unit_idx; + + // rjf: map info_off/unit_idx -> hash + U64 param_type_hash = 0; + { + D2R2_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[param_type_unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(¶m_type_info_off)); + U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count; + for(D2R2_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == param_type_info_off) + { + param_type_hash = n->dst_hash; + break; + } + } + } + + // rjf: map hash -> type + RDIM_Type *param_type = 0; + { + U64 unique_type_tag_slot_idx = param_type_hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == param_type_hash) + { + param_type = type_from_idx_map[n->order_idx]; + break; + } + } + } + + // rjf: store + dst_type->param_types[param_idx] = param_type; + } + } + }break; + case DW_TagKind_Typedef: + { + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = RDI_TypeKind_Alias; + dst_type->name = name; + dst_type->direct_type = direct_type; + dst_type->byte_size = (direct_type ? direct_type->byte_size : 0); + }break; + case DW_TagKind_BaseType: + { + if(0){} +#define CaseA(rdi_type_kind_, encoding_) else if(encoding == (encoding_)) { rdi_type_kind = (rdi_type_kind_); } +#define CaseB(rdi_type_kind_, encoding_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_)) { rdi_type_kind = (rdi_type_kind_); } +#define CaseC(rdi_type_kind_, encoding_, name_) /* assumes x64/arm64 */ else if(encoding == (encoding_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); } +#define CaseD(rdi_type_kind_, encoding_, name_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); } + CaseA(RDI_TypeKind_NULL, DW_ATE_Null) + CaseA(RDI_TypeKind_Void, DW_ATE_Address) + CaseA(RDI_TypeKind_Bool, DW_ATE_Boolean) + CaseB(RDI_TypeKind_ComplexF32, DW_ATE_ComplexFloat, 64) + CaseB(RDI_TypeKind_ComplexF64, DW_ATE_ComplexFloat, 128) + CaseB(RDI_TypeKind_ComplexF80, DW_ATE_ComplexFloat, 160) + CaseB(RDI_TypeKind_ComplexF128, DW_ATE_ComplexFloat, 256) + CaseC(RDI_TypeKind_F80, DW_ATE_Float, "__float80") + CaseC(RDI_TypeKind_F128, DW_ATE_Float, "__float128") + CaseC(RDI_TypeKind_F16, DW_ATE_Float, "_Float16") + CaseC(RDI_TypeKind_BF16, DW_ATE_Float, "__bf16") + CaseB(RDI_TypeKind_F16, DW_ATE_Float, 16) + CaseB(RDI_TypeKind_F32, DW_ATE_Float, 32) + CaseB(RDI_TypeKind_F48, DW_ATE_Float, 48) + CaseB(RDI_TypeKind_F64, DW_ATE_Float, 64) + CaseB(RDI_TypeKind_F80, DW_ATE_Float, 80) + CaseB(RDI_TypeKind_F96, DW_ATE_Float, 96) + CaseB(RDI_TypeKind_F128, DW_ATE_Float, 128) + CaseB(RDI_TypeKind_Char8, DW_ATE_SignedChar, 8) + CaseB(RDI_TypeKind_Char16, DW_ATE_SignedChar, 16) + CaseB(RDI_TypeKind_Char32, DW_ATE_SignedChar, 32) + CaseD(RDI_TypeKind_Char8, DW_ATE_Signed, "wchar_t", 8) + CaseD(RDI_TypeKind_Char16, DW_ATE_Signed, "wchar_t", 16) + CaseD(RDI_TypeKind_Char32, DW_ATE_Signed, "wchar_t", 32) + CaseB(RDI_TypeKind_S8, DW_ATE_Signed, 8) + CaseB(RDI_TypeKind_S16, DW_ATE_Signed, 16) + CaseB(RDI_TypeKind_S32, DW_ATE_Signed, 32) + CaseB(RDI_TypeKind_S64, DW_ATE_Signed, 64) + CaseB(RDI_TypeKind_S128, DW_ATE_Signed, 128) + CaseB(RDI_TypeKind_S256, DW_ATE_Signed, 256) + CaseB(RDI_TypeKind_S512, DW_ATE_Signed, 512) + CaseB(RDI_TypeKind_U8, DW_ATE_Unsigned, 8) + CaseB(RDI_TypeKind_U16, DW_ATE_Unsigned, 16) + CaseB(RDI_TypeKind_U32, DW_ATE_Unsigned, 32) + CaseB(RDI_TypeKind_U64, DW_ATE_Unsigned, 64) + CaseB(RDI_TypeKind_U128, DW_ATE_Unsigned, 128) + CaseB(RDI_TypeKind_U256, DW_ATE_Unsigned, 256) + CaseB(RDI_TypeKind_U512, DW_ATE_Unsigned, 512) + CaseB(RDI_TypeKind_UChar8, DW_ATE_UnsignedChar, 8) + CaseB(RDI_TypeKind_UChar8, DW_ATE_Utf, 8) + CaseB(RDI_TypeKind_UChar16, DW_ATE_UnsignedChar, 16) + CaseB(RDI_TypeKind_UChar16, DW_ATE_Utf, 16) + CaseB(RDI_TypeKind_UChar32, DW_ATE_UnsignedChar, 32) + CaseB(RDI_TypeKind_UChar32, DW_ATE_Utf, 32) + CaseB(RDI_TypeKind_Decimal32, DW_ATE_DecimalFloat, 32) + CaseB(RDI_TypeKind_Decimal64, DW_ATE_DecimalFloat, 64) + CaseB(RDI_TypeKind_Decimal128, DW_ATE_DecimalFloat, 128) +#undef CaseA +#undef CaseB +#undef CaseC +#undef CaseD + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = RDI_TypeKind_Alias; + dst_type->name = name; + dst_type->direct_type = d2r2_type_from_builtin_kind(rdi_type_kind); + dst_type->byte_size = bitsize/8; // TODO: byte size on __float80 alias mismatches byte size on direct type now + }break; + case DW_TagKind_PointerType: rdi_type_kind = RDI_TypeKind_Ptr; goto ptr_or_ref_type; + case DW_TagKind_ReferenceType: rdi_type_kind = RDI_TypeKind_LRef; goto ptr_or_ref_type; + case DW_TagKind_RValueReferenceType: rdi_type_kind = RDI_TypeKind_RRef; goto ptr_or_ref_type; + ptr_or_ref_type:; + { + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = rdi_type_kind; + dst_type->direct_type = direct_type; + dst_type->byte_size = arch_addr_size; + }break; + case DW_TagKind_RestrictType: + { + rdi_type_modifier_flags = RDI_TypeModifierFlag_Restrict; + }goto basic_type_operators; + case DW_TagKind_VolatileType: + { + rdi_type_modifier_flags = RDI_TypeModifierFlag_Volatile; + }goto basic_type_operators; + case DW_TagKind_ConstType: + { + rdi_type_modifier_flags = RDI_TypeModifierFlag_Const; + }goto basic_type_operators; + basic_type_operators:; + { + dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + dst_type->kind = RDI_TypeKind_Modifier; + dst_type->flags = rdi_type_modifier_flags; + dst_type->direct_type = direct_type; + dst_type->byte_size = (direct_type ? direct_type->byte_size : 0); + }break; + case DW_TagKind_ArrayType: + if(tag.has_children) + { + // rjf: parse array type children; extract dimensions (each one gets a SubrangeType) + typedef struct ArrayDimensionNode ArrayDimensionNode; + struct ArrayDimensionNode + { + ArrayDimensionNode *next; + U64 count; + }; + ArrayDimensionNode *top_dimension = 0; + { + U64 tag_children_off = info_off + tag_info_size; + S64 depth = 1; + for(U64 off = tag_children_off; contains_1u64(unit_info_tag_range, off) && depth > 0;) + { + U64 start_off = off; + DW2_Tag child_tag = {0}; + off += dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); + if(child_tag.kind == DW_TagKind_SubrangeType) + { + // rjf: extract bounds attribs + DW2_Attrib *lower_bound_attrib = &dw2_attrib_nil; + DW2_Attrib *upper_bound_attrib = &dw2_attrib_nil; + DW2_Attrib *count_attrib = &dw2_attrib_nil; + for(DW2_AttribNode *n = child_tag.attribs.first; + n != 0 && (lower_bound_attrib == &dw2_attrib_nil || (upper_bound_attrib == &dw2_attrib_nil && count_attrib == &dw2_attrib_nil)); + n = n->next) + { + if(n->v.attrib_kind == DW_AttribKind_LowerBound) + { + lower_bound_attrib = &n->v; + } + else if(n->v.attrib_kind == DW_AttribKind_UpperBound) + { + upper_bound_attrib = &n->v; + } + else if(n->v.attrib_kind == DW_AttribKind_Count) + { + count_attrib = &n->v; + } + } + + // rjf: resolve lower bound + U64 lower_bound = 0; + if(lower_bound_attrib != &dw2_attrib_nil) + { + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, lower_bound_attrib->val.kind) & DW_AttribClass_Reference) + { + log_infof("[.debug_info@0x%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off); + } + else + { + lower_bound = lower_bound_attrib->val.u128.u64[0]; + } + } + else switch(unit_parse_ctx->language) + { + default:{}break; +#define X(name, code, default_array_lower_bound) case DW_Language_##name:{lower_bound = (default_array_lower_bound);}break; + DW_Language_XList +#undef X + } + + // rjf: resolve upper bound + U64 upper_bound = 0; + { + if(count_attrib != &dw2_attrib_nil) + { + upper_bound = lower_bound + count_attrib->val.u128.u64[0]; + } + else if(upper_bound_attrib != &dw2_attrib_nil) + { + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, upper_bound_attrib->val.kind) & DW_AttribClass_Reference) + { + log_infof("[.debug_info@0x%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off); + } + else + { + upper_bound = upper_bound_attrib->val.u128.u64[0]; + upper_bound += 1; // NOTE(rjf): turn to exclusive range + } + } + } + + // rjf: push node + ArrayDimensionNode *dim_n = push_array(temp.arena, ArrayDimensionNode, 1); + SLLStackPush(top_dimension, dim_n); + dim_n->count = (upper_bound - lower_bound); + } + if(child_tag.has_children) + { + depth += 1; + } + else if(child_tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + if(off == start_off) + { + break; + } + } + } + + // rjf: create array type operators for each dimension + RDIM_Type *array_direct_type = direct_type; + for EachNode(dim_n, ArrayDimensionNode, top_dimension) + { + RDIM_Type *array_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); + array_type->kind = RDI_TypeKind_Array; + array_type->byte_size = array_direct_type ? array_direct_type->byte_size*dim_n->count : 0; + array_type->direct_type = array_direct_type; + array_direct_type = array_type; + } + + // rjf: final destination type -> the final array dimension type + dst_type = array_direct_type; + }break; + } + + // rjf: store type in type-from-idx table + type_from_idx_map[type_idx] = dst_type; + + temp_end(temp); + } + + //- rjf: combine all types from all lanes + RDIM_TypeChunkList *lanes_types = 0; + if(lane_idx() == 0) + { + lanes_types = push_array(scratch2.arena, RDIM_TypeChunkList, lane_count()); + } + lane_sync_u64(&lanes_types, 0); + lanes_types[lane_idx()] = lane_types; + lane_sync(); + if(lane_idx() == 0) + { + RDIM_TypeChunkList pass_lane_combined_types = {0}; + for EachIndex(l_idx, lane_count()) + { + rdim_type_chunk_list_concat_in_place(&pass_lane_combined_types, &lanes_types[l_idx]); + } + rdim_type_chunk_list_concat_in_place(all_types, &pass_lane_combined_types); + } + lane_sync(); + + //- rjf: find minimum next max chain count across all lanes, for next iteration + U64 *lane_next_max_chain_counts = 0; + if(lane_idx() == 0) + { + lane_next_max_chain_counts = push_array(scratch2.arena, U64, lane_count()); + } + lane_sync_u64(&lane_next_max_chain_counts, 0); + lane_next_max_chain_counts[lane_idx()] = next_max_chain_count; + lane_sync(); + if(lane_idx() == 0) + { + for EachIndex(l_idx, lane_count()) + { + next_max_chain_count = Min(next_max_chain_count, lane_next_max_chain_counts[l_idx]); + } + } + lane_sync_u64(&next_max_chain_count, 0); + + //- rjf: update max chain count + max_chain_count = next_max_chain_count; + + ProfEnd(); + scratch_end(scratch2); + } + } + lane_sync(); + + //////////////////////////// + //- rjf: convert all namespaces + // + RDIM_NamespaceChunkList *all_namespaces = 0; + RDIM_Namespace **namespace_from_idx_map = 0; + { + // rjf: set up + if(lane_idx() == 0) + { + all_namespaces = push_array(scratch.arena, RDIM_NamespaceChunkList, 1); + namespace_from_idx_map = push_array(scratch.arena, RDIM_Namespace *, namespace_count); + } + lane_sync_u64(&all_namespaces, 0); + lane_sync_u64(&namespace_from_idx_map, 0); + + // rjf: build all namespaces on this lane + RDIM_NamespaceChunkList lane_namespaces = {0}; + Rng1U64 range = lane_range(namespace_count); + for EachInRange(namespace_idx, range) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + // rjf: unpack tag node + D2R2_UniqueTagNode *tag_node = namespace_tag_nodes[namespace_idx]; + U64 info_off = tag_node->info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + + // rjf: read namespace tag + DW2_Tag tag = {0}; + dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); + DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name); + String8 name = name_attrib->val.string; + + // rjf: build namespace + RDIM_Namespace *ns = rdim_namespace_chunk_list_push(arena, &lane_namespaces, 64); + ns->name = name; + + // rjf: store in (deduped tag idx -> namespace) table + namespace_from_idx_map[tag_node->order_idx] = ns; + + scratch_end(scratch2); + } + lane_sync(); + + // rjf: join all namespaces + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + RDIM_NamespaceChunkList *lanes_namespaces = 0; + if(lane_idx() == 0) + { + lanes_namespaces = push_array(scratch2.arena, RDIM_NamespaceChunkList, lane_count()); + } + lane_sync_u64(&lanes_namespaces, 0); + lanes_namespaces[lane_idx()] = lane_namespaces; + lane_sync(); + if(lane_idx() == 0) + { + for EachIndex(l_idx, lane_count()) + { + rdim_namespace_chunk_list_concat_in_place(all_namespaces, &lanes_namespaces[l_idx]); + } + } + lane_sync(); + scratch_end(scratch2); + } + } + lane_sync(); + + //////////////////////////// + //- rjf: convert all UDTs + // + RDIM_UDTChunkList *all_udts = 0; + ProfScope("convert all UDTs") + { + //- rjf: produce UDTs across all lanes + U64 chunk_count = 512; + RDIM_UDTChunkList lane_udts = {0}; + Rng1U64 range = lane_range(type_count); + for EachInRange(type_idx, range) + { + RDIM_Type *type = type_from_idx_map[type_idx]; + if(type != 0 && + (type->kind == RDI_TypeKind_Struct || + type->kind == RDI_TypeKind_Union || + type->kind == RDI_TypeKind_Class || + type->kind == RDI_TypeKind_Enum || + type->kind == RDI_TypeKind_Alias)) + { + // rjf: produce UDT + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &lane_udts, chunk_count); + type->udt = udt; + udt->self_type = type; + + // rjf: idx -> type tag node + D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; + U64 info_off = type_tag_node->info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; + + // rjf: unpack unit + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + + // rjf: parse all tags + S64 depth = 0; + for(U64 off = info_off; contains_1u64(unit_info_tag_range, off) && (depth > 0 || off == info_off);) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + U64 start_off = off; + + // rjf: parse tag + DW2_Tag tag = {0}; + U64 tag_parse_off = off; + off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); + + // rjf: unpack tag attributes + DW2_Attrib *name_attrib = &dw2_attrib_nil; + DW2_Attrib *type_attrib = &dw2_attrib_nil; + DW2_Attrib *off_attrib = &dw2_attrib_nil; + DW2_Attrib *val_attrib = &dw2_attrib_nil; + DW2_Attrib *declfile_attrib = &dw2_attrib_nil; + DW2_Attrib *declline_attrib = &dw2_attrib_nil; + DW2_Attrib *declcol_attrib = &dw2_attrib_nil; + for EachNode(n, DW2_AttribNode, tag.attribs.first) + { + switch(n->v.attrib_kind) + { + default:{}break; +#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break + Case(name, Name); + Case(type, Type); + Case(off, DataMemberLocation); + Case(val, ConstValue); + Case(declfile, DeclFile); + Case(declline, DeclLine); + Case(declcol, DeclColumn); +#undef Case + } + } + + // rjf: unpack basic attributes + String8 name = name_attrib->val.string; + U64 off = off_attrib->val.u128.u64[0]; + U64 val = val_attrib->val.u128.u64[0]; + + // rjf: unpack type + RDIM_Type *type = 0; + if(type_attrib != &dw2_attrib_nil) + { + // rjf: attrib -> info off / unit idx + U64 type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); + U64 type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off); + type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); + } + + // rjf: (info_off, unit_idx) -> hash + U64 type_hash = 0; + { + D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; + for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == type_info_off) + { + type_hash = n->dst_hash; + break; + } + } + } + + // rjf: hash -> type + { + U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == type_hash) + { + type = type_from_idx_map[n->order_idx]; + break; + } + } + } + } + + // rjf: root-level tag -> collect decl file/line info + if(tag_parse_off == start_off) + { + // TODO(rjf): we need to have gathered the source files before this!!!!!! + // currently, they only come from line info - we need to gather/dedup the + // ones that also come from tags. + } + + // rjf: gather children + switch(tag.kind) + { + default:{}break; + case DW_TagKind_Member: + { + RDIM_UDTMember *member = rdim_udt_push_member(arena, &lane_udts, udt); + member->kind = RDI_MemberKind_DataField; + member->name = name; + member->type = type; + member->off = (RDI_U32)off; // TODO(rjf): @u64_to_u32 + }break; + case DW_TagKind_Enumerator: + { + RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, &lane_udts, udt); + enum_val->name = name; + enum_val->val = val; + }break; + } + + // rjf: tree nav + if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + if(tag.has_children) + { + depth += 1; + } + + scratch_end(scratch2); + if(off == start_off) + { + break; + } + } + } + } + lane_sync(); + + //- rjf: combine all lanes + RDIM_UDTChunkList *lanes_udts = 0; + if(lane_idx() == 0) + { + lanes_udts = push_array(scratch.arena, RDIM_UDTChunkList, lane_count()); + } + lane_sync_u64(&lanes_udts, 0); + lanes_udts[lane_idx()] = lane_udts; + lane_sync(); + if(lane_idx() == 0) + { + all_udts = push_array(scratch.arena, RDIM_UDTChunkList, 1); + for EachIndex(l_idx, lane_count()) + { + rdim_udt_chunk_list_concat_in_place(all_udts, &lanes_udts[l_idx]); + } + } + lane_sync_u64(&all_udts, 0); + } + + //////////////////////////// + //- rjf: convert all units + // + RDIM_UnitChunkList *all_units = 0; + RDIM_Unit **unit_from_idx_map = 0; + ProfScope("convert all units") + { + if(lane_idx() == 0) + { + all_units = push_array(scratch.arena, RDIM_UnitChunkList, 1); + unit_from_idx_map = push_array(scratch.arena, RDIM_Unit *, unit_count); + for EachIndex(unit_idx, unit_count) + { + unit_from_idx_map[unit_idx] = rdim_unit_chunk_list_push(arena, all_units, unit_count); + } + } + lane_sync_u64(&all_units, 0); + lane_sync_u64(&unit_from_idx_map, 0); + U64 unit_take_idx_ = 0; + U64 *unit_take_idx_ptr = &unit_take_idx_; + lane_sync_u64(&unit_take_idx_ptr, 0); + for(;;) + { + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; + if(unit_idx >= unit_count) + { + break; + } + + //- rjf: unpack unit info + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx]; + + //- rjf: unpack unit's root tag + DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; + DW2_Attrib *name_attrib = &dw2_attrib_nil; + DW2_Attrib *comp_dir_attrib = &dw2_attrib_nil; + DW2_Attrib *producer_attrib = &dw2_attrib_nil; + DW2_Attrib *lang_attrib = &dw2_attrib_nil; + DW2_Attrib *ranges_attrib = &dw2_attrib_nil; + DW2_Attrib *lopc_attrib = &dw2_attrib_nil; + DW2_Attrib *hipc_attrib = &dw2_attrib_nil; + for EachNode(n, DW2_AttribNode, unit_root_tag->attribs.first) + { + switch(n->v.attrib_kind) + { + default:{}break; +#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break + Case(name, Name); + Case(comp_dir, CompDir); + Case(producer, Producer); + Case(lang, Language); + Case(ranges, Ranges); + Case(lopc, LowPc); + Case(hipc, HighPc); +#undef Case + } + } + + //- rjf: unpack attributes + String8 unit_name = name_attrib->val.string; + String8 unit_comp_dir = comp_dir_attrib->val.string; + String8 unit_producer = producer_attrib->val.string; + RDI_Language unit_lang = RDI_Language_NULL; + { + DW_Language dw_lang = lang_attrib->val.u128.u64[0]; + switch(dw_lang) + { + default:{}break; + case DW_Language_C89: + case DW_Language_C99: + case DW_Language_C11: + case DW_Language_C: + { + unit_lang = RDI_Language_C; + }break; + case DW_Language_CPlusPlus03: + case DW_Language_CPlusPlus11: + case DW_Language_CPlusPlus14: + case DW_Language_CPlusPlus: + { + unit_lang = RDI_Language_CPlusPlus; + }break; + } + } + + //- rjf: get unit's ranges from .debug_aranges parse artifacts, if we have them + RDIM_Rng1U64ChunkList unit_voff_ranges = {0}; + if(arange_unit_from_info_off_map_slots_count != 0) + { + U64 unit_info_off = unit_info_ranges->v[unit_idx].min; + U64 hash = u64_hash_from_str8(str8_struct(&unit_info_off)); + U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count; + for(D2R2_ARangeUnitNode *n = arange_unit_from_info_off_map_slots[slot_idx]; n != 0; n = n->next) + { + if(n->info_off == unit_info_off) + { + unit_voff_ranges = n->ranges[0]; + break; + } + } + } + + //- rjf: if we have no voff ranges from .debug_aranges, then we need to extract + // this info from the unit root rag instead (via ranges & low-pc/high-pc tags) + if(unit_voff_ranges.total_count == 0) + { + // rjf: gather ranges from a ranges attribute + if(ranges_attrib != &dw2_attrib_nil) + { + Rng1U64List ranges = dw2_rnglist_from_form_val(scratch.arena, unit_parse_ctx, raw, ranges_attrib->val); + for EachNode(n, Rng1U64Node, ranges.first) + { + rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){n->v.min - base_vaddr, n->v.max - base_vaddr}); + } + } + + // rjf: gather contiguous range from low-pc / high-pc attribute + if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil) + { + U64 voff_base = lopc_attrib->val.addr - base_vaddr; + U64 voff_opl = 0; + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; + } + else + { + voff_opl = hipc_attrib->val.addr; + } + rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){voff_base, voff_opl}); + } + } + + //- rjf: fill top-level unit info + { + dst_unit->unit_name = unit_name; + dst_unit->compiler_name = unit_producer; + // TODO(rjf): dst_unit->source_file = ???; + // TODO(rjf): dst_unit->object_file = ???; + // TODO(rjf): dst_unit->archive_file = ???; + dst_unit->build_path = unit_comp_dir; + dst_unit->language = unit_lang; + dst_unit->line_table = unit_line_tables[unit_idx]; + dst_unit->voff_ranges = unit_voff_ranges; + } + } + lane_sync(); + } + + //////////////////////////// + //- rjf: convert all symbols + // + typedef struct D2R2_ScopeContainerNode D2R2_ScopeContainerNode; + struct D2R2_ScopeContainerNode + { + D2R2_ScopeContainerNode *next; + U64 info_off; + RDIM_Scope *scope; + }; + typedef struct D2R2_ScopeContainerMap D2R2_ScopeContainerMap; + struct D2R2_ScopeContainerMap + { + D2R2_ScopeContainerNode **slots; + U64 slots_count; + }; + typedef struct D2R2_SubUnitWorkArtifacts D2R2_SubUnitWorkArtifacts; + struct D2R2_SubUnitWorkArtifacts + { + RDIM_SymbolChunkList global_variables; + RDIM_SymbolChunkList thread_variables; + RDIM_SymbolChunkList constants; + RDIM_SymbolChunkList procedures; + RDIM_ScopeChunkList scopes; + RDIM_InlineSiteChunkList inline_sites; + }; + D2R2_ScopeContainerMap *scope_container_map = 0; + D2R2_SubUnitWorkArtifacts *sub_unit_work_artifacts = 0; + ProfScope("convert all symbols") + { + if(lane_idx() == 0) + { + scope_container_map = push_array(scratch.arena, D2R2_ScopeContainerMap, 1); + scope_container_map->slots_count = total_tag_count_estimate/8 + 1; + scope_container_map->slots = push_array(scratch.arena, D2R2_ScopeContainerNode *, scope_container_map->slots_count); + sub_unit_work_artifacts = push_array(scratch.arena, D2R2_SubUnitWorkArtifacts, sub_unit_works_count); + } + lane_sync_u64(&scope_container_map, 0); + lane_sync_u64(&sub_unit_work_artifacts, 0); + U64 work_take_idx_ = 0; + U64 *work_take_idx_ptr = &work_take_idx_; + lane_sync_u64(&work_take_idx_ptr, 0); + for(;;) + { + U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1; + if(work_idx >= sub_unit_works_count) + { + break; + } + + //- rjf: unpack work + U64 unit_idx = sub_unit_works[work_idx].unit_idx; + Rng1U64 root_tag_idx_range = sub_unit_works[work_idx].root_tag_idx_range; + D2R2_SubUnitWorkArtifacts *dst_artifacts = &sub_unit_work_artifacts[work_idx]; + + //- rjf: unpack unit info + DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; + Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; + + //- rjf: produce all unit symbols in this work + U64 chunk_count = 256; + for(U64 root_tag_idx = root_tag_idx_range.min; root_tag_idx < root_tag_idx_range.max; root_tag_idx += 1) + { + typedef struct D2R2_ParentNode D2R2_ParentNode; + struct D2R2_ParentNode + { + D2R2_ParentNode *next; + DW_TagKind tag_kind; + U64 info_off; + RDIM_Scope *scope; + RDIM_Type *container_type; + RDIM_Namespace *container_namespace; + RDIM_LocationCaseList framebase_location_cases; + }; + D2R2_ParentNode *top_parent = 0; + D2R2_ParentNode *free_parent = 0; + U64 root_tag_start_off = unit_info_root_tag_offs[unit_idx].v[root_tag_idx]; + S64 depth = 1; + for(U64 off = root_tag_start_off; off < unit_info_tag_range.max && (depth > 1 || off == root_tag_start_off);) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + U64 start_off = off; + + //////////////////////// + //- rjf: parse tag + // + DW2_Tag tag = {0}; + off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); + + //////////////////////// + //- rjf: gather attributes from tag + // + DW2_Attrib *name_attrib = &dw2_attrib_nil; + DW2_Attrib *linkname_attrib = &dw2_attrib_nil; + DW2_Attrib *type_attrib = &dw2_attrib_nil; + DW2_Attrib *inline_attrib = &dw2_attrib_nil; + DW2_Attrib *ranges_attrib = &dw2_attrib_nil; + DW2_Attrib *lopc_attrib = &dw2_attrib_nil; + DW2_Attrib *hipc_attrib = &dw2_attrib_nil; + DW2_Attrib *constval_attrib = &dw2_attrib_nil; + DW2_Attrib *location_attrib = &dw2_attrib_nil; + DW2_Attrib *framebase_attrib = &dw2_attrib_nil; + DW2_Attrib *external_attrib = &dw2_attrib_nil; + DW2_Attrib *decl_attrib = &dw2_attrib_nil; + for EachNode(n, DW2_AttribNode, tag.attribs.first) + { + switch(n->v.attrib_kind) + { + default:{}break; +#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break + Case(name, Name); + Case(linkname, LinkageName); + Case(type, Type); + Case(inline, Inline); + Case(ranges, Ranges); + Case(lopc, LowPc); + Case(hipc, HighPc); + Case(constval, ConstValue); + Case(location, Location); + Case(framebase, FrameBase); + Case(external, External); + Case(decl, Declaration); +#undef Case + } + } + + //////////////////////// + //- rjf: unpack basic attributes + // + String8 name = name_attrib->val.string; + String8 link_name = linkname_attrib->val.string; + DW_InlKind inl_kind = (DW_InlKind)inline_attrib->val.u128.u64[0]; + B32 is_external = (external_attrib != &dw2_attrib_nil); + B32 is_decl = (decl_attrib != &dw2_attrib_nil); + + //////////////////////// + //- rjf: unpack ranges + // + RDIM_Rng1U64List ranges = {0}; + { + if(ranges_attrib != &dw2_attrib_nil) + { + Temp temp = temp_begin(scratch2.arena); + Rng1U64List tag_ranges = dw2_rnglist_from_form_val(temp.arena, unit_parse_ctx, raw, ranges_attrib->val); + for EachNode(n, Rng1U64Node, tag_ranges.first) + { + rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){.min = n->v.min, .max = n->v.max}); + } + temp_end(temp); + } + if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil) + { + U64 voff_base = lopc_attrib->val.addr - base_vaddr; + U64 voff_opl = 0; + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; + } + else + { + voff_opl = hipc_attrib->val.addr; + } + rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){voff_base, voff_opl}); + } + } + + //////////////////////// + //- rjf: unpack type + // + RDIM_Type *type = 0; + if(type_attrib != &dw2_attrib_nil || tag.kind == DW_TagKind_SubProgram) + { + // rjf: functions need to get their type info from themselves; the type attrib + // only references the return type. so, in that case, we'll use the procedure's + // own info offset / unit index, rather than the type attribute's. + U64 type_info_off = start_off; + U64 type_unit_idx = unit_idx; + + // rjf: attrib -> (info_off, unit_idx) + if(tag.kind != DW_TagKind_SubProgram) + { + type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); + type_unit_idx = unit_idx; + if(!contains_1u64(unit_info_tag_range, type_info_off)) + { + U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off); + type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); + } + } + + // rjf: (info_off, unit_idx) -> hash + U64 type_hash = 0; + { + D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; + for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == type_info_off) + { + type_hash = n->dst_hash; + break; + } + } + } + + // rjf: hash -> type + { + U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == type_hash) + { + type = type_from_idx_map[n->order_idx]; + break; + } + } + } + } + + //////////////////////// + //- rjf: unpack location info + // + RDIM_LocationCaseList location_cases = {0}; + RDIM_LocationCaseList framebase_location_cases = {0}; + B32 location_is_tls_dependent = 0; + B32 framebase_location_is_tls_dependent = 0; + { + struct + { + DW2_Attrib *attrib; + RDIM_LocationCaseList *dst_locations; + B32 *dst_is_tls_dependent; + } + tasks[] = + { + {location_attrib, &location_cases, &location_is_tls_dependent}, + {framebase_attrib, &framebase_location_cases, &framebase_location_is_tls_dependent}, + }; + for EachElement(task_idx, tasks) + { + if(tasks[task_idx].attrib == &dw2_attrib_nil) + { + continue; + } + DW2_Attrib *attrib = tasks[task_idx].attrib; + RDIM_LocationCaseList *dst_locations = tasks[task_idx].dst_locations; + B32 *dst_is_tls_dependent = tasks[task_idx].dst_is_tls_dependent; + + ////////////////////// + //- rjf: gather DWARF location exprs + // + DW2_LocList locs = {0}; + switch(attrib->val.kind) + { + default:{}break; + case DW_FormKind_ExprLoc: + { + U64 expr_info_off = attrib->val.u128.u64[0]; + U64 expr_info_size = attrib->val.u128.u64[1]; + String8 expr = str8_substr(raw->sec[DW_SectionKind_Info].data, r1u64(expr_info_off, expr_info_off+expr_info_size)); + DW2_LocNode *n = push_array(scratch2.arena, DW2_LocNode, 1); + n->v.expr = expr; + n->v.range = r1u64(0, max_U64); + SLLQueuePush(locs.first, locs.last, n); + locs.count += 1; + }break; + case DW_FormKind_LocListx: // NOTE(rjf): dwarf5+, loclistx -> location list offset table + case DW_FormKind_SecOffset: // NOTE(rjf): pre-dwarf5, location section offset -> a location list + { + locs = dw2_loclist_from_form_val(scratch2.arena, unit_parse_ctx, raw, attrib->val); + }break; + } + + ////////////////////// + //- rjf: convert DWARF location exprs -> RDIM locations + // + // we do this for each possible frame base location case - locations in DWARF + // refer to the frame base via a special op. in our case, we want to bake that + // into the location directly to reduce extra context for evaluation. + // + // if we find that a location doesn't rely on the frame base at all, we just + // skip the rest. + // + B32 is_tls_dependent = 0; + for EachNode(n, DW2_LocNode, locs.first) + { + //- rjf: unpack location + Rng1U64 range = n->v.range; + String8 expr = n->v.expr; + + //- rjf: iterate each frame base location case, or once if there are none, + // and convert the location in that context + RDIM_LocationCase nil_framebase_loc_case = {0, {0}, {0, max_U64}}; + for(RDIM_LocationCase *framebase_loc_n = top_parent ? top_parent->framebase_location_cases.first : &nil_framebase_loc_case; + framebase_loc_n != 0; + framebase_loc_n = framebase_loc_n->next) + { + //- rjf: unpack framebase location + RDIM_Location framebase_loc = framebase_loc_n->location; + RDIM_Rng1U64 framebase_voff_range = framebase_loc_n->voff_range; + + //- rjf: set up type stack for type-evaluating bytecode + typedef struct D2R2_ExprVal D2R2_ExprVal; + struct D2R2_ExprVal + { + RDI_TypeKind type_kind; + B32 is_addr; + }; + typedef struct D2R2_ExprValNode D2R2_ExprValNode; + struct D2R2_ExprValNode + { + D2R2_ExprValNode *next; + D2R2_ExprVal v; + }; + D2R2_ExprValNode *val_stack_top = 0; + D2R2_ExprValNode *free_val = 0; + + //- rjf: process DWARF bytecode; produce RDI bytecode + mini-type-info for the expression's result + typedef struct JumpOpNode JumpOpNode; + struct JumpOpNode + { + JumpOpNode *next; + RDIM_EvalBytecodeOp *op; + S64 inst_delta; + }; + JumpOpNode *first_jump_op = 0; + JumpOpNode *last_jump_op = 0; + RDIM_EvalBytecode dst_bytecode = {0}; + B32 dst_bytecode_is_good = 1; + B32 bytecode_is_framebase_dependent = 0; + for(U64 expr_off = 0; expr_off < expr.size;) + { + U64 start_expr_off = expr_off; + + //- rjf: read next opcode + DW_ExprOp opcode = 0; + expr_off += str8_deserial_read_struct(expr, expr_off, &opcode); + + //- rjf: unpack opcode + DW_ExprOpInfo *opcode_info = dw_info_from_expr_op(unit_parse_ctx->version, unit_parse_ctx->exts, opcode); + U64 operands_count = opcode_info->operand_count; + DW_ExprOperandKind operands_kinds[2] = {opcode_info->operand_kinds[0], opcode_info->operand_kinds[1]}; + U64 push_count = opcode_info->push_count; + U64 pop_count = opcode_info->pop_count; + + //- rjf: read op operands + U64 operand_u64s[2] = {0}; + S64 operand_s64s[2] = {0}; + String8 operand_str8s[2] = {0}; + for EachIndex(operand_idx, operands_count) + { + switch(operands_kinds[operand_idx]) + { + case DW_ExprOperandKind_Null: + default:{}break; + case DW_ExprOperandKind_U8: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 1, 1);}break; + case DW_ExprOperandKind_U16: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 2, 2);}break; + case DW_ExprOperandKind_U32: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 4, 4);}break; + case DW_ExprOperandKind_U64: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 8, 8);}break; + case DW_ExprOperandKind_S8: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 1, 1);}break; + case DW_ExprOperandKind_S16: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 2, 2);}break; + case DW_ExprOperandKind_S32: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 4, 4);}break; + case DW_ExprOperandKind_S64: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 8, 8);}break; + case DW_ExprOperandKind_ULEB128: {expr_off += str8_deserial_read_uleb128(expr, expr_off, &operand_u64s[operand_idx]);} break; + case DW_ExprOperandKind_SLEB128: {expr_off += str8_deserial_read_sleb128(expr, expr_off, &operand_s64s[operand_idx]);} break; + case DW_ExprOperandKind_Addr: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], unit_parse_ctx->addr_size, unit_parse_ctx->addr_size);}break; + case DW_ExprOperandKind_DwarfUInt: {expr_off += dw2_read_fmt_u64(expr, expr_off, unit_parse_ctx->format, &operand_u64s[operand_idx]);}break; + case DW_ExprOperandKind_Block: + { + U8 block_size = 0; + expr_off += str8_deserial_read_struct(expr, expr_off, &block_size); + operand_str8s[operand_idx] = str8_substr(expr, r1u64(expr_off, expr_off+block_size)); + expr_off += block_size; + }break; + } + } + + //- rjf: pop stack values + D2R2_ExprVal popped_vals[2] = {0}; + { + pop_count = Min(pop_count, ArrayCount(popped_vals)); + for EachIndex(pop_idx, pop_count) + { + if(val_stack_top != 0) + { + D2R2_ExprValNode *popped = val_stack_top; + popped_vals[pop_idx] = popped->v; + SLLStackPop(val_stack_top); + SLLStackPush(free_val, popped); + } + } + } + + //- rjf: exec op (produce RDI bytecode, + manipulate value stack) + D2R2_ExprVal push_vals[2] = {0}; + U64 regcode_dw = 0; + S64 regval_off = 0; + B32 regread_is_addr = 0; + U64 target_pick_val_idx = 0; + RDI_EvalOp rdi_eval_op = 0; + U64 memread_size = 0; + switch(opcode) + { + default:{}break; + + //- rjf: small opcode-embedded unsigned literals + case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: + case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: + case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: + case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: + case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: + case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: + case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: + case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: + case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: + case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: + case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: + { + U64 lit_val = (U64)(opcode - DW_ExprOp_Lit0); + rdim_bytecode_push_uconst(arena, &dst_bytecode, lit_val); + }break; + + //- rjf: small unsigned literals + case DW_ExprOp_Const1U: push_vals[0].type_kind = RDI_TypeKind_U8; goto const_u; + case DW_ExprOp_Const2U: push_vals[0].type_kind = RDI_TypeKind_U16; goto const_u; + case DW_ExprOp_Const4U: push_vals[0].type_kind = RDI_TypeKind_U32; goto const_u; + case DW_ExprOp_Const8U: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u; + case DW_ExprOp_ConstU: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u; + const_u:; + { + rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]); + }break; + + //- rjf: small signed literals + case DW_ExprOp_Const1S: push_vals[0].type_kind = RDI_TypeKind_S8; goto const_s; + case DW_ExprOp_Const2S: push_vals[0].type_kind = RDI_TypeKind_S16; goto const_s; + case DW_ExprOp_Const4S: push_vals[0].type_kind = RDI_TypeKind_S32; goto const_s; + case DW_ExprOp_Const8S: push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s; + case DW_ExprOp_ConstS : push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s; + const_s:; + { + rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]); + }break; + + //- rjf: address (module offsets) + case DW_ExprOp_Addr: + { + U64 voff = operand_u64s[0] - base_vaddr; + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff); + push_vals[0].type_kind = RDI_TypeKind_U64; + push_vals[0].is_addr = 1; + }break; + case DW_ExprOp_Addrx: + if(unit_parse_ctx->addr_table != 0) + { + U64 addr_idx = operand_u64s[0]; + U64 addr = 0; + if(dw2_try_offset_from_table_idx(unit_parse_ctx->addr_table, addr_idx, &addr)) + { + U64 voff = (addr > base_vaddr) ? (addr - base_vaddr) : 0; + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff); + push_vals[0].type_kind = RDI_TypeKind_U64; + push_vals[0].is_addr = 1; + } + }break; + + //- rjf: register reads + case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: + case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: + case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: + case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: + case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: + case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: + case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: + case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: + case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: + case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: + case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: + { + regcode_dw = (U64)(opcode - DW_ExprOp_Reg0); + }goto reg_read; + case DW_ExprOp_RegX: + { + regcode_dw = operand_u64s[0]; + }goto reg_read; + case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: + case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: + case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: + case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: + case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: + case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: + case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: + case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: + case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: + case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: + case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: + { + regcode_dw = (U64)(opcode - DW_ExprOp_BReg0); + regval_off = operand_s64s[1]; + regread_is_addr = 1; + }goto reg_read; + case DW_ExprOp_BRegX: + { + regcode_dw = operand_u64s[0]; + regval_off = operand_s64s[1]; + regread_is_addr = 1; + }goto reg_read; + reg_read:; + { + // rjf: DWARF regcode -> internal regcode + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, regcode_dw); + + // rjf: internal regcode -> RDI, offset, size + RDI_RegCode regcode_rdi = 0; + U64 reg_off = 0; + U64 reg_size = 0; + { + Rng1U16 reg_rng = arch_info->reg_code_rng_table[regcode]; + reg_size = dim_1u16(reg_rng); + regcode_rdi = arch_rdi_from_reg_code_table_from_arch(arch)[regcode]; + } + + // rjf: push op + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_RegRead, RDI_EncodeRegReadParam(regcode_rdi, reg_size, reg_off)); + + // rjf: push add to value offset, if needed + if(regval_off != 0) + { + rdim_bytecode_push_sconst(arena, &dst_bytecode, regval_off); + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); + } + + // rjf: choose if we're doing a float op + B32 is_float_op = 0; + if(arch == Arch_x64 && + (regcode_rdi == RDI_RegCodeX64_st0 || + regcode_rdi == RDI_RegCodeX64_st1 || + regcode_rdi == RDI_RegCodeX64_st2 || + regcode_rdi == RDI_RegCodeX64_st3 || + regcode_rdi == RDI_RegCodeX64_st4 || + regcode_rdi == RDI_RegCodeX64_st5 || + regcode_rdi == RDI_RegCodeX64_st6 || + regcode_rdi == RDI_RegCodeX64_st7)) + { + is_float_op = 1; + } + + // rjf: set up push value + push_vals[0].is_addr = regread_is_addr; + if(is_float_op) + { + switch(reg_size) + { + default:{}break; + case 2: {push_vals[0].type_kind = RDI_TypeKind_F16;}break; + case 4: {push_vals[0].type_kind = RDI_TypeKind_F32;}break; + case 6: {push_vals[0].type_kind = RDI_TypeKind_F48;}break; + case 8: {push_vals[0].type_kind = RDI_TypeKind_F64;}break; + case 10:{push_vals[0].type_kind = RDI_TypeKind_F80;}break; + case 12:{push_vals[0].type_kind = RDI_TypeKind_F96;}break; + case 16:{push_vals[0].type_kind = RDI_TypeKind_F128;}break; + } + } + else + { + switch(reg_size) + { + default:{}break; + case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break; + case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break; + case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break; + case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break; + } + } + }break; + + //- rjf: implicit values + case DW_ExprOp_ImplicitValue: + { + if(operand_str8s[0].size <= sizeof(U64)) + { + U64 implicit_value = 0; + MemoryCopy(&implicit_value, operand_str8s[0].str, Min(sizeof(U64), operand_str8s[0].size)); + rdim_bytecode_push_uconst(arena, &dst_bytecode, implicit_value); + switch(operand_str8s[0].size) + { + default:{}break; + case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break; + case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break; + case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break; + case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break; + } + } + else + { + log_infof("[.debug_info@0x%I64x] Implicit value DWARF expression operation (DW_ExprOp_ImplicitValue) with block size >8 (%I64u) found. This is not currently supported.\n", start_off, operand_str8s[0].size); + } + }break; + + //- rjf: pieces + case DW_ExprOp_Piece: + { + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValue, operand_u64s[0]); + }break; + case DW_ExprOp_BitPiece: + { + U64 size = operand_u64s[0]; + U64 off = operand_u64s[1]; + U64 partial_value = ((size<<32)|(off)); + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValueBit, partial_value); + }break; + + //- rjf: stack ops + case DW_ExprOp_Over: {target_pick_val_idx = 1;}goto pick; + case DW_ExprOp_Pick: {target_pick_val_idx = operand_u64s[0];}goto pick; + case DW_ExprOp_Dup: {target_pick_val_idx = 0;}goto pick; + pick:; + { + // rjf: pick value from stack + D2R2_ExprVal picked_val = {0}; + { + U64 idx = 0; + for(D2R2_ExprValNode *n = val_stack_top; n != 0; n = n->next) + { + if(idx == target_pick_val_idx) + { + picked_val = n->v; + break; + } + } + } + + // rjf: push opcode + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pick, target_pick_val_idx); + + // rjf: push val + push_vals[0] = picked_val; + }break; + case DW_ExprOp_Swap: + { + push_vals[0] = popped_vals[1]; + push_vals[1] = popped_vals[0]; + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Swap, 0); + }break; + case DW_ExprOp_Drop: + { + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pop, 0); + }break; + + //- rjf: jumps + case DW_ExprOp_Skip: rdi_eval_op = RDI_EvalOp_Skip; goto jumps; + case DW_ExprOp_Bra: rdi_eval_op = RDI_EvalOp_Cond; goto jumps; + jumps:; + { + // rjf: jump delta in bytes (how the instruction is encoded) -> jump delta in instructions + S64 jump_delta_bytes = operand_s64s[0]; + S64 jump_delta_insts = 0; + { + // TODO(rjf): expr op jumps bytes -> inst counts + } + + // rjf: push incomplete op (we need to resolve inst delta -> byte delta later, once we have the full bytecode stream) + RDIM_EvalBytecodeOp *op = rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, 0); + + // rjf: gather op + JumpOpNode *n = push_array(scratch2.arena, JumpOpNode, 1); + SLLQueuePush(first_jump_op, last_jump_op, n); + n->op = op; + n->inst_delta = jump_delta_insts; + }break; + + //- rjf: containing procedure frame offsets + case DW_ExprOp_FBReg: + { + bytecode_is_framebase_dependent = 1; + for EachNode(n, RDIM_EvalBytecodeOp, framebase_loc.bytecode.first_op) + { + rdim_bytecode_push_op(arena, &dst_bytecode, n->op, n->p); + } + rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]); + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); + push_vals[0].type_kind = RDI_TypeKind_U64; + push_vals[0].is_addr = 1; + }break; + + //- rjf: memory reads + case DW_ExprOp_Deref: memread_size = unit_parse_ctx->addr_size; goto deref; + case DW_ExprOp_DerefSize: memread_size = operand_u64s[0]; goto deref; + deref:; + { + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_MemRead, memread_size); + push_vals[0].is_addr = 1; + push_vals[0].type_kind = RDI_TypeKind_U64; + }break; + + //- rjf: TLS offsets + case DW_ExprOp_FormTlsAddress: + case DW_GNU_ExprOp_PushTlsAddress: + { + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_TLSOff, 0); + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U); + push_vals[0].is_addr = 1; + push_vals[0].type_kind = RDI_TypeKind_U64; + is_tls_dependent = 1; + }break; + + //- rjf: call site value + case DW_ExprOp_EntryValue: + case DW_GNU_ExprOp_EntryValue: + { + // TODO(rjf): expr op entry value ops + }break; + + //- rjf: fixed adds + case DW_ExprOp_PlusUConst: + { + rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]); + rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U); + push_vals[0].type_kind = RDI_TypeKind_U64; // TODO(rjf): do we need to adjust this based on popped value at all, even? + }break; + + //- rjf: arithmetic ops + case DW_ExprOp_Eq: {rdi_eval_op = RDI_EvalOp_EqEq;}goto arithmetic_op; + case DW_ExprOp_Ge: {rdi_eval_op = RDI_EvalOp_GrEq;}goto arithmetic_op; + case DW_ExprOp_Gt: {rdi_eval_op = RDI_EvalOp_Grtr;}goto arithmetic_op; + case DW_ExprOp_Le: {rdi_eval_op = RDI_EvalOp_LsEq;}goto arithmetic_op; + case DW_ExprOp_Lt: {rdi_eval_op = RDI_EvalOp_Less;}goto arithmetic_op; + case DW_ExprOp_Ne: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op; + case DW_ExprOp_Div: {rdi_eval_op = RDI_EvalOp_Div;}goto arithmetic_op; + case DW_ExprOp_Minus: {rdi_eval_op = RDI_EvalOp_Sub;}goto arithmetic_op; + case DW_ExprOp_Mul: {rdi_eval_op = RDI_EvalOp_Mul;}goto arithmetic_op; + case DW_ExprOp_Plus: {rdi_eval_op = RDI_EvalOp_Add;}goto arithmetic_op; + case DW_ExprOp_Xor: {rdi_eval_op = RDI_EvalOp_BitXor;}goto arithmetic_op; + case DW_ExprOp_And: {rdi_eval_op = RDI_EvalOp_BitAnd;}goto arithmetic_op; + case DW_ExprOp_Or: {rdi_eval_op = RDI_EvalOp_BitOr;}goto arithmetic_op; + case DW_ExprOp_Shl: {rdi_eval_op = RDI_EvalOp_LShift;}goto arithmetic_op; + case DW_ExprOp_Shr: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op; + case DW_ExprOp_Shra: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op; + case DW_ExprOp_Mod: {rdi_eval_op = RDI_EvalOp_Mod;}goto arithmetic_op; + case DW_ExprOp_Abs: {rdi_eval_op = RDI_EvalOp_Abs;}goto arithmetic_op; + case DW_ExprOp_Neg: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op; + case DW_ExprOp_Not: {rdi_eval_op = RDI_EvalOp_LogNot;}goto arithmetic_op; + arithmetic_op:; + { + // TODO(rjf): eval arithmetic conversions etc. + rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, RDI_EvalTypeGroup_U); + }break; + + //- rjf: currently unsupported + case DW_ExprOp_XDeref: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression XDeref operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_XDerefSize: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression XDerefSize operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_Call2: + case DW_ExprOp_Call4: + case DW_ExprOp_CallRef: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression call operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_ImplicitPointer: + case DW_GNU_ExprOp_ImplicitPointer: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression implicit pointer operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_GNU_ExprOp_ParameterRef: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression GNU_ParameterRef operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_DerefType: + case DW_GNU_ExprOp_DerefType: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression DerefType operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_ConstType: + case DW_GNU_ExprOp_ConstType: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression ConstType operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_RegvalType: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression RegvalType operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_PushObjectAddress: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression PushObjectAddress operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_ExprOp_Rot: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression Rot (rotate) operation encountered. This is not supported for location info.\n", start_off); + }break; + case DW_GNU_ExprOp_UnInit: + { + dst_bytecode_is_good = 0; + log_infof("[.debug_info@0x%I64x] DWARF expression GNU_UnInit operation encountered. This is not supported for location info.\n", start_off); + // TODO: flag value as unitialized; this must be last opcode; possible to use with DW_ExprOp_Piece; + } break; + } + + //- rjf: push values to stack + { + push_count = Min(push_count, ArrayCount(push_vals)); + for EachIndex(push_idx, push_count) + { + D2R2_ExprValNode *n = free_val; + if(n != 0) + { + SLLStackPop(free_val); + } + else + { + n = push_array(scratch2.arena, D2R2_ExprValNode, 1); + } + n->v = push_vals[push_idx]; + SLLStackPush(val_stack_top, n); + } + } + + if(expr_off == start_expr_off) + { + break; + } + } + + //- rjf: apply RDI byte offsets to jump ops + { + // TODO(rjf): apply RDI byte offsets to jump ops + } + + //- rjf: map bytecode -> RDIM_Location - we may want to simplify + RDIM_Location loc = {0}; + { + loc.kind = val_stack_top && val_stack_top->v.is_addr ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; + loc.bytecode = dst_bytecode; + + // rjf: gather first few bytecodes for pattern matching + RDI_EvalOp beginning_ops[5] = {0}; + RDIM_EvalBytecodeOp *beginning_nodes[5] = {0}; + { + U64 idx = 0; + for EachNode(n, RDIM_EvalBytecodeOp, dst_bytecode.first_op) + { + if(idx >= ArrayCount(beginning_ops)) + { + break; + } + beginning_ops[idx] = n->op; + beginning_nodes[idx] = n; + idx += 1; + } + } + + // rjf: match val registers + if(loc.kind == RDI_LocationKind_ValBytecodeStream && + beginning_ops[0] == RDI_EvalOp_RegRead && + beginning_ops[1] == RDI_EvalOp_Stop) + { + U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0; + U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8; + U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16; + if(byte_size == unit_parse_ctx->addr_size && + byte_off == 0) + { + loc.kind = RDI_LocationKind_ValReg; + loc.reg_code = rdi_reg_code; + } + } + + // rjf: match simple register offsets + if(loc.kind == RDI_LocationKind_AddrBytecodeStream && + beginning_ops[0] == RDI_EvalOp_RegRead && + (beginning_ops[1] == RDI_EvalOp_ConstU8 || + beginning_ops[1] == RDI_EvalOp_ConstU16) && + beginning_ops[2] == RDI_EvalOp_TruncSigned && + beginning_ops[3] == RDI_EvalOp_Add && + beginning_ops[4] == RDI_EvalOp_Stop) + { + U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0; + U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8; + U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16; + if(byte_off == 0 && byte_size == unit_parse_ctx->addr_size) + { + loc.kind = RDI_LocationKind_AddrRegPlusOff; + loc.reg_code = rdi_reg_code; + loc.offset = beginning_nodes[1]->p; + } + } + + // rjf: match simple module offsets + if(beginning_ops[0] == RDI_EvalOp_ModuleOff && beginning_ops[1] == RDI_EvalOp_Stop) + { + U64 voff = beginning_nodes[0]->p; + loc.kind = RDI_LocationKind_ModuleOff; + loc.offset = voff; + } + else if(((beginning_ops[0] == RDI_EvalOp_ModuleOff && + (beginning_ops[1] == RDI_EvalOp_ConstU8 || + beginning_ops[1] == RDI_EvalOp_ConstU16 || + beginning_ops[1] == RDI_EvalOp_ConstU32 || + beginning_ops[1] == RDI_EvalOp_ConstU64)) || + (beginning_ops[1] == RDI_EvalOp_ModuleOff && + (beginning_ops[0] == RDI_EvalOp_ConstU8 || + beginning_ops[0] == RDI_EvalOp_ConstU16 || + beginning_ops[0] == RDI_EvalOp_ConstU32 || + beginning_ops[0] == RDI_EvalOp_ConstU64))) && + beginning_ops[2] == RDI_EvalOp_Add && + beginning_ops[3] == RDI_EvalOp_Stop) + { + U64 voff = beginning_nodes[0]->p + beginning_nodes[1]->p; + loc.kind = RDI_LocationKind_ModuleOff; + loc.offset = voff; + } + + // rjf: match simple TLS offsets + if(beginning_ops[0] == RDI_EvalOp_TLSOff && beginning_ops[1] == RDI_EvalOp_Stop) + { + U64 toff = beginning_nodes[0]->p; + loc.kind = RDI_LocationKind_TLSOff; + loc.offset = toff; + } + else if(((beginning_ops[0] == RDI_EvalOp_TLSOff && + (beginning_ops[1] == RDI_EvalOp_ConstU8 || + beginning_ops[1] == RDI_EvalOp_ConstU16 || + beginning_ops[1] == RDI_EvalOp_ConstU32 || + beginning_ops[1] == RDI_EvalOp_ConstU64)) || + (beginning_ops[1] == RDI_EvalOp_TLSOff && + (beginning_ops[0] == RDI_EvalOp_ConstU8 || + beginning_ops[0] == RDI_EvalOp_ConstU16 || + beginning_ops[0] == RDI_EvalOp_ConstU32 || + beginning_ops[0] == RDI_EvalOp_ConstU64))) && + beginning_ops[2] == RDI_EvalOp_Add && + beginning_ops[3] == RDI_EvalOp_Stop) + { + U64 toff = beginning_nodes[0]->p + beginning_nodes[1]->p; + loc.kind = RDI_LocationKind_TLSOff; + loc.offset = toff; + } + } + + //- rjf: collect + { + RDIM_Rng1U64 voff_range = {range.min - base_vaddr, range.max - base_vaddr}; + if(bytecode_is_framebase_dependent) + { + voff_range.min = Max(voff_range.min, framebase_voff_range.min); + voff_range.max = Min(voff_range.max, framebase_voff_range.max); + } + rdim_location_case_list_push(arena, dst_locations, loc, voff_range); + dst_is_tls_dependent[0] = is_tls_dependent; + } + + //- rjf: not frame-base dependent? -> break - don't do this per-framebase case + if(!bytecode_is_framebase_dependent) + { + break; + } + } + } + } + } + + //////////////////////// + //- rjf: produce symbols from tag + // + RDIM_Symbol *new_symbol = 0; + RDIM_Scope *new_scope_open = 0; + B32 parent_tag_is_container_to_deduped_tags = 0; + switch(tag.kind) + { + default:{}break; + + //- rjf: determine if parent tag is a container to deduplicated tags + case DW_TagKind_Namespace: + case DW_TagKind_StructureType: + case DW_TagKind_UnionType: + case DW_TagKind_ClassType: + case DW_TagKind_EnumerationType: + case DW_TagKind_Typedef: + { + parent_tag_is_container_to_deduped_tags = 1; + }break; + + //- rjf: subprograms (procedures) + case DW_TagKind_SubProgram: + if(!is_decl) + { + RDIM_Scope *root_scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count); + RDIM_Symbol *procedure = rdim_symbol_chunk_list_push(arena, &dst_artifacts->procedures, chunk_count); + procedure->name = name; + procedure->link_name = link_name; + procedure->type = type; + procedure->root_scope = root_scope; + procedure->location_cases = framebase_location_cases; + root_scope->symbol = procedure; + root_scope->voff_ranges = ranges; + dst_artifacts->scopes.scope_voff_count += 2*ranges.count; + new_symbol = procedure; + new_scope_open = root_scope; + }break; + + //- rjf: inline site + case DW_TagKind_InlinedSubroutine: + { + // TODO(rjf) + }break; + + //- rjf: variables + case DW_TagKind_Variable: + case DW_TagKind_FormalParameter: + if(name.size != 0) + { + U64 var_chunk_count = chunk_count; + RDIM_SymbolChunkList *dst_symbols = &dst_artifacts->global_variables; + if(location_is_tls_dependent) + { + dst_symbols = &dst_artifacts->thread_variables; + } + else if(top_parent != 0 && top_parent->scope != 0) + { + dst_symbols = &top_parent->scope->locals; + var_chunk_count = 8; + } + RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, dst_symbols, var_chunk_count); + var->is_extern = is_external; + var->is_param = (tag.kind == DW_TagKind_FormalParameter); + var->name = name; + var->link_name = link_name; + var->type = type; + var->location_cases = location_cases; + new_symbol = var; + }break; + + //- rjf: lexical blocks (scopes) + case DW_TagKind_LexicalBlock: + { + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count); + scope->voff_ranges = ranges; + dst_artifacts->scopes.scope_voff_count += 2*ranges.count; + new_scope_open = scope; + }break; + } + + //////////////////////// + //- rjf: if we've determined that the parent tag is a container to + // deduplicated tags (types, namespaces), then we need to record + // the .debug_info offset -> scope mapping for this scope, so it + // can be equipped as a container later. + // + if(parent_tag_is_container_to_deduped_tags) + { + U64 info_off = 0; + RDIM_Scope *scope = 0; + for(D2R2_ParentNode *n = top_parent; n != 0; n = n->next) + { + if(n->scope != 0) + { + info_off = n->info_off; + scope = n->scope; + break; + } + } + if(scope != 0) + { + D2R2_ScopeContainerNode *n = push_array(scratch.arena, D2R2_ScopeContainerNode, 1); + n->info_off = info_off; + n->scope = scope; + U64 hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 slot_idx = hash%scope_container_map->slots_count; + for(B32 gathered = 0; !gathered;) + { + U64 expected_head_value = ins_atomic_u64_eval(&scope_container_map->slots[slot_idx]); + n->next = (D2R2_ScopeContainerNode *)expected_head_value; + if(expected_head_value == ins_atomic_u64_eval_cond_assign(&scope_container_map->slots[slot_idx], (U64)n, expected_head_value)) + { + gathered = 1; + } + } + } + } + + //////////////////////// + //- rjf: equip new symbols with container info + // + if(new_symbol != 0 && top_parent != 0) + { + if(top_parent->scope != 0) + { + new_symbol->container_scope = top_parent->scope; + } + else if(top_parent->container_type != 0) + { + new_symbol->container_type = top_parent->container_type; + } + else if(top_parent->container_namespace != 0) + { + new_symbol->container_namespace = top_parent->container_namespace; + } + } + + //////////////////////// + //- rjf: insert new scopes to their parent + // + if(new_scope_open && top_parent != 0 && top_parent->scope != 0) + { + RDIM_Scope *parent = top_parent->scope; + SLLQueuePush_N(parent->first_child, parent->last_child, new_scope_open, next_sibling); + new_scope_open->parent_scope = parent; + if(new_scope_open->symbol == 0) + { + new_scope_open->symbol = parent->symbol; + } + } + + //////////////////////// + //- rjf: gather container info for new tag parents + // + RDIM_Type *container_type = 0; + RDIM_Namespace *container_namespace = 0; + if(tag.has_children) + { + // rjf: info offset -> hash + U64 hash = 0; + { + D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; + for EachNode(n, D2R2_UnitDedupedTagNode, unit_deduped_tag_map->slots[info_off_slot_idx]) + { + if(n->src_info_off == start_off) + { + hash = n->dst_hash; + break; + } + } + } + + // rjf: map hash -> unique tag node + D2R2_UniqueTagNode *tag_node = 0; + { + U64 slot_idx = hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next) + { + if(n->hash == hash) + { + tag_node = n; + break; + } + } + } + + // rjf: map tag node -> type/namespace + if(tag_node != 0) switch(tag_node->kind) + { + default:{}break; + case D2R2_UniqueTagKind_Type: {container_type = type_from_idx_map[tag_node->order_idx];}break; + case D2R2_UniqueTagKind_Namespace:{container_namespace = namespace_from_idx_map[tag_node->order_idx];}break; + } + } + + //////////////////////// + //- rjf: push tag parents + // + if(tag.has_children) + { + // rjf: push new parent to stack + D2R2_ParentNode *n = free_parent; + if(n != 0) + { + SLLStackPop(free_parent); + } + else + { + n = push_array(scratch.arena, D2R2_ParentNode, 1); + } + n->tag_kind = tag.kind; + n->info_off = start_off; + n->scope = new_scope_open ? new_scope_open : top_parent ? top_parent->scope : 0; + n->container_type = new_scope_open ? 0 : container_type; + n->container_namespace = new_scope_open ? 0 : container_namespace; + if(framebase_attrib != &dw2_attrib_nil) + { + n->framebase_location_cases = framebase_location_cases; + } + else if(top_parent != 0) + { + n->framebase_location_cases = top_parent->framebase_location_cases; + } + SLLStackPush(top_parent, n); + } + + //////////////////////// + //- rjf: pop scopes + // + if(tag.kind == DW_TagKind_Null && top_parent != 0) + { + D2R2_ParentNode *n = top_parent; + SLLStackPop(top_parent); + SLLStackPush(free_parent, n); + } + + //////////////////////// + //- rjf: tree nav + // + if(tag.has_children) + { + depth += 1; + } + if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + + scratch_end(scratch2); + if(off == start_off) + { + break; + } + } + } + } + lane_sync(); + } + + //////////////////////////// + //- rjf: join all symbols from all sub-unit works into their units + // + ProfScope("join all symbols from all sub-unit works into their units") + { + U64 unit_take_idx_ = 0; + U64 *unit_take_idx_ptr = &unit_take_idx_; + lane_sync_u64(&unit_take_idx_ptr, 0); + for(;;) + { + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; + if(unit_idx >= unit_count) + { + break; + } + RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx]; + for EachIndex(work_idx, sub_unit_works_count) + { + if(sub_unit_works[work_idx].unit_idx == unit_idx) + { + rdim_symbol_chunk_list_concat_in_place(&dst_unit->global_variables, &sub_unit_work_artifacts[work_idx].global_variables); + rdim_symbol_chunk_list_concat_in_place(&dst_unit->thread_variables, &sub_unit_work_artifacts[work_idx].thread_variables); + rdim_symbol_chunk_list_concat_in_place(&dst_unit->constants, &sub_unit_work_artifacts[work_idx].constants); + rdim_symbol_chunk_list_concat_in_place(&dst_unit->procedures, &sub_unit_work_artifacts[work_idx].procedures); + rdim_scope_chunk_list_concat_in_place(&dst_unit->scopes, &sub_unit_work_artifacts[work_idx].scopes); + rdim_inline_site_chunk_list_concat_in_place(&dst_unit->inline_sites, &sub_unit_work_artifacts[work_idx].inline_sites); + } + } + } + lane_sync(); + } + + //////////////////////////// + //- rjf: upgrade all types with namespacing info + // + ProfScope("upgrade all types with namespacing info") + { + Rng1U64 range = lane_range(type_count); + for EachInRange(type_idx, range) + { + D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; + U64 container_ancestor_info_off = type_tag_node->container_ancestor_info_off; + RDIM_Type *dst_type = type_from_idx_map[type_idx]; + if(dst_type == 0) + { + continue; + } + RDIM_UDT *dst_udt = dst_type->udt; + if(dst_udt == 0) + { + continue; + } + + // rjf: find container scopes + RDIM_Scope *container_scope = 0; + { + U64 info_off = container_ancestor_info_off; + U64 hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 slot_idx = hash%scope_container_map->slots_count; + for(D2R2_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next) + { + if(n->info_off == info_off) + { + container_scope = n->scope; + break; + } + } + } + + // rjf: find container types/namespaces + RDIM_Type *container_type = 0; + RDIM_Namespace *container_namespace = 0; + if(!container_scope) + { + U64 info_off = container_ancestor_info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; + + // rjf: info off -> hash + U64 hash = 0; + { + D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; + for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == info_off) + { + hash = n->dst_hash; + break; } } } - // update line sequence counts per source file @rdim_src_file_push_line_sequence - for EachIndex(file_idx, vm->header.file_table.count) { - if (line_seq_counts_per_file[file_idx] > 0) { - DW_LineFile *file = &vm->header.file_table.v[file_idx]; - U64 hash = d2r_hash_line_file(vm->header.dir_table.v[file->dir_idx], file); - D2R_SrcFileLookup *lookup = lfht_search(src_file_lfht, hash, &(D2R_SrcFileLookup){ .file = file, .vm = vm }, d2r_src_file_lookup_is_equal, 0); - RDIM_SrcFile *src_file = lookup->src_file; - ins_atomic_u64_add_eval(&src_file->total_line_count, line_seq_counts_per_file[file_idx]); + // rjf: hash -> type + { + U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == hash) + { + if(n->kind == D2R2_UniqueTagKind_Type) + { + container_type = type_from_idx_map[n->order_idx]; + } + else if(n->kind == D2R2_UniqueTagKind_Namespace) + { + container_namespace = namespace_from_idx_map[n->order_idx]; + } + break; + } } } } - // TODO: sync - // - // update total line sequences count in shared @rdim_src_file_push_line_sequence - for EachIndex(i, lane_count()) { - g_d2r_shared.src_files.total_line_count += line_seq_counts_per_lane[i]; - } - - // TODO: sync - for EachIndex(i, lane_count()) { - rdim_line_table_chunk_list_concat_in_place(&g_d2r_shared.line_tables, &lane_line_table_chunks[i]); - } - - // TODO: per thread task - ProfBegin("Relase line VMs"); - for EachIndex(i, cu_ranges.count) { - dw_line_vm_release(line_vms[i]); - } - ProfEnd(); - - temp_end(temp); + // rjf: fill + dst_udt->container_scope = container_scope; + dst_udt->container_type = container_type; + dst_udt->container_namespace = container_namespace; } - - //////////////////////////////// - - RDIM_Type *builtin_types[RDI_TypeKind_COUNT] = {0}; - for (RDI_TypeKind type_kind = RDI_TypeKind_FirstBuiltIn; type_kind <= RDI_TypeKind_LastBuiltIn; type_kind += 1) { - RDIM_Type *type = rdim_type_chunk_list_push(arena, &g_d2r_shared.types, D2R_TYPE_CHUNK_CAP); - type->kind = type_kind; - type->name.str = rdi_string_from_type_kind(type_kind, &type->name.size); - type->byte_size = rdi_size_from_basic_type_kind(type_kind); - builtin_types[type_kind] = type; - } - - // fixup float80 size - if (arch == Arch_x64 || arch == Arch_arm64) { - builtin_types[RDI_TypeKind_F80]->byte_size = 16; - } - - builtin_types[RDI_TypeKind_Void]->byte_size = rdi_addr_size_from_arch(g_d2r_shared.top_level_info.arch); - builtin_types[RDI_TypeKind_Handle]->byte_size = rdi_addr_size_from_arch(g_d2r_shared.top_level_info.arch); - - builtin_types[RDI_TypeKind_Variadic] = rdim_type_chunk_list_push(arena, &g_d2r_shared.types, D2R_TYPE_CHUNK_CAP); - builtin_types[RDI_TypeKind_Variadic]->kind = RDI_TypeKind_Variadic; - - //////////////////////////////// - - ProfBegin("Convert Units"); - for EachIndex(cu_idx, cu_ranges.count) { - Temp comp_temp = temp_begin(scratch.arena); - - DW_CompUnit *cu = &cu_arr[cu_idx]; - - // skip DWO - { - if (cu->dwo_id) { goto next_cu; } - - String8 dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_DwoName); - if (dwo_name.size) { goto next_cu; } - - String8 gnu_dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_GNU_DwoName); - if (gnu_dwo_name.size) { goto next_cu; } - } - - // parse and build tag tree - DW_TagTree tag_tree = dw_tag_tree_from_cu(comp_temp.arena, &input, cu); - - // build (info offset -> tag) hash table to resolve tags with abstract origin - cu->tag_ht = dw_make_tag_hash_table(comp_temp.arena, tag_tree); - - // extract compile unit info - String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); - String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); - String8 cu_prod = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Producer); - DW_Language cu_lang = dw_const_u64_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Language); - - // init type table - D2R_TypeTable *type_table = push_array(comp_temp.arena, D2R_TypeTable, 1); - type_table->ht = hash_table_init(comp_temp.arena, 0x4000); - type_table->types = &g_d2r_shared.types; - type_table->type_chunk_cap = D2R_TYPE_CHUNK_CAP; - type_table->builtin_types = builtin_types; - - // convert compile unit - RDIM_Unit *unit = rdim_unit_chunk_list_push(arena, &g_d2r_shared.units, D2R_UNIT_CHUNK_CAP); - { - RDIM_Rng1U64ChunkList cu_voff_ranges = {0}; - if (cu_idx < cu_contrib_map.count) { - cu_voff_ranges = d2r_voff_ranges_from_cu_info_off(cu_contrib_map, cu_ranges.v[cu_idx].min); - } else { - Rng1U64List range_list = d2r_range_list_from_tag(scratch.arena, &input, cu, image_base, cu->tag); - for EachNode(n, Rng1U64Node, range_list.first) { - rdim_rng1u64_chunk_list_push(arena, &cu_voff_ranges, 512, (RDIM_Rng1U64){ .min = n->v.min, .max = n->v.max }); - } - } - unit->unit_name = cu_name; - unit->compiler_name = cu_prod; - unit->source_file = str8_zero(); // TODO - unit->object_file = str8_zero(); // TODO - unit->archive_file = str8_zero(); // TODO - unit->build_path = cu_dir; - unit->language = d2r_rdi_language_from_dw_language(cu_lang); - unit->line_table = cu_line_tables_rdi[cu_idx]; - unit->voff_ranges = cu_voff_ranges; - } - - // convert debug info - d2r_convert_types(arena, type_table, &input, cu, cu_lang, arch, tag_tree.root); - d2r_convert_udts(arena, type_table, &input, cu, cu_lang, tag_tree.root); - d2r_convert_symbols(arena, type_table, &input, cu, cu_lang, image_base, arch, tag_tree.root, unit); - - next_cu:; - temp_end(comp_temp); - } - ProfEnd(); } - lane_sync(); - RDIM_BakeParams bake_params = {0}; - bake_params.subset_flags = params->subset_flags; - bake_params.top_level_info = g_d2r_shared.top_level_info; - bake_params.binary_sections = g_d2r_shared.binary_sections; - bake_params.units = g_d2r_shared.units; - bake_params.types = g_d2r_shared.types; - bake_params.udts = g_d2r_shared.udts; - bake_params.src_files = g_d2r_shared.src_files; - bake_params.line_tables = g_d2r_shared.line_tables; + //////////////////////////// + //- rjf: upgrade all namespaces with namespacing info + // + ProfScope("upgrade all namespaces with namespacing info") + { + Rng1U64 range = lane_range(namespace_count); + for EachInRange(namespace_idx, range) + { + D2R2_UniqueTagNode *unique_tag_node = namespace_tag_nodes[namespace_idx]; + U64 container_ancestor_info_off = unique_tag_node->container_ancestor_info_off; + RDIM_Namespace *dst_namespace = namespace_from_idx_map[namespace_idx]; + if(dst_namespace == 0) + { + continue; + } + + // rjf: find container scopes + RDIM_Scope *container_scope = 0; + { + U64 info_off = container_ancestor_info_off; + U64 hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 slot_idx = hash%scope_container_map->slots_count; + for(D2R2_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next) + { + if(n->info_off == info_off) + { + container_scope = n->scope; + break; + } + } + } + + // rjf: find container types/namespaces + RDIM_Type *container_type = 0; + RDIM_Namespace *container_namespace = 0; + { + U64 info_off = container_ancestor_info_off; + U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); + U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; + + // rjf: info off -> hash + U64 hash = 0; + { + D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; + U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off)); + U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; + for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) + { + if(n->src_info_off == info_off) + { + hash = n->dst_hash; + break; + } + } + } + + // rjf: hash -> type + { + U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count; + for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) + { + if(n->hash == hash) + { + if(n->kind == D2R2_UniqueTagKind_Type) + { + container_type = type_from_idx_map[n->order_idx]; + } + else if(n->kind == D2R2_UniqueTagKind_Namespace) + { + container_namespace = namespace_from_idx_map[n->order_idx]; + } + break; + } + } + } + } + + // rjf: fill + dst_namespace->parent_scope = container_scope; + dst_namespace->parent_type = container_type; + dst_namespace->parent_namespace = container_namespace; + } + } + lane_sync(); + //////////////////////////// + //- rjf: fill result + // + RDIM_BakeParams result = {0}; + { + result.subset_flags = params->subset_flags; + result.top_level_info = top_level_info; + result.binary_sections = binary_sections; + result.units = *all_units; + result.namespaces = *all_namespaces; + result.types = *all_types; + result.udts = *all_udts; + result.src_files = *all_src_files; + result.line_tables = *all_line_tables; + } + +#undef d2r2_type_from_builtin_kind + lane_sync(); scratch_end(scratch); - return bake_params; + return result; } - diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.h b/src/rdi_from_dwarf/rdi_from_dwarf.h index 550162cc..6f16e3e4 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.h +++ b/src/rdi_from_dwarf/rdi_from_dwarf.h @@ -5,271 +5,67 @@ #define RDI_FROM_DWARF_H //////////////////////////////// -//~ Conversion +//~ rjf: Unique Tag Tree Deduplication Types -typedef struct D2R_ConvertParams +typedef enum D2R2_UniqueTagKind { - String8 dbg_name; - String8 dbg_data; - String8 exe_name; - String8 exe_data; - ExecutableImageKind exe_kind; - RDIM_SubsetFlags subset_flags; - B32 deterministic; - B32 is_parse_relaxed; -} D2R_ConvertParams; + D2R2_UniqueTagKind_Type, + D2R2_UniqueTagKind_Namespace, + D2R2_UniqueTagKind_COUNT +} +D2R2_UniqueTagKind; -#define D2R_UNIT_CHUNK_CAP 512 -#define D2R_UDT_CHUNK_CAP 1024 -#define D2R_TYPE_CHUNK_CAP 1024 -#define D2R_SRC_FILE_CAP 1024 -#define D2R_LINE_TABLE_CAP 1024 -#define D2R_GVAR_CHUNK_CAP 1024 -#define D2R_TVAR_CHUNK_CAP 1024 -#define D2R_LOCATIONS_CAP 4096 -#define D2R_PROC_CHUNK_CAP 4096 -#define D2R_SCOPE_CHUNK_CAP 4096 -#define D2R_INLINE_SITE_CHUNK_CAP 4096 - -typedef struct D2R_Shared +typedef struct D2R2_UniqueTagNode D2R2_UniqueTagNode; +struct D2R2_UniqueTagNode { - RDIM_TopLevelInfo top_level_info; - RDIM_BinarySectionList binary_sections; - RDIM_UnitChunkList units; - RDIM_UDTChunkList udts; - RDIM_TypeChunkList types; - RDIM_SrcFileChunkList src_files; - RDIM_LineTableChunkList line_tables; -} D2R_Shared; - -//////////////////////////////// -//~ Value Types - -typedef enum -{ - D2R_ArithmeticType_Null, - D2R_ArithmeticType_Signed, - D2R_ArithmeticType_Unsigned, - D2R_ArithmeticType_Float, -} D2R_ArithmeticType; - -#define D2R_ValueType_Signed_XList \ -X(S8, Signed, 1 ) \ -X(S16, Signed, 2 ) \ -X(S32, Signed, 4 ) \ -X(S64, Signed, 8 ) \ -X(S128, Signed, 16) \ -X(S256, Signed, 32) \ -X(S512, Signed, 64) - -#define D2R_ValueType_Unsigned_XList \ -X(U8, Unsigned, 1 ) \ -X(U16, Unsigned, 2 ) \ -X(U32, Unsigned, 4 ) \ -X(U64, Unsigned, 8 ) \ -X(U128, Unsigned, 16) \ -X(U256, Unsigned, 32) \ -X(U512, Unsigned, 64) - -#define D2R_ValueType_Float_XList \ -X(F16, Float, 2 ) \ -X(F32, Float, 4 ) \ -X(F48, Float, 6 ) \ -X(F64, Float, 8 ) \ -X(F80, Float, 10) \ -X(F96, Float, 12) \ -X(F128, Float, 16) - -#define D2R_ValueType_XList \ -X(Generic, Null, 0 ) \ -X(ImplicitValue, Null, 0 ) \ -X(Address, Unsigned, 0 ) \ -D2R_ValueType_Signed_XList \ -D2R_ValueType_Unsigned_XList \ -D2R_ValueType_Float_XList - -typedef enum -{ -#define X(t, ...) D2R_ValueType_##t, - D2R_ValueType_XList -#undef X - D2R_ValueType_Bool = D2R_ValueType_S8, -} D2R_ValueType; - -typedef struct D2R_ValueTypeNode -{ - D2R_ValueType type; - struct D2R_ValueTypeNode *next; -} D2R_ValueTypeNode; - -typedef struct D2R_ValueTypeStack -{ - U64 count; - D2R_ValueTypeNode *top; - D2R_ValueTypeNode *free_list; -} D2R_ValueTypeStack; - -//////////////////////////////// -//~ Type Table - -typedef struct D2R_TypeTable -{ - HashTable *ht; - RDIM_TypeChunkList *types; - U64 type_chunk_cap; - RDIM_Type **builtin_types; -} D2R_TypeTable; - -//////////////////////////////// -//~ Tag Iterator - -typedef DW_TagSpare D2R_TagFlags; -enum -{ - D2R_TagFlags_TypeConverted = (1 << 0), - D2R_TagFlags_UdtConverted = (1 << 1), + D2R2_UniqueTagNode *next; + D2R2_UniqueTagKind kind; + U64 hash; + U64 info_off; + U64 container_ancestor_info_off; + U64 order_idx; }; -typedef struct D2R_TagFrame +typedef struct D2R2_UnitDedupedTagNode D2R2_UnitDedupedTagNode; +struct D2R2_UnitDedupedTagNode { - DW_TagNode *node; - RDIM_Scope *scope; - struct D2R_TagFrame *next; -} D2R_TagFrame; + D2R2_UnitDedupedTagNode *next; + U64 src_info_off; + U64 dst_hash; +}; -typedef struct D2R_TagIterator +typedef struct D2R2_UnitDedupedTagMap D2R2_UnitDedupedTagMap; +struct D2R2_UnitDedupedTagMap { - D2R_TagFrame *free_list; - D2R_TagFrame *stack; - DW_TagNode *null_tag_node; - DW_TagNode *tag_node; - B32 visit_children; -} D2R_TagIterator; + D2R2_UnitDedupedTagNode **slots; + U64 slots_count; +}; //////////////////////////////// -//~ Line Table Conversion +//~ rjf: Conversion Stage Inputs (New) -typedef struct D2R_SrcFileLookup +typedef struct D2R2_ConvertParams D2R2_ConvertParams; +struct D2R2_ConvertParams { - DW_LineFile *file; - DW_LineVM *vm; - RDIM_SrcFile *src_file; -} D2R_SrcFileLookup; + String8 exe_name; + String8 exe_data; + Arch arch; + U64 base_vaddr; + RDIM_BinarySectionList binary_sections; + DW_Raw raw; + PathStyle path_style; + RDIM_SubsetFlags subset_flags; + B32 deterministic; +}; //////////////////////////////// -//~ Contrib Map +//~ rjf: Helpers -typedef struct D2R_CompUnitContribMap -{ - U64 count; - U64 *info_off_arr; - RDIM_Rng1U64ChunkList *voff_range_arr; -} D2R_CompUnitContribMap; +internal int d2r2_unique_tag_node_is_less_than(D2R2_UniqueTagNode **l, D2R2_UniqueTagNode **r); //////////////////////////////// -//~ RDIM Bytecode Extensions +//~ rjf: Main Conversion Entry Point (New) -internal B32 rdim_is_eval_bytecode_static (RDIM_EvalBytecode bc); -internal B32 rdim_static_eval_bytecode_to_voff(RDIM_EvalBytecode bc, U64 image_base, U64 *voff_out); - -//////////////////////////////// -//~ DWARF -> RDI Enums - -internal RDI_Language d2r_rdi_language_from_dw_language(DW_Language v); -internal RDI_RegCodeX64 d2r_rdi_reg_code_from_dw_reg_x64 (DW_RegX64 v); -internal RDI_RegCode d2r_rdi_reg_code_from_dw_reg (Arch arch, DW_Reg v); - -//////////////////////////////// -//~ Value Type - -internal D2R_ValueTypeNode * d2r_value_type_stack_push(Arena *arena, D2R_ValueTypeStack *stack, D2R_ValueType type); -internal D2R_ValueType d2r_value_type_stack_pop (D2R_ValueTypeStack *stack); -internal D2R_ValueType d2r_value_type_stack_peek(D2R_ValueTypeStack *stack); - -internal D2R_ValueType d2r_signed_value_type_from_bit_size (U64 bit_size); -internal D2R_ValueType d2r_unsigned_value_type_from_bit_size(U64 bit_size); -internal D2R_ValueType d2r_float_type_from_bit_size (U64 bit_size); - -internal U64 d2r_size_from_value_type(U64 addr_size, D2R_ValueType value_type); -internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v); -internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v); -internal B32 d2r_is_value_type_signed (D2R_ValueType v); -internal B32 d2r_is_value_type_integral(D2R_ValueType v); -internal B32 d2r_is_value_type_unsigned(D2R_ValueType v); -internal B32 d2r_is_value_type_float (D2R_ValueType v); - -internal D2R_ValueType d2r_pick_common_value_type(D2R_ValueType lhs, D2R_ValueType rhs); -internal RDI_EvalTypeGroup d2r_value_type_to_rdi(D2R_ValueType v); - -internal D2R_ValueType d2r_apply_usual_arithmetic_conversions(Arena *arena, D2R_ValueType lhs, D2R_ValueType rhs, RDIM_EvalBytecode *bc); -internal void d2r_push_arithmetic_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op); -internal void d2r_push_relational_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op); - -//////////////////////////////// -//~ Expression Conversion - -internal RDIM_EvalBytecode d2r_bytecode_from_expression(Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, String8 expr, DW_CompUnit *cu, D2R_ValueType *result_type_out); -internal RDIM_Location d2r_transpile_expression (Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr); -internal RDIM_Location d2r_location_from_attrib (Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); -internal RDIM_LocationCaseList d2r_locset_from_attrib (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); -internal RDIM_LocationCaseList d2r_var_locset_from_tag (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag); - -//////////////////////////////// -//~ Type Table - -internal RDIM_Type * d2r_create_type (Arena *arena, D2R_TypeTable *type_table); -internal RDIM_Type * d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off); -internal RDIM_Type * d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off); -internal RDIM_Type * d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind); - -//////////////////////////////// -//~ Tag Iterator - -internal D2R_TagIterator * d2r_tag_iterator_init(Arena *arena, DW_TagNode *root); -internal void d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter); -internal void d2r_tag_iterator_skip_children (D2R_TagIterator *iter); -internal DW_TagNode * d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter); -internal DW_Tag d2r_tag_iterator_parent_tag (D2R_TagIterator *iter); - -//////////////////////////////// -//~ Type Conversion - -internal void d2r_flag_converted_tag(DW_TagNode *tag_node); -internal B8 d2r_is_tag_converted (DW_TagNode *tag_node); -internal RDIM_Type * d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_Tag tag, DW_AttribKind kind); -internal void d2r_convert_types(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_TagNode *root); - -//////////////////////////////// -//~ UDT Conversion - -internal B8 d2r_is_udt_tag_converted (DW_TagNode *tag_node); -internal void d2r_flag_converted_udt_tag(DW_TagNode *tag_node); -internal void d2r_inline_anonymous_udt_member(Arena *arena, RDIM_UDT *top_udt, U64 base_off, RDIM_UDT *udt); -internal void d2r_convert_udts(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, DW_TagNode *root); - -//////////////////////////////// -//~ Symbol Conversion - -internal RDIM_Scope * d2r_push_scope (Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges); -internal RDIM_Type ** d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out); -internal Rng1U64List d2r_range_list_from_tag(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag); -internal void d2r_convert_symbols(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, U64 image_base, Arch arch, DW_TagNode *root, RDIM_Unit *unit_rdi); - -//////////////////////////////// -//~ Line Table Conversion - -internal U64 d2r_hash_line_file(String8 dir_path, DW_LineFile *file); -internal void d2r_sort_ptrs(void **ptrs, U64 count, int (* is_before)(void *a, void *b)); - -//////////////////////////////// -//~ Contrib Map - -internal D2R_CompUnitContribMap d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Raw *input, U64 image_base); -internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off); - -//////////////////////////////// -//~ Entry Point - -internal RDIM_BakeParams d2r_convert(Arena *arena, D2R_ConvertParams *params); +internal RDIM_BakeParams d2r2_convert(Arena *arena, D2R2_ConvertParams *params); #endif // RDI_FROM_DWARF_H diff --git a/src/rdi_from_dwarf/rdi_from_dwarf_2.c b/src/rdi_from_dwarf/rdi_from_dwarf_2.c deleted file mode 100644 index 61ed73bc..00000000 --- a/src/rdi_from_dwarf/rdi_from_dwarf_2.c +++ /dev/null @@ -1,4392 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Helpers - -internal int -d2r2_unique_tag_node_is_less_than(D2R2_UniqueTagNode **l, D2R2_UniqueTagNode **r) -{ - int is_less_than = (l[0]->hash < r[0]->hash); - return is_less_than; -} - -//////////////////////////////// -//~ rjf: Main Conversion Entry Point (New) - -internal RDIM_BakeParams -d2r2_convert(Arena *arena, D2R2_ConvertParams *params) -{ - Temp scratch = scratch_begin(&arena, 1); - DW_Raw *raw = ¶ms->raw; - RDIM_BinarySectionList binary_sections = params->binary_sections; - Arch arch = params->arch; - ARCH_Info *arch_info = arch_info_from_arch(arch); - U64 base_vaddr = params->base_vaddr; - U64 arch_addr_size = byte_size_from_arch(arch); - - //////////////////////////// - //- rjf: determine acceptable address range - // - // in many cases, linkers seem to trample over addresses in various DWARF sections, - // potentially due to optimizations. we'd like to filter out those busted addresses - // from our final debug info - a good enough heuristic is to disqualify them by - // whether or not they actually fall into the ranges covered by the binary sections. - // - Rng1U64 acceptable_vaddr_range = {0}; - { - acceptable_vaddr_range.min = max_U64; - acceptable_vaddr_range.max = 0; - for EachNode(n, RDIM_BinarySectionNode, binary_sections.first) - { - acceptable_vaddr_range.min = Min(base_vaddr + n->v.voff_first, acceptable_vaddr_range.min); - acceptable_vaddr_range.max = Max(base_vaddr + n->v.voff_opl, acceptable_vaddr_range.max); - } - } - - //////////////////////////// - //- rjf: compute exe hash - // - U64 exe_hash = 0; - ProfScope("compute exe hash") - { - if(lane_idx() == 0) - { - exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); - } - lane_sync_u64(&exe_hash, 0); - } - - //////////////////////////// - //- rjf: produce top-level-info - // - RDIM_TopLevelInfo top_level_info = {0}; - ProfScope("produce top-level-info") - { - top_level_info.arch = rdi_arch_from_arch(arch); - top_level_info.exe_name = params->exe_name; - top_level_info.exe_hash = exe_hash; - top_level_info.voff_max = acceptable_vaddr_range.max - base_vaddr; - if(!params->deterministic) - { - // TODO(rjf): top_level_info.guid = ...; - top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL); - } - } - - //////////////////////////// - //- rjf: gather unit ranges from .debug_info, .debug_aranges - // - Rng1U64Array *unit_info_ranges = 0; - Rng1U64Array *unit_arange_ranges = 0; - ProfScope("gather unit ranges from .debug_info, .debug_aranges") if(lane_idx() == 0) - { - unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1); - *unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data); - unit_arange_ranges = push_array(scratch.arena, Rng1U64Array, 1); - *unit_arange_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_ARanges].data); - } - lane_sync_u64(&unit_info_ranges, 0); - lane_sync_u64(&unit_arange_ranges, 0); - U64 unit_count = unit_info_ranges->count; - - //////////////////////////// - //- rjf: parse all .debug_info unit headers - // - DW2_UnitHeader *unit_headers = 0; - Rng1U64 *unit_info_tag_ranges = 0; - ProfScope("parse all unit headers") - { - // rjf: set up - if(lane_idx() == 0) - { - unit_headers = push_array(scratch.arena, DW2_UnitHeader, unit_count); - unit_info_tag_ranges = push_array(scratch.arena, Rng1U64, unit_count); - } - lane_sync_u64(&unit_headers, 0); - lane_sync_u64(&unit_info_tag_ranges, 0); - - // rjf: parse all unit headers - String8 data = raw->sec[DW_SectionKind_Info].data; - Rng1U64 range = lane_range(unit_count); - for EachInRange(idx, range) - { - Rng1U64 unit_info_range = unit_info_ranges->v[idx]; - U64 bytes_read = dw2_read_unit_header(str8_substr(data, unit_info_range), 0, &unit_headers[idx]); - unit_info_tag_ranges[idx] = r1u64(unit_info_range.min + bytes_read, unit_info_range.max); - } - } - lane_sync(); - Rng1U64Array unit_info_tag_ranges_array = {unit_info_tag_ranges, unit_count}; - - //////////////////////////// - //- rjf: parse all units from .debug_aranges - // - U64 *arange_info_offs = 0; - RDIM_Rng1U64ChunkList *arange_voff_ranges = 0; - { - if(lane_idx() == 0) - { - arange_info_offs = push_array(scratch.arena, U64, unit_arange_ranges->count); - arange_voff_ranges = push_array(scratch.arena, RDIM_Rng1U64ChunkList, unit_arange_ranges->count); - } - lane_sync_u64(&arange_info_offs, 0); - lane_sync_u64(&arange_voff_ranges, 0); - U64 unit_take_idx_ = 0; - U64 *unit_take_idx_ptr = &unit_take_idx_; - lane_sync_u64(&unit_take_idx_ptr, 0); - String8 data = raw->sec[DW_SectionKind_ARanges].data; - for(;;) - { - // rjf: take unit - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; - if(unit_idx >= unit_arange_ranges->count) - { - break; - } - - // rjf: unpack - Rng1U64 arange_range = unit_arange_ranges->v[unit_idx]; - U64 off = arange_range.min; - - // rjf: read unit data size / format - U64 unit_size = 0; - DW_Format fmt = 0; - off += dw2_read_initial_length(data, off, &unit_size, &fmt); - U64 unit_opl = off + unit_size; - - // rjf: read version - DW_Version version = 0; - U64 version_off = off; - off += str8_deserial_read_struct(data, off, &version); - - // rjf: warn on non-version 2 - if(version != DW_Version_2) - { - log_infof("[.debug_aranges@0x%I64x] DWARF version for unit #%I64d was expected to be 2, but it was read as %i.\n", version_off, unit_idx, (S32)version); - } - - // rjf: read .debug_info off for this unit - U64 info_off = 0; - off += dw2_read_fmt_u64(data, off, fmt, &info_off); - - // rjf: read address / segment selector size - U8 addr_size = 0; - U8 segment_selector_size = 0; - off += str8_deserial_read_struct(data, off, &addr_size); - off += str8_deserial_read_struct(data, off, &segment_selector_size); - - // rjf: round up past padding - { - U64 tuple_size = addr_size*2 + segment_selector_size; - off += tuple_size - (off%tuple_size); - } - - // rjf: parse ranges - RDIM_Rng1U64ChunkList voff_ranges = {0}; - if(segment_selector_size != 0) - { - log_infof("[.debug_aranges@0x%I64x] Non-zero (%i) segment selector size parsed; this form of addressing is not currently supported in DWARF info.\n", off, (S32)segment_selector_size); - } - else for(;off < unit_opl;) - { - U64 start_off = off; - U64 base_addr = 0; - U64 range_size = 0; - off += str8_deserial_read(data, off, &base_addr, addr_size, addr_size); - off += str8_deserial_read(data, off, &range_size, addr_size, addr_size); - if(base_addr == 0 && range_size == 0) - { - break; - } - if(base_addr < base_vaddr) - { - log_infof("[.debug_aranges@0x%I64x] Address (0x%I64x) parsed which was less than the image base address (0x%I64x). Skipping.\n", start_off, base_addr, base_vaddr); - } - else - { - U64 voff_first = (base_addr - base_vaddr); - U64 voff_opl = voff_first + range_size; - RDIM_Rng1U64 range = {voff_first, voff_opl}; - rdim_rng1u64_chunk_list_push(arena, &voff_ranges, 256, range); - } - if(off == start_off) - { - break; - } - } - - // rjf: store - arange_info_offs[unit_idx] = info_off; - arange_voff_ranges[unit_idx] = voff_ranges; - } - lane_sync(); - } - - //////////////////////////// - //- rjf: produce info_off -> list(voff_range) map from aranges units; - // we must do this because technically we can't guarantee that unit_idxs - // inside of .debug_aranges are the same as unit_idxs inside of .debug_info, - // nor can we guarantee that they'd be in the same order, so we need to - // correllate via the encoded .debug_info offset from .debug_aranges. - // - // more excellence. - // - typedef struct D2R2_ARangeUnitNode D2R2_ARangeUnitNode; - struct D2R2_ARangeUnitNode - { - D2R2_ARangeUnitNode *next; - U64 info_off; - RDIM_Rng1U64ChunkList *ranges; - }; - U64 arange_unit_from_info_off_map_slots_count = unit_arange_ranges->count; - D2R2_ARangeUnitNode **arange_unit_from_info_off_map_slots = 0; - { - if(lane_idx() == 0) - { - arange_unit_from_info_off_map_slots = push_array(scratch.arena, D2R2_ARangeUnitNode *, arange_unit_from_info_off_map_slots_count); - } - lane_sync_u64(&arange_unit_from_info_off_map_slots, 0); - for EachIndex(arange_unit_idx, unit_arange_ranges->count) - { - U64 info_off = arange_info_offs[arange_unit_idx]; - RDIM_Rng1U64ChunkList *ranges = &arange_voff_ranges[arange_unit_idx]; - U64 hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count; - D2R2_ARangeUnitNode *n = push_array(scratch.arena, D2R2_ARangeUnitNode, 1); - SLLStackPush(arange_unit_from_info_off_map_slots[slot_idx], n); - n->info_off = info_off; - n->ranges = ranges; - } - } - lane_sync(); - - //////////////////////////// - //- rjf: build all abbreviation maps, build (unit -> abbrev map) - // - DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count); - - //////////////////////////// - //- rjf: parse all unit offsets tables - // - // on .debug_str_offsets, as one example: - // - // in an incredible twist of fate, DWARF decided to decouple these from - // compilation units. compilation units *do* contain a - // DW_AttribKind_StrOffsetsBase attribute. but this base offset does *not* - // point to the beginning of a table in .debug_str_offsets! it instead points - // PAST THE INITIAL VARIABLE-WIDTH LENGTH AND FORMAT ENCODING! this means - // you can't actually use the StrOffsetsBase attribute for ANYTHING other - // than correllating a unit to its associated string offset table - but you - // *necessarily needed to have parsed that table beforehand*, completely - // independently from units. - // - // so, we have to parse all the string offset tables up-front, then - // *binary search* their ranges to determine which unit has which table. - // - // of course, in practice, it's perhaps likely/expected that these match - // one-to-one with units, and in the same order, because that is what is - // most natural for generators. but, the format does not *guarantee this*, - // and instead specced something far more arbitrary. - // - // thank you, again, DWARF. - // - DW2_OffsetTableSet *offset_tables = 0; - ProfScope("parse all offset tables (.debug_rnglists, .debug_str_offsets, .debug_addr, .debug_loclists)") if(lane_idx() == 0) - { - offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1); - offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw); - } - lane_sync_u64(&offset_tables, 0); - - //////////////////////////// - //- rjf: build per-unit parsing contexts, parse compilation unit tags - // - DW2_ParseCtx *unit_parse_ctxs = 0; - DW2_Tag *unit_root_tags = 0; - { - if(lane_idx() == 0) - { - unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count); - unit_root_tags = push_array(scratch.arena, DW2_Tag, unit_count); - } - lane_sync_u64(&unit_parse_ctxs, 0); - lane_sync_u64(&unit_root_tags, 0); - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - DW2_UnitHeader *hdr = &unit_headers[unit_idx]; - DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; - - // rjf: equip initial info - just enough to do a bootstrapping parse of the root tag - dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min); - dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx); - - // rjf: do bootstrapping parse - // - // an incredible NOTE: the above context info is actually not sufficient for general - // parsing, because this "bootstrapping" tag parse is what informs us which - // .debug_str_offsets table each unit should be associated with (via the - // StrOffsetsBase attribute), we actually don't have the right string offset table - // *before* we parse this, which means we can't resolve some of the string attribute - // values. - // - // so, in another incredible twist of fate, we actually must do this *twice*. first, - // to find *just the StrOffsetsBase*, and then, to actually fully resolve everything. - // - // (all of the above is also true for RngListsBase) - // - DW2_Tag root_tag_bootstrap = {0}; - dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap); - - // rjf: equip info extracted from bootstrapping parse - dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables); - - // rjf: do non-bootstrapping parse - dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]); - - // rjf: equip info extracted from non-bootstrapping parse - dw2_parse_ctx_equip_unit_root_tag(ctx, &unit_root_tags[unit_idx], offset_tables); - } - } - lane_sync(); - - //////////////////////////// - //- rjf: parse each unit's line table header - // - DW2_LineTableHeader *unit_line_table_headers = 0; - ProfScope("parse each unit's line table header") - { - if(lane_idx() == 0) - { - unit_line_table_headers = push_array(scratch.arena, DW2_LineTableHeader, unit_count); - } - lane_sync_u64(&unit_line_table_headers, 0); - U64 unit_take_idx = 0; - U64 *unit_take_idx_ptr = &unit_take_idx; - lane_sync_u64(&unit_take_idx_ptr, 0); - for(;;) - { - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; - if(unit_idx >= unit_count) - { - break; - } - DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; - DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; - DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); - U64 line_info_off = stmt_list->val.u128.u64[0]; - String8 line_info_data = raw->sec[DW_SectionKind_Line].data; - dw2_read_line_table_header(scratch.arena, raw, ctx, line_info_data, line_info_off, &unit_line_table_headers[unit_idx]); - } - lane_sync(); - } - - //////////////////////////// - //- rjf: deduplicate all source files - // - typedef struct UnitSrcFileMap UnitSrcFileMap; - struct UnitSrcFileMap - { - RDIM_SrcFile **v; - }; - RDIM_SrcFileChunkList *all_src_files = 0; - UnitSrcFileMap *unit_src_file_maps = 0; - ProfScope("deduplicate all source files") if(lane_idx() == 0) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - //- rjf: count all files in all units - U64 total_file_count = 0; - for EachIndex(unit_idx, unit_count) - { - total_file_count += unit_line_table_headers[unit_idx].files.count; - } - - //- rjf: set up path -> src file map - typedef struct SrcFileNode SrcFileNode; - struct SrcFileNode - { - SrcFileNode *next; - String8 full_path; - RDIM_SrcFile *src_file; - }; - all_src_files = push_array(scratch.arena, RDIM_SrcFileChunkList, 1); - U64 slots_count = 1 + (total_file_count * 3) / 4; - SrcFileNode **slots = push_array(scratch2.arena, SrcFileNode *, slots_count); - - //- rjf: set up unit -> src file maps - unit_src_file_maps = push_array(scratch.arena, UnitSrcFileMap, unit_count); - for EachIndex(unit_idx, unit_count) - { - unit_src_file_maps[unit_idx].v = push_array(scratch.arena, RDIM_SrcFile *, unit_line_table_headers[unit_idx].files.count); - } - - //- rjf: build path -> src file map - for EachIndex(unit_idx, unit_count) - { - DW2_LineTableHeader *hdr = &unit_line_table_headers[unit_idx]; - for EachIndex(file_idx, hdr->files.count) - { - DW2_LineTableFile *f = &hdr->files.v[file_idx]; - DW2_LineTableFile *dir = &hdr->dirs.v[f->dir_idx]; - String8 full_file_path = str8f(scratch2.arena, "%S%s%S", dir->file_name, dir->file_name.size != 0 ? "/" : "", f->file_name); - U64 hash = u64_hash_from_str8(full_file_path); - U64 slot_idx = hash%slots_count; - SrcFileNode *node = 0; - for(SrcFileNode *n = slots[slot_idx]; n != 0; n = n->next) - { - if(str8_match(n->full_path, full_file_path, 0)) - { - node = n; - break; - } - } - if(!node) - { - node = push_array(scratch2.arena, SrcFileNode, 1); - node->full_path = full_file_path; - node->src_file = rdim_src_file_chunk_list_push(arena, all_src_files, slots_count); - node->src_file->path = str8_copy(arena, full_file_path); - if(f->flags & DW2_LineTableFileFlag_HasMD5) - { - node->src_file->checksum_kind = RDI_ChecksumKind_MD5; - node->src_file->checksum = str8_copy(arena, str8_struct(&f->md5)); - } - else if(f->flags & DW2_LineTableFileFlag_HasModifyTime) - { - node->src_file->checksum_kind = RDI_ChecksumKind_Timestamp; - node->src_file->checksum = str8_copy(arena, str8_struct(&f->modify_time)); - } - } - unit_src_file_maps[unit_idx].v[file_idx] = node->src_file; - } - } - scratch_end(scratch2); - } - lane_sync_u64(&all_src_files, 0); - lane_sync_u64(&unit_src_file_maps, 0); - - //////////////////////////// - //- rjf: parse each unit's line info; produce line tables - // - RDIM_LineTableChunkList *unit_line_table_chunk_lists = 0; - RDIM_LineTable **unit_line_tables = 0; - ProfScope("parse each unit's line info; produce line tables") - { - //- rjf: prep outputs - typedef struct LineSeqChunk LineSeqChunk; - struct LineSeqChunk - { - LineSeqChunk *next; - U64 *voffs; - U32 *line_nums; - U16 *col_nums; - U64 line_count; - U64 line_cap; - }; - typedef struct FileSeqNode FileSeqNode; - struct FileSeqNode - { - FileSeqNode *next; - RDIM_SrcFile *src_file; - RDIM_SrcFileLineMapFragment *first_line_map_fragment; - RDIM_SrcFileLineMapFragment *last_line_map_fragment; - }; - typedef struct FileSeqMap FileSeqMap; - struct FileSeqMap - { - U64 slots_count; - FileSeqNode **slots; - }; - FileSeqMap *unit_file_seq_maps = 0; - if(lane_idx() == 0) - { - unit_line_table_chunk_lists = push_array(scratch.arena, RDIM_LineTableChunkList, unit_count); - unit_line_tables = push_array(scratch.arena, RDIM_LineTable *, unit_count); - unit_file_seq_maps = push_array(scratch.arena, FileSeqMap, unit_count); - } - lane_sync_u64(&unit_line_table_chunk_lists, 0); - lane_sync_u64(&unit_line_tables, 0); - lane_sync_u64(&unit_file_seq_maps, 0); - - //- rjf: wide per-unit parse - U64 unit_take_idx = 0; - U64 *unit_take_idx_ptr = &unit_take_idx; - lane_sync_u64(&unit_take_idx_ptr, 0); - for(;;) - { - //- rjf: take unit - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; - if(unit_idx >= unit_count) - { - break; - } - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - //- rjf: unpack unit info - DW2_LineTableHeader *line_table_header = &unit_line_table_headers[unit_idx]; - RDIM_LineTableChunkList *dst_line_tables = &unit_line_table_chunk_lists[unit_idx]; - DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; - DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); - U64 line_info_off = stmt_list->val.u128.u64[0]; - String8 all_line_info_data = raw->sec[DW_SectionKind_Line].data; - String8 unit_line_table_data = str8_substr(all_line_info_data, r1u64(line_table_header->line_program_off, line_info_off + line_table_header->total_unit_data_size)); - - //- rjf: build unit's line table - RDIM_LineTable *dst_line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 1); - unit_line_tables[unit_idx] = dst_line_table; - - //- rjf: set up per-unit file sequence map - unit_file_seq_maps[unit_idx].slots_count = line_table_header->files.count + 1; - unit_file_seq_maps[unit_idx].slots = push_array(scratch.arena, FileSeqNode *, unit_file_seq_maps[unit_idx].slots_count); - - //- rjf: set up vm registers - DW2_LineVMRegs vm_regs = {0}; - { - vm_regs.file_index = 1; - vm_regs.line = 1; - vm_regs.is_stmt = line_table_header->default_is_stmt; - } - - //- rjf: run the line opcode program - B32 emit_line = 0; - RDIM_SrcFile *line_seq_src_file = 0; - LineSeqChunk *first_line_seq_chunk = 0; - LineSeqChunk *last_line_seq_chunk = 0; - U64 total_line_seq_count = 0; - for(U64 off = 0, next_off = 0; off <= unit_line_table_data.size; off = next_off) - { - next_off = unit_line_table_data.size; - - //- rjf: read next opcode - U64 op_read_off = off; - U8 opcode = 0; - str8_deserial_read_struct(unit_line_table_data, op_read_off, &opcode); - op_read_off += 1; - - //- rjf: apply "special opcodes" (DWARF v5 6.2.5.1) - if(opcode >= line_table_header->opcode_base) - { - U32 adjusted_opcode = (U32)(opcode - line_table_header->opcode_base); - U32 op_advance = adjusted_opcode / line_table_header->line_range; - S64 line_advance = (S64)line_table_header->line_base + (S64)adjusted_opcode%(S64)line_table_header->line_range; - U64 addr_advance = line_table_header->min_inst_length * (vm_regs.vliw_op_index + op_advance) / line_table_header->max_ops_per_inst; - vm_regs.address += addr_advance; - vm_regs.vliw_op_index = (vm_regs.vliw_op_index + op_advance) % line_table_header->max_ops_per_inst; - vm_regs.line += line_advance; - emit_line = 1; - } - - //- rjf: apply standard opcode - else - { - U64 op_advance = 0; - switch(opcode) - { - //- rjf: skip unknown opcode - default: - { - for EachIndex(uleb_idx, line_table_header->opcode_lengths[opcode - 1]) - { - U64 v = 0; - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &v); - } - }break; - - //- rjf: line emissions - case DW_StdOpcode_Copy: - { - emit_line = 1; - }break; - - //- rjf: PC advances - case DW_StdOpcode_AdvancePc: - { - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &op_advance); - }goto advance_pc; - case DW_StdOpcode_ConstAddPc: - { - op_advance = (0xffu - line_table_header->opcode_base) / line_table_header->line_range; - }goto advance_pc; - advance_pc:; - { - U64 op_index = vm_regs.vliw_op_index + op_advance; - vm_regs.address += line_table_header->min_inst_length * (op_index / line_table_header->max_ops_per_inst); - vm_regs.vliw_op_index = op_index % line_table_header->max_ops_per_inst; - }break; - - //- rjf: fixed PC advances - case DW_StdOpcode_FixedAdvancePc: - { - U16 fixed_advance = 0; - str8_deserial_read_struct(unit_line_table_data, op_read_off, &fixed_advance); - op_read_off += sizeof(U16); - vm_regs.address += fixed_advance; - vm_regs.vliw_op_index = 0; - }break; - - //- rjf: line number advance - case DW_StdOpcode_AdvanceLine: - { - S64 advance = 0; - op_read_off += str8_deserial_read_sleb128(unit_line_table_data, op_read_off, &advance); - vm_regs.line += advance; - }break; - - //- rjf: set file - case DW_StdOpcode_SetFile: - { - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.file_index); - }break; - - //- rjf: set column - case DW_StdOpcode_SetColumn: - { - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.column); - }break; - - //- rjf: negate statment - case DW_StdOpcode_NegateStmt: - { - vm_regs.is_stmt = !vm_regs.is_stmt; - }break; - - //- rjf: flag sets - case DW_StdOpcode_SetBasicBlock: - { - vm_regs.basic_block = 1; - }break; - case DW_StdOpcode_SetPrologueEnd: - { - vm_regs.prologue_end = 1; - }break; - case DW_StdOpcode_SetEpilogueBegin: - { - vm_regs.epilogue_begin = 1; - }break; - - //- rjf: isa - case DW_StdOpcode_SetIsa: - { - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.isa); - }break; - - //- rjf: extended opcode - case DW_StdOpcode_ExtendedOpcode: - { - // rjf: read extended opcode size - U64 ext_opcode_size = 0; - op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &ext_opcode_size); - - // rjf: read extended opcode - U8 ext_opcode = 0; - op_read_off += str8_deserial_read_struct(unit_line_table_data, op_read_off, &ext_opcode); - - // rjf: grab truncated data for just this extended opcode - String8 ext_op_data = str8_prefix(unit_line_table_data, op_read_off + ext_opcode_size); - - // rjf: do extended opcode - switch(ext_opcode) - { - default: - case DW_ExtOpcode_Undefined: - case DW_ExtOpcode_UserLo: - case DW_ExtOpcode_UserHi: - {}break; - case DW_ExtOpcode_EndSequence: - { - emit_line = 1; - vm_regs.end_sequence = 1; - }break; - case DW_ExtOpcode_SetAddress: - { - U64 addr = 0; - op_read_off += str8_deserial_read(ext_op_data, op_read_off, &addr, line_table_header->addr_size, line_table_header->addr_size); - vm_regs.address = addr; - vm_regs.vliw_op_index = 0; - }break; - case DW_ExtOpcode_DefineFile: - { - String8 file_name = {0}; - U64 dir_idx = 0; - U64 modify_time = 0; - U64 file_size = 0; - op_read_off += str8_deserial_read_cstr(ext_op_data, op_read_off, &file_name); - op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &dir_idx); - op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &modify_time); - op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &file_size); - // - // TODO(rjf): this is a real problem, because at this point, we've already gathered & deduped - // all source files, but now we're in a situation where a per-lane line info parse can produce - // new source files, which may - of course - be duplicates of files defined in other unit line - // info. - // - }break; - case DW_ExtOpcode_SetDiscriminator: - { - op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &vm_regs.discriminator); - }break; - } - }break; - } - } - - //- rjf: advance to next op - if(op_read_off > off) - { - next_off = op_read_off; - } - - //- rjf: map file index -> rdim src file - RDIM_SrcFile *src_file = 0; - if(vm_regs.file_index < line_table_header->files.count) - { - src_file = unit_src_file_maps[unit_idx].v[vm_regs.file_index]; - } - - //- rjf: sequence ended explicitly, or file change, or end of stream? -> push to line table - if(line_seq_src_file != 0 && (vm_regs.end_sequence || (src_file != line_seq_src_file && first_line_seq_chunk != 0) || off >= unit_line_table_data.size)) - { - // rjf: combine voffs/lines/cols - U64 seq_line_count = total_line_seq_count; - U64 *seq_voffs = push_array(arena, U64, seq_line_count+1); - U32 *seq_lines = push_array(arena, U32, seq_line_count); - U16 *seq_cols = push_array(arena, U16, 2*seq_line_count); - { - U64 voff_idx = 0; - U64 line_idx = 0; - U64 col_idx = 0; - for(LineSeqChunk *c = first_line_seq_chunk; c != 0; c = c->next) - { - MemoryCopy(seq_voffs + voff_idx, c->voffs, sizeof(c->voffs[0]) * c->line_count); - MemoryCopy(seq_lines + line_idx, c->line_nums, sizeof(c->line_nums[0]) * c->line_count); - MemoryCopy(seq_cols + col_idx, c->col_nums, sizeof(c->col_nums[0]) * 2 * c->line_count); - voff_idx += c->line_count; - line_idx += c->line_count; - col_idx += 2*c->line_count; - } - seq_voffs[seq_line_count] = vm_regs.address - base_vaddr; - } - - // rjf: push sequence to line table - RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, dst_line_tables, dst_line_table, line_seq_src_file, seq_voffs, seq_lines, seq_cols, seq_line_count); - - // rjf: map src file -> file seq node - FileSeqNode *file_seq_n = 0; - { - U64 hash = u64_hash_from_str8(str8_struct(&line_seq_src_file)); - U64 slot_idx = hash%unit_file_seq_maps[unit_idx].slots_count; - for(FileSeqNode *n = unit_file_seq_maps[unit_idx].slots[slot_idx]; n != 0; n = n->next) - { - if(n->src_file == line_seq_src_file) - { - file_seq_n = n; - break; - } - } - if(file_seq_n == 0) - { - file_seq_n = push_array(scratch.arena, FileSeqNode, 1); - SLLStackPush(unit_file_seq_maps[unit_idx].slots[slot_idx], file_seq_n); - file_seq_n->src_file = line_seq_src_file; - } - } - - // rjf: record sequence in file seq node - { - RDIM_SrcFileLineMapFragment *f = push_array(scratch.arena, RDIM_SrcFileLineMapFragment, 1); - SLLQueuePush(file_seq_n->first_line_map_fragment, file_seq_n->last_line_map_fragment, f); - f->seq = seq; - } - - // rjf: reset - first_line_seq_chunk = last_line_seq_chunk = 0; - total_line_seq_count = 0; - line_seq_src_file = 0; - if(vm_regs.end_sequence) - { - MemoryZeroStruct(&vm_regs); - vm_regs.file_index = 1; - vm_regs.line = 1; - vm_regs.is_stmt = line_table_header->default_is_stmt; - } - } - - //- rjf: emit lines - if(emit_line && vm_regs.address != 0 && vm_regs.line != 0) - { - emit_line = 0; - - // rjf: grab the last emitted line info - U32 last_line = 0; - U16 last_col = 0; - if(last_line_seq_chunk != 0 && last_line_seq_chunk->line_count > 0) - { - last_line = last_line_seq_chunk->line_nums[last_line_seq_chunk->line_count-1]; - last_col = last_line_seq_chunk->col_nums[last_line_seq_chunk->line_count-1]; - } - - // rjf: determine if we need a new line - // - // TODO(rjf): in some cases this can trigger w/ DWARF due to columnar changes; - // we need to adjust this when we want to start correctly supporting columnar - // line info / stepping - // - B32 need_new_line = (last_line != (U32)vm_regs.line); - - // rjf: need new line -> grab chunk - LineSeqChunk *chunk = last_line_seq_chunk; - if(need_new_line && (chunk == 0 || chunk->line_count >= chunk->line_cap)) - { - chunk = push_array(scratch.arena, LineSeqChunk, 1); - SLLQueuePush(first_line_seq_chunk, last_line_seq_chunk, chunk); - chunk->line_cap = 64; - chunk->voffs = push_array(scratch.arena, U64, chunk->line_cap + 1); - chunk->line_nums = push_array(scratch.arena, U32, chunk->line_cap); - chunk->col_nums = push_array(scratch.arena, U16, 2*chunk->line_cap); - } - - // rjf: need new line -> push - if(need_new_line) - { - U64 chunk_line_idx = chunk->line_count; - chunk->voffs[chunk_line_idx] = vm_regs.address - base_vaddr; - chunk->line_nums[chunk_line_idx] = (U32)vm_regs.line; - chunk->col_nums[chunk_line_idx] = (U16)vm_regs.column; - chunk->line_count += 1; - total_line_seq_count += 1; - line_seq_src_file = src_file; - // NOTE(rjf): use for comparing against llvm-dwarfdump --debug-line -#if 0 - printf("0x%016I64x %6i %6i %6i %3i %13I64x %7i %s%s%s%s\n", - vm_regs.address, - (int)vm_regs.line, - (int)vm_regs.column, - (int)vm_regs.file_index, - (int)vm_regs.isa, - vm_regs.discriminator, - (int)vm_regs.vliw_op_index, - vm_regs.is_stmt ? " is_stmt" : "", - vm_regs.prologue_end ? " prologue_end" : "", - vm_regs.epilogue_begin ? " epilogue_begin" : "", - vm_regs.end_sequence ? " end_sequence" : ""); -#endif - } - vm_regs.discriminator = 0; - vm_regs.basic_block = 0; - vm_regs.prologue_end = 0; - vm_regs.epilogue_begin = 0; - } - } - - scratch_end(scratch2); - } - lane_sync(); - - //- rjf: equip source files with their fragments - // - // TODO(rjf): this can *almost* be wide, but we are relying on a few top-level - // summations inside the RDIM_SrcFileChunkList when we push a sequence to a src - // file. if we just summed those later when baking (probably fine), then this - // could go wide across all src files, which would be very nice. just lane-0ing - // for now. when possible, we can probably do the same thing in PDB too. - // - if(lane_idx() == 0) - { - for EachIndex(unit_idx, unit_count) - { - FileSeqMap *file_seq_map = &unit_file_seq_maps[unit_idx]; - for EachIndex(slot_idx, file_seq_map->slots_count) - { - for(FileSeqNode *n = file_seq_map->slots[slot_idx]; n != 0; n = n->next) - { - for(RDIM_SrcFileLineMapFragment *f = n->first_line_map_fragment; f != 0; f = f->next) - { - rdim_src_file_push_line_sequence(arena, all_src_files, n->src_file, f->seq); - } - } - } - } - } - lane_sync(); - } - - //////////////////////////// - //- rjf: join all line tables - // - RDIM_LineTableChunkList *all_line_tables = 0; - if(lane_idx() == 0) - { - all_line_tables = push_array(scratch.arena, RDIM_LineTableChunkList, 1); - for EachIndex(unit_idx, unit_count) - { - rdim_line_table_chunk_list_concat_in_place(all_line_tables, &unit_line_table_chunk_lists[unit_idx]); - } - } - lane_sync_u64(&all_line_tables, 0); - - //////////////////////////// - //- rjf: find all offsets of top-level tag trees across all units - // - U64Array *unit_info_root_tag_offs = 0; - U64 total_root_tag_count = 0; - U64 *total_root_tag_count_ptr = &total_root_tag_count; - lane_sync_u64(&total_root_tag_count_ptr, 0); - ProfScope("find all offsets of top-level tag trees across all units") - { - if(lane_idx() == 0) - { - unit_info_root_tag_offs = push_array(scratch.arena, U64Array, unit_count); - } - lane_sync_u64(&unit_info_root_tag_offs, 0); - typedef struct D2R2_UnitTagTreeOffChunkNode D2R2_UnitTagTreeOffChunkNode; - struct D2R2_UnitTagTreeOffChunkNode - { - D2R2_UnitTagTreeOffChunkNode *next; - U64 *v; - U64 count; - U64 cap; - }; - typedef struct D2R2_UnitTagTreeOffChunkList D2R2_UnitTagTreeOffChunkList; - struct D2R2_UnitTagTreeOffChunkList - { - D2R2_UnitTagTreeOffChunkNode *first; - D2R2_UnitTagTreeOffChunkNode *last; - U64 chunk_count; - U64 total_count; - }; - U64 unit_take_idx_ = 0; - U64 *unit_take_idx_ptr = &unit_take_idx_; - lane_sync_u64(&unit_take_idx_ptr, 0); - for(;;) - { - //- rjf: take the next unit - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; - if(unit_idx >= unit_count) - { - break; - } - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - //- rjf: unpack this unit - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - - //- rjf: gather all offsets of root-level trees - D2R2_UnitTagTreeOffChunkList tree_offs = {0}; - S64 depth = 0; - for(U64 off = unit_info_tag_range.min; contains_1u64(unit_info_tag_range, off);) - { - U64 start_off = off; - Temp scratch3 = scratch_begin(&scratch2.arena, 1); - - //- rjf: if this next tag is root-level, gather - if(depth == 1) - { - D2R2_UnitTagTreeOffChunkNode *chunk = tree_offs.last; - if(chunk == 0 || chunk->count >= chunk->cap) - { - chunk = push_array(scratch2.arena, D2R2_UnitTagTreeOffChunkNode, 1); - chunk->cap = 512; - chunk->v = push_array(scratch2.arena, U64, chunk->cap); - SLLQueuePush(tree_offs.first, tree_offs.last, chunk); - tree_offs.chunk_count += 1; - } - chunk->v[chunk->count] = off; - chunk->count += 1; - tree_offs.total_count += 1; - } - - //- rjf: reading the next tag - DW2_Tag tag = {0}; - off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); - - //- rjf: do tree navigations - if(tag.has_children) - { - depth += 1; - } - if(tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - - scratch_end(scratch3); - if(off == start_off) - { - break; - } - } - - //- rjf: flatten & store - { - U64Array tree_offs_array = {0}; - tree_offs_array.count = tree_offs.total_count; - tree_offs_array.v = push_array(scratch.arena, U64, tree_offs_array.count); - { - U64 idx = 0; - for EachNode(n, D2R2_UnitTagTreeOffChunkNode, tree_offs.first) - { - MemoryCopy(tree_offs_array.v + idx, n->v, sizeof(n->v[0])*n->count); - idx += n->count; - } - } - unit_info_root_tag_offs[unit_idx] = tree_offs_array; - ins_atomic_u64_add_eval(total_root_tag_count_ptr, tree_offs_array.count); - } - - scratch_end(scratch2); - } - lane_sync(); - total_root_tag_count = *total_root_tag_count_ptr; - } - - //////////////////////////// - //- rjf: produce list of (unit * range(root_tag_idx)), so that we can easily - // subdivide work from all units across all lanes - // - typedef struct D2R2_SubUnitWork D2R2_SubUnitWork; - struct D2R2_SubUnitWork - { - U64 unit_idx; - Rng1U64 root_tag_idx_range; - }; - U64 sub_unit_works_count = 0; - D2R2_SubUnitWork *sub_unit_works = 0; - ProfScope("produce sub-unit work division") if(lane_idx() == 0) - { - U64 root_tags_per_work = 1024; - for(B32 build = 0; build <= 1; build += 1) - { - U64 sub_unit_work_idx = 0; - for EachIndex(unit_idx, unit_count) - { - U64 root_tags_in_this_unit = unit_info_root_tag_offs[unit_idx].count; - U64 works_per_this_unit = (root_tags_in_this_unit+root_tags_per_work-1) / root_tags_per_work; - if(build) - { - D2R2_SubUnitWork *works = sub_unit_works + sub_unit_work_idx; - U64 works_count = works_per_this_unit; - U64 root_tag_idx = 0; - for EachIndex(work_idx, works_count) - { - U64 root_tag_idx_opl = root_tag_idx + root_tags_per_work; - root_tag_idx_opl = ClampTop(root_tag_idx_opl, root_tags_in_this_unit); - works[work_idx].unit_idx = unit_idx; - works[work_idx].root_tag_idx_range = r1u64(root_tag_idx, root_tag_idx_opl); - root_tag_idx = root_tag_idx_opl; - } - } - sub_unit_work_idx += works_per_this_unit; - } - if(!build) - { - sub_unit_works_count = sub_unit_work_idx; - sub_unit_works = push_array(scratch.arena, D2R2_SubUnitWork, sub_unit_works_count); - } - } - } - lane_sync_u64(&sub_unit_works_count, 0); - lane_sync_u64(&sub_unit_works, 0); - - //////////////////////////// - //- rjf: build built-in types - // - RDIM_TypeChunkList *builtin_types = 0; - RDIM_Type **builtin_type_from_kind_map = 0; // [RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1] - U64 builtin_type_count = RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1; - ProfScope("build built-in types") if(lane_idx() == 0) - { - builtin_types = push_array(scratch.arena, RDIM_TypeChunkList, 1); - builtin_type_from_kind_map = push_array(scratch.arena, RDIM_Type *, builtin_type_count); - for(RDI_TypeKind k = RDI_TypeKind_FirstBuiltIn; k <= RDI_TypeKind_LastBuiltIn; k += 1) - { - RDIM_Type *type = rdim_type_chunk_list_push(arena, builtin_types, builtin_type_count); - type->kind = k; - type->name.str = rdi_string_from_type_kind(k, &type->name.size); - type->byte_size = rdi_size_from_basic_type_kind(k); - if(type->byte_size == max_U32) { type->byte_size = byte_size_from_arch(arch); } - builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] = type; - } - } - lane_sync_u64(&builtin_types, 0); - lane_sync_u64(&builtin_type_from_kind_map, 0); -#define d2r2_type_from_builtin_kind(k) ((RDI_TypeKind_FirstBuiltIn <= (k) && (k) <= RDI_TypeKind_LastBuiltIn) ? builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] : builtin_type_from_kind_map[RDI_TypeKind_Void]) - - //////////////////////////// - //- rjf: predict the total number of tags in all units - // - U64 total_tag_count_estimate = 1; - { - U64 tag_size_estimate = 32; - for EachIndex(unit_idx, unit_count) - { - total_tag_count_estimate += dim_1u64(unit_info_tag_ranges[unit_idx]) / tag_size_estimate; - } - } - - //////////////////////////// - //- rjf: gather all unique, to-be-deduplicated tags across all units (types, namespaces) - // - D2R2_UniqueTagNode **unique_tag_slots = 0; - U64 unique_tag_slots_count = total_tag_count_estimate/8 + 1; - D2R2_UnitDedupedTagMap *unit_deduped_tag_maps = 0; - ProfScope("gather all unique, to-be-deduplicated tags across all units (types, namespaces)") - { - //- rjf: set up tables - if(lane_idx() == 0) - { - unique_tag_slots = push_array(scratch.arena, D2R2_UniqueTagNode *, unique_tag_slots_count); - unit_deduped_tag_maps = push_array(scratch.arena, D2R2_UnitDedupedTagMap, unit_count); - } - lane_sync_u64(&unique_tag_slots, 0); - lane_sync_u64(&unit_deduped_tag_maps, 0); - - //- rjf: set up per-unit deduplicated tag maps - { - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - unit_deduped_tag_maps[unit_idx].slots_count = dim_1u64(unit_info_tag_range) / 256 + 1; - unit_deduped_tag_maps[unit_idx].slots = push_array(scratch.arena, D2R2_UnitDedupedTagNode *, unit_deduped_tag_maps[unit_idx].slots_count); - } - lane_sync(); - } - - //- rjf: gather unique tags - { - U64 work_take_idx_ = 0; - U64 *work_take_idx_ptr = &work_take_idx_; - lane_sync_u64(&work_take_idx_ptr, 0); - for(;;) - { - //- rjf: take next work - U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1; - if(work_idx >= sub_unit_works_count) - { - break; - } - Temp work_scratch = scratch_begin(&scratch.arena, 1); - - //- 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 - DW2_ParseCtx *origin_unit_parse_ctx = &unit_parse_ctxs[origin_unit_idx]; - Rng1U64 origin_unit_info_tag_range = unit_info_tag_ranges[origin_unit_idx]; - - //- rjf: gather all tags which we want to deduplicate - typedef struct D2R2_TagNode D2R2_TagNode; - struct D2R2_TagNode - { - D2R2_TagNode *next; - D2R2_UniqueTagKind kind; - U64 info_off; - U64 container_ancestor_info_off; - U64 hash_seed; - }; - D2R2_TagNode *first_tag_to_dedup = 0; - D2R2_TagNode *last_tag_to_dedup = 0; - 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) - { - U64 root_tag_start_off = unit_info_root_tag_offs[origin_unit_idx].v[root_tag_idx]; - typedef struct D2R2_ParentTagNode D2R2_ParentTagNode; - struct D2R2_ParentTagNode - { - D2R2_ParentTagNode *next; - DW_TagKind tag_kind; - U64 info_off; - U64 hash_seed; - }; - D2R2_ParentTagNode *top_parent = 0; - D2R2_ParentTagNode *free_parent = 0; - S64 depth = 0; - for(U64 off = root_tag_start_off; contains_1u64(origin_unit_info_tag_range, off) && (depth > 0 || off == root_tag_start_off);) - { - Temp tag_scratch = scratch_begin(&work_scratch.arena, 1); - U64 start_off = off; - - // rjf: read tag - DW2_Tag tag = {0}; - off += dw2_read_tag(tag_scratch.arena, raw, origin_unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); - - // rjf: is this one of the tags we'd like to dedup? -> gather - if(tag.kind == DW_TagKind_ArrayType || - tag.kind == DW_TagKind_ClassType || - tag.kind == DW_TagKind_EnumerationType || - tag.kind == DW_TagKind_PointerType || - tag.kind == DW_TagKind_ReferenceType || - tag.kind == DW_TagKind_StringType || - tag.kind == DW_TagKind_StructureType || - tag.kind == DW_TagKind_SubroutineType || - tag.kind == DW_TagKind_SubProgram || - tag.kind == DW_TagKind_Typedef || - tag.kind == DW_TagKind_UnionType || - tag.kind == DW_TagKind_PtrToMemberType || - tag.kind == DW_TagKind_SetType || - tag.kind == DW_TagKind_BaseType || - tag.kind == DW_TagKind_ConstType || - tag.kind == DW_TagKind_FileType || - tag.kind == DW_TagKind_PackedType || - tag.kind == DW_TagKind_VolatileType || - tag.kind == DW_TagKind_RestrictType || - tag.kind == DW_TagKind_InterfaceType || - tag.kind == DW_TagKind_UnspecifiedType || - tag.kind == DW_TagKind_SharedType || - tag.kind == DW_TagKind_RValueReferenceType || - tag.kind == DW_TagKind_CoarrayType || - tag.kind == DW_TagKind_DynamicType || - tag.kind == DW_TagKind_AtomicType || - tag.kind == DW_TagKind_ImmutableType || - tag.kind == DW_TagKind_Namespace) - { - U64 container_ancestor_info_off = 0; - for EachNode(n, D2R2_ParentTagNode, top_parent) - { - if(n->tag_kind == DW_TagKind_StructureType || - n->tag_kind == DW_TagKind_UnionType || - n->tag_kind == DW_TagKind_ClassType || - n->tag_kind == DW_TagKind_SubProgram || - n->tag_kind == DW_TagKind_LexicalBlock || - n->tag_kind == DW_TagKind_Namespace) - { - container_ancestor_info_off = n->info_off; - break; - } - } - D2R2_TagNode *n = push_array(work_scratch.arena, D2R2_TagNode, 1); - n->kind = (tag.kind == DW_TagKind_Namespace ? D2R2_UniqueTagKind_Namespace : D2R2_UniqueTagKind_Type); - n->info_off = start_off; - n->container_ancestor_info_off = container_ancestor_info_off; - n->hash_seed = top_parent ? top_parent->hash_seed : 0; - SLLQueuePush(first_tag_to_dedup, last_tag_to_dedup, n); - } - - // rjf: compute hash seed for this tag - U64 hash_seed = (top_parent ? top_parent->hash_seed : 0); - if(tag.kind == DW_TagKind_SubProgram || - tag.kind == DW_TagKind_StructureType || - tag.kind == DW_TagKind_UnionType || - tag.kind == DW_TagKind_ClassType || - tag.kind == DW_TagKind_Namespace) - { - DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name); - if(name_attrib != &dw2_attrib_nil) - { - hash_seed = u64_hash_from_str8(name_attrib->val.string); - } - } - - // rjf: update depth / parent stack - if(tag.has_children) - { - depth += 1; - D2R2_ParentTagNode *n = free_parent; - if(n != 0) - { - SLLStackPop(free_parent); - } - else - { - n = push_array(work_scratch.arena, D2R2_ParentTagNode, 1); - } - SLLStackPush(top_parent, n); - n->tag_kind = tag.kind; - n->info_off = start_off; - n->hash_seed = hash_seed; - } - if(tag.kind == DW_TagKind_Null) - { - depth -= 1; - depth = Max(0, depth); - if(top_parent != 0) - { - D2R2_ParentTagNode *popped = top_parent; - SLLStackPop(top_parent); - SLLStackPush(free_parent, popped); - } - } - - scratch_end(tag_scratch); - if(off == start_off) - { - break; - } - } - } - - //- rjf: hash all tags we need to deduplicate & gather - for(D2R2_TagNode *tag_n = first_tag_to_dedup; tag_n != 0; tag_n = tag_n->next) - { - Temp dedup_root_scratch = scratch_begin(&scratch.arena, 1); - U64 start_off = tag_n->info_off; - D2R2_UniqueTagKind unique_tag_kind = tag_n->kind; - U64 container_ancestor_info_off = tag_n->container_ancestor_info_off; - U64 hash_seed = tag_n->hash_seed; - - //- rjf: hash all tags & dependency tag trees - U64 hash = hash_seed; - { - typedef struct TagTask TagTask; - struct TagTask - { - TagTask *next; - U64 unit_idx; - U64 off; - U64 order_idx; - }; - TagTask start_task = {0, origin_unit_idx, start_off}; - TagTask *top_task = &start_task; - TagTask *free_task = 0; - U64 seen_task_slots_count = 16; - TagTask **seen_task_slots = push_array(dedup_root_scratch.arena, TagTask *, seen_task_slots_count); - for(TagTask *t = top_task, *next = 0; t != 0; t = next) - { - next = 0; - U64 t_off = t->off; - - // rjf: record this task in our seen task table - { - U64 hash = u64_hash_from_str8(str8_struct(&t->off)); - U64 slot_idx = hash%seen_task_slots_count; - SLLStackPush(seen_task_slots[slot_idx], t); - } - - // rjf: unpack unit - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[t->unit_idx]; - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[t->unit_idx]; - - // rjf: read/hash the full tag tree at `t_off`; kick off additional - // tasks for referenced dependency types - S64 depth = 0; - B32 hash_should_include_children = 1; - for(;unit_info_tag_range.min <= t_off && t_off < unit_info_tag_range.max && (depth > 0 || t_off == t->off);) - { - Temp tag_scratch = scratch_begin(&dedup_root_scratch.arena, 1); - U64 t_start_off = t_off; - - // rjf: read tag - DW2_Tag tag = {0}; - t_off += dw2_read_tag(tag_scratch.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, t_off, &tag); - - // rjf: determine if tag should be skipped from hashing - B32 should_skip_tag = (tag.kind == DW_TagKind_LexicalBlock || - tag.kind == DW_TagKind_Variable); - - // rjf: determine if we want to consider tag children as part of the content hash - if(t_start_off == t->off && tag.kind == DW_TagKind_Namespace) - { - hash_should_include_children = 0; - } - - // rjf: if we want to combine this tag into the hash -> combine - if(!should_skip_tag) - { - // rjf: combine tag's kind - hash = u64_hash_from_seed_str8(hash, str8_struct(&tag.kind)); - - // rjf: combine non-reference attributes (references could be different, - // because of deduping, but they could match ultimately). for any - // referenced dependency types, kick them off - for(DW2_AttribNode *n = tag.attribs.first; n != 0; n = n->next) - { - // rjf: non-reference? -> combine attribute value info - if(n->v.val.kind != DW_FormKind_RefAddr && - n->v.val.kind != DW_FormKind_Ref1 && - n->v.val.kind != DW_FormKind_Ref2 && - n->v.val.kind != DW_FormKind_Ref4 && - n->v.val.kind != DW_FormKind_Ref8 && - n->v.val.kind != DW_FormKind_RefUData && - n->v.val.kind != DW_FormKind_RefSup4 && - n->v.val.kind != DW_FormKind_RefSig8 && - ((tag.kind != DW_TagKind_SubProgram && - tag.kind != DW_TagKind_FormalParameter) || - (n->v.attrib_kind != DW_AttribKind_Name && - n->v.attrib_kind != DW_AttribKind_DeclFile && - n->v.attrib_kind != DW_AttribKind_DeclLine && - n->v.attrib_kind != DW_AttribKind_Prototyped && - n->v.attrib_kind != DW_AttribKind_External && - n->v.attrib_kind != DW_AttribKind_FrameBase && - n->v.attrib_kind != DW_AttribKind_Location && - n->v.attrib_kind != DW_AttribKind_LowPc && - n->v.attrib_kind != DW_AttribKind_HighPc))) - { - hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.kind)); - if(n->v.val.string.size != 0) - { - hash = u64_hash_from_seed_str8(hash, n->v.val.string); - } - else if(n->v.val.addr != 0) - { - hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.addr)); - } - else - { - hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.u128)); - } - } - - // rjf: type reference? -> if seen, combine the order; if not, recurse - if(n->v.attrib_kind == DW_AttribKind_Type) - { - // rjf: unpack reference - U64 ref_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &n->v.val); - - // rjf: determine if we've seen this reference - B32 already_seen = 0; - U64 already_seen_order_idx = 0; - { - U64 off_hash = u64_hash_from_str8(str8_struct(&ref_info_off)); - U64 off_slot_idx = off_hash%seen_task_slots_count; - for(TagTask *t = seen_task_slots[off_slot_idx]; t != 0; t = t->next) - { - if(t->off == ref_info_off) - { - already_seen = 1; - already_seen_order_idx = t->order_idx; - break; - } - } - } - - // rjf: if we've seen -> hash the order - if(already_seen) - { - hash = u64_hash_from_seed_str8(hash, str8_struct(&already_seen_order_idx)); - } - - // rjf: if we've not seen -> descend - if(!already_seen) - { - TagTask *dependency_task = free_task; - if(dependency_task != 0) - { - SLLStackPop(free_task); - } - else - { - dependency_task = push_array(dedup_root_scratch.arena, TagTask, 1); - } - dependency_task->next = next; - next = dependency_task; - dependency_task->off = ref_info_off; - dependency_task->unit_idx = t->unit_idx; - if(!contains_1u64(unit_info_tag_range, dependency_task->off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, dependency_task->off); - if(0 < new_unit_num && new_unit_num <= unit_count) - { - dependency_task->unit_idx = new_unit_num-1; - } - } - dependency_task->order_idx = t->order_idx+1; - } - } - } - } - - // rjf: tree nav if we need to - if(hash_should_include_children) - { - if(tag.has_children) - { - depth += 1; - } - if(tag.kind == DW_TagKind_Null) - { - depth -= 1; - depth = Max(0, depth); - } - } - scratch_end(tag_scratch); - } - } - } - - //- rjf: atomically gather this hash if not already gathered - { - B32 gathered = 0; - U64 slot_idx = hash%unique_tag_slots_count; - for(;!gathered;) - { - // rjf: read existing slot head pointer *before* we lookup / insert - U64 slot_head_val = (U64)ins_atomic_u64_eval(&unique_tag_slots[slot_idx]); - - // rjf: determine if this hash has been gathered - D2R2_UniqueTagNode *already_gathered_node = 0; - for(D2R2_UniqueTagNode *n = (D2R2_UniqueTagNode *)slot_head_val; n != 0; n = n->next) - { - if(n->hash == hash) - { - gathered = 1; - already_gathered_node = n; - break; - } - } - - // rjf: if this hash *has* been gathered, we want to prefer a - // deterministic unit/info offset - so we'll just prefer the - // lowest unit/offset. - // - // TODO(rjf): this seems to only be necessary because of a flaw - // in the deduplicator logic (without this part). in theory, if we - // trust the hash (all experiments suggested that we should - e.g. - // no hash collisions w/ different data were observed), then the - // same hash should imply an identical type graph. this may be false - // for reasons I do not currently understand, or due to a mistake - // somewhere. but for now, I've added this, to ensure that when there - // are many type tag trees which map to an identical hash, we always - // parse the one from the lowest .debug_info offset, just to make - // sure it's deterministic. in a multi-threaded conversion, this - // is not true in general without this step; one thread which finds - // the same type with a higher .debug_info offset may win, and so - // where exactly one type hash is parsed is not deterministic. but, - // the theory is, that shouldn't matter - the type graphs should - // match nonetheless. in any case, if that turns out to be a broken - // theory, this should be a relatively cheap step to guarantee - // deterministic (hash -> .debug_info offset), which is likely a - // desirable property regardless. but I wanted to leave this here - // so that I can more closely verify what is going on later. - // - if(gathered && already_gathered_node != 0) - { - B32 minimized = 0; - for(;!minimized;) - { - U64 stored_info_off = ins_atomic_u64_eval(&already_gathered_node->info_off); - if(start_off < stored_info_off) - { - if(ins_atomic_u64_eval_cond_assign(&already_gathered_node->info_off, start_off, stored_info_off) == stored_info_off) - { - minimized = 1; - } - } - else - { - minimized = 1; - } - } - } - - // rjf: if this hash has *not* been gathered, try an insert. we: - // - // 1. allocate/fill a node - // 2. set it up to point to the old head - // 3. compare/exchange the old head with the new head - IFF the head matches what we expect from above - // 4. if we fail, another thread has touched this slot, we pop the allocated node & try again - // (we may find that another thread has filled this hash, so we'll just be done) - // - if(!gathered) - { - Temp insert_temp = temp_begin(scratch.arena); - D2R2_UniqueTagNode *n = push_array(scratch.arena, D2R2_UniqueTagNode, 1); - n->next = (D2R2_UniqueTagNode *)slot_head_val; - n->kind = unique_tag_kind; - n->hash = hash; - n->info_off = start_off; - n->container_ancestor_info_off = container_ancestor_info_off; - U64 new_head_val = (U64)n; - if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unique_tag_slots[slot_idx], new_head_val, slot_head_val)) - { - gathered = 1; - } - else - { - temp_end(insert_temp); - } - } - } - } - - //- rjf: atomically record this (info_off -> hash) mapping, so that when we have - // later references to this type, we can redirect to the deduplicated type with - // the right hash later. - { - U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_maps[origin_unit_idx].slots_count; - D2R2_UnitDedupedTagNode *n = push_array(scratch.arena, D2R2_UnitDedupedTagNode, 1); - n->src_info_off = start_off; - n->dst_hash = hash; - for(B32 inserted = 0; !inserted;) - { - U64 slot_head_val = ins_atomic_u64_eval(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx]); - n->next = (D2R2_UnitDedupedTagNode *)slot_head_val; - if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx], (U64)n, slot_head_val)) - { - inserted = 1; - } - } - } - scratch_end(dedup_root_scratch); - } - - scratch_end(work_scratch); - } - lane_sync(); - } - } - - //////////////////////////// - //- rjf: sort all unique tag table slots - // - ProfScope("sort all unique tag table slots") - { - Rng1U64 range = lane_range(unique_tag_slots_count); - for EachInRange(slot_idx, range) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - D2R2_UniqueTagNode **slot = &unique_tag_slots[slot_idx]; - - // rjf: count nodes in this slot - U64 node_count = 0; - for EachNode(n, D2R2_UniqueTagNode, slot[0]) - { - node_count += 1; - } - - // rjf: flatten - D2R2_UniqueTagNode **slot_nodes_array = push_array(scratch2.arena, D2R2_UniqueTagNode *, node_count); - { - U64 idx = 0; - for EachNode(n, D2R2_UniqueTagNode, slot[0]) - { - slot_nodes_array[idx] = n; - idx += 1; - } - } - - // rjf: sort - radsort(slot_nodes_array, node_count, (int (*)(void *, void *))d2r2_unique_tag_node_is_less_than); - - // rjf: re-order in the slot - { - unique_tag_slots[slot_idx] = 0; - for EachIndex(idx, node_count) - { - SLLStackPush(unique_tag_slots[slot_idx], slot_nodes_array[idx]); - } - } - - scratch_end(scratch2); - } - lane_sync(); - } - - //////////////////////////// - //- rjf: produce [0...n) <-> unique-tag-node mapping for all categories of deduplicated tags - // - U64 *deduped_tag_counts = 0; // [D2R2_UniqueTagKind_COUNT] - D2R2_UniqueTagNode ***deduped_tag_nodes = 0; // [D2R2_UniqueTagKind_COUNT] - ProfScope("produce [0...n) -> hash mapping for all categories of deduplicated tags") if(lane_idx() == 0) - { - deduped_tag_counts = push_array(scratch.arena, U64, D2R2_UniqueTagKind_COUNT); - deduped_tag_nodes = push_array(scratch.arena, D2R2_UniqueTagNode **, D2R2_UniqueTagKind_COUNT); - for EachIndex(slot_idx, unique_tag_slots_count) - { - for EachNode(n, D2R2_UniqueTagNode, unique_tag_slots[slot_idx]) - { - deduped_tag_counts[n->kind] += 1; - } - } - for EachEnumVal(D2R2_UniqueTagKind, k) - { - deduped_tag_nodes[k] = push_array(scratch.arena, D2R2_UniqueTagNode *, deduped_tag_counts[k]); - } - U64 idxs[D2R2_UniqueTagKind_COUNT] = {0}; - for EachIndex(slot_idx, unique_tag_slots_count) - { - for EachNode(n, D2R2_UniqueTagNode, unique_tag_slots[slot_idx]) - { - deduped_tag_nodes[n->kind][idxs[n->kind]] = n; - n->order_idx = idxs[n->kind]; - idxs[n->kind] += 1; - } - } - } - lane_sync_u64(&deduped_tag_counts, 0); - lane_sync_u64(&deduped_tag_nodes, 0); - U64 type_count = deduped_tag_counts[D2R2_UniqueTagKind_Type]; - D2R2_UniqueTagNode **type_tag_nodes = deduped_tag_nodes[D2R2_UniqueTagKind_Type]; - U64 namespace_count = deduped_tag_counts[D2R2_UniqueTagKind_Namespace]; - D2R2_UniqueTagNode **namespace_tag_nodes = deduped_tag_nodes[D2R2_UniqueTagKind_Namespace]; - - //////////////////////////// - //- rjf: gather per-type dependency chains - // - typedef struct TypeDepChain TypeDepChain; - struct TypeDepChain - { - TypeDepChain *next; - U64 type_idx; - }; - U64 *type_dep_chains_counts = 0; - ProfScope("gather per-type dependency chains") - { - if(lane_idx() == 0) - { - type_dep_chains_counts = push_array(scratch.arena, U64, type_count); - } - lane_sync_u64(&type_dep_chains_counts, 0); - Rng1U64 range = lane_range(type_count); - for EachInRange(root_type_idx, range) - { - typedef struct TypeChainTask TypeChainTask; - struct TypeChainTask - { - TypeChainTask *next; - U64 hash; - }; - TypeChainTask start_task = {0, type_tag_nodes[root_type_idx]->hash}; - TypeChainTask *top_task = &start_task; - TypeChainTask *free_task = 0; - TypeChainTask *last_t = 0; - for(TypeChainTask *t = top_task; t != 0; (last_t = t, t = t->next)) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - // rjf: recycle old tasks - if(last_t != 0) - { - SLLStackPush(free_task, last_t); - } - - // rjf: unpack task - U64 hash = t->hash; - - // rjf: hash -> unique type tag node - D2R2_UniqueTagNode *type_tag_node = 0; - { - U64 slot_idx = hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next) - { - if(n->hash == hash) - { - type_tag_node = n; - break; - } - } - } - - // rjf: unpack type tag node - U64 info_off = 0; - U64 unit_idx = 0; - if(type_tag_node != 0) - { - info_off = type_tag_node->info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - unit_idx = unit_num > 0 ? unit_num-1 : 0; - } - - // rjf: record this type in the dependency chain count - if(type_tag_node != 0) - { - type_dep_chains_counts[root_type_idx] += 1; - } - - // rjf: unpack unit - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - - // rjf: parse this type's tag - U64 read_off = info_off; - DW2_Tag tag = {0}; - read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &tag); - - // rjf: find direct types from this type tag - typedef struct DirectTypeNode DirectTypeNode; - struct DirectTypeNode - { - DirectTypeNode *next; - U64 info_off; - U64 unit_idx; - }; - DirectTypeNode *first_direct_type = 0; - DirectTypeNode *last_direct_type = 0; - switch(tag.kind) - { - default:{}break; - case DW_TagKind_PointerType: - case DW_TagKind_ReferenceType: - case DW_TagKind_RValueReferenceType: - case DW_TagKind_RestrictType: - case DW_TagKind_VolatileType: - case DW_TagKind_ConstType: - case DW_TagKind_ArrayType: - case DW_TagKind_Typedef: - case DW_TagKind_SubProgram: - case DW_TagKind_SubroutineType: - case DW_TagKind_EnumerationType: - { - // rjf: gather direct type - { - DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Type); - U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); - U64 direct_type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); - if(0 < new_unit_num && new_unit_num <= unit_count) - { - direct_type_unit_idx = new_unit_num-1; - } - } - DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1); - n->info_off = direct_type_info_off; - n->unit_idx = direct_type_unit_idx; - SLLQueuePush(first_direct_type, last_direct_type, n); - } - - // rjf: functions -> gather parameters - if(tag.has_children && (tag.kind == DW_TagKind_SubProgram || tag.kind == DW_TagKind_SubroutineType)) - { - S64 depth = 1; - for(;depth > 0 && contains_1u64(unit_info_tag_range, read_off);) - { - U64 start_read_off = read_off; - - // rjf: read child tag - DW2_Tag child_tag = {0}; - read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &child_tag); - - // rjf: formal parameters -> gather direct types - if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) - { - DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type); - U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); - U64 direct_type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); - if(0 < new_unit_num && new_unit_num <= unit_count) - { - direct_type_unit_idx = new_unit_num-1; - } - } - DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1); - n->info_off = direct_type_info_off; - n->unit_idx = direct_type_unit_idx; - SLLQueuePush(first_direct_type, last_direct_type, n); - } - - // rjf: tree navigations - if(child_tag.has_children) - { - depth += 1; - } - if(child_tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - - if(read_off == start_read_off) - { - break; - } - } - } - }break; - } - - // rjf: for each dependency type, look up their hash, + spawn new tasks for them - for EachNode(n, DirectTypeNode, first_direct_type) - { - U64 direct_type_info_off = n->info_off; - U64 direct_type_unit_idx = n->unit_idx; - - // rjf: direct type info offset -> hash - U64 direct_type_hash = 0; - { - U64 off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off)); - U64 off_slot_idx = off_hash%unit_deduped_tag_maps[direct_type_unit_idx].slots_count; - for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_maps[direct_type_unit_idx].slots[off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == direct_type_info_off) - { - direct_type_hash = n->dst_hash; - break; - } - } - } - - // rjf: spawn task - if(direct_type_hash != 0) - { - TypeChainTask *new_task = free_task; - if(new_task != 0) - { - SLLStackPop(free_task); - } - else - { - new_task = push_array(scratch.arena, TypeChainTask, 1); - } - new_task->next = t->next; - new_task->hash = direct_type_hash; - t->next = new_task; - } - } - - scratch_end(scratch2); - } - } - lane_sync(); - } - - //////////////////////////// - //- rjf: build all types - build types w/ 1 dependency (leaves) first, then 2, 3, etc., - // to ensure dependencies always travel backwards - // - // NOTE: * DWARF vs RDI Array Type Graph * - // - // For example lets take following decl: - // - // int (*foo[2])[3]; - // - // This compiles to in DWARF: - // - // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] - // \ - // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] - // \ - // -> (B1) DW_TAG_BaseType (int) - // - // RDI expects: - // - // foo -> Array[2] -> Pointer -> Array[3] -> int - // - // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and - // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. - // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from - // B to A. - // - RDIM_TypeChunkList *all_types = 0; - RDIM_Type **type_from_idx_map = 0; - ProfScope("build all types") - { - if(lane_idx() == 0) - { - all_types = push_array(scratch.arena, RDIM_TypeChunkList, 1); - type_from_idx_map = push_array(scratch.arena, RDIM_Type *, type_count); - rdim_type_chunk_list_concat_in_place(all_types, builtin_types); - } - lane_sync_u64(&all_types, 0); - lane_sync_u64(&type_from_idx_map, 0); - Rng1U64 range = lane_range(type_count); - U64 max_chain_count = 1; - for(;max_chain_count < max_U64;) - { - 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; - // find this lane's next dependency restriction - U64 next_max_chain_count = max_U64; - RDIM_TypeChunkList lane_types = {0}; - U64 lane_types_chunk_count = 256; - for EachInRange(type_idx, range) - { - // rjf: if this type has a higher chain count than the current, - // but it is lower than our next maximum chain count, collect it, - // so we will hit - // - // if this type has a lower chain count than the current, - // we should've already built it from a previous pass. - // - if(type_dep_chains_counts[type_idx] > max_chain_count) - { - next_max_chain_count = Min(type_dep_chains_counts[type_idx], next_max_chain_count); - continue; - } - else if(type_dep_chains_counts[type_idx] < max_chain_count) - { - continue; - } - Temp temp = temp_begin(scratch2.arena); - - // rjf: idx -> type tag node - D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; - U64 info_off = type_tag_node->info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - - // rjf: unpack unit - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - - // rjf: parse root-level tag - DW2_Tag tag = {0}; - U64 tag_info_size = dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); - - // rjf: extract common attributes - DW2_Attrib *name_attrib = &dw2_attrib_nil; - DW2_Attrib *direct_type_attrib = &dw2_attrib_nil; - DW2_Attrib *bitsize_attrib = &dw2_attrib_nil; - DW2_Attrib *bytesize_attrib = &dw2_attrib_nil; - DW2_Attrib *encoding_attrib = &dw2_attrib_nil; - DW2_Attrib *decl_attrib = &dw2_attrib_nil; - DW2_Attrib *prototyped_attrib = &dw2_attrib_nil; - // - // TODO(rjf): if a DW_AttribKing_GNU_Vector is found on an DW_TagKind_ArrayType, - // then: @native_vector_support extract byte size from the base type tag - // and convert to U256, U512, S256 and S512 - // - for EachNode(n, DW2_AttribNode, tag.attribs.first) - { - switch(n->v.attrib_kind) - { - default:{}break; -#define Case(dst_name, src_name) case DW_AttribKind_##src_name:{dst_name##_attrib = &n->v;}break - Case(name, Name); - Case(direct_type, Type); - Case(bitsize, BitSize); - Case(bytesize, ByteSize); - Case(encoding, Encoding); - Case(decl, Declaration); -#undef Case - } - } - - // rjf: unpack common attributes - String8 name = name_attrib->val.string; - DW_ATE encoding = encoding_attrib->val.u128.u64[0]; - RDIM_Type *direct_type = d2r2_type_from_builtin_kind(RDI_TypeKind_Void); - U64 bitsize = 0; - B32 is_decl = (decl_attrib != &dw2_attrib_nil); - { - // rjf: unpack direct type - if(direct_type_attrib != &dw2_attrib_nil) - { - U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val); - U64 direct_type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, direct_type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off); - direct_type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); - } - D2R2_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[direct_type_unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off)); - U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count; - U64 direct_type_hash = 0; - for(D2R2_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == direct_type_info_off) - { - direct_type_hash = n->dst_hash; - break; - } - } - U64 unique_type_tag_slot_idx = direct_type_hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == direct_type_hash) - { - direct_type = type_from_idx_map[n->order_idx]; - break; - } - } - } - - // rjf: unpack bit/byte sizes - if(bitsize_attrib != &dw2_attrib_nil) - { - bitsize = bitsize_attrib->val.u128.u64[0]; - } - else if(bytesize_attrib != &dw2_attrib_nil) - { - bitsize = bytesize_attrib->val.u128.u64[0]*8; - } - } - - // rjf: convert type - RDIM_Type *dst_type = 0; - RDI_TypeKind rdi_type_kind = RDI_TypeKind_NULL; - RDI_TypeModifierFlags rdi_type_modifier_flags = 0; - switch(tag.kind) - { - default:{}break; - case DW_TagKind_ClassType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_Class; goto struct_type; - case DW_TagKind_StructureType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteStruct : RDI_TypeKind_Struct; goto struct_type; - case DW_TagKind_UnionType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteUnion : RDI_TypeKind_Union; goto struct_type; - struct_type:; - { - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = rdi_type_kind; - dst_type->name = name; - dst_type->byte_size = bitsize/8; - }break; - case DW_TagKind_EnumerationType: - { - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = is_decl ? RDI_TypeKind_IncompleteEnum : RDI_TypeKind_Enum; - dst_type->name = name; - dst_type->direct_type = direct_type; - dst_type->byte_size = (direct_type != 0 ? direct_type->byte_size : bitsize/8); - }break; - case DW_TagKind_SubProgram: - case DW_TagKind_SubroutineType: - { - // rjf: build type - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = RDI_TypeKind_Function; - dst_type->direct_type = direct_type; - dst_type->byte_size = arch_addr_size; - - // rjf: gather all parameter types - typedef struct ParamNode ParamNode; - struct ParamNode - { - ParamNode *next; - U64 type_unit_idx; - U64 type_info_off; - }; - ParamNode *first_param = 0; - ParamNode *last_param = 0; - U64 param_count = 0; - if(tag.has_children) - { - U64 tag_children_off = info_off + tag_info_size; - S64 depth = 1; - for(U64 off = tag_children_off; depth > 0 && contains_1u64(unit_info_tag_range, off);) - { - Temp scratch3 = scratch_begin(&scratch2.arena, 1); - U64 start_off = off; - - // rjf: read child tag - DW2_Tag child_tag = {0}; - off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); - - // rjf: gather parameters - if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) - { - DW2_Attrib *type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type); - ParamNode *n = push_array(scratch2.arena, ParamNode, 1); - SLLQueuePush(first_param, last_param, n); - n->type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); - n->type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, n->type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, n->type_info_off); - n->type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); - } - param_count += 1; - } - - // rjf: navigate tree - if(child_tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - if(child_tag.has_children) - { - depth += 1; - } - - scratch_end(scratch3); - if(off == start_off) - { - break; - } - } - } - - // rjf: tighten parameter types - dst_type->count = (RDI_U32)param_count; // TODO(rjf): @u64_to_u32 - dst_type->param_types = push_array(arena, RDIM_Type *, dst_type->count); - { - U64 param_idx = 0; - for(ParamNode *n = first_param; n != 0; n = n->next, param_idx += 1) - { - U64 param_type_info_off = n->type_info_off; - U64 param_type_unit_idx = n->type_unit_idx; - - // rjf: map info_off/unit_idx -> hash - U64 param_type_hash = 0; - { - D2R2_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[param_type_unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(¶m_type_info_off)); - U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count; - for(D2R2_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == param_type_info_off) - { - param_type_hash = n->dst_hash; - break; - } - } - } - - // rjf: map hash -> type - RDIM_Type *param_type = 0; - { - U64 unique_type_tag_slot_idx = param_type_hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == param_type_hash) - { - param_type = type_from_idx_map[n->order_idx]; - break; - } - } - } - - // rjf: store - dst_type->param_types[param_idx] = param_type; - } - } - }break; - case DW_TagKind_Typedef: - { - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = RDI_TypeKind_Alias; - dst_type->name = name; - dst_type->direct_type = direct_type; - dst_type->byte_size = (direct_type ? direct_type->byte_size : 0); - }break; - case DW_TagKind_BaseType: - { - if(0){} -#define CaseA(rdi_type_kind_, encoding_) else if(encoding == (encoding_)) { rdi_type_kind = (rdi_type_kind_); } -#define CaseB(rdi_type_kind_, encoding_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_)) { rdi_type_kind = (rdi_type_kind_); } -#define CaseC(rdi_type_kind_, encoding_, name_) /* assumes x64/arm64 */ else if(encoding == (encoding_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); } -#define CaseD(rdi_type_kind_, encoding_, name_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); } - CaseA(RDI_TypeKind_NULL, DW_ATE_Null) - CaseA(RDI_TypeKind_Void, DW_ATE_Address) - CaseA(RDI_TypeKind_Bool, DW_ATE_Boolean) - CaseB(RDI_TypeKind_ComplexF32, DW_ATE_ComplexFloat, 64) - CaseB(RDI_TypeKind_ComplexF64, DW_ATE_ComplexFloat, 128) - CaseB(RDI_TypeKind_ComplexF80, DW_ATE_ComplexFloat, 160) - CaseB(RDI_TypeKind_ComplexF128, DW_ATE_ComplexFloat, 256) - CaseC(RDI_TypeKind_F80, DW_ATE_Float, "__float80") - CaseC(RDI_TypeKind_F128, DW_ATE_Float, "__float128") - CaseC(RDI_TypeKind_F16, DW_ATE_Float, "_Float16") - CaseC(RDI_TypeKind_BF16, DW_ATE_Float, "__bf16") - CaseB(RDI_TypeKind_F16, DW_ATE_Float, 16) - CaseB(RDI_TypeKind_F32, DW_ATE_Float, 32) - CaseB(RDI_TypeKind_F48, DW_ATE_Float, 48) - CaseB(RDI_TypeKind_F64, DW_ATE_Float, 64) - CaseB(RDI_TypeKind_F80, DW_ATE_Float, 80) - CaseB(RDI_TypeKind_F96, DW_ATE_Float, 96) - CaseB(RDI_TypeKind_F128, DW_ATE_Float, 128) - CaseB(RDI_TypeKind_Char8, DW_ATE_SignedChar, 8) - CaseB(RDI_TypeKind_Char16, DW_ATE_SignedChar, 16) - CaseB(RDI_TypeKind_Char32, DW_ATE_SignedChar, 32) - CaseD(RDI_TypeKind_Char8, DW_ATE_Signed, "wchar_t", 8) - CaseD(RDI_TypeKind_Char16, DW_ATE_Signed, "wchar_t", 16) - CaseD(RDI_TypeKind_Char32, DW_ATE_Signed, "wchar_t", 32) - CaseB(RDI_TypeKind_S8, DW_ATE_Signed, 8) - CaseB(RDI_TypeKind_S16, DW_ATE_Signed, 16) - CaseB(RDI_TypeKind_S32, DW_ATE_Signed, 32) - CaseB(RDI_TypeKind_S64, DW_ATE_Signed, 64) - CaseB(RDI_TypeKind_S128, DW_ATE_Signed, 128) - CaseB(RDI_TypeKind_S256, DW_ATE_Signed, 256) - CaseB(RDI_TypeKind_S512, DW_ATE_Signed, 512) - CaseB(RDI_TypeKind_U8, DW_ATE_Unsigned, 8) - CaseB(RDI_TypeKind_U16, DW_ATE_Unsigned, 16) - CaseB(RDI_TypeKind_U32, DW_ATE_Unsigned, 32) - CaseB(RDI_TypeKind_U64, DW_ATE_Unsigned, 64) - CaseB(RDI_TypeKind_U128, DW_ATE_Unsigned, 128) - CaseB(RDI_TypeKind_U256, DW_ATE_Unsigned, 256) - CaseB(RDI_TypeKind_U512, DW_ATE_Unsigned, 512) - CaseB(RDI_TypeKind_UChar8, DW_ATE_UnsignedChar, 8) - CaseB(RDI_TypeKind_UChar8, DW_ATE_Utf, 8) - CaseB(RDI_TypeKind_UChar16, DW_ATE_UnsignedChar, 16) - CaseB(RDI_TypeKind_UChar16, DW_ATE_Utf, 16) - CaseB(RDI_TypeKind_UChar32, DW_ATE_UnsignedChar, 32) - CaseB(RDI_TypeKind_UChar32, DW_ATE_Utf, 32) - CaseB(RDI_TypeKind_Decimal32, DW_ATE_DecimalFloat, 32) - CaseB(RDI_TypeKind_Decimal64, DW_ATE_DecimalFloat, 64) - CaseB(RDI_TypeKind_Decimal128, DW_ATE_DecimalFloat, 128) -#undef CaseA -#undef CaseB -#undef CaseC -#undef CaseD - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = RDI_TypeKind_Alias; - dst_type->name = name; - dst_type->direct_type = d2r2_type_from_builtin_kind(rdi_type_kind); - dst_type->byte_size = bitsize/8; // TODO: byte size on __float80 alias mismatches byte size on direct type now - }break; - case DW_TagKind_PointerType: rdi_type_kind = RDI_TypeKind_Ptr; goto ptr_or_ref_type; - case DW_TagKind_ReferenceType: rdi_type_kind = RDI_TypeKind_LRef; goto ptr_or_ref_type; - case DW_TagKind_RValueReferenceType: rdi_type_kind = RDI_TypeKind_RRef; goto ptr_or_ref_type; - ptr_or_ref_type:; - { - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = rdi_type_kind; - dst_type->direct_type = direct_type; - dst_type->byte_size = arch_addr_size; - }break; - case DW_TagKind_RestrictType: - { - rdi_type_modifier_flags = RDI_TypeModifierFlag_Restrict; - }goto basic_type_operators; - case DW_TagKind_VolatileType: - { - rdi_type_modifier_flags = RDI_TypeModifierFlag_Volatile; - }goto basic_type_operators; - case DW_TagKind_ConstType: - { - rdi_type_modifier_flags = RDI_TypeModifierFlag_Const; - }goto basic_type_operators; - basic_type_operators:; - { - dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - dst_type->kind = RDI_TypeKind_Modifier; - dst_type->flags = rdi_type_modifier_flags; - dst_type->direct_type = direct_type; - dst_type->byte_size = (direct_type ? direct_type->byte_size : 0); - }break; - case DW_TagKind_ArrayType: - if(tag.has_children) - { - // rjf: parse array type children; extract dimensions (each one gets a SubrangeType) - typedef struct ArrayDimensionNode ArrayDimensionNode; - struct ArrayDimensionNode - { - ArrayDimensionNode *next; - U64 count; - }; - ArrayDimensionNode *top_dimension = 0; - { - U64 tag_children_off = info_off + tag_info_size; - S64 depth = 1; - for(U64 off = tag_children_off; contains_1u64(unit_info_tag_range, off) && depth > 0;) - { - U64 start_off = off; - DW2_Tag child_tag = {0}; - off += dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); - if(child_tag.kind == DW_TagKind_SubrangeType) - { - // rjf: extract bounds attribs - DW2_Attrib *lower_bound_attrib = &dw2_attrib_nil; - DW2_Attrib *upper_bound_attrib = &dw2_attrib_nil; - DW2_Attrib *count_attrib = &dw2_attrib_nil; - for(DW2_AttribNode *n = child_tag.attribs.first; - n != 0 && (lower_bound_attrib == &dw2_attrib_nil || (upper_bound_attrib == &dw2_attrib_nil && count_attrib == &dw2_attrib_nil)); - n = n->next) - { - if(n->v.attrib_kind == DW_AttribKind_LowerBound) - { - lower_bound_attrib = &n->v; - } - else if(n->v.attrib_kind == DW_AttribKind_UpperBound) - { - upper_bound_attrib = &n->v; - } - else if(n->v.attrib_kind == DW_AttribKind_Count) - { - count_attrib = &n->v; - } - } - - // rjf: resolve lower bound - U64 lower_bound = 0; - if(lower_bound_attrib != &dw2_attrib_nil) - { - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, lower_bound_attrib->val.kind) & DW_AttribClass_Reference) - { - log_infof("[.debug_info@0x%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off); - } - else - { - lower_bound = lower_bound_attrib->val.u128.u64[0]; - } - } - else switch(unit_parse_ctx->language) - { - default:{}break; -#define X(name, code, default_array_lower_bound) case DW_Language_##name:{lower_bound = (default_array_lower_bound);}break; - DW_Language_XList -#undef X - } - - // rjf: resolve upper bound - U64 upper_bound = 0; - { - if(count_attrib != &dw2_attrib_nil) - { - upper_bound = lower_bound + count_attrib->val.u128.u64[0]; - } - else if(upper_bound_attrib != &dw2_attrib_nil) - { - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, upper_bound_attrib->val.kind) & DW_AttribClass_Reference) - { - log_infof("[.debug_info@0x%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off); - } - else - { - upper_bound = upper_bound_attrib->val.u128.u64[0]; - upper_bound += 1; // NOTE(rjf): turn to exclusive range - } - } - } - - // rjf: push node - ArrayDimensionNode *dim_n = push_array(temp.arena, ArrayDimensionNode, 1); - SLLStackPush(top_dimension, dim_n); - dim_n->count = (upper_bound - lower_bound); - } - if(child_tag.has_children) - { - depth += 1; - } - else if(child_tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - if(off == start_off) - { - break; - } - } - } - - // rjf: create array type operators for each dimension - RDIM_Type *array_direct_type = direct_type; - for EachNode(dim_n, ArrayDimensionNode, top_dimension) - { - RDIM_Type *array_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count); - array_type->kind = RDI_TypeKind_Array; - array_type->byte_size = array_direct_type ? array_direct_type->byte_size*dim_n->count : 0; - array_type->direct_type = array_direct_type; - array_direct_type = array_type; - } - - // rjf: final destination type -> the final array dimension type - dst_type = array_direct_type; - }break; - } - - // rjf: store type in type-from-idx table - type_from_idx_map[type_idx] = dst_type; - - temp_end(temp); - } - - //- rjf: combine all types from all lanes - RDIM_TypeChunkList *lanes_types = 0; - if(lane_idx() == 0) - { - lanes_types = push_array(scratch2.arena, RDIM_TypeChunkList, lane_count()); - } - lane_sync_u64(&lanes_types, 0); - lanes_types[lane_idx()] = lane_types; - lane_sync(); - if(lane_idx() == 0) - { - RDIM_TypeChunkList pass_lane_combined_types = {0}; - for EachIndex(l_idx, lane_count()) - { - rdim_type_chunk_list_concat_in_place(&pass_lane_combined_types, &lanes_types[l_idx]); - } - rdim_type_chunk_list_concat_in_place(all_types, &pass_lane_combined_types); - } - lane_sync(); - - //- rjf: find minimum next max chain count across all lanes, for next iteration - U64 *lane_next_max_chain_counts = 0; - if(lane_idx() == 0) - { - lane_next_max_chain_counts = push_array(scratch2.arena, U64, lane_count()); - } - lane_sync_u64(&lane_next_max_chain_counts, 0); - lane_next_max_chain_counts[lane_idx()] = next_max_chain_count; - lane_sync(); - if(lane_idx() == 0) - { - for EachIndex(l_idx, lane_count()) - { - next_max_chain_count = Min(next_max_chain_count, lane_next_max_chain_counts[l_idx]); - } - } - lane_sync_u64(&next_max_chain_count, 0); - - //- rjf: update max chain count - max_chain_count = next_max_chain_count; - - ProfEnd(); - scratch_end(scratch2); - } - } - lane_sync(); - - //////////////////////////// - //- rjf: convert all namespaces - // - RDIM_NamespaceChunkList *all_namespaces = 0; - RDIM_Namespace **namespace_from_idx_map = 0; - { - // rjf: set up - if(lane_idx() == 0) - { - all_namespaces = push_array(scratch.arena, RDIM_NamespaceChunkList, 1); - namespace_from_idx_map = push_array(scratch.arena, RDIM_Namespace *, namespace_count); - } - lane_sync_u64(&all_namespaces, 0); - lane_sync_u64(&namespace_from_idx_map, 0); - - // rjf: build all namespaces on this lane - RDIM_NamespaceChunkList lane_namespaces = {0}; - Rng1U64 range = lane_range(namespace_count); - for EachInRange(namespace_idx, range) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - // rjf: unpack tag node - D2R2_UniqueTagNode *tag_node = namespace_tag_nodes[namespace_idx]; - U64 info_off = tag_node->info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - - // rjf: read namespace tag - DW2_Tag tag = {0}; - dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); - DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name); - String8 name = name_attrib->val.string; - - // rjf: build namespace - RDIM_Namespace *ns = rdim_namespace_chunk_list_push(arena, &lane_namespaces, 64); - ns->name = name; - - // rjf: store in (deduped tag idx -> namespace) table - namespace_from_idx_map[tag_node->order_idx] = ns; - - scratch_end(scratch2); - } - lane_sync(); - - // rjf: join all namespaces - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - RDIM_NamespaceChunkList *lanes_namespaces = 0; - if(lane_idx() == 0) - { - lanes_namespaces = push_array(scratch2.arena, RDIM_NamespaceChunkList, lane_count()); - } - lane_sync_u64(&lanes_namespaces, 0); - lanes_namespaces[lane_idx()] = lane_namespaces; - lane_sync(); - if(lane_idx() == 0) - { - for EachIndex(l_idx, lane_count()) - { - rdim_namespace_chunk_list_concat_in_place(all_namespaces, &lanes_namespaces[l_idx]); - } - } - lane_sync(); - scratch_end(scratch2); - } - } - lane_sync(); - - //////////////////////////// - //- rjf: convert all UDTs - // - RDIM_UDTChunkList *all_udts = 0; - ProfScope("convert all UDTs") - { - //- rjf: produce UDTs across all lanes - U64 chunk_count = 512; - RDIM_UDTChunkList lane_udts = {0}; - Rng1U64 range = lane_range(type_count); - for EachInRange(type_idx, range) - { - RDIM_Type *type = type_from_idx_map[type_idx]; - if(type != 0 && - (type->kind == RDI_TypeKind_Struct || - type->kind == RDI_TypeKind_Union || - type->kind == RDI_TypeKind_Class || - type->kind == RDI_TypeKind_Enum || - type->kind == RDI_TypeKind_Alias)) - { - // rjf: produce UDT - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &lane_udts, chunk_count); - type->udt = udt; - udt->self_type = type; - - // rjf: idx -> type tag node - D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; - U64 info_off = type_tag_node->info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - - // rjf: unpack unit - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - - // rjf: parse all tags - S64 depth = 0; - for(U64 off = info_off; contains_1u64(unit_info_tag_range, off) && (depth > 0 || off == info_off);) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - U64 start_off = off; - - // rjf: parse tag - DW2_Tag tag = {0}; - U64 tag_parse_off = off; - off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); - - // rjf: unpack tag attributes - DW2_Attrib *name_attrib = &dw2_attrib_nil; - DW2_Attrib *type_attrib = &dw2_attrib_nil; - DW2_Attrib *off_attrib = &dw2_attrib_nil; - DW2_Attrib *val_attrib = &dw2_attrib_nil; - DW2_Attrib *declfile_attrib = &dw2_attrib_nil; - DW2_Attrib *declline_attrib = &dw2_attrib_nil; - DW2_Attrib *declcol_attrib = &dw2_attrib_nil; - for EachNode(n, DW2_AttribNode, tag.attribs.first) - { - switch(n->v.attrib_kind) - { - default:{}break; -#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break - Case(name, Name); - Case(type, Type); - Case(off, DataMemberLocation); - Case(val, ConstValue); - Case(declfile, DeclFile); - Case(declline, DeclLine); - Case(declcol, DeclColumn); -#undef Case - } - } - - // rjf: unpack basic attributes - String8 name = name_attrib->val.string; - U64 off = off_attrib->val.u128.u64[0]; - U64 val = val_attrib->val.u128.u64[0]; - - // rjf: unpack type - RDIM_Type *type = 0; - if(type_attrib != &dw2_attrib_nil) - { - // rjf: attrib -> info off / unit idx - U64 type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); - U64 type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off); - type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); - } - - // rjf: (info_off, unit_idx) -> hash - U64 type_hash = 0; - { - D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; - for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == type_info_off) - { - type_hash = n->dst_hash; - break; - } - } - } - - // rjf: hash -> type - { - U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == type_hash) - { - type = type_from_idx_map[n->order_idx]; - break; - } - } - } - } - - // rjf: root-level tag -> collect decl file/line info - if(tag_parse_off == start_off) - { - // TODO(rjf): we need to have gathered the source files before this!!!!!! - // currently, they only come from line info - we need to gather/dedup the - // ones that also come from tags. - } - - // rjf: gather children - switch(tag.kind) - { - default:{}break; - case DW_TagKind_Member: - { - RDIM_UDTMember *member = rdim_udt_push_member(arena, &lane_udts, udt); - member->kind = RDI_MemberKind_DataField; - member->name = name; - member->type = type; - member->off = (RDI_U32)off; // TODO(rjf): @u64_to_u32 - }break; - case DW_TagKind_Enumerator: - { - RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, &lane_udts, udt); - enum_val->name = name; - enum_val->val = val; - }break; - } - - // rjf: tree nav - if(tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - if(tag.has_children) - { - depth += 1; - } - - scratch_end(scratch2); - if(off == start_off) - { - break; - } - } - } - } - lane_sync(); - - //- rjf: combine all lanes - RDIM_UDTChunkList *lanes_udts = 0; - if(lane_idx() == 0) - { - lanes_udts = push_array(scratch.arena, RDIM_UDTChunkList, lane_count()); - } - lane_sync_u64(&lanes_udts, 0); - lanes_udts[lane_idx()] = lane_udts; - lane_sync(); - if(lane_idx() == 0) - { - all_udts = push_array(scratch.arena, RDIM_UDTChunkList, 1); - for EachIndex(l_idx, lane_count()) - { - rdim_udt_chunk_list_concat_in_place(all_udts, &lanes_udts[l_idx]); - } - } - lane_sync_u64(&all_udts, 0); - } - - //////////////////////////// - //- rjf: convert all units - // - RDIM_UnitChunkList *all_units = 0; - RDIM_Unit **unit_from_idx_map = 0; - ProfScope("convert all units") - { - if(lane_idx() == 0) - { - all_units = push_array(scratch.arena, RDIM_UnitChunkList, 1); - unit_from_idx_map = push_array(scratch.arena, RDIM_Unit *, unit_count); - for EachIndex(unit_idx, unit_count) - { - unit_from_idx_map[unit_idx] = rdim_unit_chunk_list_push(arena, all_units, unit_count); - } - } - lane_sync_u64(&all_units, 0); - lane_sync_u64(&unit_from_idx_map, 0); - U64 unit_take_idx_ = 0; - U64 *unit_take_idx_ptr = &unit_take_idx_; - lane_sync_u64(&unit_take_idx_ptr, 0); - for(;;) - { - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1; - if(unit_idx >= unit_count) - { - break; - } - - //- rjf: unpack unit info - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx]; - - //- rjf: unpack unit's root tag - DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; - DW2_Attrib *name_attrib = &dw2_attrib_nil; - DW2_Attrib *comp_dir_attrib = &dw2_attrib_nil; - DW2_Attrib *producer_attrib = &dw2_attrib_nil; - DW2_Attrib *lang_attrib = &dw2_attrib_nil; - DW2_Attrib *ranges_attrib = &dw2_attrib_nil; - DW2_Attrib *lopc_attrib = &dw2_attrib_nil; - DW2_Attrib *hipc_attrib = &dw2_attrib_nil; - for EachNode(n, DW2_AttribNode, unit_root_tag->attribs.first) - { - switch(n->v.attrib_kind) - { - default:{}break; -#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break - Case(name, Name); - Case(comp_dir, CompDir); - Case(producer, Producer); - Case(lang, Language); - Case(ranges, Ranges); - Case(lopc, LowPc); - Case(hipc, HighPc); -#undef Case - } - } - - //- rjf: unpack attributes - String8 unit_name = name_attrib->val.string; - String8 unit_comp_dir = comp_dir_attrib->val.string; - String8 unit_producer = producer_attrib->val.string; - RDI_Language unit_lang = RDI_Language_NULL; - { - DW_Language dw_lang = lang_attrib->val.u128.u64[0]; - switch(dw_lang) - { - default:{}break; - case DW_Language_C89: - case DW_Language_C99: - case DW_Language_C11: - case DW_Language_C: - { - unit_lang = RDI_Language_C; - }break; - case DW_Language_CPlusPlus03: - case DW_Language_CPlusPlus11: - case DW_Language_CPlusPlus14: - case DW_Language_CPlusPlus: - { - unit_lang = RDI_Language_CPlusPlus; - }break; - } - } - - //- rjf: get unit's ranges from .debug_aranges parse artifacts, if we have them - RDIM_Rng1U64ChunkList unit_voff_ranges = {0}; - if(arange_unit_from_info_off_map_slots_count != 0) - { - U64 unit_info_off = unit_info_ranges->v[unit_idx].min; - U64 hash = u64_hash_from_str8(str8_struct(&unit_info_off)); - U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count; - for(D2R2_ARangeUnitNode *n = arange_unit_from_info_off_map_slots[slot_idx]; n != 0; n = n->next) - { - if(n->info_off == unit_info_off) - { - unit_voff_ranges = n->ranges[0]; - break; - } - } - } - - //- rjf: if we have no voff ranges from .debug_aranges, then we need to extract - // this info from the unit root rag instead (via ranges & low-pc/high-pc tags) - if(unit_voff_ranges.total_count == 0) - { - // rjf: gather ranges from a ranges attribute - if(ranges_attrib != &dw2_attrib_nil) - { - Rng1U64List ranges = dw2_rnglist_from_form_val(scratch.arena, unit_parse_ctx, raw, ranges_attrib->val); - for EachNode(n, Rng1U64Node, ranges.first) - { - rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){n->v.min - base_vaddr, n->v.max - base_vaddr}); - } - } - - // rjf: gather contiguous range from low-pc / high-pc attribute - if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil) - { - U64 voff_base = lopc_attrib->val.addr - base_vaddr; - U64 voff_opl = 0; - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; - } - else - { - voff_opl = hipc_attrib->val.addr; - } - rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){voff_base, voff_opl}); - } - } - - //- rjf: fill top-level unit info - { - dst_unit->unit_name = unit_name; - dst_unit->compiler_name = unit_producer; - // TODO(rjf): dst_unit->source_file = ???; - // TODO(rjf): dst_unit->object_file = ???; - // TODO(rjf): dst_unit->archive_file = ???; - dst_unit->build_path = unit_comp_dir; - dst_unit->language = unit_lang; - dst_unit->line_table = unit_line_tables[unit_idx]; - dst_unit->voff_ranges = unit_voff_ranges; - } - } - lane_sync(); - } - - //////////////////////////// - //- rjf: convert all symbols - // - typedef struct D2R2_ScopeContainerNode D2R2_ScopeContainerNode; - struct D2R2_ScopeContainerNode - { - D2R2_ScopeContainerNode *next; - U64 info_off; - RDIM_Scope *scope; - }; - typedef struct D2R2_ScopeContainerMap D2R2_ScopeContainerMap; - struct D2R2_ScopeContainerMap - { - D2R2_ScopeContainerNode **slots; - U64 slots_count; - }; - typedef struct D2R2_SubUnitWorkArtifacts D2R2_SubUnitWorkArtifacts; - struct D2R2_SubUnitWorkArtifacts - { - RDIM_SymbolChunkList global_variables; - RDIM_SymbolChunkList thread_variables; - RDIM_SymbolChunkList constants; - RDIM_SymbolChunkList procedures; - RDIM_ScopeChunkList scopes; - RDIM_InlineSiteChunkList inline_sites; - }; - D2R2_ScopeContainerMap *scope_container_map = 0; - D2R2_SubUnitWorkArtifacts *sub_unit_work_artifacts = 0; - ProfScope("convert all symbols") - { - if(lane_idx() == 0) - { - scope_container_map = push_array(scratch.arena, D2R2_ScopeContainerMap, 1); - scope_container_map->slots_count = total_tag_count_estimate/8 + 1; - scope_container_map->slots = push_array(scratch.arena, D2R2_ScopeContainerNode *, scope_container_map->slots_count); - sub_unit_work_artifacts = push_array(scratch.arena, D2R2_SubUnitWorkArtifacts, sub_unit_works_count); - } - lane_sync_u64(&scope_container_map, 0); - lane_sync_u64(&sub_unit_work_artifacts, 0); - U64 work_take_idx_ = 0; - U64 *work_take_idx_ptr = &work_take_idx_; - lane_sync_u64(&work_take_idx_ptr, 0); - for(;;) - { - U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1; - if(work_idx >= sub_unit_works_count) - { - break; - } - - //- rjf: unpack work - U64 unit_idx = sub_unit_works[work_idx].unit_idx; - Rng1U64 root_tag_idx_range = sub_unit_works[work_idx].root_tag_idx_range; - D2R2_SubUnitWorkArtifacts *dst_artifacts = &sub_unit_work_artifacts[work_idx]; - - //- rjf: unpack unit info - DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx]; - Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx]; - - //- rjf: produce all unit symbols in this work - U64 chunk_count = 256; - for(U64 root_tag_idx = root_tag_idx_range.min; root_tag_idx < root_tag_idx_range.max; root_tag_idx += 1) - { - typedef struct D2R2_ParentNode D2R2_ParentNode; - struct D2R2_ParentNode - { - D2R2_ParentNode *next; - DW_TagKind tag_kind; - U64 info_off; - RDIM_Scope *scope; - RDIM_Type *container_type; - RDIM_Namespace *container_namespace; - RDIM_LocationCaseList framebase_location_cases; - }; - D2R2_ParentNode *top_parent = 0; - D2R2_ParentNode *free_parent = 0; - U64 root_tag_start_off = unit_info_root_tag_offs[unit_idx].v[root_tag_idx]; - S64 depth = 1; - for(U64 off = root_tag_start_off; off < unit_info_tag_range.max && (depth > 1 || off == root_tag_start_off);) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - U64 start_off = off; - - //////////////////////// - //- rjf: parse tag - // - DW2_Tag tag = {0}; - off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); - - //////////////////////// - //- rjf: gather attributes from tag - // - DW2_Attrib *name_attrib = &dw2_attrib_nil; - DW2_Attrib *linkname_attrib = &dw2_attrib_nil; - DW2_Attrib *type_attrib = &dw2_attrib_nil; - DW2_Attrib *inline_attrib = &dw2_attrib_nil; - DW2_Attrib *ranges_attrib = &dw2_attrib_nil; - DW2_Attrib *lopc_attrib = &dw2_attrib_nil; - DW2_Attrib *hipc_attrib = &dw2_attrib_nil; - DW2_Attrib *constval_attrib = &dw2_attrib_nil; - DW2_Attrib *location_attrib = &dw2_attrib_nil; - DW2_Attrib *framebase_attrib = &dw2_attrib_nil; - DW2_Attrib *external_attrib = &dw2_attrib_nil; - DW2_Attrib *decl_attrib = &dw2_attrib_nil; - for EachNode(n, DW2_AttribNode, tag.attribs.first) - { - switch(n->v.attrib_kind) - { - default:{}break; -#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break - Case(name, Name); - Case(linkname, LinkageName); - Case(type, Type); - Case(inline, Inline); - Case(ranges, Ranges); - Case(lopc, LowPc); - Case(hipc, HighPc); - Case(constval, ConstValue); - Case(location, Location); - Case(framebase, FrameBase); - Case(external, External); - Case(decl, Declaration); -#undef Case - } - } - - //////////////////////// - //- rjf: unpack basic attributes - // - String8 name = name_attrib->val.string; - String8 link_name = linkname_attrib->val.string; - DW_InlKind inl_kind = (DW_InlKind)inline_attrib->val.u128.u64[0]; - B32 is_external = (external_attrib != &dw2_attrib_nil); - B32 is_decl = (decl_attrib != &dw2_attrib_nil); - - //////////////////////// - //- rjf: unpack ranges - // - RDIM_Rng1U64List ranges = {0}; - { - if(ranges_attrib != &dw2_attrib_nil) - { - Temp temp = temp_begin(scratch2.arena); - Rng1U64List tag_ranges = dw2_rnglist_from_form_val(temp.arena, unit_parse_ctx, raw, ranges_attrib->val); - for EachNode(n, Rng1U64Node, tag_ranges.first) - { - rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){.min = n->v.min, .max = n->v.max}); - } - temp_end(temp); - } - if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil) - { - U64 voff_base = lopc_attrib->val.addr - base_vaddr; - U64 voff_opl = 0; - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; - } - else - { - voff_opl = hipc_attrib->val.addr; - } - rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){voff_base, voff_opl}); - } - } - - //////////////////////// - //- rjf: unpack type - // - RDIM_Type *type = 0; - if(type_attrib != &dw2_attrib_nil || tag.kind == DW_TagKind_SubProgram) - { - // rjf: functions need to get their type info from themselves; the type attrib - // only references the return type. so, in that case, we'll use the procedure's - // own info offset / unit index, rather than the type attribute's. - U64 type_info_off = start_off; - U64 type_unit_idx = unit_idx; - - // rjf: attrib -> (info_off, unit_idx) - if(tag.kind != DW_TagKind_SubProgram) - { - type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val); - type_unit_idx = unit_idx; - if(!contains_1u64(unit_info_tag_range, type_info_off)) - { - U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off); - type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx); - } - } - - // rjf: (info_off, unit_idx) -> hash - U64 type_hash = 0; - { - D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; - for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == type_info_off) - { - type_hash = n->dst_hash; - break; - } - } - } - - // rjf: hash -> type - { - U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == type_hash) - { - type = type_from_idx_map[n->order_idx]; - break; - } - } - } - } - - //////////////////////// - //- rjf: unpack location info - // - RDIM_LocationCaseList location_cases = {0}; - RDIM_LocationCaseList framebase_location_cases = {0}; - B32 location_is_tls_dependent = 0; - B32 framebase_location_is_tls_dependent = 0; - { - struct - { - DW2_Attrib *attrib; - RDIM_LocationCaseList *dst_locations; - B32 *dst_is_tls_dependent; - } - tasks[] = - { - {location_attrib, &location_cases, &location_is_tls_dependent}, - {framebase_attrib, &framebase_location_cases, &framebase_location_is_tls_dependent}, - }; - for EachElement(task_idx, tasks) - { - if(tasks[task_idx].attrib == &dw2_attrib_nil) - { - continue; - } - DW2_Attrib *attrib = tasks[task_idx].attrib; - RDIM_LocationCaseList *dst_locations = tasks[task_idx].dst_locations; - B32 *dst_is_tls_dependent = tasks[task_idx].dst_is_tls_dependent; - - ////////////////////// - //- rjf: gather DWARF location exprs - // - DW2_LocList locs = {0}; - switch(attrib->val.kind) - { - default:{}break; - case DW_FormKind_ExprLoc: - { - U64 expr_info_off = attrib->val.u128.u64[0]; - U64 expr_info_size = attrib->val.u128.u64[1]; - String8 expr = str8_substr(raw->sec[DW_SectionKind_Info].data, r1u64(expr_info_off, expr_info_off+expr_info_size)); - DW2_LocNode *n = push_array(scratch2.arena, DW2_LocNode, 1); - n->v.expr = expr; - n->v.range = r1u64(0, max_U64); - SLLQueuePush(locs.first, locs.last, n); - locs.count += 1; - }break; - case DW_FormKind_LocListx: // NOTE(rjf): dwarf5+, loclistx -> location list offset table - case DW_FormKind_SecOffset: // NOTE(rjf): pre-dwarf5, location section offset -> a location list - { - locs = dw2_loclist_from_form_val(scratch2.arena, unit_parse_ctx, raw, attrib->val); - }break; - } - - ////////////////////// - //- rjf: convert DWARF location exprs -> RDIM locations - // - // we do this for each possible frame base location case - locations in DWARF - // refer to the frame base via a special op. in our case, we want to bake that - // into the location directly to reduce extra context for evaluation. - // - // if we find that a location doesn't rely on the frame base at all, we just - // skip the rest. - // - B32 is_tls_dependent = 0; - for EachNode(n, DW2_LocNode, locs.first) - { - //- rjf: unpack location - Rng1U64 range = n->v.range; - String8 expr = n->v.expr; - - //- rjf: iterate each frame base location case, or once if there are none, - // and convert the location in that context - RDIM_LocationCase nil_framebase_loc_case = {0, {0}, {0, max_U64}}; - for(RDIM_LocationCase *framebase_loc_n = top_parent ? top_parent->framebase_location_cases.first : &nil_framebase_loc_case; - framebase_loc_n != 0; - framebase_loc_n = framebase_loc_n->next) - { - //- rjf: unpack framebase location - RDIM_Location framebase_loc = framebase_loc_n->location; - RDIM_Rng1U64 framebase_voff_range = framebase_loc_n->voff_range; - - //- rjf: set up type stack for type-evaluating bytecode - typedef struct D2R2_ExprVal D2R2_ExprVal; - struct D2R2_ExprVal - { - RDI_TypeKind type_kind; - B32 is_addr; - }; - typedef struct D2R2_ExprValNode D2R2_ExprValNode; - struct D2R2_ExprValNode - { - D2R2_ExprValNode *next; - D2R2_ExprVal v; - }; - D2R2_ExprValNode *val_stack_top = 0; - D2R2_ExprValNode *free_val = 0; - - //- rjf: process DWARF bytecode; produce RDI bytecode + mini-type-info for the expression's result - typedef struct JumpOpNode JumpOpNode; - struct JumpOpNode - { - JumpOpNode *next; - RDIM_EvalBytecodeOp *op; - S64 inst_delta; - }; - JumpOpNode *first_jump_op = 0; - JumpOpNode *last_jump_op = 0; - RDIM_EvalBytecode dst_bytecode = {0}; - B32 dst_bytecode_is_good = 1; - B32 bytecode_is_framebase_dependent = 0; - for(U64 expr_off = 0; expr_off < expr.size;) - { - U64 start_expr_off = expr_off; - - //- rjf: read next opcode - DW_ExprOp opcode = 0; - expr_off += str8_deserial_read_struct(expr, expr_off, &opcode); - - //- rjf: unpack opcode - DW_ExprOpInfo *opcode_info = dw_info_from_expr_op(unit_parse_ctx->version, unit_parse_ctx->exts, opcode); - U64 operands_count = opcode_info->operand_count; - DW_ExprOperandKind operands_kinds[2] = {opcode_info->operand_kinds[0], opcode_info->operand_kinds[1]}; - U64 push_count = opcode_info->push_count; - U64 pop_count = opcode_info->pop_count; - - //- rjf: read op operands - U64 operand_u64s[2] = {0}; - S64 operand_s64s[2] = {0}; - String8 operand_str8s[2] = {0}; - for EachIndex(operand_idx, operands_count) - { - switch(operands_kinds[operand_idx]) - { - case DW_ExprOperandKind_Null: - default:{}break; - case DW_ExprOperandKind_U8: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 1, 1);}break; - case DW_ExprOperandKind_U16: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 2, 2);}break; - case DW_ExprOperandKind_U32: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 4, 4);}break; - case DW_ExprOperandKind_U64: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 8, 8);}break; - case DW_ExprOperandKind_S8: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 1, 1);}break; - case DW_ExprOperandKind_S16: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 2, 2);}break; - case DW_ExprOperandKind_S32: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 4, 4);}break; - case DW_ExprOperandKind_S64: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 8, 8);}break; - case DW_ExprOperandKind_ULEB128: {expr_off += str8_deserial_read_uleb128(expr, expr_off, &operand_u64s[operand_idx]);} break; - case DW_ExprOperandKind_SLEB128: {expr_off += str8_deserial_read_sleb128(expr, expr_off, &operand_s64s[operand_idx]);} break; - case DW_ExprOperandKind_Addr: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], unit_parse_ctx->addr_size, unit_parse_ctx->addr_size);}break; - case DW_ExprOperandKind_DwarfUInt: {expr_off += dw2_read_fmt_u64(expr, expr_off, unit_parse_ctx->format, &operand_u64s[operand_idx]);}break; - case DW_ExprOperandKind_Block: - { - U8 block_size = 0; - expr_off += str8_deserial_read_struct(expr, expr_off, &block_size); - operand_str8s[operand_idx] = str8_substr(expr, r1u64(expr_off, expr_off+block_size)); - expr_off += block_size; - }break; - } - } - - //- rjf: pop stack values - D2R2_ExprVal popped_vals[2] = {0}; - { - pop_count = Min(pop_count, ArrayCount(popped_vals)); - for EachIndex(pop_idx, pop_count) - { - if(val_stack_top != 0) - { - D2R2_ExprValNode *popped = val_stack_top; - popped_vals[pop_idx] = popped->v; - SLLStackPop(val_stack_top); - SLLStackPush(free_val, popped); - } - } - } - - //- rjf: exec op (produce RDI bytecode, + manipulate value stack) - D2R2_ExprVal push_vals[2] = {0}; - U64 regcode_dw = 0; - S64 regval_off = 0; - B32 regread_is_addr = 0; - U64 target_pick_val_idx = 0; - RDI_EvalOp rdi_eval_op = 0; - U64 memread_size = 0; - switch(opcode) - { - default:{}break; - - //- rjf: small opcode-embedded unsigned literals - case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: - case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: - case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: - case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: - case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: - case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: - case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: - case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: - case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: - case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: - case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: - { - U64 lit_val = (U64)(opcode - DW_ExprOp_Lit0); - rdim_bytecode_push_uconst(arena, &dst_bytecode, lit_val); - }break; - - //- rjf: small unsigned literals - case DW_ExprOp_Const1U: push_vals[0].type_kind = RDI_TypeKind_U8; goto const_u; - case DW_ExprOp_Const2U: push_vals[0].type_kind = RDI_TypeKind_U16; goto const_u; - case DW_ExprOp_Const4U: push_vals[0].type_kind = RDI_TypeKind_U32; goto const_u; - case DW_ExprOp_Const8U: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u; - case DW_ExprOp_ConstU: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u; - const_u:; - { - rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]); - }break; - - //- rjf: small signed literals - case DW_ExprOp_Const1S: push_vals[0].type_kind = RDI_TypeKind_S8; goto const_s; - case DW_ExprOp_Const2S: push_vals[0].type_kind = RDI_TypeKind_S16; goto const_s; - case DW_ExprOp_Const4S: push_vals[0].type_kind = RDI_TypeKind_S32; goto const_s; - case DW_ExprOp_Const8S: push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s; - case DW_ExprOp_ConstS : push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s; - const_s:; - { - rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]); - }break; - - //- rjf: address (module offsets) - case DW_ExprOp_Addr: - { - U64 voff = operand_u64s[0] - base_vaddr; - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff); - push_vals[0].type_kind = RDI_TypeKind_U64; - push_vals[0].is_addr = 1; - }break; - case DW_ExprOp_Addrx: - if(unit_parse_ctx->addr_table != 0) - { - U64 addr_idx = operand_u64s[0]; - U64 addr = 0; - if(dw2_try_offset_from_table_idx(unit_parse_ctx->addr_table, addr_idx, &addr)) - { - U64 voff = (addr > base_vaddr) ? (addr - base_vaddr) : 0; - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff); - push_vals[0].type_kind = RDI_TypeKind_U64; - push_vals[0].is_addr = 1; - } - }break; - - //- rjf: register reads - case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: - case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: - case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: - case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: - case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: - case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: - case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: - case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: - case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: - case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: - case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: - { - regcode_dw = (U64)(opcode - DW_ExprOp_Reg0); - }goto reg_read; - case DW_ExprOp_RegX: - { - regcode_dw = operand_u64s[0]; - }goto reg_read; - case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: - case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: - case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: - case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: - case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: - case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: - case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: - case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: - case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: - case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: - case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: - { - regcode_dw = (U64)(opcode - DW_ExprOp_BReg0); - regval_off = operand_s64s[1]; - regread_is_addr = 1; - }goto reg_read; - case DW_ExprOp_BRegX: - { - regcode_dw = operand_u64s[0]; - regval_off = operand_s64s[1]; - regread_is_addr = 1; - }goto reg_read; - reg_read:; - { - // rjf: DWARF regcode -> internal regcode - ARCH_RegCode regcode = arch_reg_code_from_dw(arch, regcode_dw); - - // rjf: internal regcode -> RDI, offset, size - RDI_RegCode regcode_rdi = 0; - U64 reg_off = 0; - U64 reg_size = 0; - { - Rng1U16 reg_rng = arch_info->reg_code_rng_table[regcode]; - reg_size = dim_1u16(reg_rng); - regcode_rdi = arch_rdi_from_reg_code_table_from_arch(arch)[regcode]; - } - - // rjf: push op - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_RegRead, RDI_EncodeRegReadParam(regcode_rdi, reg_size, reg_off)); - - // rjf: push add to value offset, if needed - if(regval_off != 0) - { - rdim_bytecode_push_sconst(arena, &dst_bytecode, regval_off); - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); - } - - // rjf: choose if we're doing a float op - B32 is_float_op = 0; - if(arch == Arch_x64 && - (regcode_rdi == RDI_RegCodeX64_st0 || - regcode_rdi == RDI_RegCodeX64_st1 || - regcode_rdi == RDI_RegCodeX64_st2 || - regcode_rdi == RDI_RegCodeX64_st3 || - regcode_rdi == RDI_RegCodeX64_st4 || - regcode_rdi == RDI_RegCodeX64_st5 || - regcode_rdi == RDI_RegCodeX64_st6 || - regcode_rdi == RDI_RegCodeX64_st7)) - { - is_float_op = 1; - } - - // rjf: set up push value - push_vals[0].is_addr = regread_is_addr; - if(is_float_op) - { - switch(reg_size) - { - default:{}break; - case 2: {push_vals[0].type_kind = RDI_TypeKind_F16;}break; - case 4: {push_vals[0].type_kind = RDI_TypeKind_F32;}break; - case 6: {push_vals[0].type_kind = RDI_TypeKind_F48;}break; - case 8: {push_vals[0].type_kind = RDI_TypeKind_F64;}break; - case 10:{push_vals[0].type_kind = RDI_TypeKind_F80;}break; - case 12:{push_vals[0].type_kind = RDI_TypeKind_F96;}break; - case 16:{push_vals[0].type_kind = RDI_TypeKind_F128;}break; - } - } - else - { - switch(reg_size) - { - default:{}break; - case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break; - case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break; - case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break; - case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break; - } - } - }break; - - //- rjf: implicit values - case DW_ExprOp_ImplicitValue: - { - if(operand_str8s[0].size <= sizeof(U64)) - { - U64 implicit_value = 0; - MemoryCopy(&implicit_value, operand_str8s[0].str, Min(sizeof(U64), operand_str8s[0].size)); - rdim_bytecode_push_uconst(arena, &dst_bytecode, implicit_value); - switch(operand_str8s[0].size) - { - default:{}break; - case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break; - case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break; - case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break; - case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break; - } - } - else - { - log_infof("[.debug_info@0x%I64x] Implicit value DWARF expression operation (DW_ExprOp_ImplicitValue) with block size >8 (%I64u) found. This is not currently supported.\n", start_off, operand_str8s[0].size); - } - }break; - - //- rjf: pieces - case DW_ExprOp_Piece: - { - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValue, operand_u64s[0]); - }break; - case DW_ExprOp_BitPiece: - { - U64 size = operand_u64s[0]; - U64 off = operand_u64s[1]; - U64 partial_value = ((size<<32)|(off)); - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValueBit, partial_value); - }break; - - //- rjf: stack ops - case DW_ExprOp_Over: {target_pick_val_idx = 1;}goto pick; - case DW_ExprOp_Pick: {target_pick_val_idx = operand_u64s[0];}goto pick; - case DW_ExprOp_Dup: {target_pick_val_idx = 0;}goto pick; - pick:; - { - // rjf: pick value from stack - D2R2_ExprVal picked_val = {0}; - { - U64 idx = 0; - for(D2R2_ExprValNode *n = val_stack_top; n != 0; n = n->next) - { - if(idx == target_pick_val_idx) - { - picked_val = n->v; - break; - } - } - } - - // rjf: push opcode - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pick, target_pick_val_idx); - - // rjf: push val - push_vals[0] = picked_val; - }break; - case DW_ExprOp_Swap: - { - push_vals[0] = popped_vals[1]; - push_vals[1] = popped_vals[0]; - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Swap, 0); - }break; - case DW_ExprOp_Drop: - { - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pop, 0); - }break; - - //- rjf: jumps - case DW_ExprOp_Skip: rdi_eval_op = RDI_EvalOp_Skip; goto jumps; - case DW_ExprOp_Bra: rdi_eval_op = RDI_EvalOp_Cond; goto jumps; - jumps:; - { - // rjf: jump delta in bytes (how the instruction is encoded) -> jump delta in instructions - S64 jump_delta_bytes = operand_s64s[0]; - S64 jump_delta_insts = 0; - { - // TODO(rjf): expr op jumps bytes -> inst counts - } - - // rjf: push incomplete op (we need to resolve inst delta -> byte delta later, once we have the full bytecode stream) - RDIM_EvalBytecodeOp *op = rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, 0); - - // rjf: gather op - JumpOpNode *n = push_array(scratch2.arena, JumpOpNode, 1); - SLLQueuePush(first_jump_op, last_jump_op, n); - n->op = op; - n->inst_delta = jump_delta_insts; - }break; - - //- rjf: containing procedure frame offsets - case DW_ExprOp_FBReg: - { - bytecode_is_framebase_dependent = 1; - for EachNode(n, RDIM_EvalBytecodeOp, framebase_loc.bytecode.first_op) - { - rdim_bytecode_push_op(arena, &dst_bytecode, n->op, n->p); - } - rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]); - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S); - push_vals[0].type_kind = RDI_TypeKind_U64; - push_vals[0].is_addr = 1; - }break; - - //- rjf: memory reads - case DW_ExprOp_Deref: memread_size = unit_parse_ctx->addr_size; goto deref; - case DW_ExprOp_DerefSize: memread_size = operand_u64s[0]; goto deref; - deref:; - { - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_MemRead, memread_size); - push_vals[0].is_addr = 1; - push_vals[0].type_kind = RDI_TypeKind_U64; - }break; - - //- rjf: TLS offsets - case DW_ExprOp_FormTlsAddress: - case DW_GNU_ExprOp_PushTlsAddress: - { - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_TLSOff, 0); - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U); - push_vals[0].is_addr = 1; - push_vals[0].type_kind = RDI_TypeKind_U64; - is_tls_dependent = 1; - }break; - - //- rjf: call site value - case DW_ExprOp_EntryValue: - case DW_GNU_ExprOp_EntryValue: - { - // TODO(rjf): expr op entry value ops - }break; - - //- rjf: fixed adds - case DW_ExprOp_PlusUConst: - { - rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]); - rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U); - push_vals[0].type_kind = RDI_TypeKind_U64; // TODO(rjf): do we need to adjust this based on popped value at all, even? - }break; - - //- rjf: arithmetic ops - case DW_ExprOp_Eq: {rdi_eval_op = RDI_EvalOp_EqEq;}goto arithmetic_op; - case DW_ExprOp_Ge: {rdi_eval_op = RDI_EvalOp_GrEq;}goto arithmetic_op; - case DW_ExprOp_Gt: {rdi_eval_op = RDI_EvalOp_Grtr;}goto arithmetic_op; - case DW_ExprOp_Le: {rdi_eval_op = RDI_EvalOp_LsEq;}goto arithmetic_op; - case DW_ExprOp_Lt: {rdi_eval_op = RDI_EvalOp_Less;}goto arithmetic_op; - case DW_ExprOp_Ne: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op; - case DW_ExprOp_Div: {rdi_eval_op = RDI_EvalOp_Div;}goto arithmetic_op; - case DW_ExprOp_Minus: {rdi_eval_op = RDI_EvalOp_Sub;}goto arithmetic_op; - case DW_ExprOp_Mul: {rdi_eval_op = RDI_EvalOp_Mul;}goto arithmetic_op; - case DW_ExprOp_Plus: {rdi_eval_op = RDI_EvalOp_Add;}goto arithmetic_op; - case DW_ExprOp_Xor: {rdi_eval_op = RDI_EvalOp_BitXor;}goto arithmetic_op; - case DW_ExprOp_And: {rdi_eval_op = RDI_EvalOp_BitAnd;}goto arithmetic_op; - case DW_ExprOp_Or: {rdi_eval_op = RDI_EvalOp_BitOr;}goto arithmetic_op; - case DW_ExprOp_Shl: {rdi_eval_op = RDI_EvalOp_LShift;}goto arithmetic_op; - case DW_ExprOp_Shr: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op; - case DW_ExprOp_Shra: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op; - case DW_ExprOp_Mod: {rdi_eval_op = RDI_EvalOp_Mod;}goto arithmetic_op; - case DW_ExprOp_Abs: {rdi_eval_op = RDI_EvalOp_Abs;}goto arithmetic_op; - case DW_ExprOp_Neg: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op; - case DW_ExprOp_Not: {rdi_eval_op = RDI_EvalOp_LogNot;}goto arithmetic_op; - arithmetic_op:; - { - // TODO(rjf): eval arithmetic conversions etc. - rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, RDI_EvalTypeGroup_U); - }break; - - //- rjf: currently unsupported - case DW_ExprOp_XDeref: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression XDeref operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_XDerefSize: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression XDerefSize operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_Call2: - case DW_ExprOp_Call4: - case DW_ExprOp_CallRef: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression call operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_ImplicitPointer: - case DW_GNU_ExprOp_ImplicitPointer: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression implicit pointer operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_GNU_ExprOp_ParameterRef: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression GNU_ParameterRef operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_DerefType: - case DW_GNU_ExprOp_DerefType: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression DerefType operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_ConstType: - case DW_GNU_ExprOp_ConstType: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression ConstType operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_RegvalType: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression RegvalType operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_PushObjectAddress: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression PushObjectAddress operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_ExprOp_Rot: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression Rot (rotate) operation encountered. This is not supported for location info.\n", start_off); - }break; - case DW_GNU_ExprOp_UnInit: - { - dst_bytecode_is_good = 0; - log_infof("[.debug_info@0x%I64x] DWARF expression GNU_UnInit operation encountered. This is not supported for location info.\n", start_off); - // TODO: flag value as unitialized; this must be last opcode; possible to use with DW_ExprOp_Piece; - } break; - } - - //- rjf: push values to stack - { - push_count = Min(push_count, ArrayCount(push_vals)); - for EachIndex(push_idx, push_count) - { - D2R2_ExprValNode *n = free_val; - if(n != 0) - { - SLLStackPop(free_val); - } - else - { - n = push_array(scratch2.arena, D2R2_ExprValNode, 1); - } - n->v = push_vals[push_idx]; - SLLStackPush(val_stack_top, n); - } - } - - if(expr_off == start_expr_off) - { - break; - } - } - - //- rjf: apply RDI byte offsets to jump ops - { - // TODO(rjf): apply RDI byte offsets to jump ops - } - - //- rjf: map bytecode -> RDIM_Location - we may want to simplify - RDIM_Location loc = {0}; - { - loc.kind = val_stack_top && val_stack_top->v.is_addr ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; - loc.bytecode = dst_bytecode; - - // rjf: gather first few bytecodes for pattern matching - RDI_EvalOp beginning_ops[5] = {0}; - RDIM_EvalBytecodeOp *beginning_nodes[5] = {0}; - { - U64 idx = 0; - for EachNode(n, RDIM_EvalBytecodeOp, dst_bytecode.first_op) - { - if(idx >= ArrayCount(beginning_ops)) - { - break; - } - beginning_ops[idx] = n->op; - beginning_nodes[idx] = n; - idx += 1; - } - } - - // rjf: match val registers - if(loc.kind == RDI_LocationKind_ValBytecodeStream && - beginning_ops[0] == RDI_EvalOp_RegRead && - beginning_ops[1] == RDI_EvalOp_Stop) - { - U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0; - U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8; - U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16; - if(byte_size == unit_parse_ctx->addr_size && - byte_off == 0) - { - loc.kind = RDI_LocationKind_ValReg; - loc.reg_code = rdi_reg_code; - } - } - - // rjf: match simple register offsets - if(loc.kind == RDI_LocationKind_AddrBytecodeStream && - beginning_ops[0] == RDI_EvalOp_RegRead && - (beginning_ops[1] == RDI_EvalOp_ConstU8 || - beginning_ops[1] == RDI_EvalOp_ConstU16) && - beginning_ops[2] == RDI_EvalOp_TruncSigned && - beginning_ops[3] == RDI_EvalOp_Add && - beginning_ops[4] == RDI_EvalOp_Stop) - { - U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0; - U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8; - U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16; - if(byte_off == 0 && byte_size == unit_parse_ctx->addr_size) - { - loc.kind = RDI_LocationKind_AddrRegPlusOff; - loc.reg_code = rdi_reg_code; - loc.offset = beginning_nodes[1]->p; - } - } - - // rjf: match simple module offsets - if(beginning_ops[0] == RDI_EvalOp_ModuleOff && beginning_ops[1] == RDI_EvalOp_Stop) - { - U64 voff = beginning_nodes[0]->p; - loc.kind = RDI_LocationKind_ModuleOff; - loc.offset = voff; - } - else if(((beginning_ops[0] == RDI_EvalOp_ModuleOff && - (beginning_ops[1] == RDI_EvalOp_ConstU8 || - beginning_ops[1] == RDI_EvalOp_ConstU16 || - beginning_ops[1] == RDI_EvalOp_ConstU32 || - beginning_ops[1] == RDI_EvalOp_ConstU64)) || - (beginning_ops[1] == RDI_EvalOp_ModuleOff && - (beginning_ops[0] == RDI_EvalOp_ConstU8 || - beginning_ops[0] == RDI_EvalOp_ConstU16 || - beginning_ops[0] == RDI_EvalOp_ConstU32 || - beginning_ops[0] == RDI_EvalOp_ConstU64))) && - beginning_ops[2] == RDI_EvalOp_Add && - beginning_ops[3] == RDI_EvalOp_Stop) - { - U64 voff = beginning_nodes[0]->p + beginning_nodes[1]->p; - loc.kind = RDI_LocationKind_ModuleOff; - loc.offset = voff; - } - - // rjf: match simple TLS offsets - if(beginning_ops[0] == RDI_EvalOp_TLSOff && beginning_ops[1] == RDI_EvalOp_Stop) - { - U64 toff = beginning_nodes[0]->p; - loc.kind = RDI_LocationKind_TLSOff; - loc.offset = toff; - } - else if(((beginning_ops[0] == RDI_EvalOp_TLSOff && - (beginning_ops[1] == RDI_EvalOp_ConstU8 || - beginning_ops[1] == RDI_EvalOp_ConstU16 || - beginning_ops[1] == RDI_EvalOp_ConstU32 || - beginning_ops[1] == RDI_EvalOp_ConstU64)) || - (beginning_ops[1] == RDI_EvalOp_TLSOff && - (beginning_ops[0] == RDI_EvalOp_ConstU8 || - beginning_ops[0] == RDI_EvalOp_ConstU16 || - beginning_ops[0] == RDI_EvalOp_ConstU32 || - beginning_ops[0] == RDI_EvalOp_ConstU64))) && - beginning_ops[2] == RDI_EvalOp_Add && - beginning_ops[3] == RDI_EvalOp_Stop) - { - U64 toff = beginning_nodes[0]->p + beginning_nodes[1]->p; - loc.kind = RDI_LocationKind_TLSOff; - loc.offset = toff; - } - } - - //- rjf: collect - { - RDIM_Rng1U64 voff_range = {range.min - base_vaddr, range.max - base_vaddr}; - if(bytecode_is_framebase_dependent) - { - voff_range.min = Max(voff_range.min, framebase_voff_range.min); - voff_range.max = Min(voff_range.max, framebase_voff_range.max); - } - rdim_location_case_list_push(arena, dst_locations, loc, voff_range); - dst_is_tls_dependent[0] = is_tls_dependent; - } - - //- rjf: not frame-base dependent? -> break - don't do this per-framebase case - if(!bytecode_is_framebase_dependent) - { - break; - } - } - } - } - } - - //////////////////////// - //- rjf: produce symbols from tag - // - RDIM_Symbol *new_symbol = 0; - RDIM_Scope *new_scope_open = 0; - B32 parent_tag_is_container_to_deduped_tags = 0; - switch(tag.kind) - { - default:{}break; - - //- rjf: determine if parent tag is a container to deduplicated tags - case DW_TagKind_Namespace: - case DW_TagKind_StructureType: - case DW_TagKind_UnionType: - case DW_TagKind_ClassType: - case DW_TagKind_EnumerationType: - case DW_TagKind_Typedef: - { - parent_tag_is_container_to_deduped_tags = 1; - }break; - - //- rjf: subprograms (procedures) - case DW_TagKind_SubProgram: - if(!is_decl) - { - RDIM_Scope *root_scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count); - RDIM_Symbol *procedure = rdim_symbol_chunk_list_push(arena, &dst_artifacts->procedures, chunk_count); - procedure->name = name; - procedure->link_name = link_name; - procedure->type = type; - procedure->root_scope = root_scope; - procedure->location_cases = framebase_location_cases; - root_scope->symbol = procedure; - root_scope->voff_ranges = ranges; - dst_artifacts->scopes.scope_voff_count += 2*ranges.count; - new_symbol = procedure; - new_scope_open = root_scope; - }break; - - //- rjf: inline site - case DW_TagKind_InlinedSubroutine: - { - // TODO(rjf) - }break; - - //- rjf: variables - case DW_TagKind_Variable: - case DW_TagKind_FormalParameter: - if(name.size != 0) - { - U64 var_chunk_count = chunk_count; - RDIM_SymbolChunkList *dst_symbols = &dst_artifacts->global_variables; - if(location_is_tls_dependent) - { - dst_symbols = &dst_artifacts->thread_variables; - } - else if(top_parent != 0 && top_parent->scope != 0) - { - dst_symbols = &top_parent->scope->locals; - var_chunk_count = 8; - } - RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, dst_symbols, var_chunk_count); - var->is_extern = is_external; - var->is_param = (tag.kind == DW_TagKind_FormalParameter); - var->name = name; - var->link_name = link_name; - var->type = type; - var->location_cases = location_cases; - new_symbol = var; - }break; - - //- rjf: lexical blocks (scopes) - case DW_TagKind_LexicalBlock: - { - RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count); - scope->voff_ranges = ranges; - dst_artifacts->scopes.scope_voff_count += 2*ranges.count; - new_scope_open = scope; - }break; - } - - //////////////////////// - //- rjf: if we've determined that the parent tag is a container to - // deduplicated tags (types, namespaces), then we need to record - // the .debug_info offset -> scope mapping for this scope, so it - // can be equipped as a container later. - // - if(parent_tag_is_container_to_deduped_tags) - { - U64 info_off = 0; - RDIM_Scope *scope = 0; - for(D2R2_ParentNode *n = top_parent; n != 0; n = n->next) - { - if(n->scope != 0) - { - info_off = n->info_off; - scope = n->scope; - break; - } - } - if(scope != 0) - { - D2R2_ScopeContainerNode *n = push_array(scratch.arena, D2R2_ScopeContainerNode, 1); - n->info_off = info_off; - n->scope = scope; - U64 hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 slot_idx = hash%scope_container_map->slots_count; - for(B32 gathered = 0; !gathered;) - { - U64 expected_head_value = ins_atomic_u64_eval(&scope_container_map->slots[slot_idx]); - n->next = (D2R2_ScopeContainerNode *)expected_head_value; - if(expected_head_value == ins_atomic_u64_eval_cond_assign(&scope_container_map->slots[slot_idx], (U64)n, expected_head_value)) - { - gathered = 1; - } - } - } - } - - //////////////////////// - //- rjf: equip new symbols with container info - // - if(new_symbol != 0 && top_parent != 0) - { - if(top_parent->scope != 0) - { - new_symbol->container_scope = top_parent->scope; - } - else if(top_parent->container_type != 0) - { - new_symbol->container_type = top_parent->container_type; - } - else if(top_parent->container_namespace != 0) - { - new_symbol->container_namespace = top_parent->container_namespace; - } - } - - //////////////////////// - //- rjf: insert new scopes to their parent - // - if(new_scope_open && top_parent != 0 && top_parent->scope != 0) - { - RDIM_Scope *parent = top_parent->scope; - SLLQueuePush_N(parent->first_child, parent->last_child, new_scope_open, next_sibling); - new_scope_open->parent_scope = parent; - if(new_scope_open->symbol == 0) - { - new_scope_open->symbol = parent->symbol; - } - } - - //////////////////////// - //- rjf: gather container info for new tag parents - // - RDIM_Type *container_type = 0; - RDIM_Namespace *container_namespace = 0; - if(tag.has_children) - { - // rjf: info offset -> hash - U64 hash = 0; - { - D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; - for EachNode(n, D2R2_UnitDedupedTagNode, unit_deduped_tag_map->slots[info_off_slot_idx]) - { - if(n->src_info_off == start_off) - { - hash = n->dst_hash; - break; - } - } - } - - // rjf: map hash -> unique tag node - D2R2_UniqueTagNode *tag_node = 0; - { - U64 slot_idx = hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next) - { - if(n->hash == hash) - { - tag_node = n; - break; - } - } - } - - // rjf: map tag node -> type/namespace - if(tag_node != 0) switch(tag_node->kind) - { - default:{}break; - case D2R2_UniqueTagKind_Type: {container_type = type_from_idx_map[tag_node->order_idx];}break; - case D2R2_UniqueTagKind_Namespace:{container_namespace = namespace_from_idx_map[tag_node->order_idx];}break; - } - } - - //////////////////////// - //- rjf: push tag parents - // - if(tag.has_children) - { - // rjf: push new parent to stack - D2R2_ParentNode *n = free_parent; - if(n != 0) - { - SLLStackPop(free_parent); - } - else - { - n = push_array(scratch.arena, D2R2_ParentNode, 1); - } - n->tag_kind = tag.kind; - n->info_off = start_off; - n->scope = new_scope_open ? new_scope_open : top_parent ? top_parent->scope : 0; - n->container_type = new_scope_open ? 0 : container_type; - n->container_namespace = new_scope_open ? 0 : container_namespace; - if(framebase_attrib != &dw2_attrib_nil) - { - n->framebase_location_cases = framebase_location_cases; - } - else if(top_parent != 0) - { - n->framebase_location_cases = top_parent->framebase_location_cases; - } - SLLStackPush(top_parent, n); - } - - //////////////////////// - //- rjf: pop scopes - // - if(tag.kind == DW_TagKind_Null && top_parent != 0) - { - D2R2_ParentNode *n = top_parent; - SLLStackPop(top_parent); - SLLStackPush(free_parent, n); - } - - //////////////////////// - //- rjf: tree nav - // - if(tag.has_children) - { - depth += 1; - } - if(tag.kind == DW_TagKind_Null) - { - depth -= 1; - } - - scratch_end(scratch2); - if(off == start_off) - { - break; - } - } - } - } - lane_sync(); - } - - //////////////////////////// - //- rjf: join all symbols from all sub-unit works into their units - // - ProfScope("join all symbols from all sub-unit works into their units") - { - U64 unit_take_idx_ = 0; - U64 *unit_take_idx_ptr = &unit_take_idx_; - lane_sync_u64(&unit_take_idx_ptr, 0); - for(;;) - { - U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; - if(unit_idx >= unit_count) - { - break; - } - RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx]; - for EachIndex(work_idx, sub_unit_works_count) - { - if(sub_unit_works[work_idx].unit_idx == unit_idx) - { - rdim_symbol_chunk_list_concat_in_place(&dst_unit->global_variables, &sub_unit_work_artifacts[work_idx].global_variables); - rdim_symbol_chunk_list_concat_in_place(&dst_unit->thread_variables, &sub_unit_work_artifacts[work_idx].thread_variables); - rdim_symbol_chunk_list_concat_in_place(&dst_unit->constants, &sub_unit_work_artifacts[work_idx].constants); - rdim_symbol_chunk_list_concat_in_place(&dst_unit->procedures, &sub_unit_work_artifacts[work_idx].procedures); - rdim_scope_chunk_list_concat_in_place(&dst_unit->scopes, &sub_unit_work_artifacts[work_idx].scopes); - rdim_inline_site_chunk_list_concat_in_place(&dst_unit->inline_sites, &sub_unit_work_artifacts[work_idx].inline_sites); - } - } - } - lane_sync(); - } - - //////////////////////////// - //- rjf: upgrade all types with namespacing info - // - ProfScope("upgrade all types with namespacing info") - { - Rng1U64 range = lane_range(type_count); - for EachInRange(type_idx, range) - { - D2R2_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx]; - U64 container_ancestor_info_off = type_tag_node->container_ancestor_info_off; - RDIM_Type *dst_type = type_from_idx_map[type_idx]; - if(dst_type == 0) - { - continue; - } - RDIM_UDT *dst_udt = dst_type->udt; - if(dst_udt == 0) - { - continue; - } - - // rjf: find container scopes - RDIM_Scope *container_scope = 0; - { - U64 info_off = container_ancestor_info_off; - U64 hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 slot_idx = hash%scope_container_map->slots_count; - for(D2R2_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next) - { - if(n->info_off == info_off) - { - container_scope = n->scope; - break; - } - } - } - - // rjf: find container types/namespaces - RDIM_Type *container_type = 0; - RDIM_Namespace *container_namespace = 0; - if(!container_scope) - { - U64 info_off = container_ancestor_info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - - // rjf: info off -> hash - U64 hash = 0; - { - D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; - for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == info_off) - { - hash = n->dst_hash; - break; - } - } - } - - // rjf: hash -> type - { - U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == hash) - { - if(n->kind == D2R2_UniqueTagKind_Type) - { - container_type = type_from_idx_map[n->order_idx]; - } - else if(n->kind == D2R2_UniqueTagKind_Namespace) - { - container_namespace = namespace_from_idx_map[n->order_idx]; - } - break; - } - } - } - } - - // rjf: fill - dst_udt->container_scope = container_scope; - dst_udt->container_type = container_type; - dst_udt->container_namespace = container_namespace; - } - } - lane_sync(); - - //////////////////////////// - //- rjf: upgrade all namespaces with namespacing info - // - ProfScope("upgrade all namespaces with namespacing info") - { - Rng1U64 range = lane_range(namespace_count); - for EachInRange(namespace_idx, range) - { - D2R2_UniqueTagNode *unique_tag_node = namespace_tag_nodes[namespace_idx]; - U64 container_ancestor_info_off = unique_tag_node->container_ancestor_info_off; - RDIM_Namespace *dst_namespace = namespace_from_idx_map[namespace_idx]; - if(dst_namespace == 0) - { - continue; - } - - // rjf: find container scopes - RDIM_Scope *container_scope = 0; - { - U64 info_off = container_ancestor_info_off; - U64 hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 slot_idx = hash%scope_container_map->slots_count; - for(D2R2_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next) - { - if(n->info_off == info_off) - { - container_scope = n->scope; - break; - } - } - } - - // rjf: find container types/namespaces - RDIM_Type *container_type = 0; - RDIM_Namespace *container_namespace = 0; - { - U64 info_off = container_ancestor_info_off; - U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off); - U64 unit_idx = unit_num > 0 ? unit_num-1 : 0; - - // rjf: info off -> hash - U64 hash = 0; - { - D2R2_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx]; - U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off)); - U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count; - for(D2R2_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next) - { - if(n->src_info_off == info_off) - { - hash = n->dst_hash; - break; - } - } - } - - // rjf: hash -> type - { - U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count; - for(D2R2_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next) - { - if(n->hash == hash) - { - if(n->kind == D2R2_UniqueTagKind_Type) - { - container_type = type_from_idx_map[n->order_idx]; - } - else if(n->kind == D2R2_UniqueTagKind_Namespace) - { - container_namespace = namespace_from_idx_map[n->order_idx]; - } - break; - } - } - } - } - - // rjf: fill - dst_namespace->parent_scope = container_scope; - dst_namespace->parent_type = container_type; - dst_namespace->parent_namespace = container_namespace; - } - } - lane_sync(); - - //////////////////////////// - //- rjf: fill result - // - RDIM_BakeParams result = {0}; - { - result.subset_flags = params->subset_flags; - result.top_level_info = top_level_info; - result.binary_sections = binary_sections; - result.units = *all_units; - result.namespaces = *all_namespaces; - result.types = *all_types; - result.udts = *all_udts; - result.src_files = *all_src_files; - result.line_tables = *all_line_tables; - } - -#undef d2r2_type_from_builtin_kind - lane_sync(); - scratch_end(scratch); - return result; -} diff --git a/src/rdi_from_dwarf/rdi_from_dwarf_2.h b/src/rdi_from_dwarf/rdi_from_dwarf_2.h deleted file mode 100644 index a0b63230..00000000 --- a/src/rdi_from_dwarf/rdi_from_dwarf_2.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_FROM_DWARF_2_H -#define RDI_FROM_DWARF_2_H - -//////////////////////////////// -//~ rjf: Unique Tag Tree Deduplication Types - -typedef enum D2R2_UniqueTagKind -{ - D2R2_UniqueTagKind_Type, - D2R2_UniqueTagKind_Namespace, - D2R2_UniqueTagKind_COUNT -} -D2R2_UniqueTagKind; - -typedef struct D2R2_UniqueTagNode D2R2_UniqueTagNode; -struct D2R2_UniqueTagNode -{ - D2R2_UniqueTagNode *next; - D2R2_UniqueTagKind kind; - U64 hash; - U64 info_off; - U64 container_ancestor_info_off; - U64 order_idx; -}; - -typedef struct D2R2_UnitDedupedTagNode D2R2_UnitDedupedTagNode; -struct D2R2_UnitDedupedTagNode -{ - D2R2_UnitDedupedTagNode *next; - U64 src_info_off; - U64 dst_hash; -}; - -typedef struct D2R2_UnitDedupedTagMap D2R2_UnitDedupedTagMap; -struct D2R2_UnitDedupedTagMap -{ - D2R2_UnitDedupedTagNode **slots; - U64 slots_count; -}; - -//////////////////////////////// -//~ rjf: Conversion Stage Inputs (New) - -typedef struct D2R2_ConvertParams D2R2_ConvertParams; -struct D2R2_ConvertParams -{ - String8 exe_name; - String8 exe_data; - Arch arch; - U64 base_vaddr; - RDIM_BinarySectionList binary_sections; - DW_Raw raw; - PathStyle path_style; - RDIM_SubsetFlags subset_flags; - B32 deterministic; -}; - -//////////////////////////////// -//~ rjf: Helpers - -internal int d2r2_unique_tag_node_is_less_than(D2R2_UniqueTagNode **l, D2R2_UniqueTagNode **r); - -//////////////////////////////// -//~ rjf: Main Conversion Entry Point (New) - -internal RDIM_BakeParams d2r2_convert(Arena *arena, D2R2_ConvertParams *params); - -#endif // RDI_FROM_DWARF_2_H diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c index 4de5a57a..567157c9 100644 --- a/src/torture/torture_main.c +++ b/src/torture/torture_main.c @@ -65,7 +65,7 @@ #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" -#include "rdi_from_dwarf/rdi_from_dwarf_2.h" +#include "rdi_from_dwarf/rdi_from_dwarf.h" #include "obj/obj.h" #include "radbin/radbin.h" #include "arch/arch_inc.h" @@ -142,7 +142,7 @@ #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" -#include "rdi_from_dwarf/rdi_from_dwarf_2.c" +#include "rdi_from_dwarf/rdi_from_dwarf.c" #include "obj/obj.c" #include "radbin/radbin.c" #include "arch/arch_inc.c"