/* SPU target-dependent code for GDB, the GNU debugger.
- Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
Based on a port by Sid Manning <sid@us.ibm.com>.
#include "floatformat.h"
#include "block.h"
#include "observer.h"
+#include "infcall.h"
#include "spu-tdep.h"
/* Whether to stop for new SPE contexts. */
static int spu_stop_on_load_p = 0;
+/* Whether to automatically flush the SW-managed cache. */
+static int spu_auto_flush_cache_p = 1;
/* The tdep structure. */
return default_register_reggroup_p (gdbarch, regnum, group);
}
-/* Address conversion. */
+
+/* Address handling. */
static int
spu_gdbarch_id (struct gdbarch *gdbarch)
return strtoulst (buf, NULL, 16);
}
+static int
+spu_address_class_type_flags (int byte_size, int dwarf2_addr_class)
+{
+ if (dwarf2_addr_class == 1)
+ return TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
+ else
+ return 0;
+}
+
+static const char *
+spu_address_class_type_flags_to_name (struct gdbarch *gdbarch, int type_flags)
+{
+ if (type_flags & TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1)
+ return "__ea";
+ else
+ return NULL;
+}
+
+static int
+spu_address_class_name_to_type_flags (struct gdbarch *gdbarch,
+ const char *name, int *type_flags_ptr)
+{
+ if (strcmp (name, "__ea") == 0)
+ {
+ *type_flags_ptr = TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
+ return 1;
+ }
+ else
+ return 0;
+}
+
static void
spu_address_to_pointer (struct gdbarch *gdbarch,
struct type *type, gdb_byte *buf, CORE_ADDR addr)
ULONGEST addr
= extract_unsigned_integer (buf, TYPE_LENGTH (type), byte_order);
+ /* Do not convert __ea pointers. */
+ if (TYPE_ADDRESS_CLASS_1 (type))
+ return addr;
+
return addr? SPUADDR (id, addr & lslr) : 0;
}
int found_sp = 0;
int found_fp = 0;
int found_lr = 0;
+ int found_bc = 0;
int reg_immed[SPU_NUM_GPRS];
gdb_byte buf[16];
CORE_ADDR prolog_pc = start_pc;
- The first instruction to set up the stack pointer.
- The first instruction to set up the frame pointer.
- The first instruction to save the link register.
+ - The first instruction to save the backchain.
- We return the instruction after the latest of these three,
+ We return the instruction after the latest of these four,
or the incoming PC if none is found. The first instruction
to set up the stack pointer also defines the frame size.
found_lr = 1;
prolog_pc = pc + 4;
}
+
+ if (ra == SPU_RAW_SP_REGNUM
+ && (found_sp? immed == 0 : rt == SPU_RAW_SP_REGNUM)
+ && !found_bc)
+ {
+ found_bc = 1;
+ prolog_pc = pc + 4;
+ }
}
/* _start uses SELB to set up the stack pointer. */
spu_software_single_step (struct frame_info *frame)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct address_space *aspace = get_frame_address_space (frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR pc, next_pc;
unsigned int insn;
else
next_pc = (SPUADDR_ADDR (pc) + 4) & (SPU_LS_SIZE - 1);
- insert_single_step_breakpoint (gdbarch, SPUADDR (SPUADDR_SPU (pc), next_pc));
+ insert_single_step_breakpoint (gdbarch,
+ aspace, SPUADDR (SPUADDR_SPU (pc), next_pc));
if (is_branch (insn, &offset, ®))
{
target = target & (SPU_LS_SIZE - 1);
if (target != next_pc)
- insert_single_step_breakpoint (gdbarch,
+ insert_single_step_breakpoint (gdbarch, aspace,
SPUADDR (SPUADDR_SPU (pc), target));
}
struct symbol *sym;
struct symtab_and_line sal;
- sym = lookup_block_symbol (block, "main", NULL, VAR_DOMAIN);
+ sym = lookup_block_symbol (block, "main", VAR_DOMAIN);
if (sym)
{
fixup_symbol_section (sym, objfile);
/* Use a numerical address for the set_breakpoint command to avoid having
the breakpoint re-set incorrectly. */
xsnprintf (buf, sizeof buf, "*%s", core_addr_to_string (pc));
- set_breakpoint (get_objfile_arch (objfile),
- buf, NULL /* condition */,
- 0 /* hardwareflag */, 1 /* tempflag */,
- -1 /* thread */, 0 /* ignore_count */,
- 0 /* pending */, 1 /* enabled */);
+ create_breakpoint (get_objfile_arch (objfile), buf /* arg */,
+ NULL /* cond_string */, -1 /* thread */,
+ 0 /* parse_condition_and_thread */, 1 /* tempflag */,
+ 0 /* hardwareflag */, 0 /* traceflag */,
+ 0 /* ignore_count */,
+ AUTO_BOOLEAN_FALSE /* pending_break_support */,
+ NULL /* ops */, 0 /* from_tty */, 1 /* enabled */);
+}
+
+
+/* Look up OBJFILE loaded into FRAME's SPU context. */
+static struct objfile *
+spu_objfile_from_frame (struct frame_info *frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ struct objfile *obj;
+
+ if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
+ return NULL;
+
+ ALL_OBJFILES (obj)
+ {
+ if (obj->sections != obj->sections_end
+ && SPUADDR_SPU (obj_section_addr (obj->sections)) == tdep->id)
+ return obj;
+ }
+
+ return NULL;
+}
+
+/* Flush cache for ea pointer access if available. */
+static void
+flush_ea_cache (void)
+{
+ struct minimal_symbol *msymbol;
+ struct objfile *obj;
+
+ if (!has_stack_frames ())
+ return;
+
+ obj = spu_objfile_from_frame (get_current_frame ());
+ if (obj == NULL)
+ return;
+
+ /* Lookup inferior function __cache_flush. */
+ msymbol = lookup_minimal_symbol ("__cache_flush", NULL, obj);
+ if (msymbol != NULL)
+ {
+ struct type *type;
+ CORE_ADDR addr;
+
+ type = objfile_type (obj)->builtin_void;
+ type = lookup_function_type (type);
+ type = lookup_pointer_type (type);
+ addr = SYMBOL_VALUE_ADDRESS (msymbol);
+
+ call_function_by_hand (value_from_pointer (type, addr), 0, NULL);
+ }
+}
+
+/* This handler is called when the inferior has stopped. If it is stopped in
+ SPU architecture then flush the ea cache if used. */
+static void
+spu_attach_normal_stop (struct bpstats *bs, int print_frame)
+{
+ if (!spu_auto_flush_cache_p)
+ return;
+
+ /* Temporarily reset spu_auto_flush_cache_p to avoid recursively
+ re-entering this function when __cache_flush stops. */
+ spu_auto_flush_cache_p = 0;
+ flush_ea_cache ();
+ spu_auto_flush_cache_p = 1;
}
value);
}
+static void
+show_spu_auto_flush_cache (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Automatic software-cache flush is %s.\n"),
+ value);
+}
+
/* Set up gdbarch struct. */
set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
- /* Address conversion. */
+ /* Address handling. */
set_gdbarch_address_to_pointer (gdbarch, spu_address_to_pointer);
set_gdbarch_pointer_to_address (gdbarch, spu_pointer_to_address);
set_gdbarch_integer_to_address (gdbarch, spu_integer_to_address);
+ set_gdbarch_address_class_type_flags (gdbarch, spu_address_class_type_flags);
+ set_gdbarch_address_class_type_flags_to_name
+ (gdbarch, spu_address_class_type_flags_to_name);
+ set_gdbarch_address_class_name_to_type_flags
+ (gdbarch, spu_address_class_name_to_type_flags);
+
/* Inferior function calls. */
set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
/* Install spu stop-on-load handler. */
observer_attach_new_objfile (spu_catch_start);
+ /* Add ourselves to normal_stop event chain. */
+ observer_attach_normal_stop (spu_attach_normal_stop);
+
/* Add root prefix command for all "set spu"/"show spu" commands. */
add_prefix_cmd ("spu", no_class, set_spu_command,
_("Various SPU specific commands."),
show_spu_stop_on_load,
&setspucmdlist, &showspucmdlist);
+ /* Toggle whether or not to automatically flush the software-managed
+ cache whenever SPE execution stops. */
+ add_setshow_boolean_cmd ("auto-flush-cache", class_support,
+ &spu_auto_flush_cache_p, _("\
+Set whether to automatically flush the software-managed cache."),
+ _("\
+Show whether to automatically flush the software-managed cache."),
+ _("\
+Use \"on\" to automatically flush the software-managed cache\n\
+whenever SPE execution stops.\n\
+Use \"off\" to never automatically flush the software-managed cache."),
+ NULL,
+ show_spu_auto_flush_cache,
+ &setspucmdlist, &showspucmdlist);
+
/* Add root prefix command for all "info spu" commands. */
add_prefix_cmd ("spu", class_info, info_spu_command,
_("Various SPU specific commands."),