---
 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

Reply via email to