---
gcc/pdbout.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++-
gcc/pdbout.h | 21 +++++
2 files changed, 260 insertions(+), 3 deletions(-)
diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 2f5b52b6fc3..29b0d1c131f 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -51,6 +51,7 @@ static void pdbout_finish (const char *filename);
static void pdbout_begin_function (tree func);
static void pdbout_late_global_decl (tree var);
static void pdbout_function_decl (tree decl);
+static void pdbout_var_location (rtx_insn * loc_note);
static void pdbout_begin_block (unsigned int line ATTRIBUTE_UNUSED,
unsigned int blocknum);
static void pdbout_end_block (unsigned int line ATTRIBUTE_UNUSED,
@@ -62,6 +63,7 @@ static struct pdb_func *funcs = NULL, *cur_func = NULL;
static struct pdb_block *cur_block = NULL;
static struct pdb_global_var *global_vars = NULL;
static struct pdb_type *types = NULL, *last_type = NULL;
+static unsigned int var_loc_number = 1;
static hash_table <pdb_type_tree_hasher> tree_hash_table (31);
static struct pdb_type *byte_type, *signed_byte_type, *wchar_type,
*char16_type, *uint16_type, *int16_type, *char32_type, *uint32_type,
@@ -107,7 +109,7 @@ const struct gcc_debug_hooks pdb_debug_hooks = {
debug_nothing_tree, /* outlining_inline_function */
debug_nothing_rtx_code_label, /* label */
debug_nothing_int, /* handle_pch */
- debug_nothing_rtx_insn, /* var_location */
+ pdbout_var_location,
debug_nothing_tree, /* inline_entry */
debug_nothing_tree, /* size_function */
debug_nothing_void, /* switch_text_section */
@@ -134,12 +136,143 @@ pdbout_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
fprintf (asm_out_file, FUNC_END_LABEL "%u:\n", current_function_funcdef_no);
}
+/* Output DEFRANGESYMREGISTER or DEFRANGESYMREGISTERREL structure, describing
+ * the scope range, register, and offset at which a local variable can be
+ * found. */
+static void
+write_var_location (struct pdb_var_location *var_loc,
+ unsigned int next_var_loc_number, unsigned int func_num)
+{
+ switch (var_loc->type)
+ {
+ case pdb_var_loc_register:
+ fprintf (asm_out_file, "\t.short\t0xe\n");
+ fprintf (asm_out_file, "\t.short\t0x%x\n", S_DEFRANGE_REGISTER);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", var_loc->reg);
+ fprintf (asm_out_file, "\t.short\t0\n"); // range attr
+ fprintf (asm_out_file, "\t.secrel32\t.Lvarloc%u\n",
+ var_loc->var_loc_number);
+ fprintf (asm_out_file, "\t.secidx\t.Lvarloc%u\n",
+ var_loc->var_loc_number);
+
+ if (next_var_loc_number != 0)
+ {
+ fprintf (asm_out_file, "\t.short\t[.Lvarloc%u]-[.Lvarloc%u]\n",
+ next_var_loc_number, var_loc->var_loc_number);
+ }
+ else
+ {
+ fprintf (asm_out_file,
+ "\t.short\t[" FUNC_END_LABEL "%u]-[.Lvarloc%u]\n",
+ func_num, var_loc->var_loc_number); // to end of function
+ }
+
+ break;
+
+ case pdb_var_loc_regrel:
+ fprintf (asm_out_file, "\t.short\t0x12\n");
+ fprintf (asm_out_file, "\t.short\t0x%x\n", S_DEFRANGE_REGISTER_REL);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", var_loc->reg);
+
+ // spilledUdtMember, padding, offsetParent
+ fprintf (asm_out_file, "\t.short\t0\n");
+
+ fprintf (asm_out_file, "\t.long\t0x%x\n", var_loc->offset);
+ fprintf (asm_out_file, "\t.secrel32\t.Lvarloc%u\n",
+ var_loc->var_loc_number);
+ fprintf (asm_out_file, "\t.secidx\t.Lvarloc%u\n",
+ var_loc->var_loc_number);
+
+ if (next_var_loc_number != 0)
+ {
+ fprintf (asm_out_file, "\t.short\t[.Lvarloc%u]-[.Lvarloc%u]\n",
+ next_var_loc_number, var_loc->var_loc_number);
+ }
+ else
+ {
+ fprintf (asm_out_file,
+ "\t.short\t[" FUNC_END_LABEL "%u]-[.Lvarloc%u]\n",
+ func_num, var_loc->var_loc_number); // to end of function
+ }
+
+ break;
+
+ case pdb_var_loc_unknown:
+ break;
+ }
+}
+
+/* We have encountered an optimized local variable, i.e. one which doesn't
+ * live in the same place for the duration of a function.
+ * Output a LOCALSYM struct. */
+static void
+pdbout_optimized_local_variable (struct pdb_local_var *v,
+ struct pdb_var_location *var_locs,
+ unsigned int func_num)
+{
+ uint16_t len, align;
+ size_t name_len = strlen (v->name);
+ struct pdb_var_location *last_pvl = var_locs, *pvl;
+
+ len = 11 + name_len;
+
+ if (len % 4 != 0)
+ {
+ align = 4 - (len % 4);
+ len += 4 - (len % 4);
+ }
+ else
+ align = 0;
+
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+ (uint16_t) (len - sizeof (uint16_t)));
+ fprintf (asm_out_file, "\t.short\t0x%x\n", S_LOCAL);
+ fprintf (asm_out_file, "\t.long\t0x%x\n", v->type ? v->type->id : 0);
+ fprintf (asm_out_file, "\t.short\t0\n"); // flags
+
+ ASM_OUTPUT_ASCII (asm_out_file, v->name, name_len + 1);
+
+ for (unsigned int i = 0; i < align; i++)
+ {
+ fprintf (asm_out_file, "\t.byte\t0\n");
+ }
+
+ pvl = var_locs->next;
+ while (pvl)
+ {
+ if (pvl->var == v->t)
+ {
+ write_var_location (last_pvl, pvl->var_loc_number, func_num);
+ last_pvl = pvl;
+ }
+
+ pvl = pvl->next;
+ }
+
+ write_var_location (last_pvl, 0, func_num);
+}
+
/* Output the information as to where to a local variable can be found. */
static void
-pdbout_local_variable (struct pdb_local_var *v)
+pdbout_local_variable (struct pdb_local_var *v,
+ struct pdb_var_location *var_locs,
+ unsigned int func_num)
{
uint16_t len, align;
size_t name_len = strlen (v->name);
+ struct pdb_var_location *pvl;
+
+ pvl = var_locs;
+ while (pvl)
+ {
+ if (pvl->var == v->t)
+ {
+ pdbout_optimized_local_variable (v, pvl, func_num);
+ return;
+ }
+
+ pvl = pvl->next;
+ }
switch (v->var_type)
{
@@ -277,7 +410,7 @@ pdbout_block (struct pdb_block *block, struct pdb_func
*func)
while (local_var)
{
if (local_var->block_num == block->num)
- pdbout_local_variable (local_var);
+ pdbout_local_variable (local_var, func->var_locs, func->num);
local_var = local_var->next;
}
@@ -398,6 +531,15 @@ pdbout_proc32 (struct pdb_func *func)
func->local_vars = n;
}
+
+ while (func->var_locs)
+ {
+ struct pdb_var_location *n = func->var_locs->next;
+
+ free (func->var_locs);
+
+ func->var_locs = n;
+ }
}
/* Output DATASYM32 structure, describing a global variable: either
@@ -531,6 +673,7 @@ pdbout_begin_function (tree func)
f->public_flag = TREE_PUBLIC (func);
f->type = find_type (TREE_TYPE (func));
f->local_vars = f->last_local_var = NULL;
+ f->var_locs = f->last_var_loc = NULL;
f->block.next = NULL;
f->block.parent = NULL;
@@ -1854,6 +1997,99 @@ pdbout_function_decl (tree decl)
cur_block = NULL;
}
+/* We've been given the details of where an optimized local variable resides,
+ * i.e. one that doesn't stay in the same place on the stack for the function
+ * duration. Record them so we can output them later.
+ * CodeView seems quite limited in this regard compared to DWARF - e.g. there's
+ * no way of saying that we know a variable would always have a constant value
+ * at such-and-such a point. There's hints in the header files that such
+ * functionality once existed, but MSVC won't output it and the debugger
+ * doesn't seem to understand it. */
+static void
+pdbout_var_location (rtx_insn * loc_note)
+{
+ rtx value, orig_rtl;
+ tree var;
+ struct pdb_var_location *var_loc;
+
+ if (!cur_func)
+ return;
+
+ if (!NOTE_P (loc_note))
+ return;
+
+ if (NOTE_KIND (loc_note) != NOTE_INSN_VAR_LOCATION)
+ return;
+
+ var = NOTE_VAR_LOCATION_DECL (loc_note);
+ value = orig_rtl = NOTE_VAR_LOCATION_LOC (loc_note);
+
+ if (value)
+ value = eliminate_regs (value, VOIDmode, NULL_RTX);
+
+ var_loc =
+ (struct pdb_var_location *) xmalloc (sizeof (struct pdb_var_location));
+
+ var_loc->next = NULL;
+ var_loc->var = var;
+ var_loc->var_loc_number = var_loc_number;
+
+ if (value)
+ {
+ switch (GET_CODE (value))
+ {
+ case REG:
+ var_loc->type = pdb_var_loc_register;
+ var_loc->reg = map_register_no (REGNO (value), GET_MODE (value));
+ break;
+
+ case MEM:
+ if (GET_CODE (XEXP (value, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (value, 0), 1)) == CONST_INT)
+ {
+ var_loc->type = pdb_var_loc_regrel;
+ var_loc->reg =
+ map_register_no (REGNO (XEXP (XEXP (value, 0), 0)),
+ GET_MODE (XEXP (XEXP (value, 0), 0)));
+ var_loc->offset = XINT (XEXP (XEXP (value, 0), 1), 0);
+ }
+ else if (GET_CODE (XEXP (value, 0)) == REG)
+ {
+ var_loc->type = pdb_var_loc_regrel;
+ var_loc->reg =
+ map_register_no (REGNO (XEXP (value, 0)),
+ GET_MODE (XEXP (value, 0)));
+ var_loc->offset = 0;
+ }
+ else
+ var_loc->type = pdb_var_loc_unknown;
+
+ break;
+
+ default:
+ var_loc->type = pdb_var_loc_unknown;
+ break;
+ }
+
+ if (var_loc->type == pdb_var_loc_regrel)
+ var_loc->offset =
+ fix_variable_offset (orig_rtl, var_loc->reg, var_loc->offset);
+ }
+
+ fprintf (asm_out_file, ".Lvarloc%u:\n", var_loc_number);
+
+ if (cur_func->last_var_loc)
+ cur_func->last_var_loc->next = var_loc;
+
+ cur_func->last_var_loc = var_loc;
+
+ if (!cur_func->var_locs)
+ cur_func->var_locs = var_loc;
+
+ var_loc_number++;
+}
+
/* We've encountered the start of a scope block - output an asm label so
* it can be referred to elsewhere. */
static void
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 782e8d0faa1..9a92a4f9972 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -29,6 +29,9 @@
#define S_LPROC32 0x110f
#define S_GPROC32 0x1110
#define S_REGREL32 0x1111
+#define S_LOCAL 0x113e
+#define S_DEFRANGE_REGISTER 0x1141
+#define S_DEFRANGE_REGISTER_REL 0x1145
/* Format version as of MSVC 7 */
#define CV_SIGNATURE_C13 4
@@ -56,6 +59,23 @@ struct pdb_local_var
char name[1];
};
+enum pdb_var_loc_type
+{
+ pdb_var_loc_unknown,
+ pdb_var_loc_register,
+ pdb_var_loc_regrel
+};
+
+struct pdb_var_location
+{
+ struct pdb_var_location *next;
+ tree var;
+ unsigned int var_loc_number;
+ enum pdb_var_loc_type type;
+ unsigned int reg;
+ int32_t offset;
+};
+
struct pdb_block
{
struct pdb_block *next;
@@ -72,6 +92,7 @@ struct pdb_func
unsigned int public_flag;
struct pdb_type *type;
struct pdb_local_var *local_vars, *last_local_var;
+ struct pdb_var_location *var_locs, *last_var_loc;
struct pdb_block block;
};
--
2.26.2