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);
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_frame_base, DW_FORM_block1);
//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();
}
void be_dwarf_method_begin(const ir_entity *entity)
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);
}
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
......@@ -362,6 +362,13 @@ typedef enum dwarf_location_op {
DW_OP_deref_size = 0x94,
DW_OP_xderef_size = 0x95,
DW_OP_nop = 0x96,
DW_OP_push_object_address = 0x97,
DW_OP_call2 = 0x98,
DW_OP_call4 = 0x99,
DW_OP_call_ref = 0x9a,
DW_OP_form_tls_address = 0x9b,
DW_OP_call_frame_cfa = 0x9c,
DW_OP_bit_piece = 0x9d,
} dwarf_location_op;
#endif
......@@ -89,6 +89,7 @@ static void emit_section_macho(be_gas_section_t section)
case GAS_SECTION_DEBUG_ABBREV: name = "section __DWARF,__debug_abbrev,regular,debug"; break;
case GAS_SECTION_DEBUG_LINE: name = "section __DWARF,__debug_line,regular,debug"; break;
case GAS_SECTION_DEBUG_PUBNAMES: name = "section __DWARF,__debug_pubnames,regular,debug"; break;
case GAS_SECTION_DEBUG_FRAME: name = "section __DWARF,__debug_frame,regular,debug"; break;
default: panic("unsupported scetion type 0x%X", section);
}
be_emit_irprintf("\t.%s\n", name);
......@@ -127,6 +128,7 @@ static void emit_section_sparc(be_gas_section_t section, const ir_entity *entity
"debug_abbrev",
"debug_line",
"debug_pubnames"
"debug_frame",
};
if (current_section == section && !(section & GAS_SECTION_FLAG_COMDAT))
......@@ -193,6 +195,7 @@ static void emit_section(be_gas_section_t section, const ir_entity *entity)
{ "debug_abbrev", "progbits", "" },
{ "debug_line", "progbits", "" },
{ "debug_pubnames", "progbits", "" },
{ "debug_frame", "progbits", "" },
};
if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
......@@ -528,18 +531,17 @@ static void emit_visibility(const ir_entity *entity)
}
}
void be_gas_emit_function_prolog(const ir_entity *entity, unsigned po2alignment)
void be_gas_emit_function_prolog(const ir_entity *entity, unsigned po2alignment, const parameter_dbg_info_t *parameter_infos)
{
be_gas_section_t section;
be_dwarf_method_begin(entity);
be_dwarf_method_before(entity, parameter_infos);
section = determine_section(NULL, entity);
emit_section(section, entity);
/* write the begin line (makes the life easier for scripts parsing the
* assembler) */
be_emit_write_line();
be_emit_cstring("# -- Begin ");
be_gas_emit_entity(entity);
be_emit_char('\n');
......@@ -585,10 +587,14 @@ void be_gas_emit_function_prolog(const ir_entity *entity, unsigned po2alignment)
be_gas_emit_entity(entity);
be_emit_cstring(":\n");
be_emit_write_line();
be_dwarf_method_begin();
}
void be_gas_emit_function_epilog(const ir_entity *entity)
{
be_dwarf_method_end();
if (be_gas_object_file_format == OBJECT_FILE_FORMAT_ELF) {
be_emit_cstring("\t.size\t");
be_gas_emit_entity(entity);
......@@ -603,8 +609,6 @@ void be_gas_emit_function_epilog(const ir_entity *entity)
be_emit_char('\n');
be_emit_write_line();
be_dwarf_method_end();
be_emit_char('\n');
be_emit_write_line();
}
......
......@@ -29,6 +29,7 @@
#include <stdbool.h>
#include "be_types.h"
#include "beemitter.h"
#include "bedwarf.h"
typedef enum {
GAS_SECTION_TEXT, /**< text section - program code */
......@@ -44,7 +45,8 @@ typedef enum {
GAS_SECTION_DEBUG_ABBREV, /**< dwarf debug abbrev */
GAS_SECTION_DEBUG_LINE, /**< dwarf debug line */
GAS_SECTION_DEBUG_PUBNAMES, /**< dwarf pub names */
GAS_SECTION_LAST = GAS_SECTION_DEBUG_PUBNAMES,
GAS_SECTION_DEBUG_FRAME, /**< dwarf callframe infos */
GAS_SECTION_LAST = GAS_SECTION_DEBUG_FRAME,
GAS_SECTION_TYPE_MASK = 0xFF,
GAS_SECTION_FLAG_TLS = 1 << 8, /**< thread local flag */
......@@ -86,7 +88,8 @@ void be_gas_emit_switch_section(be_gas_section_t section);
* emit assembler instructions necessary before starting function code
*/
void be_gas_emit_function_prolog(const ir_entity *entity,
unsigned po2alignment);
unsigned po2alignment,
const parameter_dbg_info_t *paramter_infos);
void be_gas_emit_function_epilog(const ir_entity *entity);
......
......@@ -72,7 +72,6 @@ struct be_stack_layout_t {
ir_type *order[N_FRAME_TYPES]; /**< arg, between and frame types ordered. */
ir_entity **param_map; /**< An array mapping type parameters to arg_type entries */
int initial_offset; /**< the initial difference between stack pointer and frame pointer */
int initial_bias; /**< the initial stack bias */
bool sp_relative : 1; /**< entities are addressed relative to
......
......@@ -89,6 +89,10 @@ static ir_label_t exc_label_id;
static int mark_spill_reload = 0;
static int do_pic;
static bool sp_relative;
static int frame_type_size;
static int callframe_offset;
/** Return the next block in Block schedule */
static ir_node *get_prev_block_sched(const ir_node *block)
{
......@@ -1661,6 +1665,15 @@ static void ia32_emit_node(ir_node *node)
ir_fprintf(stderr, "Error: No emit handler for node %+F (%+G, graph %+F)\n", node, node, current_ir_graph);
abort();
}
if (sp_relative) {
int sp_change = arch_get_sp_bias(node);
if (sp_change != 0) {
assert(sp_change != SP_BIAS_RESET);
callframe_offset += sp_change;
be_dwarf_callframe_offset(callframe_offset);
}
}
}
/**
......@@ -1812,6 +1825,16 @@ static void ia32_gen_block(ir_node *block)
ia32_emit_block_header(block);
if (sp_relative) {
ir_graph *irg = get_irn_irg(block);
callframe_offset = 4; /* 4 bytes for the return address */
/* ESP guessing, TODO perform a real ESP simulation */
if (block != get_irg_start_block(irg)) {
callframe_offset += frame_type_size;
}
be_dwarf_callframe_offset(callframe_offset);
}
/* emit the contents of the block */
be_dwarf_location(get_irn_dbg_info(block));
sched_foreach(block, node) {
......@@ -1864,6 +1887,33 @@ static int cmp_exc_entry(const void *a, const void *b)
return +1;
}
static parameter_dbg_info_t *construct_parameter_infos(ir_graph *irg)
{
ir_entity *entity = get_irg_entity(irg);
ir_type *type = get_entity_type(entity);
size_t n_params = get_method_n_params(type);
be_stack_layout_t *layout = be_get_irg_stack_layout(irg);
ir_type *arg_type = layout->arg_type;