Commit cc9d3be2 authored by Matthias Braun's avatar Matthias Braun
Browse files

dwarf: initial support for callframe and params

- We now always output dwarf3 so we can use DW_OP_call_frame_cfa and avoid
  construction location lists, but just reuse the callframe info lists.
- Backends have to emit debug info as callframe calculation changes:
  The ia32 backend has a preliminary implementation which assumes esp
  offset of frame_type_size at the beginning of a block (currently
  always true), the no-omit-fp mode assumes ebp relative addressing
  (which is correct except for the prolog/epilogue insns)
parent 5f6c325f
......@@ -257,7 +257,7 @@ void TEMPLATE_emit_routine(ir_graph *irg)
block_schedule = be_create_block_schedule(irg);
/* emit assembler prolog */
be_gas_emit_function_prolog(entity, 4);
be_gas_emit_function_prolog(entity, 4, NULL);
/* populate jump link fields with their destinations */
irg_block_walk_graph(irg, TEMPLATE_gen_labels, NULL, NULL);
......
......@@ -579,7 +579,7 @@ void amd64_gen_routine(ir_graph *irg)
blk_sched = be_create_block_schedule(irg);
be_gas_emit_function_prolog(entity, 4);
be_gas_emit_function_prolog(entity, 4, NULL);
irg_block_walk_graph(irg, amd64_gen_labels, NULL, NULL);
......
......@@ -955,7 +955,7 @@ void arm_gen_routine(ir_graph *irg)
/* create the block schedule */
blk_sched = be_create_block_schedule(irg);
be_gas_emit_function_prolog(entity, 4);
be_gas_emit_function_prolog(entity, 4, NULL);
irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL);
......
......@@ -1695,7 +1695,6 @@ static void create_stacklayout(ir_graph *irg)
layout->frame_type = get_irg_frame_type(irg);
layout->between_type = arm_get_between_type();
layout->arg_type = arg_type;
layout->param_map = NULL; /* TODO */
layout->initial_offset = 0;
layout->initial_bias = 0;
layout->sp_relative = true;
......
......@@ -313,13 +313,11 @@ static void be_abi_call_free(be_abi_call_t *call)
* @param args the stack argument layout type
* @param between the between layout type
* @param locals the method frame type
* @param param_map an array mapping method argument positions to the stack argument type
*
* @return the initialized stack layout
*/
static be_stack_layout_t *stack_frame_init(be_stack_layout_t *frame, ir_type *args,
ir_type *between, ir_type *locals,
ir_entity *param_map[])
ir_type *between, ir_type *locals)
{
frame->arg_type = args;
frame->between_type = between;
......@@ -327,7 +325,6 @@ static be_stack_layout_t *stack_frame_init(be_stack_layout_t *frame, ir_type *ar
frame->initial_offset = 0;
frame->initial_bias = 0;
frame->order[1] = between;
frame->param_map = param_map;
/* typical decreasing stack: locals have the
* lowest addresses, arguments the highest */
......@@ -1107,13 +1104,11 @@ static void process_calls(ir_graph *irg)
*
* @param call the current call ABI
* @param method_type the method type
* @param param_map an array mapping method arguments to the stack layout
* type
*
* @return the stack argument layout type
*/
static ir_type *compute_arg_type(ir_graph *irg, be_abi_call_t *call,
ir_type *method_type, ir_entity ***param_map)
ir_type *method_type)
{
struct obstack *obst = be_get_be_obst(irg);
ir_type *frame_type = get_irg_frame_type(irg);
......@@ -1125,9 +1120,7 @@ static ir_type *compute_arg_type(ir_graph *irg, be_abi_call_t *call,
ir_type *res;
size_t i;
ir_entity **map;
*param_map = map = OALLOCNZ(obst, ir_entity*, n_params);
ir_entity **map = OALLOCNZ(obst, ir_entity*, n_params);
res = new_type_struct(new_id_from_chars("arg_type", 8));
/* collect existing entities for value_param_types */
......@@ -1430,7 +1423,6 @@ static void modify_irg(ir_graph *irg)
const ir_edge_t *edge;
ir_type *arg_type, *bet_type;
lower_frame_sels_env_t ctx;
ir_entity **param_map;
DBG((dbg, LEVEL_1, "introducing abi on %+F\n", irg));
......@@ -1438,7 +1430,7 @@ static void modify_irg(ir_graph *irg)
irp_reserve_resources(irp, IRP_RESOURCE_ENTITY_LINK);
arg_type = compute_arg_type(irg, call, method_type, &param_map);
arg_type = compute_arg_type(irg, call, method_type);
/* Convert the Sel nodes in the irg to frame addr nodes: */
ctx.frame = get_irg_frame(irg);
......@@ -1482,7 +1474,7 @@ static void modify_irg(ir_graph *irg)
stack_layout->sp_relative = call->flags.bits.try_omit_fp;
bet_type = call->cb->get_between_type(irg);
stack_frame_init(stack_layout, arg_type, bet_type,
get_irg_frame_type(irg), param_map);
get_irg_frame_type(irg));
/* Count the register params and add them to the number of Projs for the RegParams node */
for (i = 0; i < n_params; ++i) {
......
......@@ -65,7 +65,9 @@ static int debug_level = LEVEL_NONE;
typedef enum custom_abbrevs {
abbrev_void_pointer_type = 100,
abbrev_unnamed_formal_parameter,
abbrev_formal_parameter_no_location,
abbrev_void_subroutine_type,
abbrev_void_subprogram,
abbrev_bitfield_member,
} custom_abbrevs;
......@@ -74,7 +76,6 @@ typedef enum custom_abbrevs {
*/
typedef struct dwarf_t {
const ir_entity *cur_ent; /**< current method entity */
const be_stack_layout_t *layout; /**< current stack layout */
unsigned next_type_nr; /**< next type number */
pmap *file_map; /**< a map from file names to number in file list */
const char **file_list;
......@@ -101,6 +102,7 @@ static unsigned insert_file(const char *filename)
ARR_APP1(const char*, env.file_list, filename);
num = (unsigned)ARR_LEN(env.file_list);
pmap_insert(env.file_map, filename, INT_TO_PTR(num));
/* TODO: quote chars in string */
be_emit_irprintf("\t.file %u \"%s\"\n", num, filename);
return num;
......@@ -140,8 +142,25 @@ static unsigned get_uleb128_size(unsigned value)
return size;
}
static void emit_sleb128(long value)
{
be_emit_irprintf("\t.sleb128 %ld\n", value);
be_emit_write_line();
}
static unsigned get_sleb128_size(long value)
{
unsigned size = 0;
do {
value >>= 7;
size += 1;
} while (value != 0 && value != -1);
return size;
}
static void emit_string(const char *string)
{
/* TODO: quote special chars */
be_emit_irprintf("\t.asciz \"%s\"\n", string);
be_emit_write_line();
}
......@@ -307,6 +326,33 @@ void be_dwarf_location(dbg_info *dbgi)
be_emit_write_line();
}
void be_dwarf_callframe_register(const arch_register_t *reg)
{
if (debug_level < LEVEL_FRAMEINFO)
return;
be_emit_cstring("\t.cfi_def_cfa_register ");
be_emit_irprintf("%d\n", reg->dwarf_number);
be_emit_write_line();
}
void be_dwarf_callframe_offset(int offset)
{
if (debug_level < LEVEL_FRAMEINFO)
return;
be_emit_cstring("\t.cfi_def_cfa_offset ");
be_emit_irprintf("%d\n", offset);
be_emit_write_line();
}
void be_dwarf_callframe_spilloffset(const arch_register_t *reg, int offset)
{
if (debug_level < LEVEL_FRAMEINFO)
return;
be_emit_cstring("\t.cfi_offset ");
be_emit_irprintf("%d, %d\n", reg->dwarf_number, offset);
be_emit_write_line();
}
static bool is_extern_entity(const ir_entity *entity)
{
ir_visited_t visibility = get_entity_visibility(entity);
......@@ -337,38 +383,147 @@ static void emit_dbginfo(const dbg_info *dbgi)
emit_uleb128(loc.column);
}
static void emit_type_address(const ir_type *type)
{
be_emit_irprintf("\t.long %sT%ld - %sinfo_begin\n",
be_gas_get_private_prefix(),
get_type_nr(type), be_gas_get_private_prefix());
be_emit_write_line();
}
static void emit_subprogram_abbrev(void)
{
begin_abbrev(DW_TAG_subprogram, DW_TAG_subprogram, DW_CHILDREN_no);
begin_abbrev(DW_TAG_subprogram, DW_TAG_subprogram, DW_CHILDREN_yes);
register_attribute(DW_AT_name, DW_FORM_string);
register_dbginfo_attributes();
register_attribute(DW_AT_type, DW_FORM_ref4);
register_attribute(DW_AT_external, DW_FORM_flag);
register_attribute(DW_AT_low_pc, DW_FORM_addr);
register_attribute(DW_AT_high_pc, DW_FORM_addr);
//register_attribute(DW_AT_prototyped, DW_FORM_flag);
//register_attribute(DW_AT_type, DW_FORM_ref4);
register_attribute(DW_AT_external, DW_FORM_flag);
register_attribute(DW_AT_low_pc, DW_FORM_addr);
register_attribute(DW_AT_high_pc, DW_FORM_addr);
//register_attribute(DW_AT_frame_base, DW_FORM_block1);
if (debug_level >= LEVEL_FRAMEINFO)
register_attribute(DW_AT_frame_base, DW_FORM_block1);
end_abbrev();
begin_abbrev(abbrev_void_subprogram, DW_TAG_subprogram, DW_CHILDREN_yes);
register_attribute(DW_AT_name, DW_FORM_string);
register_dbginfo_attributes();
register_attribute(DW_AT_external, DW_FORM_flag);
register_attribute(DW_AT_low_pc, DW_FORM_addr);
register_attribute(DW_AT_high_pc, DW_FORM_addr);
//register_attribute(DW_AT_prototyped, DW_FORM_flag);
if (debug_level >= LEVEL_FRAMEINFO)
register_attribute(DW_AT_frame_base, DW_FORM_block1);
end_abbrev();
begin_abbrev(DW_TAG_formal_parameter, DW_TAG_formal_parameter,
DW_CHILDREN_no);
register_attribute(DW_AT_name, DW_FORM_string);
register_dbginfo_attributes();
register_attribute(DW_AT_type, DW_FORM_ref4);
register_attribute(DW_AT_location, DW_FORM_block1);
end_abbrev();
begin_abbrev(abbrev_formal_parameter_no_location, DW_TAG_formal_parameter,
DW_CHILDREN_no);
register_attribute(DW_AT_name, DW_FORM_string);
register_dbginfo_attributes();
register_attribute(DW_AT_type, DW_FORM_ref4);
end_abbrev();
}
static void emit_type(ir_type *type);
static void emit_stack_location(long offset)
{
unsigned size = 1 + get_sleb128_size(offset);
emit_int8(size);
emit_int8(DW_OP_fbreg);
emit_sleb128(offset);
}
void be_dwarf_method_begin(const ir_entity *entity)
static void emit_function_parameters(const ir_entity *entity,
const parameter_dbg_info_t *infos)
{
ir_type *type = get_entity_type(entity);
size_t n_params = get_method_n_params(type);
dbg_info *dbgi = get_entity_dbg_info(entity);
size_t i;
for (i = 0; i < n_params; ++i) {
ir_type *param_type = get_method_param_type(type, i);
if (infos != NULL && infos[i].entity != NULL) {
const ir_entity *entity = infos[i].entity;
long offset = get_entity_offset(entity);
emit_uleb128(DW_TAG_formal_parameter);
emit_string_printf("arg%u", (unsigned)i);
emit_dbginfo(dbgi);
emit_type_address(param_type);
emit_stack_location(offset);
} else {
emit_uleb128(abbrev_formal_parameter_no_location);
emit_string_printf("arg%u", (unsigned)i);
emit_dbginfo(dbgi);
emit_type_address(param_type);
}
}
}
void be_dwarf_method_before(const ir_entity *entity,
const parameter_dbg_info_t *parameter_infos)
{
if (debug_level < LEVEL_BASIC)
return;
{
ir_type *type = get_entity_type(entity);
size_t n_ress = get_method_n_ress(type);
size_t n_params = get_method_n_params(type);
size_t i;
(void)parameter_infos;
be_gas_emit_switch_section(GAS_SECTION_DEBUG_INFO);
if (n_ress > 0) {
ir_type *res = get_method_res_type(type, 0);
emit_type(res);
}
for (i = 0; i < n_params; ++i) {
ir_type *param_type = get_method_param_type(type, i);
emit_type(param_type);
}
emit_entity_label(entity);
emit_uleb128(DW_TAG_subprogram);
emit_uleb128(n_ress == 0 ? abbrev_void_subprogram : DW_TAG_subprogram);
emit_string(get_entity_ld_name(entity));
emit_dbginfo(get_entity_dbg_info(entity));
if (n_ress > 0) {
ir_type *res = get_method_res_type(type, 0);
emit_type_address(res);
}
emit_int8(is_extern_entity(entity));
emit_ref(entity);
be_emit_irprintf("\t.long %smethod_end_%s\n", be_gas_get_private_prefix(),
get_entity_ld_name(entity));
/* frame_base prog */
emit_int8(1);
emit_int8(DW_OP_call_frame_cfa);
emit_function_parameters(entity, parameter_infos);
emit_int8(0);
ARR_APP1(const ir_entity*, env.pubnames_list, entity);
env.cur_ent = entity;
}
}
void be_dwarf_method_begin(void)
{
if (debug_level < LEVEL_FRAMEINFO)
return;
be_emit_cstring("\t.cfi_startproc\n");
be_emit_write_line();
}
void be_dwarf_method_end(void)
......@@ -378,9 +533,12 @@ void be_dwarf_method_end(void)
const ir_entity *entity = env.cur_ent;
be_emit_irprintf("%smethod_end_%s:\n", be_gas_get_private_prefix(),
get_entity_ld_name(entity));
}
static void emit_type(ir_type *type);
if (debug_level >= LEVEL_FRAMEINFO) {
be_emit_cstring("\t.cfi_endproc\n");
be_emit_write_line();
}
}
static void emit_base_type_abbrev(void)
{
......@@ -397,14 +555,6 @@ static void emit_type_label(const ir_type *type)
be_emit_write_line();
}
static void emit_type_address(const ir_type *type)
{
be_emit_irprintf("\t.long %sT%ld - %sinfo_begin\n",
be_gas_get_private_prefix(),
get_type_nr(type), be_gas_get_private_prefix());
be_emit_write_line();
}
static void emit_base_type(const ir_type *type)
{
char buf[128];
......@@ -694,6 +844,9 @@ void be_dwarf_variable(const ir_entity *entity)
return;
if (get_entity_ld_name(entity)[0] == '\0')
return;
/* skip external variables */
if (get_entity_visibility(entity) == ir_visibility_external)
return;
be_gas_emit_switch_section(GAS_SECTION_DEBUG_INFO);
......@@ -756,9 +909,9 @@ void be_dwarf_unit_begin(const char *filename)
/* length of compilation unit info */
emit_size("compile_unit_begin", "compile_unit_end");
emit_label("compile_unit_begin");
emit_int16(2); /* dwarf version */
emit_int16(3); /* dwarf version */
emit_address("abbrev_begin");
emit_int8(4); /* pointer size */
emit_int8(4); /* pointer size, TODO: query backend */
/* compile_unit die */
emit_uleb128(DW_TAG_compile_unit);
......@@ -771,6 +924,13 @@ void be_dwarf_unit_begin(const char *filename)
emit_int16(language);
if (comp_dir != NULL)
emit_string(comp_dir);
/* tell gas to emit cfi in debug_frame
* TODO: if we produce exception handling code then this should be
* .eh_frame (I also wonder if bad things happen if simply always
* use eh_frame) */
be_emit_cstring("\t.cfi_sections .debug_frame\n");
be_emit_write_line();
}
void be_dwarf_unit_end(void)
......
......@@ -27,6 +27,11 @@
#include "beabi.h"
typedef struct parameter_dbg_info_t {
const ir_entity *entity;
const arch_register_t *reg;
} parameter_dbg_info_t;
/** initialize and open debug handle */
void be_dwarf_open(void);
......@@ -39,8 +44,12 @@ void be_dwarf_unit_begin(const char *filename);
/** end compilation unit */
void be_dwarf_unit_end(void);
/** debug for a method begin */
void be_dwarf_method_begin(const ir_entity *ent);
/** output debug info necessary right before defining a method */
void be_dwarf_method_before(const ir_entity *ent,
const parameter_dbg_info_t *infos);
/** output debug info right before beginning to output assembly instructions */
void be_dwarf_method_begin(void);
/** debug for a method end */
void be_dwarf_method_end(void);
......@@ -52,4 +61,19 @@ void be_dwarf_variable(const ir_entity *ent);
* assembly instructions */
void be_dwarf_location(dbg_info *dbgi);
/** set base register that points to callframe */
void be_dwarf_callframe_register(const arch_register_t *reg);
/** set offset from base register that points to the callframe.
* Note: callframe is defined as in the dwarf documentation here which is the
* stackpointer before the call has happened. (Which would be the beginning of
* the between type in our backend) */
void be_dwarf_callframe_offset(int offset);
/**
* Indicate at which offset (relative to the CFA) a caller saved register has
* been saved.
*/
void be_dwarf_callframe_spilloffset(const arch_register_t *reg, int offset);
#endif
......@@ -217,151 +217,158 @@ typedef enum dwarf_line_number_x_ops {
} dwarf_line_number_x_ops;
typedef enum dwarf_location_op {
DW_OP_addr = 0x03,
DW_OP_deref = 0x06,
DW_OP_const1u = 0x08,
DW_OP_const1s = 0x09,
DW_OP_const2u = 0x0A,
DW_OP_const2s = 0x0B,
DW_OP_const4u = 0x0C,
DW_OP_const4s = 0x0D,
DW_OP_const8u = 0x0E,
DW_OP_const8s = 0x0F,
DW_OP_constu = 0x10,
DW_OP_consts = 0x11,
DW_OP_dup = 0x12,
DW_OP_drop = 0x13,
DW_OP_over = 0x14,
DW_OP_pick = 0x15,
DW_OP_swap = 0x16,
DW_OP_rot = 0x17,
DW_OP_xderef = 0x18,
DW_OP_abs = 0x19,
DW_OP_and = 0x1A,
DW_OP_div = 0x1B,
DW_OP_minus = 0x1C,
DW_OP_mod = 0x1D,
DW_OP_mul = 0x1E,
DW_OP_neg = 0x1F,
DW_OP_not = 0x20,
DW_OP_or = 0x21,
DW_OP_plus = 0x22,
DW_OP_plus_uconst = 0x23,
DW_OP_shl = 0x24,
DW_OP_shr = 0x25,
DW_OP_shra = 0x26,
DW_OP_xor = 0x27,
DW_OP_bra = 0x28,
DW_OP_eq = 0x29,
DW_OP_ge = 0x2A,
DW_OP_gt = 0x2B,
DW_OP_le = 0x2C,
DW_OP_lt = 0x2D,
DW_OP_ne = 0x2E,
DW_OP_skip = 0x2F,
DW_OP_lit0 = 0x30,
DW_OP_lit1 = 0x31,
DW_OP_lit2 = 0x32,
DW_OP_lit3 = 0x33,
DW_OP_lit4 = 0x34,
DW_OP_lit5 = 0x35,
DW_OP_lit6 = 0x36,
DW_OP_lit7 = 0x37,
DW_OP_lit8 = 0x38,
DW_OP_lit9 = 0x39,
DW_OP_lit10 = 0x3A,
DW_OP_lit11 = 0x3B,
DW_OP_lit12 = 0x3C,
DW_OP_lit13 = 0x3D,
DW_OP_lit14 = 0x3E,
DW_OP_lit15 = 0x3F,
DW_OP_lit16 = 0x40,
DW_OP_lit17 = 0x41,
DW_OP_lit18 = 0x42,
DW_OP_lit19 = 0x43,
DW_OP_lit20 = 0x44,
DW_OP_lit21 = 0x45,
DW_OP_lit22 = 0x46,
DW_OP_lit23 = 0x47,
DW_OP_lit24 = 0x48,
DW_OP_lit25 = 0x49,
DW_OP_lit26 = 0x4A,
DW_OP_lit27 = 0x4B,
DW_OP_lit28 = 0x4C,
DW_OP_lit29 = 0x4D,
DW_OP_lit30 = 0x4E,
DW_OP_lit31 = 0x4F,
DW_OP_reg0 = 0x50,
DW_OP_reg1 = 0x51,
DW_OP_reg2 = 0x52,
DW_OP_reg3 = 0x53,
DW_OP_reg4 = 0x54,
DW_OP_reg5 = 0x55,
DW_OP_reg6 = 0x56,
DW_OP_reg7 = 0x57,
DW_OP_reg8 = 0x58,
DW_OP_reg9 = 0x59,
DW_OP_reg10 = 0x5A,
DW_OP_reg11 = 0x5B,
DW_OP_reg12 = 0x5C,
DW_OP_reg13 = 0x5D,
DW_OP_reg14 = 0x5E,
DW_OP_reg15 = 0x5F,
DW_OP_reg16 = 0x60,
DW_OP_reg17 = 0x61,
DW_OP_reg18 = 0x62,
DW_OP_reg19 = 0x63,
DW_OP_reg20 = 0x64,
DW_OP_reg21 = 0x65,
DW_OP_reg22 = 0x66,
DW_OP_reg23 = 0x67,
DW_OP_reg24 = 0x68,
DW_OP_reg25 = 0x69,
DW_OP_reg26 = 0x6A,
DW_OP_reg27 = 0x6B,
DW_OP_reg28 = 0x6C,
DW_OP_reg29 = 0x6D,
DW_OP_reg30 = 0x6E,
DW_OP_reg31 = 0x6F,
DW_OP_breg0 = 0x70,
DW_OP_breg1 = 0x71,
DW_OP_breg2 = 0x72,
DW_OP_breg3 = 0x73,
DW_OP_breg4 = 0x74,
DW_OP_breg5 = 0x75,
DW_OP_breg6 = 0x76,
DW_OP_breg7 = 0x77,
DW_OP_breg8 = 0x78,
DW_OP_breg9 = 0x79,
DW_OP_breg10 = 0x7A,
DW_OP_breg11 = 0x7B,
DW_OP_breg12 = 0x7C,
DW_OP_breg13 = 0x7D,
DW_OP_breg14 = 0x7E,
DW_OP_breg15 = 0x7F,
DW_OP_breg16 = 0x80,
DW_OP_breg17 = 0x81,
DW_OP_breg18 = 0x82,
DW_OP_breg19 = 0x83,
DW_OP_breg20 = 0x84,
DW_OP_breg21 = 0x85,
DW_OP_breg22 = 0x86,
DW_OP_breg23 = 0x87,
DW_OP_breg24 = 0x88,
DW_OP_breg25 = 0x89,
DW_OP_breg26 = 0x8A,
DW_OP_breg27 = 0x8B,
DW_OP_breg28 = 0x8C,
DW_OP_breg29 = 0x8D,
DW_OP_breg30 = 0x8E,
DW_OP_breg31 = 0x8F,
DW_OP_regx = 0x90,
DW_OP_fbreg = 0x91,
DW_OP_bregx = 0x92,
DW_OP_piece = 0x93,
DW_OP_deref_size = 0x94,
DW_OP_xderef_size = 0x95,
DW_OP_nop = 0x96,
DW_OP_addr = 0x03,
DW_OP_deref = 0x06,
DW_OP_const1u = 0x08,
DW_OP_const1s = 0x09,
DW_OP_const2u = 0x0A,
DW_OP_const2s = 0x0B,
DW_OP_const4u = 0x0C,
DW_OP_const4s = 0x0D,
DW_OP_const8u = 0x0E,
DW_OP_const8s = 0x0F,
DW_OP_constu = 0x10,
DW_OP_consts = 0x11,
DW_OP_dup = 0x12,
DW_OP_drop = 0x13,
DW_OP_over = 0x14,
DW_OP_pick = 0x15,
DW_OP_swap = 0x16,