diff options
author | Mark Harmstone <mark@harmstone.com> | 2024-08-08 03:18:11 +0100 |
---|---|---|
committer | Mark Harmstone <mark@harmstone.com> | 2024-08-25 17:35:20 +0100 |
commit | ee020b4a6cb796c3363bed562fb43d7b36603108 (patch) | |
tree | bc2ee9bc4eb168b26ed3ac87986791e5fd56461f /gcc/dwarf2codeview.cc | |
parent | 07d62a1711f3e3bbdd2146ab5914d3bc5e246509 (diff) | |
download | gcc-ee020b4a6cb796c3363bed562fb43d7b36603108.zip gcc-ee020b4a6cb796c3363bed562fb43d7b36603108.tar.gz gcc-ee020b4a6cb796c3363bed562fb43d7b36603108.tar.bz2 |
Write CodeView information about enregistered optimized variables
Enable variable tracking when outputting CodeView debug information, and make
it so that we issue debug symbols for optimized variables in registers. This
consists of S_LOCAL symbols, which give the name and the type of local
variables, followed by S_DEFRANGE_REGISTER symbols for the register and the
code for which this applies.
gcc/
* dwarf2codeview.cc (enum cv_sym_type): Add S_LOCAL and
S_DEFRANGE_REGISTER.
(write_s_local): New function.
(write_defrange_register): New function.
(write_optimized_local_variable_loc): New function.
(write_optimized_local_variable): New function.
(write_optimized_function_vars): New function.
(write_function): Call write_optimized_function_vars if variable
tracking enabled.
* dwarf2out.cc (typedef var_loc_view): Move to dwarf2out.h.
(struct dw_loc_list_struct): Likewise.
* dwarf2out.h (typedef var_loc_view): Move from dwarf2out.h.
(struct dw_loc_list_struct): Likewise.
* opts.cc (finish_options): Enable variable tracking for CodeView.
Diffstat (limited to 'gcc/dwarf2codeview.cc')
-rw-r--r-- | gcc/dwarf2codeview.cc | 316 |
1 files changed, 315 insertions, 1 deletions
diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc index e01515a..1525397 100644 --- a/gcc/dwarf2codeview.cc +++ b/gcc/dwarf2codeview.cc @@ -77,6 +77,8 @@ enum cv_sym_type { S_GDATA32 = 0x110d, S_REGREL32 = 0x1111, S_COMPILE3 = 0x113c, + S_LOCAL = 0x113e, + S_DEFRANGE_REGISTER = 0x1141, S_LPROC32_ID = 0x1146, S_GPROC32_ID = 0x1147, S_PROC_ID_END = 0x114f @@ -1946,6 +1948,56 @@ end: free (s->data_symbol.name); } +/* Write an S_LOCAL symbol, representing an optimized variable. This is then + followed by various S_DEFRANGE_* symbols, which describe how to find the + value of a variable and the range for which this is valid. */ + +static void +write_s_local (dw_die_ref die) +{ + unsigned int label_num = ++sym_label_num; + const char *name = get_AT_string (die, DW_AT_name); + uint32_t type; + + /* This is struct LOCALSYM in Microsoft's cvinfo.h: + + struct LOCALSYM { + uint16_t reclen; + uint16_t rectyp; + uint32_t typind; + uint16_t flags; + char name[]; + }; + */ + + fputs (integer_asm_op (2, false), asm_out_file); + asm_fprintf (asm_out_file, + "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n", + label_num, label_num); + + targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, S_LOCAL); + putc ('\n', asm_out_file); + + type = get_type_num (get_AT_ref (die, DW_AT_type), false, false); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, type); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, 0); + putc ('\n', asm_out_file); + + ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1); + + ASM_OUTPUT_ALIGN (asm_out_file, 2); + + targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num); +} + /* Write an S_LDATA32 symbol, representing a static variable within a function. This symbol can also appear outside of a function block - see write_data_symbol. */ @@ -2278,6 +2330,194 @@ write_fbreg_variable (dw_die_ref die, dw_loc_descr_ref loc_ref, targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num); } +/* Write an S_DEFRANGE_REGISTER symbol, which describes a range for which an + S_LOCAL variable is held in a certain register. */ + +static void +write_defrange_register (dw_loc_descr_ref expr, rtx range_start, rtx range_end) +{ + unsigned int label_num = ++sym_label_num; + uint16_t regno; + + /* This is defrange_register in binutils and DEFRANGESYMREGISTER in + Microsoft's cvinfo.h: + + struct lvar_addr_range + { + uint32_t offset; + uint16_t section; + uint16_t length; + } ATTRIBUTE_PACKED; + + struct lvar_addr_gap { + uint16_t offset; + uint16_t length; + } ATTRIBUTE_PACKED; + + struct defrange_register + { + uint16_t size; + uint16_t kind; + uint16_t reg; + uint16_t attributes; + struct lvar_addr_range range; + struct lvar_addr_gap gaps[]; + } ATTRIBUTE_PACKED; + */ + + if (expr->dw_loc_opc == DW_OP_regx) + regno = dwarf_reg_to_cv (expr->dw_loc_oprnd1.v.val_int); + else + regno = dwarf_reg_to_cv (expr->dw_loc_opc - DW_OP_reg0); + + if (regno == 0) + return; + + fputs (integer_asm_op (2, false), asm_out_file); + asm_fprintf (asm_out_file, + "%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n", + label_num, label_num); + + targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, S_DEFRANGE_REGISTER); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, regno); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, 0); + putc ('\n', asm_out_file); + + asm_fprintf (asm_out_file, "\t.secrel32 "); + output_addr_const (asm_out_file, range_start); + fputc ('\n', asm_out_file); + + asm_fprintf (asm_out_file, "\t.secidx "); + output_addr_const (asm_out_file, range_start); + fputc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + output_addr_const (asm_out_file, range_end); + fputs (" - ", asm_out_file); + output_addr_const (asm_out_file, range_start); + putc ('\n', asm_out_file); + + targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num); +} + +/* Try to write an S_DEFRANGE_* symbol for the given DWARF location. */ + +static void +write_optimized_local_variable_loc (dw_loc_descr_ref expr, rtx range_start, + rtx range_end) +{ + if (expr->dw_loc_next) + return; + + if (!range_start) + return; + + if (!range_end) + return; + + switch (expr->dw_loc_opc) + { + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + case DW_OP_regx: + write_defrange_register (expr, range_start, range_end); + break; + + default: + break; + } +} + +/* Write an optimized local variable, given by an S_LOCAL symbol followed by + any number of S_DEFRANGE_* symbols. We can't mix and match optimized and + unoptimized variables in the same function, so even if it stays in the same + place for the whole block we need to write an S_LOCAL. */ + +static void +write_optimized_local_variable (dw_die_ref die, rtx block_start, rtx block_end) +{ + dw_attr_node *loc; + dw_loc_list_ref loc_list; + + loc = get_AT (die, DW_AT_location); + if (!loc) + return; + + switch (loc->dw_attr_val.val_class) + { + case dw_val_class_loc_list: + loc_list = loc->dw_attr_val.v.val_loc_list; + + write_s_local (die); + + while (loc_list) + { + rtx range_start = NULL, range_end = NULL; + + if (loc_list->begin) + range_start = gen_rtx_SYMBOL_REF (Pmode, loc_list->begin); + + if (loc_list->end) + range_end = gen_rtx_SYMBOL_REF (Pmode, loc_list->end); + + write_optimized_local_variable_loc (loc_list->expr, range_start, + range_end); + + loc_list = loc_list->dw_loc_next; + } + break; + + case dw_val_class_loc: + write_s_local (die); + + write_optimized_local_variable_loc (loc->dw_attr_val.v.val_loc, + block_start, block_end); + break; + + default: + break; + } +} + /* Write a symbol representing an unoptimized variable within a function, if we're able to translate the DIE's DW_AT_location into its CodeView equivalent. */ @@ -2517,6 +2757,77 @@ write_unoptimized_function_vars (dw_die_ref die, dw_loc_descr_ref fbloc) while (c != first_child); } +/* Write the variables in an optimized function or block. There's no S_BLOCK32s + here, with the range determining the lifetime of a variable. Unfortunately + for us CodeView is much less expressive than DWARF when it comes to variable + locations, so some degree of "optimized out"s is inevitable. */ + +static void +write_optimized_function_vars (dw_die_ref die, rtx block_start, rtx block_end) +{ + dw_die_ref first_child, c; + + first_child = dw_get_die_child (die); + + if (!first_child) + return; + + c = first_child; + do + { + c = dw_get_die_sib (c); + + switch (dw_get_die_tag (c)) + { + case DW_TAG_formal_parameter: + case DW_TAG_variable: + write_optimized_local_variable (c, block_start, block_end); + break; + + case DW_TAG_lexical_block: + { + dw_attr_node *loc_low, *loc_high; + const char *label_low, *label_high; + rtx rtx_low, rtx_high; + + loc_low = get_AT (die, DW_AT_low_pc); + if (!loc_low) + break; + + if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id) + break; + + label_low = loc_low->dw_attr_val.v.val_lbl_id; + if (!label_low) + break; + + rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low); + + loc_high = get_AT (die, DW_AT_high_pc); + if (!loc_high) + break; + + if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc) + break; + + label_high = loc_high->dw_attr_val.v.val_lbl_id; + if (!label_high) + break; + + rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high); + + write_optimized_function_vars (c, rtx_low, rtx_high); + + break; + } + + default: + break; + } + } + while (c != first_child); +} + /* Write an S_GPROC32_ID symbol, representing a global function, or an S_LPROC32_ID symbol, for a static function. */ @@ -2648,7 +2959,10 @@ write_function (codeview_symbol *s) if (frame_base && frame_base->dw_attr_val.val_class == dw_val_class_loc) fbloc = frame_base->dw_attr_val.v.val_loc; - write_unoptimized_function_vars (s->function.die, fbloc); + if (flag_var_tracking) + write_optimized_function_vars (s->function.die, rtx_low, rtx_high); + else + write_unoptimized_function_vars (s->function.die, fbloc); /* Output the S_PROC_ID_END record. */ |