6c2659886f
I'd like to enable the -Wmissing-declarations warning. However, it
warns for every _initialize function, for example:
CXX dcache.o
/home/smarchi/src/binutils-gdb/gdb/dcache.c: In function ‘void _initialize_dcache()’:
/home/smarchi/src/binutils-gdb/gdb/dcache.c:688:1: error: no previous declaration for ‘void _initialize_dcache()’ [-Werror=missing-declarations]
_initialize_dcache (void)
^~~~~~~~~~~~~~~~~~
The only practical way forward I found is to add back the declarations,
which were removed by this commit:
commit 481695ed5f
Author: John Baldwin <jhb@FreeBSD.org>
Date: Sat Sep 9 11:02:37 2017 -0700
Remove unnecessary function prototypes.
I don't think it's a big problem to have the declarations for these
functions, but if anybody has a better solution for this, I'll be happy
to use it.
gdb/ChangeLog:
* aarch64-fbsd-nat.c (_initialize_aarch64_fbsd_nat): Add declaration.
* aarch64-fbsd-tdep.c (_initialize_aarch64_fbsd_tdep): Add declaration.
* aarch64-linux-nat.c (_initialize_aarch64_linux_nat): Add declaration.
* aarch64-linux-tdep.c (_initialize_aarch64_linux_tdep): Add declaration.
* aarch64-newlib-tdep.c (_initialize_aarch64_newlib_tdep): Add declaration.
* aarch64-tdep.c (_initialize_aarch64_tdep): Add declaration.
* ada-exp.y (_initialize_ada_exp): Add declaration.
* ada-lang.c (_initialize_ada_language): Add declaration.
* ada-tasks.c (_initialize_tasks): Add declaration.
* agent.c (_initialize_agent): Add declaration.
* aix-thread.c (_initialize_aix_thread): Add declaration.
* alpha-bsd-nat.c (_initialize_alphabsd_nat): Add declaration.
* alpha-linux-nat.c (_initialize_alpha_linux_nat): Add declaration.
* alpha-linux-tdep.c (_initialize_alpha_linux_tdep): Add declaration.
* alpha-nbsd-tdep.c (_initialize_alphanbsd_tdep): Add declaration.
* alpha-obsd-tdep.c (_initialize_alphaobsd_tdep): Add declaration.
* alpha-tdep.c (_initialize_alpha_tdep): Add declaration.
* amd64-darwin-tdep.c (_initialize_amd64_darwin_tdep): Add declaration.
* amd64-dicos-tdep.c (_initialize_amd64_dicos_tdep): Add declaration.
* amd64-fbsd-nat.c (_initialize_amd64fbsd_nat): Add declaration.
* amd64-fbsd-tdep.c (_initialize_amd64fbsd_tdep): Add declaration.
* amd64-linux-nat.c (_initialize_amd64_linux_nat): Add declaration.
* amd64-linux-tdep.c (_initialize_amd64_linux_tdep): Add declaration.
* amd64-nbsd-nat.c (_initialize_amd64nbsd_nat): Add declaration.
* amd64-nbsd-tdep.c (_initialize_amd64nbsd_tdep): Add declaration.
* amd64-obsd-nat.c (_initialize_amd64obsd_nat): Add declaration.
* amd64-obsd-tdep.c (_initialize_amd64obsd_tdep): Add declaration.
* amd64-sol2-tdep.c (_initialize_amd64_sol2_tdep): Add declaration.
* amd64-tdep.c (_initialize_amd64_tdep): Add declaration.
* amd64-windows-nat.c (_initialize_amd64_windows_nat): Add declaration.
* amd64-windows-tdep.c (_initialize_amd64_windows_tdep): Add declaration.
* annotate.c (_initialize_annotate): Add declaration.
* arc-newlib-tdep.c (_initialize_arc_newlib_tdep): Add declaration.
* arc-tdep.c (_initialize_arc_tdep): Add declaration.
* arch-utils.c (_initialize_gdbarch_utils): Add declaration.
* arm-fbsd-nat.c (_initialize_arm_fbsd_nat): Add declaration.
* arm-fbsd-tdep.c (_initialize_arm_fbsd_tdep): Add declaration.
* arm-linux-nat.c (_initialize_arm_linux_nat): Add declaration.
* arm-linux-tdep.c (_initialize_arm_linux_tdep): Add declaration.
* arm-nbsd-nat.c (_initialize_arm_netbsd_nat): Add declaration.
* arm-nbsd-tdep.c (_initialize_arm_netbsd_tdep): Add declaration.
* arm-obsd-tdep.c (_initialize_armobsd_tdep): Add declaration.
* arm-pikeos-tdep.c (_initialize_arm_pikeos_tdep): Add declaration.
* arm-symbian-tdep.c (_initialize_arm_symbian_tdep): Add declaration.
* arm-tdep.c (_initialize_arm_tdep): Add declaration.
* arm-wince-tdep.c (_initialize_arm_wince_tdep): Add declaration.
* auto-load.c (_initialize_auto_load): Add declaration.
* auxv.c (_initialize_auxv): Add declaration.
* avr-tdep.c (_initialize_avr_tdep): Add declaration.
* ax-gdb.c (_initialize_ax_gdb): Add declaration.
* bfin-linux-tdep.c (_initialize_bfin_linux_tdep): Add declaration.
* bfin-tdep.c (_initialize_bfin_tdep): Add declaration.
* break-catch-sig.c (_initialize_break_catch_sig): Add declaration.
* break-catch-syscall.c (_initialize_break_catch_syscall): Add declaration.
* break-catch-throw.c (_initialize_break_catch_throw): Add declaration.
* breakpoint.c (_initialize_breakpoint): Add declaration.
* bsd-uthread.c (_initialize_bsd_uthread): Add declaration.
* btrace.c (_initialize_btrace): Add declaration.
* charset.c (_initialize_charset): Add declaration.
* cli/cli-cmds.c (_initialize_cli_cmds): Add declaration.
* cli/cli-dump.c (_initialize_cli_dump): Add declaration.
* cli/cli-interp.c (_initialize_cli_interp): Add declaration.
* cli/cli-logging.c (_initialize_cli_logging): Add declaration.
* cli/cli-script.c (_initialize_cli_script): Add declaration.
* cli/cli-style.c (_initialize_cli_style): Add declaration.
* coff-pe-read.c (_initialize_coff_pe_read): Add declaration.
* coffread.c (_initialize_coffread): Add declaration.
* compile/compile-cplus-types.c (_initialize_compile_cplus_types): Add declaration.
* compile/compile.c (_initialize_compile): Add declaration.
* complaints.c (_initialize_complaints): Add declaration.
* completer.c (_initialize_completer): Add declaration.
* copying.c (_initialize_copying): Add declaration.
* corefile.c (_initialize_core): Add declaration.
* corelow.c (_initialize_corelow): Add declaration.
* cp-abi.c (_initialize_cp_abi): Add declaration.
* cp-namespace.c (_initialize_cp_namespace): Add declaration.
* cp-support.c (_initialize_cp_support): Add declaration.
* cp-valprint.c (_initialize_cp_valprint): Add declaration.
* cris-linux-tdep.c (_initialize_cris_linux_tdep): Add declaration.
* cris-tdep.c (_initialize_cris_tdep): Add declaration.
* csky-linux-tdep.c (_initialize_csky_linux_tdep): Add declaration.
* csky-tdep.c (_initialize_csky_tdep): Add declaration.
* ctfread.c (_initialize_ctfread): Add declaration.
* d-lang.c (_initialize_d_language): Add declaration.
* darwin-nat-info.c (_initialize_darwin_info_commands): Add declaration.
* darwin-nat.c (_initialize_darwin_nat): Add declaration.
* dbxread.c (_initialize_dbxread): Add declaration.
* dcache.c (_initialize_dcache): Add declaration.
* disasm-selftests.c (_initialize_disasm_selftests): Add declaration.
* disasm.c (_initialize_disasm): Add declaration.
* dtrace-probe.c (_initialize_dtrace_probe): Add declaration.
* dummy-frame.c (_initialize_dummy_frame): Add declaration.
* dwarf-index-cache.c (_initialize_index_cache): Add declaration.
* dwarf-index-write.c (_initialize_dwarf_index_write): Add declaration.
* dwarf2-frame-tailcall.c (_initialize_tailcall_frame): Add declaration.
* dwarf2-frame.c (_initialize_dwarf2_frame): Add declaration.
* dwarf2expr.c (_initialize_dwarf2expr): Add declaration.
* dwarf2loc.c (_initialize_dwarf2loc): Add declaration.
* dwarf2read.c (_initialize_dwarf2_read): Add declaration.
* elfread.c (_initialize_elfread): Add declaration.
* exec.c (_initialize_exec): Add declaration.
* extension.c (_initialize_extension): Add declaration.
* f-lang.c (_initialize_f_language): Add declaration.
* f-valprint.c (_initialize_f_valprint): Add declaration.
* fbsd-nat.c (_initialize_fbsd_nat): Add declaration.
* fbsd-tdep.c (_initialize_fbsd_tdep): Add declaration.
* filesystem.c (_initialize_filesystem): Add declaration.
* findcmd.c (_initialize_mem_search): Add declaration.
* findvar.c (_initialize_findvar): Add declaration.
* fork-child.c (_initialize_fork_child): Add declaration.
* frame-base.c (_initialize_frame_base): Add declaration.
* frame-unwind.c (_initialize_frame_unwind): Add declaration.
* frame.c (_initialize_frame): Add declaration.
* frv-linux-tdep.c (_initialize_frv_linux_tdep): Add declaration.
* frv-tdep.c (_initialize_frv_tdep): Add declaration.
* ft32-tdep.c (_initialize_ft32_tdep): Add declaration.
* gcore.c (_initialize_gcore): Add declaration.
* gdb-demangle.c (_initialize_gdb_demangle): Add declaration.
* gdb_bfd.c (_initialize_gdb_bfd): Add declaration.
* gdbarch-selftests.c (_initialize_gdbarch_selftests): Add declaration.
* gdbarch.c (_initialize_gdbarch): Add declaration.
* gdbtypes.c (_initialize_gdbtypes): Add declaration.
* gnu-nat.c (_initialize_gnu_nat): Add declaration.
* gnu-v2-abi.c (_initialize_gnu_v2_abi): Add declaration.
* gnu-v3-abi.c (_initialize_gnu_v3_abi): Add declaration.
* go-lang.c (_initialize_go_language): Add declaration.
* go32-nat.c (_initialize_go32_nat): Add declaration.
* guile/guile.c (_initialize_guile): Add declaration.
* h8300-tdep.c (_initialize_h8300_tdep): Add declaration.
* hppa-linux-nat.c (_initialize_hppa_linux_nat): Add declaration.
* hppa-linux-tdep.c (_initialize_hppa_linux_tdep): Add declaration.
* hppa-nbsd-nat.c (_initialize_hppanbsd_nat): Add declaration.
* hppa-nbsd-tdep.c (_initialize_hppanbsd_tdep): Add declaration.
* hppa-obsd-nat.c (_initialize_hppaobsd_nat): Add declaration.
* hppa-obsd-tdep.c (_initialize_hppabsd_tdep): Add declaration.
* hppa-tdep.c (_initialize_hppa_tdep): Add declaration.
* i386-bsd-nat.c (_initialize_i386bsd_nat): Add declaration.
* i386-cygwin-tdep.c (_initialize_i386_cygwin_tdep): Add declaration.
* i386-darwin-nat.c (_initialize_i386_darwin_nat): Add declaration.
* i386-darwin-tdep.c (_initialize_i386_darwin_tdep): Add declaration.
* i386-dicos-tdep.c (_initialize_i386_dicos_tdep): Add declaration.
* i386-fbsd-nat.c (_initialize_i386fbsd_nat): Add declaration.
* i386-fbsd-tdep.c (_initialize_i386fbsd_tdep): Add declaration.
* i386-gnu-nat.c (_initialize_i386gnu_nat): Add declaration.
* i386-gnu-tdep.c (_initialize_i386gnu_tdep): Add declaration.
* i386-go32-tdep.c (_initialize_i386_go32_tdep): Add declaration.
* i386-linux-nat.c (_initialize_i386_linux_nat): Add declaration.
* i386-linux-tdep.c (_initialize_i386_linux_tdep): Add declaration.
* i386-nbsd-nat.c (_initialize_i386nbsd_nat): Add declaration.
* i386-nbsd-tdep.c (_initialize_i386nbsd_tdep): Add declaration.
* i386-nto-tdep.c (_initialize_i386nto_tdep): Add declaration.
* i386-obsd-nat.c (_initialize_i386obsd_nat): Add declaration.
* i386-obsd-tdep.c (_initialize_i386obsd_tdep): Add declaration.
* i386-sol2-nat.c (_initialize_amd64_sol2_nat): Add declaration.
* i386-sol2-tdep.c (_initialize_i386_sol2_tdep): Add declaration.
* i386-tdep.c (_initialize_i386_tdep): Add declaration.
* i386-windows-nat.c (_initialize_i386_windows_nat): Add declaration.
* ia64-libunwind-tdep.c (_initialize_libunwind_frame): Add declaration.
* ia64-linux-nat.c (_initialize_ia64_linux_nat): Add declaration.
* ia64-linux-tdep.c (_initialize_ia64_linux_tdep): Add declaration.
* ia64-tdep.c (_initialize_ia64_tdep): Add declaration.
* ia64-vms-tdep.c (_initialize_ia64_vms_tdep): Add declaration.
* infcall.c (_initialize_infcall): Add declaration.
* infcmd.c (_initialize_infcmd): Add declaration.
* inflow.c (_initialize_inflow): Add declaration.
* infrun.c (_initialize_infrun): Add declaration.
* interps.c (_initialize_interpreter): Add declaration.
* iq2000-tdep.c (_initialize_iq2000_tdep): Add declaration.
* jit.c (_initialize_jit): Add declaration.
* language.c (_initialize_language): Add declaration.
* linux-fork.c (_initialize_linux_fork): Add declaration.
* linux-nat.c (_initialize_linux_nat): Add declaration.
* linux-tdep.c (_initialize_linux_tdep): Add declaration.
* linux-thread-db.c (_initialize_thread_db): Add declaration.
* lm32-tdep.c (_initialize_lm32_tdep): Add declaration.
* m2-lang.c (_initialize_m2_language): Add declaration.
* m32c-tdep.c (_initialize_m32c_tdep): Add declaration.
* m32r-linux-nat.c (_initialize_m32r_linux_nat): Add declaration.
* m32r-linux-tdep.c (_initialize_m32r_linux_tdep): Add declaration.
* m32r-tdep.c (_initialize_m32r_tdep): Add declaration.
* m68hc11-tdep.c (_initialize_m68hc11_tdep): Add declaration.
* m68k-bsd-nat.c (_initialize_m68kbsd_nat): Add declaration.
* m68k-bsd-tdep.c (_initialize_m68kbsd_tdep): Add declaration.
* m68k-linux-nat.c (_initialize_m68k_linux_nat): Add declaration.
* m68k-linux-tdep.c (_initialize_m68k_linux_tdep): Add declaration.
* m68k-tdep.c (_initialize_m68k_tdep): Add declaration.
* machoread.c (_initialize_machoread): Add declaration.
* macrocmd.c (_initialize_macrocmd): Add declaration.
* macroscope.c (_initialize_macroscope): Add declaration.
* maint-test-options.c (_initialize_maint_test_options): Add declaration.
* maint-test-settings.c (_initialize_maint_test_settings): Add declaration.
* maint.c (_initialize_maint_cmds): Add declaration.
* mdebugread.c (_initialize_mdebugread): Add declaration.
* memattr.c (_initialize_mem): Add declaration.
* mep-tdep.c (_initialize_mep_tdep): Add declaration.
* mi/mi-cmd-env.c (_initialize_mi_cmd_env): Add declaration.
* mi/mi-cmds.c (_initialize_mi_cmds): Add declaration.
* mi/mi-interp.c (_initialize_mi_interp): Add declaration.
* mi/mi-main.c (_initialize_mi_main): Add declaration.
* microblaze-linux-tdep.c (_initialize_microblaze_linux_tdep): Add declaration.
* microblaze-tdep.c (_initialize_microblaze_tdep): Add declaration.
* mips-fbsd-nat.c (_initialize_mips_fbsd_nat): Add declaration.
* mips-fbsd-tdep.c (_initialize_mips_fbsd_tdep): Add declaration.
* mips-linux-nat.c (_initialize_mips_linux_nat): Add declaration.
* mips-linux-tdep.c (_initialize_mips_linux_tdep): Add declaration.
* mips-nbsd-nat.c (_initialize_mipsnbsd_nat): Add declaration.
* mips-nbsd-tdep.c (_initialize_mipsnbsd_tdep): Add declaration.
* mips-sde-tdep.c (_initialize_mips_sde_tdep): Add declaration.
* mips-tdep.c (_initialize_mips_tdep): Add declaration.
* mips64-obsd-nat.c (_initialize_mips64obsd_nat): Add declaration.
* mips64-obsd-tdep.c (_initialize_mips64obsd_tdep): Add declaration.
* mipsread.c (_initialize_mipsread): Add declaration.
* mn10300-linux-tdep.c (_initialize_mn10300_linux_tdep): Add declaration.
* mn10300-tdep.c (_initialize_mn10300_tdep): Add declaration.
* moxie-tdep.c (_initialize_moxie_tdep): Add declaration.
* msp430-tdep.c (_initialize_msp430_tdep): Add declaration.
* nds32-tdep.c (_initialize_nds32_tdep): Add declaration.
* nios2-linux-tdep.c (_initialize_nios2_linux_tdep): Add declaration.
* nios2-tdep.c (_initialize_nios2_tdep): Add declaration.
* nto-procfs.c (_initialize_procfs): Add declaration.
* objc-lang.c (_initialize_objc_language): Add declaration.
* observable.c (_initialize_observer): Add declaration.
* opencl-lang.c (_initialize_opencl_language): Add declaration.
* or1k-linux-tdep.c (_initialize_or1k_linux_tdep): Add declaration.
* or1k-tdep.c (_initialize_or1k_tdep): Add declaration.
* osabi.c (_initialize_gdb_osabi): Add declaration.
* osdata.c (_initialize_osdata): Add declaration.
* p-valprint.c (_initialize_pascal_valprint): Add declaration.
* parse.c (_initialize_parse): Add declaration.
* ppc-fbsd-nat.c (_initialize_ppcfbsd_nat): Add declaration.
* ppc-fbsd-tdep.c (_initialize_ppcfbsd_tdep): Add declaration.
* ppc-linux-nat.c (_initialize_ppc_linux_nat): Add declaration.
* ppc-linux-tdep.c (_initialize_ppc_linux_tdep): Add declaration.
* ppc-nbsd-nat.c (_initialize_ppcnbsd_nat): Add declaration.
* ppc-nbsd-tdep.c (_initialize_ppcnbsd_tdep): Add declaration.
* ppc-obsd-nat.c (_initialize_ppcobsd_nat): Add declaration.
* ppc-obsd-tdep.c (_initialize_ppcobsd_tdep): Add declaration.
* printcmd.c (_initialize_printcmd): Add declaration.
* probe.c (_initialize_probe): Add declaration.
* proc-api.c (_initialize_proc_api): Add declaration.
* proc-events.c (_initialize_proc_events): Add declaration.
* proc-service.c (_initialize_proc_service): Add declaration.
* procfs.c (_initialize_procfs): Add declaration.
* producer.c (_initialize_producer): Add declaration.
* psymtab.c (_initialize_psymtab): Add declaration.
* python/python.c (_initialize_python): Add declaration.
* ravenscar-thread.c (_initialize_ravenscar): Add declaration.
* record-btrace.c (_initialize_record_btrace): Add declaration.
* record-full.c (_initialize_record_full): Add declaration.
* record.c (_initialize_record): Add declaration.
* regcache-dump.c (_initialize_regcache_dump): Add declaration.
* regcache.c (_initialize_regcache): Add declaration.
* reggroups.c (_initialize_reggroup): Add declaration.
* remote-notif.c (_initialize_notif): Add declaration.
* remote-sim.c (_initialize_remote_sim): Add declaration.
* remote.c (_initialize_remote): Add declaration.
* reverse.c (_initialize_reverse): Add declaration.
* riscv-fbsd-nat.c (_initialize_riscv_fbsd_nat): Add declaration.
* riscv-fbsd-tdep.c (_initialize_riscv_fbsd_tdep): Add declaration.
* riscv-linux-nat.c (_initialize_riscv_linux_nat): Add declaration.
* riscv-linux-tdep.c (_initialize_riscv_linux_tdep): Add declaration.
* riscv-tdep.c (_initialize_riscv_tdep): Add declaration.
* rl78-tdep.c (_initialize_rl78_tdep): Add declaration.
* rs6000-aix-tdep.c (_initialize_rs6000_aix_tdep): Add declaration.
* rs6000-lynx178-tdep.c (_initialize_rs6000_lynx178_tdep):
Add declaration.
* rs6000-nat.c (_initialize_rs6000_nat): Add declaration.
* rs6000-tdep.c (_initialize_rs6000_tdep): Add declaration.
* run-on-main-thread.c (_initialize_run_on_main_thread): Add declaration.
* rust-exp.y (_initialize_rust_exp): Add declaration.
* rx-tdep.c (_initialize_rx_tdep): Add declaration.
* s12z-tdep.c (_initialize_s12z_tdep): Add declaration.
* s390-linux-nat.c (_initialize_s390_nat): Add declaration.
* s390-linux-tdep.c (_initialize_s390_linux_tdep): Add declaration.
* s390-tdep.c (_initialize_s390_tdep): Add declaration.
* score-tdep.c (_initialize_score_tdep): Add declaration.
* ser-go32.c (_initialize_ser_dos): Add declaration.
* ser-mingw.c (_initialize_ser_windows): Add declaration.
* ser-pipe.c (_initialize_ser_pipe): Add declaration.
* ser-tcp.c (_initialize_ser_tcp): Add declaration.
* ser-uds.c (_initialize_ser_socket): Add declaration.
* ser-unix.c (_initialize_ser_hardwire): Add declaration.
* serial.c (_initialize_serial): Add declaration.
* sh-linux-tdep.c (_initialize_sh_linux_tdep): Add declaration.
* sh-nbsd-nat.c (_initialize_shnbsd_nat): Add declaration.
* sh-nbsd-tdep.c (_initialize_shnbsd_tdep): Add declaration.
* sh-tdep.c (_initialize_sh_tdep): Add declaration.
* skip.c (_initialize_step_skip): Add declaration.
* sol-thread.c (_initialize_sol_thread): Add declaration.
* solib-aix.c (_initialize_solib_aix): Add declaration.
* solib-darwin.c (_initialize_darwin_solib): Add declaration.
* solib-dsbt.c (_initialize_dsbt_solib): Add declaration.
* solib-frv.c (_initialize_frv_solib): Add declaration.
* solib-svr4.c (_initialize_svr4_solib): Add declaration.
* solib-target.c (_initialize_solib_target): Add declaration.
* solib.c (_initialize_solib): Add declaration.
* source-cache.c (_initialize_source_cache): Add declaration.
* source.c (_initialize_source): Add declaration.
* sparc-linux-nat.c (_initialize_sparc_linux_nat): Add declaration.
* sparc-linux-tdep.c (_initialize_sparc_linux_tdep): Add declaration.
* sparc-nat.c (_initialize_sparc_nat): Add declaration.
* sparc-nbsd-nat.c (_initialize_sparcnbsd_nat): Add declaration.
* sparc-nbsd-tdep.c (_initialize_sparcnbsd_tdep): Add declaration.
* sparc-obsd-tdep.c (_initialize_sparc32obsd_tdep): Add declaration.
* sparc-sol2-tdep.c (_initialize_sparc_sol2_tdep): Add declaration.
* sparc-tdep.c (_initialize_sparc_tdep): Add declaration.
* sparc64-fbsd-nat.c (_initialize_sparc64fbsd_nat): Add declaration.
* sparc64-fbsd-tdep.c (_initialize_sparc64fbsd_tdep): Add declaration.
* sparc64-linux-nat.c (_initialize_sparc64_linux_nat): Add declaration.
* sparc64-linux-tdep.c (_initialize_sparc64_linux_tdep): Add declaration.
* sparc64-nat.c (_initialize_sparc64_nat): Add declaration.
* sparc64-nbsd-nat.c (_initialize_sparc64nbsd_nat): Add declaration.
* sparc64-nbsd-tdep.c (_initialize_sparc64nbsd_tdep): Add declaration.
* sparc64-obsd-nat.c (_initialize_sparc64obsd_nat): Add declaration.
* sparc64-obsd-tdep.c (_initialize_sparc64obsd_tdep): Add declaration.
* sparc64-sol2-tdep.c (_initialize_sparc64_sol2_tdep): Add declaration.
* sparc64-tdep.c (_initialize_sparc64_adi_tdep): Add declaration.
* stabsread.c (_initialize_stabsread): Add declaration.
* stack.c (_initialize_stack): Add declaration.
* stap-probe.c (_initialize_stap_probe): Add declaration.
* std-regs.c (_initialize_frame_reg): Add declaration.
* symfile-debug.c (_initialize_symfile_debug): Add declaration.
* symfile-mem.c (_initialize_symfile_mem): Add declaration.
* symfile.c (_initialize_symfile): Add declaration.
* symmisc.c (_initialize_symmisc): Add declaration.
* symtab.c (_initialize_symtab): Add declaration.
* target.c (_initialize_target): Add declaration.
* target-connection.c (_initialize_target_connection): Add
declaration.
* target-dcache.c (_initialize_target_dcache): Add declaration.
* target-descriptions.c (_initialize_target_descriptions): Add declaration.
* thread.c (_initialize_thread): Add declaration.
* tic6x-linux-tdep.c (_initialize_tic6x_linux_tdep): Add declaration.
* tic6x-tdep.c (_initialize_tic6x_tdep): Add declaration.
* tilegx-linux-nat.c (_initialize_tile_linux_nat): Add declaration.
* tilegx-linux-tdep.c (_initialize_tilegx_linux_tdep): Add declaration.
* tilegx-tdep.c (_initialize_tilegx_tdep): Add declaration.
* tracectf.c (_initialize_ctf): Add declaration.
* tracefile-tfile.c (_initialize_tracefile_tfile): Add declaration.
* tracefile.c (_initialize_tracefile): Add declaration.
* tracepoint.c (_initialize_tracepoint): Add declaration.
* tui/tui-hooks.c (_initialize_tui_hooks): Add declaration.
* tui/tui-interp.c (_initialize_tui_interp): Add declaration.
* tui/tui-layout.c (_initialize_tui_layout): Add declaration.
* tui/tui-regs.c (_initialize_tui_regs): Add declaration.
* tui/tui-stack.c (_initialize_tui_stack): Add declaration.
* tui/tui-win.c (_initialize_tui_win): Add declaration.
* tui/tui.c (_initialize_tui): Add declaration.
* typeprint.c (_initialize_typeprint): Add declaration.
* ui-style.c (_initialize_ui_style): Add declaration.
* unittests/array-view-selftests.c (_initialize_array_view_selftests): Add declaration.
* unittests/child-path-selftests.c (_initialize_child_path_selftests): Add declaration.
* unittests/cli-utils-selftests.c (_initialize_cli_utils_selftests): Add declaration.
* unittests/common-utils-selftests.c (_initialize_common_utils_selftests): Add declaration.
* unittests/copy_bitwise-selftests.c (_initialize_copy_bitwise_utils_selftests): Add declaration.
* unittests/environ-selftests.c (_initialize_environ_selftests): Add declaration.
* unittests/filtered_iterator-selftests.c
(_initialize_filtered_iterator_selftests): Add declaration.
* unittests/format_pieces-selftests.c (_initialize_format_pieces_selftests): Add declaration.
* unittests/function-view-selftests.c (_initialize_function_view_selftests): Add declaration.
* unittests/help-doc-selftests.c (_initialize_help_doc_selftests): Add declaration.
* unittests/lookup_name_info-selftests.c (_initialize_lookup_name_info_selftests): Add declaration.
* unittests/main-thread-selftests.c
(_initialize_main_thread_selftests): Add declaration.
* unittests/memory-map-selftests.c (_initialize_memory_map_selftests): Add declaration.
* unittests/memrange-selftests.c (_initialize_memrange_selftests): Add declaration.
* unittests/mkdir-recursive-selftests.c (_initialize_mkdir_recursive_selftests): Add declaration.
* unittests/observable-selftests.c (_initialize_observer_selftest): Add declaration.
* unittests/offset-type-selftests.c (_initialize_offset_type_selftests): Add declaration.
* unittests/optional-selftests.c (_initialize_optional_selftests): Add declaration.
* unittests/parse-connection-spec-selftests.c (_initialize_parse_connection_spec_selftests): Add declaration.
* unittests/rsp-low-selftests.c (_initialize_rsp_low_selftests): Add declaration.
* unittests/scoped_fd-selftests.c (_initialize_scoped_fd_selftests): Add declaration.
* unittests/scoped_mmap-selftests.c (_initialize_scoped_mmap_selftests): Add declaration.
* unittests/scoped_restore-selftests.c (_initialize_scoped_restore_selftests): Add declaration.
* unittests/string_view-selftests.c (_initialize_string_view_selftests): Add declaration.
* unittests/style-selftests.c (_initialize_style_selftest): Add declaration.
* unittests/tracepoint-selftests.c (_initialize_tracepoint_selftests): Add declaration.
* unittests/tui-selftests.c (_initialize_tui_selftest): Add
declaration.
* unittests/unpack-selftests.c (_initialize_unpack_selftests): Add declaration.
* unittests/utils-selftests.c (_initialize_utils_selftests): Add declaration.
* unittests/vec-utils-selftests.c (_initialize_vec_utils_selftests): Add declaration.
* unittests/xml-utils-selftests.c (_initialize_xml_utils): Add declaration.
* user-regs.c (_initialize_user_regs): Add declaration.
* utils.c (_initialize_utils): Add declaration.
* v850-tdep.c (_initialize_v850_tdep): Add declaration.
* valops.c (_initialize_valops): Add declaration.
* valprint.c (_initialize_valprint): Add declaration.
* value.c (_initialize_values): Add declaration.
* varobj.c (_initialize_varobj): Add declaration.
* vax-bsd-nat.c (_initialize_vaxbsd_nat): Add declaration.
* vax-nbsd-tdep.c (_initialize_vaxnbsd_tdep): Add declaration.
* vax-tdep.c (_initialize_vax_tdep): Add declaration.
* windows-nat.c (_initialize_windows_nat): Add declaration.
(_initialize_check_for_gdb_ini): Add declaration.
(_initialize_loadable): Add declaration.
* windows-tdep.c (_initialize_windows_tdep): Add declaration.
* x86-bsd-nat.c (_initialize_x86_bsd_nat): Add declaration.
* x86-linux-nat.c (_initialize_x86_linux_nat): Add declaration.
* xcoffread.c (_initialize_xcoffread): Add declaration.
* xml-support.c (_initialize_xml_support): Add declaration.
* xstormy16-tdep.c (_initialize_xstormy16_tdep): Add declaration.
* xtensa-linux-nat.c (_initialize_xtensa_linux_nat): Add declaration.
* xtensa-linux-tdep.c (_initialize_xtensa_linux_tdep): Add declaration.
* xtensa-tdep.c (_initialize_xtensa_tdep): Add declaration.
Change-Id: I13eec7e0ed2b3c427377a7bdb055cf46da64def9
2463 lines
84 KiB
C
2463 lines
84 KiB
C
/* Target-dependent code for the Toshiba MeP for GDB, the GNU debugger.
|
||
|
||
Copyright (C) 2001-2020 Free Software Foundation, Inc.
|
||
|
||
Contributed by Red Hat, Inc.
|
||
|
||
This file is part of GDB.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include "defs.h"
|
||
#include "frame.h"
|
||
#include "frame-unwind.h"
|
||
#include "frame-base.h"
|
||
#include "symtab.h"
|
||
#include "gdbtypes.h"
|
||
#include "gdbcmd.h"
|
||
#include "gdbcore.h"
|
||
#include "value.h"
|
||
#include "inferior.h"
|
||
#include "dis-asm.h"
|
||
#include "symfile.h"
|
||
#include "objfiles.h"
|
||
#include "language.h"
|
||
#include "arch-utils.h"
|
||
#include "regcache.h"
|
||
#include "remote.h"
|
||
#include "sim-regno.h"
|
||
#include "disasm.h"
|
||
#include "trad-frame.h"
|
||
#include "reggroups.h"
|
||
#include "elf-bfd.h"
|
||
#include "elf/mep.h"
|
||
#include "prologue-value.h"
|
||
#include "cgen/bitset.h"
|
||
#include "infcall.h"
|
||
|
||
/* Get the user's customized MeP coprocessor register names from
|
||
libopcodes. */
|
||
#include "opcodes/mep-desc.h"
|
||
#include "opcodes/mep-opc.h"
|
||
|
||
|
||
/* The gdbarch_tdep structure. */
|
||
|
||
/* A quick recap for GDB hackers not familiar with the whole Toshiba
|
||
Media Processor story:
|
||
|
||
The MeP media engine is a configureable processor: users can design
|
||
their own coprocessors, implement custom instructions, adjust cache
|
||
sizes, select optional standard facilities like add-and-saturate
|
||
instructions, and so on. Then, they can build custom versions of
|
||
the GNU toolchain to support their customized chips. The
|
||
MeP-Integrator program (see utils/mep) takes a GNU toolchain source
|
||
tree, and a config file pointing to various files provided by the
|
||
user describing their customizations, and edits the source tree to
|
||
produce a compiler that can generate their custom instructions, an
|
||
assembler that can assemble them and recognize their custom
|
||
register names, and so on.
|
||
|
||
Furthermore, the user can actually specify several of these custom
|
||
configurations, called 'me_modules', and get a toolchain which can
|
||
produce code for any of them, given a compiler/assembler switch;
|
||
you say something like 'gcc -mconfig=mm_max' to generate code for
|
||
the me_module named 'mm_max'.
|
||
|
||
GDB, in particular, needs to:
|
||
|
||
- use the coprocessor control register names provided by the user
|
||
in their hardware description, in expressions, 'info register'
|
||
output, and disassembly,
|
||
|
||
- know the number, names, and types of the coprocessor's
|
||
general-purpose registers, adjust the 'info all-registers' output
|
||
accordingly, and print error messages if the user refers to one
|
||
that doesn't exist
|
||
|
||
- allow access to the control bus space only when the configuration
|
||
actually has a control bus, and recognize which regions of the
|
||
control bus space are actually populated,
|
||
|
||
- disassemble using the user's provided mnemonics for their custom
|
||
instructions, and
|
||
|
||
- recognize whether the $hi and $lo registers are present, and
|
||
allow access to them only when they are actually there.
|
||
|
||
There are three sources of information about what sort of me_module
|
||
we're actually dealing with:
|
||
|
||
- A MeP executable file indicates which me_module it was compiled
|
||
for, and libopcodes has tables describing each module. So, given
|
||
an executable file, we can find out about the processor it was
|
||
compiled for.
|
||
|
||
- There are SID command-line options to select a particular
|
||
me_module, overriding the one specified in the ELF file. SID
|
||
provides GDB with a fake read-only register, 'module', which
|
||
indicates which me_module GDB is communicating with an instance
|
||
of.
|
||
|
||
- There are SID command-line options to enable or disable certain
|
||
optional processor features, overriding the defaults for the
|
||
selected me_module. The MeP $OPT register indicates which
|
||
options are present on the current processor. */
|
||
|
||
|
||
struct gdbarch_tdep
|
||
{
|
||
/* A CGEN cpu descriptor for this BFD architecture and machine.
|
||
|
||
Note: this is *not* customized for any particular me_module; the
|
||
MeP libopcodes machinery actually puts off module-specific
|
||
customization until the last minute. So this contains
|
||
information about all supported me_modules. */
|
||
CGEN_CPU_DESC cpu_desc;
|
||
|
||
/* The me_module index from the ELF file we used to select this
|
||
architecture, or CONFIG_NONE if there was none.
|
||
|
||
Note that we should prefer to use the me_module number available
|
||
via the 'module' register, whenever we're actually talking to a
|
||
real target.
|
||
|
||
In the absence of live information, we'd like to get the
|
||
me_module number from the ELF file. But which ELF file: the
|
||
executable file, the core file, ... ? The answer is, "the last
|
||
ELF file we used to set the current architecture". Thus, we
|
||
create a separate instance of the gdbarch structure for each
|
||
me_module value mep_gdbarch_init sees, and store the me_module
|
||
value from the ELF file here. */
|
||
CONFIG_ATTR me_module;
|
||
};
|
||
|
||
|
||
|
||
/* Getting me_module information from the CGEN tables. */
|
||
|
||
|
||
/* Find an entry in the DESC's hardware table whose name begins with
|
||
PREFIX, and whose ISA mask intersects COPRO_ISA_MASK, but does not
|
||
intersect with GENERIC_ISA_MASK. If there is no matching entry,
|
||
return zero. */
|
||
static const CGEN_HW_ENTRY *
|
||
find_hw_entry_by_prefix_and_isa (CGEN_CPU_DESC desc,
|
||
const char *prefix,
|
||
CGEN_BITSET *copro_isa_mask,
|
||
CGEN_BITSET *generic_isa_mask)
|
||
{
|
||
int prefix_len = strlen (prefix);
|
||
int i;
|
||
|
||
for (i = 0; i < desc->hw_table.num_entries; i++)
|
||
{
|
||
const CGEN_HW_ENTRY *hw = desc->hw_table.entries[i];
|
||
if (strncmp (prefix, hw->name, prefix_len) == 0)
|
||
{
|
||
CGEN_BITSET *hw_isa_mask
|
||
= ((CGEN_BITSET *)
|
||
&CGEN_ATTR_CGEN_HW_ISA_VALUE (CGEN_HW_ATTRS (hw)));
|
||
|
||
if (cgen_bitset_intersect_p (hw_isa_mask, copro_isa_mask)
|
||
&& ! cgen_bitset_intersect_p (hw_isa_mask, generic_isa_mask))
|
||
return hw;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Find an entry in DESC's hardware table whose type is TYPE. Return
|
||
zero if there is none. */
|
||
static const CGEN_HW_ENTRY *
|
||
find_hw_entry_by_type (CGEN_CPU_DESC desc, CGEN_HW_TYPE type)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < desc->hw_table.num_entries; i++)
|
||
{
|
||
const CGEN_HW_ENTRY *hw = desc->hw_table.entries[i];
|
||
|
||
if (hw->type == type)
|
||
return hw;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Return the CGEN hardware table entry for the coprocessor register
|
||
set for ME_MODULE, whose name prefix is PREFIX. If ME_MODULE has
|
||
no such register set, return zero. If ME_MODULE is the generic
|
||
me_module CONFIG_NONE, return the table entry for the register set
|
||
whose hardware type is GENERIC_TYPE. */
|
||
static const CGEN_HW_ENTRY *
|
||
me_module_register_set (CONFIG_ATTR me_module,
|
||
const char *prefix,
|
||
CGEN_HW_TYPE generic_type)
|
||
{
|
||
/* This is kind of tricky, because the hardware table is constructed
|
||
in a way that isn't very helpful. Perhaps we can fix that, but
|
||
here's how it works at the moment:
|
||
|
||
The configuration map, `mep_config_map', is indexed by me_module
|
||
number, and indicates which coprocessor and core ISAs that
|
||
me_module supports. The 'core_isa' mask includes all the core
|
||
ISAs, and the 'cop_isa' mask includes all the coprocessor ISAs.
|
||
The entry for the generic me_module, CONFIG_NONE, has an empty
|
||
'cop_isa', and its 'core_isa' selects only the standard MeP
|
||
instruction set.
|
||
|
||
The CGEN CPU descriptor's hardware table, desc->hw_table, has
|
||
entries for all the register sets, for all me_modules. Each
|
||
entry has a mask indicating which ISAs use that register set.
|
||
So, if an me_module supports some coprocessor ISA, we can find
|
||
applicable register sets by scanning the hardware table for
|
||
register sets whose masks include (at least some of) those ISAs.
|
||
|
||
Each hardware table entry also has a name, whose prefix says
|
||
whether it's a general-purpose ("h-cr") or control ("h-ccr")
|
||
coprocessor register set. It might be nicer to have an attribute
|
||
indicating what sort of register set it was, that we could use
|
||
instead of pattern-matching on the name.
|
||
|
||
When there is no hardware table entry whose mask includes a
|
||
particular coprocessor ISA and whose name starts with a given
|
||
prefix, then that means that that coprocessor doesn't have any
|
||
registers of that type. In such cases, this function must return
|
||
a null pointer.
|
||
|
||
Coprocessor register sets' masks may or may not include the core
|
||
ISA for the me_module they belong to. Those generated by a2cgen
|
||
do, but the sample me_module included in the unconfigured tree,
|
||
'ccfx', does not.
|
||
|
||
There are generic coprocessor register sets, intended only for
|
||
use with the generic me_module. Unfortunately, their masks
|
||
include *all* ISAs --- even those for coprocessors that don't
|
||
have such register sets. This makes detecting the case where a
|
||
coprocessor lacks a particular register set more complicated.
|
||
|
||
So, here's the approach we take:
|
||
|
||
- For CONFIG_NONE, we return the generic coprocessor register set.
|
||
|
||
- For any other me_module, we search for a register set whose
|
||
mask contains any of the me_module's coprocessor ISAs,
|
||
specifically excluding the generic coprocessor register sets. */
|
||
|
||
CGEN_CPU_DESC desc = gdbarch_tdep (target_gdbarch ())->cpu_desc;
|
||
const CGEN_HW_ENTRY *hw;
|
||
|
||
if (me_module == CONFIG_NONE)
|
||
hw = find_hw_entry_by_type (desc, generic_type);
|
||
else
|
||
{
|
||
CGEN_BITSET *cop = &mep_config_map[me_module].cop_isa;
|
||
CGEN_BITSET *core = &mep_config_map[me_module].core_isa;
|
||
CGEN_BITSET *generic = &mep_config_map[CONFIG_NONE].core_isa;
|
||
CGEN_BITSET *cop_and_core;
|
||
|
||
/* The coprocessor ISAs include the ISA for the specific core which
|
||
has that coprocessor. */
|
||
cop_and_core = cgen_bitset_copy (cop);
|
||
cgen_bitset_union (cop, core, cop_and_core);
|
||
hw = find_hw_entry_by_prefix_and_isa (desc, prefix, cop_and_core, generic);
|
||
}
|
||
|
||
return hw;
|
||
}
|
||
|
||
|
||
/* Given a hardware table entry HW representing a register set, return
|
||
a pointer to the keyword table with all the register names. If HW
|
||
is NULL, return NULL, to propagate the "no such register set" info
|
||
along. */
|
||
static CGEN_KEYWORD *
|
||
register_set_keyword_table (const CGEN_HW_ENTRY *hw)
|
||
{
|
||
if (! hw)
|
||
return NULL;
|
||
|
||
/* Check that HW is actually a keyword table. */
|
||
gdb_assert (hw->asm_type == CGEN_ASM_KEYWORD);
|
||
|
||
/* The 'asm_data' field of a register set's hardware table entry
|
||
refers to a keyword table. */
|
||
return (CGEN_KEYWORD *) hw->asm_data;
|
||
}
|
||
|
||
|
||
/* Given a keyword table KEYWORD and a register number REGNUM, return
|
||
the name of the register, or "" if KEYWORD contains no register
|
||
whose number is REGNUM. */
|
||
static const char *
|
||
register_name_from_keyword (CGEN_KEYWORD *keyword_table, int regnum)
|
||
{
|
||
const CGEN_KEYWORD_ENTRY *entry
|
||
= cgen_keyword_lookup_value (keyword_table, regnum);
|
||
|
||
if (entry)
|
||
{
|
||
char *name = entry->name;
|
||
|
||
/* The CGEN keyword entries for register names include the
|
||
leading $, which appears in MeP assembly as well as in GDB.
|
||
But we don't want to return that; GDB core code adds that
|
||
itself. */
|
||
if (name[0] == '$')
|
||
name++;
|
||
|
||
return name;
|
||
}
|
||
else
|
||
return "";
|
||
}
|
||
|
||
|
||
/* Masks for option bits in the OPT special-purpose register. */
|
||
enum {
|
||
MEP_OPT_DIV = 1 << 25, /* 32-bit divide instruction option */
|
||
MEP_OPT_MUL = 1 << 24, /* 32-bit multiply instruction option */
|
||
MEP_OPT_BIT = 1 << 23, /* bit manipulation instruction option */
|
||
MEP_OPT_SAT = 1 << 22, /* saturation instruction option */
|
||
MEP_OPT_CLP = 1 << 21, /* clip instruction option */
|
||
MEP_OPT_MIN = 1 << 20, /* min/max instruction option */
|
||
MEP_OPT_AVE = 1 << 19, /* average instruction option */
|
||
MEP_OPT_ABS = 1 << 18, /* absolute difference instruction option */
|
||
MEP_OPT_LDZ = 1 << 16, /* leading zero instruction option */
|
||
MEP_OPT_VL64 = 1 << 6, /* 64-bit VLIW operation mode option */
|
||
MEP_OPT_VL32 = 1 << 5, /* 32-bit VLIW operation mode option */
|
||
MEP_OPT_COP = 1 << 4, /* coprocessor option */
|
||
MEP_OPT_DSP = 1 << 2, /* DSP option */
|
||
MEP_OPT_UCI = 1 << 1, /* UCI option */
|
||
MEP_OPT_DBG = 1 << 0, /* DBG function option */
|
||
};
|
||
|
||
|
||
/* Given the option_mask value for a particular entry in
|
||
mep_config_map, produce the value the processor's OPT register
|
||
would use to represent the same set of options. */
|
||
static unsigned int
|
||
opt_from_option_mask (unsigned int option_mask)
|
||
{
|
||
/* A table mapping OPT register bits onto CGEN config map option
|
||
bits. */
|
||
struct {
|
||
unsigned int opt_bit, option_mask_bit;
|
||
} bits[] = {
|
||
{ MEP_OPT_DIV, 1 << CGEN_INSN_OPTIONAL_DIV_INSN },
|
||
{ MEP_OPT_MUL, 1 << CGEN_INSN_OPTIONAL_MUL_INSN },
|
||
{ MEP_OPT_DIV, 1 << CGEN_INSN_OPTIONAL_DIV_INSN },
|
||
{ MEP_OPT_DBG, 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN },
|
||
{ MEP_OPT_LDZ, 1 << CGEN_INSN_OPTIONAL_LDZ_INSN },
|
||
{ MEP_OPT_ABS, 1 << CGEN_INSN_OPTIONAL_ABS_INSN },
|
||
{ MEP_OPT_AVE, 1 << CGEN_INSN_OPTIONAL_AVE_INSN },
|
||
{ MEP_OPT_MIN, 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN },
|
||
{ MEP_OPT_CLP, 1 << CGEN_INSN_OPTIONAL_CLIP_INSN },
|
||
{ MEP_OPT_SAT, 1 << CGEN_INSN_OPTIONAL_SAT_INSN },
|
||
{ MEP_OPT_UCI, 1 << CGEN_INSN_OPTIONAL_UCI_INSN },
|
||
{ MEP_OPT_DSP, 1 << CGEN_INSN_OPTIONAL_DSP_INSN },
|
||
{ MEP_OPT_COP, 1 << CGEN_INSN_OPTIONAL_CP_INSN },
|
||
};
|
||
|
||
int i;
|
||
unsigned int opt = 0;
|
||
|
||
for (i = 0; i < (sizeof (bits) / sizeof (bits[0])); i++)
|
||
if (option_mask & bits[i].option_mask_bit)
|
||
opt |= bits[i].opt_bit;
|
||
|
||
return opt;
|
||
}
|
||
|
||
|
||
/* Return the value the $OPT register would use to represent the set
|
||
of options for ME_MODULE. */
|
||
static unsigned int
|
||
me_module_opt (CONFIG_ATTR me_module)
|
||
{
|
||
return opt_from_option_mask (mep_config_map[me_module].option_mask);
|
||
}
|
||
|
||
|
||
/* Return the width of ME_MODULE's coprocessor data bus, in bits.
|
||
This is either 32 or 64. */
|
||
static int
|
||
me_module_cop_data_bus_width (CONFIG_ATTR me_module)
|
||
{
|
||
if (mep_config_map[me_module].option_mask
|
||
& (1 << CGEN_INSN_OPTIONAL_CP64_INSN))
|
||
return 64;
|
||
else
|
||
return 32;
|
||
}
|
||
|
||
|
||
/* Return true if ME_MODULE is big-endian, false otherwise. */
|
||
static int
|
||
me_module_big_endian (CONFIG_ATTR me_module)
|
||
{
|
||
return mep_config_map[me_module].big_endian;
|
||
}
|
||
|
||
|
||
/* Return the name of ME_MODULE, or NULL if it has no name. */
|
||
static const char *
|
||
me_module_name (CONFIG_ATTR me_module)
|
||
{
|
||
/* The default me_module has "" as its name, but it's easier for our
|
||
callers to test for NULL. */
|
||
if (! mep_config_map[me_module].name
|
||
|| mep_config_map[me_module].name[0] == '\0')
|
||
return NULL;
|
||
else
|
||
return mep_config_map[me_module].name;
|
||
}
|
||
|
||
/* Register set. */
|
||
|
||
|
||
/* The MeP spec defines the following registers:
|
||
16 general purpose registers (r0-r15)
|
||
32 control/special registers (csr0-csr31)
|
||
32 coprocessor general-purpose registers (c0 -- c31)
|
||
64 coprocessor control registers (ccr0 -- ccr63)
|
||
|
||
For the raw registers, we assign numbers here explicitly, instead
|
||
of letting the enum assign them for us; the numbers are a matter of
|
||
external protocol, and shouldn't shift around as things are edited.
|
||
|
||
We access the control/special registers via pseudoregisters, to
|
||
enforce read-only portions that some registers have.
|
||
|
||
We access the coprocessor general purpose and control registers via
|
||
pseudoregisters, to make sure they appear in the proper order in
|
||
the 'info all-registers' command (which uses the register number
|
||
ordering), and also to allow them to be renamed and resized
|
||
depending on the me_module in use.
|
||
|
||
The MeP allows coprocessor general-purpose registers to be either
|
||
32 or 64 bits long, depending on the configuration. Since we don't
|
||
want the format of the 'g' packet to vary from one core to another,
|
||
the raw coprocessor GPRs are always 64 bits. GDB doesn't allow the
|
||
types of registers to change (see the implementation of
|
||
register_type), so we have four banks of pseudoregisters for the
|
||
coprocessor gprs --- 32-bit vs. 64-bit, and integer
|
||
vs. floating-point --- and we show or hide them depending on the
|
||
configuration. */
|
||
enum
|
||
{
|
||
MEP_FIRST_RAW_REGNUM = 0,
|
||
|
||
MEP_FIRST_GPR_REGNUM = 0,
|
||
MEP_R0_REGNUM = 0,
|
||
MEP_R1_REGNUM = 1,
|
||
MEP_R2_REGNUM = 2,
|
||
MEP_R3_REGNUM = 3,
|
||
MEP_R4_REGNUM = 4,
|
||
MEP_R5_REGNUM = 5,
|
||
MEP_R6_REGNUM = 6,
|
||
MEP_R7_REGNUM = 7,
|
||
MEP_R8_REGNUM = 8,
|
||
MEP_R9_REGNUM = 9,
|
||
MEP_R10_REGNUM = 10,
|
||
MEP_R11_REGNUM = 11,
|
||
MEP_R12_REGNUM = 12,
|
||
MEP_FP_REGNUM = MEP_R8_REGNUM,
|
||
MEP_R13_REGNUM = 13,
|
||
MEP_TP_REGNUM = MEP_R13_REGNUM, /* (r13) Tiny data pointer */
|
||
MEP_R14_REGNUM = 14,
|
||
MEP_GP_REGNUM = MEP_R14_REGNUM, /* (r14) Global pointer */
|
||
MEP_R15_REGNUM = 15,
|
||
MEP_SP_REGNUM = MEP_R15_REGNUM, /* (r15) Stack pointer */
|
||
MEP_LAST_GPR_REGNUM = MEP_R15_REGNUM,
|
||
|
||
/* The raw control registers. These are the values as received via
|
||
the remote protocol, directly from the target; we only let user
|
||
code touch the via the pseudoregisters, which enforce read-only
|
||
bits. */
|
||
MEP_FIRST_RAW_CSR_REGNUM = 16,
|
||
MEP_RAW_PC_REGNUM = 16, /* Program counter */
|
||
MEP_RAW_LP_REGNUM = 17, /* Link pointer */
|
||
MEP_RAW_SAR_REGNUM = 18, /* Raw shift amount */
|
||
MEP_RAW_CSR3_REGNUM = 19, /* csr3: reserved */
|
||
MEP_RAW_RPB_REGNUM = 20, /* Raw repeat begin address */
|
||
MEP_RAW_RPE_REGNUM = 21, /* Repeat end address */
|
||
MEP_RAW_RPC_REGNUM = 22, /* Repeat count */
|
||
MEP_RAW_HI_REGNUM = 23, /* Upper 32 bits of result of 64 bit mult/div */
|
||
MEP_RAW_LO_REGNUM = 24, /* Lower 32 bits of result of 64 bit mult/div */
|
||
MEP_RAW_CSR9_REGNUM = 25, /* csr3: reserved */
|
||
MEP_RAW_CSR10_REGNUM = 26, /* csr3: reserved */
|
||
MEP_RAW_CSR11_REGNUM = 27, /* csr3: reserved */
|
||
MEP_RAW_MB0_REGNUM = 28, /* Raw modulo begin address 0 */
|
||
MEP_RAW_ME0_REGNUM = 29, /* Raw modulo end address 0 */
|
||
MEP_RAW_MB1_REGNUM = 30, /* Raw modulo begin address 1 */
|
||
MEP_RAW_ME1_REGNUM = 31, /* Raw modulo end address 1 */
|
||
MEP_RAW_PSW_REGNUM = 32, /* Raw program status word */
|
||
MEP_RAW_ID_REGNUM = 33, /* Raw processor ID/revision */
|
||
MEP_RAW_TMP_REGNUM = 34, /* Temporary */
|
||
MEP_RAW_EPC_REGNUM = 35, /* Exception program counter */
|
||
MEP_RAW_EXC_REGNUM = 36, /* Raw exception cause */
|
||
MEP_RAW_CFG_REGNUM = 37, /* Raw processor configuration*/
|
||
MEP_RAW_CSR22_REGNUM = 38, /* csr3: reserved */
|
||
MEP_RAW_NPC_REGNUM = 39, /* Nonmaskable interrupt PC */
|
||
MEP_RAW_DBG_REGNUM = 40, /* Raw debug */
|
||
MEP_RAW_DEPC_REGNUM = 41, /* Debug exception PC */
|
||
MEP_RAW_OPT_REGNUM = 42, /* Raw options */
|
||
MEP_RAW_RCFG_REGNUM = 43, /* Raw local ram config */
|
||
MEP_RAW_CCFG_REGNUM = 44, /* Raw cache config */
|
||
MEP_RAW_CSR29_REGNUM = 45, /* csr3: reserved */
|
||
MEP_RAW_CSR30_REGNUM = 46, /* csr3: reserved */
|
||
MEP_RAW_CSR31_REGNUM = 47, /* csr3: reserved */
|
||
MEP_LAST_RAW_CSR_REGNUM = MEP_RAW_CSR31_REGNUM,
|
||
|
||
/* The raw coprocessor general-purpose registers. These are all 64
|
||
bits wide. */
|
||
MEP_FIRST_RAW_CR_REGNUM = 48,
|
||
MEP_LAST_RAW_CR_REGNUM = MEP_FIRST_RAW_CR_REGNUM + 31,
|
||
|
||
MEP_FIRST_RAW_CCR_REGNUM = 80,
|
||
MEP_LAST_RAW_CCR_REGNUM = MEP_FIRST_RAW_CCR_REGNUM + 63,
|
||
|
||
/* The module number register. This is the index of the me_module
|
||
of which the current target is an instance. (This is not a real
|
||
MeP-specified register; it's provided by SID.) */
|
||
MEP_MODULE_REGNUM,
|
||
|
||
MEP_LAST_RAW_REGNUM = MEP_MODULE_REGNUM,
|
||
|
||
MEP_NUM_RAW_REGS = MEP_LAST_RAW_REGNUM + 1,
|
||
|
||
/* Pseudoregisters. See mep_pseudo_register_read and
|
||
mep_pseudo_register_write. */
|
||
MEP_FIRST_PSEUDO_REGNUM = MEP_NUM_RAW_REGS,
|
||
|
||
/* We have a pseudoregister for every control/special register, to
|
||
implement registers with read-only bits. */
|
||
MEP_FIRST_CSR_REGNUM = MEP_FIRST_PSEUDO_REGNUM,
|
||
MEP_PC_REGNUM = MEP_FIRST_CSR_REGNUM, /* Program counter */
|
||
MEP_LP_REGNUM, /* Link pointer */
|
||
MEP_SAR_REGNUM, /* shift amount */
|
||
MEP_CSR3_REGNUM, /* csr3: reserved */
|
||
MEP_RPB_REGNUM, /* repeat begin address */
|
||
MEP_RPE_REGNUM, /* Repeat end address */
|
||
MEP_RPC_REGNUM, /* Repeat count */
|
||
MEP_HI_REGNUM, /* Upper 32 bits of the result of 64 bit mult/div */
|
||
MEP_LO_REGNUM, /* Lower 32 bits of the result of 64 bit mult/div */
|
||
MEP_CSR9_REGNUM, /* csr3: reserved */
|
||
MEP_CSR10_REGNUM, /* csr3: reserved */
|
||
MEP_CSR11_REGNUM, /* csr3: reserved */
|
||
MEP_MB0_REGNUM, /* modulo begin address 0 */
|
||
MEP_ME0_REGNUM, /* modulo end address 0 */
|
||
MEP_MB1_REGNUM, /* modulo begin address 1 */
|
||
MEP_ME1_REGNUM, /* modulo end address 1 */
|
||
MEP_PSW_REGNUM, /* program status word */
|
||
MEP_ID_REGNUM, /* processor ID/revision */
|
||
MEP_TMP_REGNUM, /* Temporary */
|
||
MEP_EPC_REGNUM, /* Exception program counter */
|
||
MEP_EXC_REGNUM, /* exception cause */
|
||
MEP_CFG_REGNUM, /* processor configuration*/
|
||
MEP_CSR22_REGNUM, /* csr3: reserved */
|
||
MEP_NPC_REGNUM, /* Nonmaskable interrupt PC */
|
||
MEP_DBG_REGNUM, /* debug */
|
||
MEP_DEPC_REGNUM, /* Debug exception PC */
|
||
MEP_OPT_REGNUM, /* options */
|
||
MEP_RCFG_REGNUM, /* local ram config */
|
||
MEP_CCFG_REGNUM, /* cache config */
|
||
MEP_CSR29_REGNUM, /* csr3: reserved */
|
||
MEP_CSR30_REGNUM, /* csr3: reserved */
|
||
MEP_CSR31_REGNUM, /* csr3: reserved */
|
||
MEP_LAST_CSR_REGNUM = MEP_CSR31_REGNUM,
|
||
|
||
/* The 32-bit integer view of the coprocessor GPR's. */
|
||
MEP_FIRST_CR32_REGNUM,
|
||
MEP_LAST_CR32_REGNUM = MEP_FIRST_CR32_REGNUM + 31,
|
||
|
||
/* The 32-bit floating-point view of the coprocessor GPR's. */
|
||
MEP_FIRST_FP_CR32_REGNUM,
|
||
MEP_LAST_FP_CR32_REGNUM = MEP_FIRST_FP_CR32_REGNUM + 31,
|
||
|
||
/* The 64-bit integer view of the coprocessor GPR's. */
|
||
MEP_FIRST_CR64_REGNUM,
|
||
MEP_LAST_CR64_REGNUM = MEP_FIRST_CR64_REGNUM + 31,
|
||
|
||
/* The 64-bit floating-point view of the coprocessor GPR's. */
|
||
MEP_FIRST_FP_CR64_REGNUM,
|
||
MEP_LAST_FP_CR64_REGNUM = MEP_FIRST_FP_CR64_REGNUM + 31,
|
||
|
||
MEP_FIRST_CCR_REGNUM,
|
||
MEP_LAST_CCR_REGNUM = MEP_FIRST_CCR_REGNUM + 63,
|
||
|
||
MEP_LAST_PSEUDO_REGNUM = MEP_LAST_CCR_REGNUM,
|
||
|
||
MEP_NUM_PSEUDO_REGS = (MEP_LAST_PSEUDO_REGNUM - MEP_LAST_RAW_REGNUM),
|
||
|
||
MEP_NUM_REGS = MEP_NUM_RAW_REGS + MEP_NUM_PSEUDO_REGS
|
||
};
|
||
|
||
|
||
#define IN_SET(set, n) \
|
||
(MEP_FIRST_ ## set ## _REGNUM <= (n) && (n) <= MEP_LAST_ ## set ## _REGNUM)
|
||
|
||
#define IS_GPR_REGNUM(n) (IN_SET (GPR, (n)))
|
||
#define IS_RAW_CSR_REGNUM(n) (IN_SET (RAW_CSR, (n)))
|
||
#define IS_RAW_CR_REGNUM(n) (IN_SET (RAW_CR, (n)))
|
||
#define IS_RAW_CCR_REGNUM(n) (IN_SET (RAW_CCR, (n)))
|
||
|
||
#define IS_CSR_REGNUM(n) (IN_SET (CSR, (n)))
|
||
#define IS_CR32_REGNUM(n) (IN_SET (CR32, (n)))
|
||
#define IS_FP_CR32_REGNUM(n) (IN_SET (FP_CR32, (n)))
|
||
#define IS_CR64_REGNUM(n) (IN_SET (CR64, (n)))
|
||
#define IS_FP_CR64_REGNUM(n) (IN_SET (FP_CR64, (n)))
|
||
#define IS_CR_REGNUM(n) (IS_CR32_REGNUM (n) || IS_FP_CR32_REGNUM (n) \
|
||
|| IS_CR64_REGNUM (n) || IS_FP_CR64_REGNUM (n))
|
||
#define IS_CCR_REGNUM(n) (IN_SET (CCR, (n)))
|
||
|
||
#define IS_RAW_REGNUM(n) (IN_SET (RAW, (n)))
|
||
#define IS_PSEUDO_REGNUM(n) (IN_SET (PSEUDO, (n)))
|
||
|
||
#define NUM_REGS_IN_SET(set) \
|
||
(MEP_LAST_ ## set ## _REGNUM - MEP_FIRST_ ## set ## _REGNUM + 1)
|
||
|
||
#define MEP_GPR_SIZE (4) /* Size of a MeP general-purpose register. */
|
||
#define MEP_PSW_SIZE (4) /* Size of the PSW register. */
|
||
#define MEP_LP_SIZE (4) /* Size of the LP register. */
|
||
|
||
|
||
/* Many of the control/special registers contain bits that cannot be
|
||
written to; some are entirely read-only. So we present them all as
|
||
pseudoregisters.
|
||
|
||
The following table describes the special properties of each CSR. */
|
||
struct mep_csr_register
|
||
{
|
||
/* The number of this CSR's raw register. */
|
||
int raw;
|
||
|
||
/* The number of this CSR's pseudoregister. */
|
||
int pseudo;
|
||
|
||
/* A mask of the bits that are writeable: if a bit is set here, then
|
||
it can be modified; if the bit is clear, then it cannot. */
|
||
LONGEST writeable_bits;
|
||
};
|
||
|
||
|
||
/* mep_csr_registers[i] describes the i'th CSR.
|
||
We just list the register numbers here explicitly to help catch
|
||
typos. */
|
||
#define CSR(name) MEP_RAW_ ## name ## _REGNUM, MEP_ ## name ## _REGNUM
|
||
struct mep_csr_register mep_csr_registers[] = {
|
||
{ CSR(PC), 0xffffffff }, /* manual says r/o, but we can write it */
|
||
{ CSR(LP), 0xffffffff },
|
||
{ CSR(SAR), 0x0000003f },
|
||
{ CSR(CSR3), 0xffffffff },
|
||
{ CSR(RPB), 0xfffffffe },
|
||
{ CSR(RPE), 0xffffffff },
|
||
{ CSR(RPC), 0xffffffff },
|
||
{ CSR(HI), 0xffffffff },
|
||
{ CSR(LO), 0xffffffff },
|
||
{ CSR(CSR9), 0xffffffff },
|
||
{ CSR(CSR10), 0xffffffff },
|
||
{ CSR(CSR11), 0xffffffff },
|
||
{ CSR(MB0), 0x0000ffff },
|
||
{ CSR(ME0), 0x0000ffff },
|
||
{ CSR(MB1), 0x0000ffff },
|
||
{ CSR(ME1), 0x0000ffff },
|
||
{ CSR(PSW), 0x000003ff },
|
||
{ CSR(ID), 0x00000000 },
|
||
{ CSR(TMP), 0xffffffff },
|
||
{ CSR(EPC), 0xffffffff },
|
||
{ CSR(EXC), 0x000030f0 },
|
||
{ CSR(CFG), 0x00c0001b },
|
||
{ CSR(CSR22), 0xffffffff },
|
||
{ CSR(NPC), 0xffffffff },
|
||
{ CSR(DBG), 0x00000580 },
|
||
{ CSR(DEPC), 0xffffffff },
|
||
{ CSR(OPT), 0x00000000 },
|
||
{ CSR(RCFG), 0x00000000 },
|
||
{ CSR(CCFG), 0x00000000 },
|
||
{ CSR(CSR29), 0xffffffff },
|
||
{ CSR(CSR30), 0xffffffff },
|
||
{ CSR(CSR31), 0xffffffff },
|
||
};
|
||
|
||
|
||
/* If R is the number of a raw register, then mep_raw_to_pseudo[R] is
|
||
the number of the corresponding pseudoregister. Otherwise,
|
||
mep_raw_to_pseudo[R] == R. */
|
||
static int mep_raw_to_pseudo[MEP_NUM_REGS];
|
||
|
||
/* If R is the number of a pseudoregister, then mep_pseudo_to_raw[R]
|
||
is the number of the underlying raw register. Otherwise
|
||
mep_pseudo_to_raw[R] == R. */
|
||
static int mep_pseudo_to_raw[MEP_NUM_REGS];
|
||
|
||
static void
|
||
mep_init_pseudoregister_maps (void)
|
||
{
|
||
int i;
|
||
|
||
/* Verify that mep_csr_registers covers all the CSRs, in order. */
|
||
gdb_assert (ARRAY_SIZE (mep_csr_registers) == NUM_REGS_IN_SET (CSR));
|
||
gdb_assert (ARRAY_SIZE (mep_csr_registers) == NUM_REGS_IN_SET (RAW_CSR));
|
||
|
||
/* Verify that the raw and pseudo ranges have matching sizes. */
|
||
gdb_assert (NUM_REGS_IN_SET (RAW_CSR) == NUM_REGS_IN_SET (CSR));
|
||
gdb_assert (NUM_REGS_IN_SET (RAW_CR) == NUM_REGS_IN_SET (CR32));
|
||
gdb_assert (NUM_REGS_IN_SET (RAW_CR) == NUM_REGS_IN_SET (CR64));
|
||
gdb_assert (NUM_REGS_IN_SET (RAW_CCR) == NUM_REGS_IN_SET (CCR));
|
||
|
||
for (i = 0; i < ARRAY_SIZE (mep_csr_registers); i++)
|
||
{
|
||
struct mep_csr_register *r = &mep_csr_registers[i];
|
||
|
||
gdb_assert (r->pseudo == MEP_FIRST_CSR_REGNUM + i);
|
||
gdb_assert (r->raw == MEP_FIRST_RAW_CSR_REGNUM + i);
|
||
}
|
||
|
||
/* Set up the initial raw<->pseudo mappings. */
|
||
for (i = 0; i < MEP_NUM_REGS; i++)
|
||
{
|
||
mep_raw_to_pseudo[i] = i;
|
||
mep_pseudo_to_raw[i] = i;
|
||
}
|
||
|
||
/* Add the CSR raw<->pseudo mappings. */
|
||
for (i = 0; i < ARRAY_SIZE (mep_csr_registers); i++)
|
||
{
|
||
struct mep_csr_register *r = &mep_csr_registers[i];
|
||
|
||
mep_raw_to_pseudo[r->raw] = r->pseudo;
|
||
mep_pseudo_to_raw[r->pseudo] = r->raw;
|
||
}
|
||
|
||
/* Add the CR raw<->pseudo mappings. */
|
||
for (i = 0; i < NUM_REGS_IN_SET (RAW_CR); i++)
|
||
{
|
||
int raw = MEP_FIRST_RAW_CR_REGNUM + i;
|
||
int pseudo32 = MEP_FIRST_CR32_REGNUM + i;
|
||
int pseudofp32 = MEP_FIRST_FP_CR32_REGNUM + i;
|
||
int pseudo64 = MEP_FIRST_CR64_REGNUM + i;
|
||
int pseudofp64 = MEP_FIRST_FP_CR64_REGNUM + i;
|
||
|
||
/* Truly, the raw->pseudo mapping depends on the current module.
|
||
But we use the raw->pseudo mapping when we read the debugging
|
||
info; at that point, we don't know what module we'll actually
|
||
be running yet. So, we always supply the 64-bit register
|
||
numbers; GDB knows how to pick a smaller value out of a
|
||
larger register properly. */
|
||
mep_raw_to_pseudo[raw] = pseudo64;
|
||
mep_pseudo_to_raw[pseudo32] = raw;
|
||
mep_pseudo_to_raw[pseudofp32] = raw;
|
||
mep_pseudo_to_raw[pseudo64] = raw;
|
||
mep_pseudo_to_raw[pseudofp64] = raw;
|
||
}
|
||
|
||
/* Add the CCR raw<->pseudo mappings. */
|
||
for (i = 0; i < NUM_REGS_IN_SET (CCR); i++)
|
||
{
|
||
int raw = MEP_FIRST_RAW_CCR_REGNUM + i;
|
||
int pseudo = MEP_FIRST_CCR_REGNUM + i;
|
||
mep_raw_to_pseudo[raw] = pseudo;
|
||
mep_pseudo_to_raw[pseudo] = raw;
|
||
}
|
||
}
|
||
|
||
|
||
static int
|
||
mep_debug_reg_to_regnum (struct gdbarch *gdbarch, int debug_reg)
|
||
{
|
||
/* The debug info uses the raw register numbers. */
|
||
if (debug_reg >= 0 && debug_reg < ARRAY_SIZE (mep_raw_to_pseudo))
|
||
return mep_raw_to_pseudo[debug_reg];
|
||
return -1;
|
||
}
|
||
|
||
|
||
/* Return the size, in bits, of the coprocessor pseudoregister
|
||
numbered PSEUDO. */
|
||
static int
|
||
mep_pseudo_cr_size (int pseudo)
|
||
{
|
||
if (IS_CR32_REGNUM (pseudo)
|
||
|| IS_FP_CR32_REGNUM (pseudo))
|
||
return 32;
|
||
else if (IS_CR64_REGNUM (pseudo)
|
||
|| IS_FP_CR64_REGNUM (pseudo))
|
||
return 64;
|
||
else
|
||
gdb_assert_not_reached ("unexpected coprocessor pseudo register");
|
||
}
|
||
|
||
|
||
/* If the coprocessor pseudoregister numbered PSEUDO is a
|
||
floating-point register, return non-zero; if it is an integer
|
||
register, return zero. */
|
||
static int
|
||
mep_pseudo_cr_is_float (int pseudo)
|
||
{
|
||
return (IS_FP_CR32_REGNUM (pseudo)
|
||
|| IS_FP_CR64_REGNUM (pseudo));
|
||
}
|
||
|
||
|
||
/* Given a coprocessor GPR pseudoregister number, return its index
|
||
within that register bank. */
|
||
static int
|
||
mep_pseudo_cr_index (int pseudo)
|
||
{
|
||
if (IS_CR32_REGNUM (pseudo))
|
||
return pseudo - MEP_FIRST_CR32_REGNUM;
|
||
else if (IS_FP_CR32_REGNUM (pseudo))
|
||
return pseudo - MEP_FIRST_FP_CR32_REGNUM;
|
||
else if (IS_CR64_REGNUM (pseudo))
|
||
return pseudo - MEP_FIRST_CR64_REGNUM;
|
||
else if (IS_FP_CR64_REGNUM (pseudo))
|
||
return pseudo - MEP_FIRST_FP_CR64_REGNUM;
|
||
else
|
||
gdb_assert_not_reached ("unexpected coprocessor pseudo register");
|
||
}
|
||
|
||
|
||
/* Return the me_module index describing the current target.
|
||
|
||
If the current target has registers (e.g., simulator, remote
|
||
target), then this uses the value of the 'module' register, raw
|
||
register MEP_MODULE_REGNUM. Otherwise, this retrieves the value
|
||
from the ELF header's e_flags field of the current executable
|
||
file. */
|
||
static CONFIG_ATTR
|
||
current_me_module (void)
|
||
{
|
||
if (target_has_registers)
|
||
{
|
||
ULONGEST regval;
|
||
regcache_cooked_read_unsigned (get_current_regcache (),
|
||
MEP_MODULE_REGNUM, ®val);
|
||
return (CONFIG_ATTR) regval;
|
||
}
|
||
else
|
||
return gdbarch_tdep (target_gdbarch ())->me_module;
|
||
}
|
||
|
||
|
||
/* Return the set of options for the current target, in the form that
|
||
the OPT register would use.
|
||
|
||
If the current target has registers (e.g., simulator, remote
|
||
target), then this is the actual value of the OPT register. If the
|
||
current target does not have registers (e.g., an executable file),
|
||
then use the 'module_opt' field we computed when we build the
|
||
gdbarch object for this module. */
|
||
static unsigned int
|
||
current_options (void)
|
||
{
|
||
if (target_has_registers)
|
||
{
|
||
ULONGEST regval;
|
||
regcache_cooked_read_unsigned (get_current_regcache (),
|
||
MEP_OPT_REGNUM, ®val);
|
||
return regval;
|
||
}
|
||
else
|
||
return me_module_opt (current_me_module ());
|
||
}
|
||
|
||
|
||
/* Return the width of the current me_module's coprocessor data bus,
|
||
in bits. This is either 32 or 64. */
|
||
static int
|
||
current_cop_data_bus_width (void)
|
||
{
|
||
return me_module_cop_data_bus_width (current_me_module ());
|
||
}
|
||
|
||
|
||
/* Return the keyword table of coprocessor general-purpose register
|
||
names appropriate for the me_module we're dealing with. */
|
||
static CGEN_KEYWORD *
|
||
current_cr_names (void)
|
||
{
|
||
const CGEN_HW_ENTRY *hw
|
||
= me_module_register_set (current_me_module (), "h-cr-", HW_H_CR);
|
||
|
||
return register_set_keyword_table (hw);
|
||
}
|
||
|
||
|
||
/* Return non-zero if the coprocessor general-purpose registers are
|
||
floating-point values, zero otherwise. */
|
||
static int
|
||
current_cr_is_float (void)
|
||
{
|
||
const CGEN_HW_ENTRY *hw
|
||
= me_module_register_set (current_me_module (), "h-cr-", HW_H_CR);
|
||
|
||
return CGEN_ATTR_CGEN_HW_IS_FLOAT_VALUE (CGEN_HW_ATTRS (hw));
|
||
}
|
||
|
||
|
||
/* Return the keyword table of coprocessor control register names
|
||
appropriate for the me_module we're dealing with. */
|
||
static CGEN_KEYWORD *
|
||
current_ccr_names (void)
|
||
{
|
||
const CGEN_HW_ENTRY *hw
|
||
= me_module_register_set (current_me_module (), "h-ccr-", HW_H_CCR);
|
||
|
||
return register_set_keyword_table (hw);
|
||
}
|
||
|
||
|
||
static const char *
|
||
mep_register_name (struct gdbarch *gdbarch, int regnr)
|
||
{
|
||
/* General-purpose registers. */
|
||
static const char *gpr_names[] = {
|
||
"r0", "r1", "r2", "r3", /* 0 */
|
||
"r4", "r5", "r6", "r7", /* 4 */
|
||
"fp", "r9", "r10", "r11", /* 8 */
|
||
"r12", "tp", "gp", "sp" /* 12 */
|
||
};
|
||
|
||
/* Special-purpose registers. */
|
||
static const char *csr_names[] = {
|
||
"pc", "lp", "sar", "", /* 0 csr3: reserved */
|
||
"rpb", "rpe", "rpc", "hi", /* 4 */
|
||
"lo", "", "", "", /* 8 csr9-csr11: reserved */
|
||
"mb0", "me0", "mb1", "me1", /* 12 */
|
||
|
||
"psw", "id", "tmp", "epc", /* 16 */
|
||
"exc", "cfg", "", "npc", /* 20 csr22: reserved */
|
||
"dbg", "depc", "opt", "rcfg", /* 24 */
|
||
"ccfg", "", "", "" /* 28 csr29-csr31: reserved */
|
||
};
|
||
|
||
if (IS_GPR_REGNUM (regnr))
|
||
return gpr_names[regnr - MEP_R0_REGNUM];
|
||
else if (IS_CSR_REGNUM (regnr))
|
||
{
|
||
/* The 'hi' and 'lo' registers are only present on processors
|
||
that have the 'MUL' or 'DIV' instructions enabled. */
|
||
if ((regnr == MEP_HI_REGNUM || regnr == MEP_LO_REGNUM)
|
||
&& (! (current_options () & (MEP_OPT_MUL | MEP_OPT_DIV))))
|
||
return "";
|
||
|
||
return csr_names[regnr - MEP_FIRST_CSR_REGNUM];
|
||
}
|
||
else if (IS_CR_REGNUM (regnr))
|
||
{
|
||
CGEN_KEYWORD *names;
|
||
int cr_size;
|
||
int cr_is_float;
|
||
|
||
/* Does this module have a coprocessor at all? */
|
||
if (! (current_options () & MEP_OPT_COP))
|
||
return "";
|
||
|
||
names = current_cr_names ();
|
||
if (! names)
|
||
/* This module's coprocessor has no general-purpose registers. */
|
||
return "";
|
||
|
||
cr_size = current_cop_data_bus_width ();
|
||
if (cr_size != mep_pseudo_cr_size (regnr))
|
||
/* This module's coprocessor's GPR's are of a different size. */
|
||
return "";
|
||
|
||
cr_is_float = current_cr_is_float ();
|
||
/* The extra ! operators ensure we get boolean equality, not
|
||
numeric equality. */
|
||
if (! cr_is_float != ! mep_pseudo_cr_is_float (regnr))
|
||
/* This module's coprocessor's GPR's are of a different type. */
|
||
return "";
|
||
|
||
return register_name_from_keyword (names, mep_pseudo_cr_index (regnr));
|
||
}
|
||
else if (IS_CCR_REGNUM (regnr))
|
||
{
|
||
/* Does this module have a coprocessor at all? */
|
||
if (! (current_options () & MEP_OPT_COP))
|
||
return "";
|
||
|
||
{
|
||
CGEN_KEYWORD *names = current_ccr_names ();
|
||
|
||
if (! names)
|
||
/* This me_module's coprocessor has no control registers. */
|
||
return "";
|
||
|
||
return register_name_from_keyword (names, regnr-MEP_FIRST_CCR_REGNUM);
|
||
}
|
||
}
|
||
|
||
/* It might be nice to give the 'module' register a name, but that
|
||
would affect the output of 'info all-registers', which would
|
||
disturb the test suites. So we leave it invisible. */
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
|
||
/* Custom register groups for the MeP. */
|
||
static struct reggroup *mep_csr_reggroup; /* control/special */
|
||
static struct reggroup *mep_cr_reggroup; /* coprocessor general-purpose */
|
||
static struct reggroup *mep_ccr_reggroup; /* coprocessor control */
|
||
|
||
|
||
static int
|
||
mep_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
|
||
struct reggroup *group)
|
||
{
|
||
/* Filter reserved or unused register numbers. */
|
||
{
|
||
const char *name = mep_register_name (gdbarch, regnum);
|
||
|
||
if (! name || name[0] == '\0')
|
||
return 0;
|
||
}
|
||
|
||
/* We could separate the GPRs and the CSRs. Toshiba has approved of
|
||
the existing behavior, so we'd want to run that by them. */
|
||
if (group == general_reggroup)
|
||
return (IS_GPR_REGNUM (regnum)
|
||
|| IS_CSR_REGNUM (regnum));
|
||
|
||
/* Everything is in the 'all' reggroup, except for the raw CSR's. */
|
||
else if (group == all_reggroup)
|
||
return (IS_GPR_REGNUM (regnum)
|
||
|| IS_CSR_REGNUM (regnum)
|
||
|| IS_CR_REGNUM (regnum)
|
||
|| IS_CCR_REGNUM (regnum));
|
||
|
||
/* All registers should be saved and restored, except for the raw
|
||
CSR's.
|
||
|
||
This is probably right if the coprocessor is something like a
|
||
floating-point unit, but would be wrong if the coprocessor is
|
||
something that does I/O, where register accesses actually cause
|
||
externally-visible actions. But I get the impression that the
|
||
coprocessor isn't supposed to do things like that --- you'd use a
|
||
hardware engine, perhaps. */
|
||
else if (group == save_reggroup || group == restore_reggroup)
|
||
return (IS_GPR_REGNUM (regnum)
|
||
|| IS_CSR_REGNUM (regnum)
|
||
|| IS_CR_REGNUM (regnum)
|
||
|| IS_CCR_REGNUM (regnum));
|
||
|
||
else if (group == mep_csr_reggroup)
|
||
return IS_CSR_REGNUM (regnum);
|
||
else if (group == mep_cr_reggroup)
|
||
return IS_CR_REGNUM (regnum);
|
||
else if (group == mep_ccr_reggroup)
|
||
return IS_CCR_REGNUM (regnum);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
|
||
static struct type *
|
||
mep_register_type (struct gdbarch *gdbarch, int reg_nr)
|
||
{
|
||
/* Coprocessor general-purpose registers may be either 32 or 64 bits
|
||
long. So for them, the raw registers are always 64 bits long (to
|
||
keep the 'g' packet format fixed), and the pseudoregisters vary
|
||
in length. */
|
||
if (IS_RAW_CR_REGNUM (reg_nr))
|
||
return builtin_type (gdbarch)->builtin_uint64;
|
||
|
||
/* Since GDB doesn't allow registers to change type, we have two
|
||
banks of pseudoregisters for the coprocessor general-purpose
|
||
registers: one that gives a 32-bit view, and one that gives a
|
||
64-bit view. We hide or show one or the other depending on the
|
||
current module. */
|
||
if (IS_CR_REGNUM (reg_nr))
|
||
{
|
||
int size = mep_pseudo_cr_size (reg_nr);
|
||
if (size == 32)
|
||
{
|
||
if (mep_pseudo_cr_is_float (reg_nr))
|
||
return builtin_type (gdbarch)->builtin_float;
|
||
else
|
||
return builtin_type (gdbarch)->builtin_uint32;
|
||
}
|
||
else if (size == 64)
|
||
{
|
||
if (mep_pseudo_cr_is_float (reg_nr))
|
||
return builtin_type (gdbarch)->builtin_double;
|
||
else
|
||
return builtin_type (gdbarch)->builtin_uint64;
|
||
}
|
||
else
|
||
gdb_assert_not_reached ("unexpected cr size");
|
||
}
|
||
|
||
/* All other registers are 32 bits long. */
|
||
else
|
||
return builtin_type (gdbarch)->builtin_uint32;
|
||
}
|
||
|
||
static enum register_status
|
||
mep_pseudo_cr32_read (struct gdbarch *gdbarch,
|
||
readable_regcache *regcache,
|
||
int cookednum,
|
||
gdb_byte *buf)
|
||
{
|
||
enum register_status status;
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
/* Read the raw register into a 64-bit buffer, and then return the
|
||
appropriate end of that buffer. */
|
||
int rawnum = mep_pseudo_to_raw[cookednum];
|
||
gdb_byte buf64[8];
|
||
|
||
gdb_assert (TYPE_LENGTH (register_type (gdbarch, rawnum)) == sizeof (buf64));
|
||
gdb_assert (TYPE_LENGTH (register_type (gdbarch, cookednum)) == 4);
|
||
status = regcache->raw_read (rawnum, buf64);
|
||
if (status == REG_VALID)
|
||
{
|
||
/* Slow, but legible. */
|
||
store_unsigned_integer (buf, 4, byte_order,
|
||
extract_unsigned_integer (buf64, 8, byte_order));
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
static enum register_status
|
||
mep_pseudo_cr64_read (struct gdbarch *gdbarch,
|
||
readable_regcache *regcache,
|
||
int cookednum,
|
||
gdb_byte *buf)
|
||
{
|
||
return regcache->raw_read (mep_pseudo_to_raw[cookednum], buf);
|
||
}
|
||
|
||
|
||
static enum register_status
|
||
mep_pseudo_register_read (struct gdbarch *gdbarch,
|
||
readable_regcache *regcache,
|
||
int cookednum,
|
||
gdb_byte *buf)
|
||
{
|
||
if (IS_CSR_REGNUM (cookednum)
|
||
|| IS_CCR_REGNUM (cookednum))
|
||
return regcache->raw_read (mep_pseudo_to_raw[cookednum], buf);
|
||
else if (IS_CR32_REGNUM (cookednum)
|
||
|| IS_FP_CR32_REGNUM (cookednum))
|
||
return mep_pseudo_cr32_read (gdbarch, regcache, cookednum, buf);
|
||
else if (IS_CR64_REGNUM (cookednum)
|
||
|| IS_FP_CR64_REGNUM (cookednum))
|
||
return mep_pseudo_cr64_read (gdbarch, regcache, cookednum, buf);
|
||
else
|
||
gdb_assert_not_reached ("unexpected pseudo register");
|
||
}
|
||
|
||
|
||
static void
|
||
mep_pseudo_csr_write (struct gdbarch *gdbarch,
|
||
struct regcache *regcache,
|
||
int cookednum,
|
||
const gdb_byte *buf)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
int size = register_size (gdbarch, cookednum);
|
||
struct mep_csr_register *r
|
||
= &mep_csr_registers[cookednum - MEP_FIRST_CSR_REGNUM];
|
||
|
||
if (r->writeable_bits == 0)
|
||
/* A completely read-only register; avoid the read-modify-
|
||
write cycle, and juts ignore the entire write. */
|
||
;
|
||
else
|
||
{
|
||
/* A partially writeable register; do a read-modify-write cycle. */
|
||
ULONGEST old_bits;
|
||
ULONGEST new_bits;
|
||
ULONGEST mixed_bits;
|
||
|
||
regcache_raw_read_unsigned (regcache, r->raw, &old_bits);
|
||
new_bits = extract_unsigned_integer (buf, size, byte_order);
|
||
mixed_bits = ((r->writeable_bits & new_bits)
|
||
| (~r->writeable_bits & old_bits));
|
||
regcache_raw_write_unsigned (regcache, r->raw, mixed_bits);
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
mep_pseudo_cr32_write (struct gdbarch *gdbarch,
|
||
struct regcache *regcache,
|
||
int cookednum,
|
||
const gdb_byte *buf)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
/* Expand the 32-bit value into a 64-bit value, and write that to
|
||
the pseudoregister. */
|
||
int rawnum = mep_pseudo_to_raw[cookednum];
|
||
gdb_byte buf64[8];
|
||
|
||
gdb_assert (TYPE_LENGTH (register_type (gdbarch, rawnum)) == sizeof (buf64));
|
||
gdb_assert (TYPE_LENGTH (register_type (gdbarch, cookednum)) == 4);
|
||
/* Slow, but legible. */
|
||
store_unsigned_integer (buf64, 8, byte_order,
|
||
extract_unsigned_integer (buf, 4, byte_order));
|
||
regcache->raw_write (rawnum, buf64);
|
||
}
|
||
|
||
|
||
static void
|
||
mep_pseudo_cr64_write (struct gdbarch *gdbarch,
|
||
struct regcache *regcache,
|
||
int cookednum,
|
||
const gdb_byte *buf)
|
||
{
|
||
regcache->raw_write (mep_pseudo_to_raw[cookednum], buf);
|
||
}
|
||
|
||
|
||
static void
|
||
mep_pseudo_register_write (struct gdbarch *gdbarch,
|
||
struct regcache *regcache,
|
||
int cookednum,
|
||
const gdb_byte *buf)
|
||
{
|
||
if (IS_CSR_REGNUM (cookednum))
|
||
mep_pseudo_csr_write (gdbarch, regcache, cookednum, buf);
|
||
else if (IS_CR32_REGNUM (cookednum)
|
||
|| IS_FP_CR32_REGNUM (cookednum))
|
||
mep_pseudo_cr32_write (gdbarch, regcache, cookednum, buf);
|
||
else if (IS_CR64_REGNUM (cookednum)
|
||
|| IS_FP_CR64_REGNUM (cookednum))
|
||
mep_pseudo_cr64_write (gdbarch, regcache, cookednum, buf);
|
||
else if (IS_CCR_REGNUM (cookednum))
|
||
regcache->raw_write (mep_pseudo_to_raw[cookednum], buf);
|
||
else
|
||
gdb_assert_not_reached ("unexpected pseudo register");
|
||
}
|
||
|
||
|
||
|
||
/* Disassembly. */
|
||
|
||
static int
|
||
mep_gdb_print_insn (bfd_vma pc, disassemble_info * info)
|
||
{
|
||
struct obj_section * s = find_pc_section (pc);
|
||
|
||
info->arch = bfd_arch_mep;
|
||
if (s)
|
||
{
|
||
/* The libopcodes disassembly code uses the section to find the
|
||
BFD, the BFD to find the ELF header, the ELF header to find
|
||
the me_module index, and the me_module index to select the
|
||
right instructions to print. */
|
||
info->section = s->the_bfd_section;
|
||
}
|
||
|
||
return print_insn_mep (pc, info);
|
||
}
|
||
|
||
|
||
/* Prologue analysis. */
|
||
|
||
|
||
/* The MeP has two classes of instructions: "core" instructions, which
|
||
are pretty normal RISC chip stuff, and "coprocessor" instructions,
|
||
which are mostly concerned with moving data in and out of
|
||
coprocessor registers, and branching on coprocessor condition
|
||
codes. There's space in the instruction set for custom coprocessor
|
||
instructions, too.
|
||
|
||
Instructions can be 16 or 32 bits long; the top two bits of the
|
||
first byte indicate the length. The coprocessor instructions are
|
||
mixed in with the core instructions, and there's no easy way to
|
||
distinguish them; you have to completely decode them to tell one
|
||
from the other.
|
||
|
||
The MeP also supports a "VLIW" operation mode, where instructions
|
||
always occur in fixed-width bundles. The bundles are either 32
|
||
bits or 64 bits long, depending on a fixed configuration flag. You
|
||
decode the first part of the bundle as normal; if it's a core
|
||
instruction, and there's any space left in the bundle, the
|
||
remainder of the bundle is a coprocessor instruction, which will
|
||
execute in parallel with the core instruction. If the first part
|
||
of the bundle is a coprocessor instruction, it occupies the entire
|
||
bundle.
|
||
|
||
So, here are all the cases:
|
||
|
||
- 32-bit VLIW mode:
|
||
Every bundle is four bytes long, and naturally aligned, and can hold
|
||
one or two instructions:
|
||
- 16-bit core instruction; 16-bit coprocessor instruction
|
||
These execute in parallel.
|
||
- 32-bit core instruction
|
||
- 32-bit coprocessor instruction
|
||
|
||
- 64-bit VLIW mode:
|
||
Every bundle is eight bytes long, and naturally aligned, and can hold
|
||
one or two instructions:
|
||
- 16-bit core instruction; 48-bit (!) coprocessor instruction
|
||
These execute in parallel.
|
||
- 32-bit core instruction; 32-bit coprocessor instruction
|
||
These execute in parallel.
|
||
- 64-bit coprocessor instruction
|
||
|
||
Now, the MeP manual doesn't define any 48- or 64-bit coprocessor
|
||
instruction, so I don't really know what's up there; perhaps these
|
||
are always the user-defined coprocessor instructions. */
|
||
|
||
|
||
/* Return non-zero if PC is in a VLIW code section, zero
|
||
otherwise. */
|
||
static int
|
||
mep_pc_in_vliw_section (CORE_ADDR pc)
|
||
{
|
||
struct obj_section *s = find_pc_section (pc);
|
||
if (s)
|
||
return (s->the_bfd_section->flags & SEC_MEP_VLIW);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Set *INSN to the next core instruction at PC, and return the
|
||
address of the next instruction.
|
||
|
||
The MeP instruction encoding is endian-dependent. 16- and 32-bit
|
||
instructions are encoded as one or two two-byte parts, and each
|
||
part is byte-swapped independently. Thus:
|
||
|
||
void
|
||
foo (void)
|
||
{
|
||
asm ("movu $1, 0x123456");
|
||
asm ("sb $1,0x5678($2)");
|
||
asm ("clip $1, 19");
|
||
}
|
||
|
||
compiles to this big-endian code:
|
||
|
||
0: d1 56 12 34 movu $1,0x123456
|
||
4: c1 28 56 78 sb $1,22136($2)
|
||
8: f1 01 10 98 clip $1,0x13
|
||
c: 70 02 ret
|
||
|
||
and this little-endian code:
|
||
|
||
0: 56 d1 34 12 movu $1,0x123456
|
||
4: 28 c1 78 56 sb $1,22136($2)
|
||
8: 01 f1 98 10 clip $1,0x13
|
||
c: 02 70 ret
|
||
|
||
Instructions are returned in *INSN in an endian-independent form: a
|
||
given instruction always appears in *INSN the same way, regardless
|
||
of whether the instruction stream is big-endian or little-endian.
|
||
|
||
*INSN's most significant 16 bits are the first (i.e., at lower
|
||
addresses) 16 bit part of the instruction. Its least significant
|
||
16 bits are the second (i.e., higher-addressed) 16 bit part of the
|
||
instruction, or zero for a 16-bit instruction. Both 16-bit parts
|
||
are fetched using the current endianness.
|
||
|
||
So, the *INSN values for the instruction sequence above would be
|
||
the following, in either endianness:
|
||
|
||
0xd1561234 movu $1,0x123456
|
||
0xc1285678 sb $1,22136($2)
|
||
0xf1011098 clip $1,0x13
|
||
0x70020000 ret
|
||
|
||
(In a sense, it would be more natural to return 16-bit instructions
|
||
in the least significant 16 bits of *INSN, but that would be
|
||
ambiguous. In order to tell whether you're looking at a 16- or a
|
||
32-bit instruction, you have to consult the major opcode field ---
|
||
the most significant four bits of the instruction's first 16-bit
|
||
part. But if we put 16-bit instructions at the least significant
|
||
end of *INSN, then you don't know where to find the major opcode
|
||
field until you know if it's a 16- or a 32-bit instruction ---
|
||
which is where we started.)
|
||
|
||
If PC points to a core / coprocessor bundle in a VLIW section, set
|
||
*INSN to the core instruction, and return the address of the next
|
||
bundle. This has the effect of skipping the bundled coprocessor
|
||
instruction. That's okay, since coprocessor instructions aren't
|
||
significant to prologue analysis --- for the time being,
|
||
anyway. */
|
||
|
||
static CORE_ADDR
|
||
mep_get_insn (struct gdbarch *gdbarch, CORE_ADDR pc, unsigned long *insn)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
int pc_in_vliw_section;
|
||
int vliw_mode;
|
||
int insn_len;
|
||
gdb_byte buf[2];
|
||
|
||
*insn = 0;
|
||
|
||
/* Are we in a VLIW section? */
|
||
pc_in_vliw_section = mep_pc_in_vliw_section (pc);
|
||
if (pc_in_vliw_section)
|
||
{
|
||
/* Yes, find out which bundle size. */
|
||
vliw_mode = current_options () & (MEP_OPT_VL32 | MEP_OPT_VL64);
|
||
|
||
/* If PC is in a VLIW section, but the current core doesn't say
|
||
that it supports either VLIW mode, then we don't have enough
|
||
information to parse the instruction stream it contains.
|
||
Since the "undifferentiated" standard core doesn't have
|
||
either VLIW mode bit set, this could happen.
|
||
|
||
But it shouldn't be an error to (say) set a breakpoint in a
|
||
VLIW section, if you know you'll never reach it. (Perhaps
|
||
you have a script that sets a bunch of standard breakpoints.)
|
||
|
||
So we'll just return zero here, and hope for the best. */
|
||
if (! (vliw_mode & (MEP_OPT_VL32 | MEP_OPT_VL64)))
|
||
return 0;
|
||
|
||
/* If both VL32 and VL64 are set, that's bogus, too. */
|
||
if (vliw_mode == (MEP_OPT_VL32 | MEP_OPT_VL64))
|
||
return 0;
|
||
}
|
||
else
|
||
vliw_mode = 0;
|
||
|
||
read_memory (pc, buf, sizeof (buf));
|
||
*insn = extract_unsigned_integer (buf, 2, byte_order) << 16;
|
||
|
||
/* The major opcode --- the top four bits of the first 16-bit
|
||
part --- indicates whether this instruction is 16 or 32 bits
|
||
long. All 32-bit instructions have a major opcode whose top
|
||
two bits are 11; all the rest are 16-bit instructions. */
|
||
if ((*insn & 0xc0000000) == 0xc0000000)
|
||
{
|
||
/* Fetch the second 16-bit part of the instruction. */
|
||
read_memory (pc + 2, buf, sizeof (buf));
|
||
*insn = *insn | extract_unsigned_integer (buf, 2, byte_order);
|
||
}
|
||
|
||
/* If we're in VLIW code, then the VLIW width determines the address
|
||
of the next instruction. */
|
||
if (vliw_mode)
|
||
{
|
||
/* In 32-bit VLIW code, all bundles are 32 bits long. We ignore the
|
||
coprocessor half of a core / copro bundle. */
|
||
if (vliw_mode == MEP_OPT_VL32)
|
||
insn_len = 4;
|
||
|
||
/* In 64-bit VLIW code, all bundles are 64 bits long. We ignore the
|
||
coprocessor half of a core / copro bundle. */
|
||
else if (vliw_mode == MEP_OPT_VL64)
|
||
insn_len = 8;
|
||
|
||
/* We'd better be in either core, 32-bit VLIW, or 64-bit VLIW mode. */
|
||
else
|
||
gdb_assert_not_reached ("unexpected vliw mode");
|
||
}
|
||
|
||
/* Otherwise, the top two bits of the major opcode are (again) what
|
||
we need to check. */
|
||
else if ((*insn & 0xc0000000) == 0xc0000000)
|
||
insn_len = 4;
|
||
else
|
||
insn_len = 2;
|
||
|
||
return pc + insn_len;
|
||
}
|
||
|
||
|
||
/* Sign-extend the LEN-bit value N. */
|
||
#define SEXT(n, len) ((((int) (n)) ^ (1 << ((len) - 1))) - (1 << ((len) - 1)))
|
||
|
||
/* Return the LEN-bit field at POS from I. */
|
||
#define FIELD(i, pos, len) (((i) >> (pos)) & ((1 << (len)) - 1))
|
||
|
||
/* Like FIELD, but sign-extend the field's value. */
|
||
#define SFIELD(i, pos, len) (SEXT (FIELD ((i), (pos), (len)), (len)))
|
||
|
||
|
||
/* Macros for decoding instructions.
|
||
|
||
Remember that 16-bit instructions are placed in bits 16..31 of i,
|
||
not at the least significant end; this means that the major opcode
|
||
field is always in the same place, regardless of the width of the
|
||
instruction. As a reminder of this, we show the lower 16 bits of a
|
||
16-bit instruction as xxxx_xxxx_xxxx_xxxx. */
|
||
|
||
/* SB Rn,(Rm) 0000_nnnn_mmmm_1000 */
|
||
/* SH Rn,(Rm) 0000_nnnn_mmmm_1001 */
|
||
/* SW Rn,(Rm) 0000_nnnn_mmmm_1010 */
|
||
|
||
/* SW Rn,disp16(Rm) 1100_nnnn_mmmm_1010 dddd_dddd_dddd_dddd */
|
||
#define IS_SW(i) (((i) & 0xf00f0000) == 0xc00a0000)
|
||
/* SB Rn,disp16(Rm) 1100_nnnn_mmmm_1000 dddd_dddd_dddd_dddd */
|
||
#define IS_SB(i) (((i) & 0xf00f0000) == 0xc0080000)
|
||
/* SH Rn,disp16(Rm) 1100_nnnn_mmmm_1001 dddd_dddd_dddd_dddd */
|
||
#define IS_SH(i) (((i) & 0xf00f0000) == 0xc0090000)
|
||
#define SWBH_32_BASE(i) (FIELD (i, 20, 4))
|
||
#define SWBH_32_SOURCE(i) (FIELD (i, 24, 4))
|
||
#define SWBH_32_OFFSET(i) (SFIELD (i, 0, 16))
|
||
|
||
/* SW Rn,disp7.align4(SP) 0100_nnnn_0ddd_dd10 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_SW_IMMD(i) (((i) & 0xf0830000) == 0x40020000)
|
||
#define SW_IMMD_SOURCE(i) (FIELD (i, 24, 4))
|
||
#define SW_IMMD_OFFSET(i) (FIELD (i, 18, 5) << 2)
|
||
|
||
/* SW Rn,(Rm) 0000_nnnn_mmmm_1010 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_SW_REG(i) (((i) & 0xf00f0000) == 0x000a0000)
|
||
#define SW_REG_SOURCE(i) (FIELD (i, 24, 4))
|
||
#define SW_REG_BASE(i) (FIELD (i, 20, 4))
|
||
|
||
/* ADD3 Rl,Rn,Rm 1001_nnnn_mmmm_llll xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_ADD3_16_REG(i) (((i) & 0xf0000000) == 0x90000000)
|
||
#define ADD3_16_REG_SRC1(i) (FIELD (i, 20, 4)) /* n */
|
||
#define ADD3_16_REG_SRC2(i) (FIELD (i, 24, 4)) /* m */
|
||
|
||
/* ADD3 Rn,Rm,imm16 1100_nnnn_mmmm_0000 iiii_iiii_iiii_iiii */
|
||
#define IS_ADD3_32(i) (((i) & 0xf00f0000) == 0xc0000000)
|
||
#define ADD3_32_TARGET(i) (FIELD (i, 24, 4))
|
||
#define ADD3_32_SOURCE(i) (FIELD (i, 20, 4))
|
||
#define ADD3_32_OFFSET(i) (SFIELD (i, 0, 16))
|
||
|
||
/* ADD3 Rn,SP,imm7.align4 0100_nnnn_0iii_ii00 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_ADD3_16(i) (((i) & 0xf0830000) == 0x40000000)
|
||
#define ADD3_16_TARGET(i) (FIELD (i, 24, 4))
|
||
#define ADD3_16_OFFSET(i) (FIELD (i, 18, 5) << 2)
|
||
|
||
/* ADD Rn,imm6 0110_nnnn_iiii_ii00 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_ADD(i) (((i) & 0xf0030000) == 0x60000000)
|
||
#define ADD_TARGET(i) (FIELD (i, 24, 4))
|
||
#define ADD_OFFSET(i) (SFIELD (i, 18, 6))
|
||
|
||
/* LDC Rn,imm5 0111_nnnn_iiii_101I xxxx_xxxx_xxxx_xxxx
|
||
imm5 = I||i[7:4] */
|
||
#define IS_LDC(i) (((i) & 0xf00e0000) == 0x700a0000)
|
||
#define LDC_IMM(i) ((FIELD (i, 16, 1) << 4) | FIELD (i, 20, 4))
|
||
#define LDC_TARGET(i) (FIELD (i, 24, 4))
|
||
|
||
/* LW Rn,disp16(Rm) 1100_nnnn_mmmm_1110 dddd_dddd_dddd_dddd */
|
||
#define IS_LW(i) (((i) & 0xf00f0000) == 0xc00e0000)
|
||
#define LW_TARGET(i) (FIELD (i, 24, 4))
|
||
#define LW_BASE(i) (FIELD (i, 20, 4))
|
||
#define LW_OFFSET(i) (SFIELD (i, 0, 16))
|
||
|
||
/* MOV Rn,Rm 0000_nnnn_mmmm_0000 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_MOV(i) (((i) & 0xf00f0000) == 0x00000000)
|
||
#define MOV_TARGET(i) (FIELD (i, 24, 4))
|
||
#define MOV_SOURCE(i) (FIELD (i, 20, 4))
|
||
|
||
/* BRA disp12.align2 1011_dddd_dddd_ddd0 xxxx_xxxx_xxxx_xxxx */
|
||
#define IS_BRA(i) (((i) & 0xf0010000) == 0xb0000000)
|
||
#define BRA_DISP(i) (SFIELD (i, 17, 11) << 1)
|
||
|
||
|
||
/* This structure holds the results of a prologue analysis. */
|
||
struct mep_prologue
|
||
{
|
||
/* The architecture for which we generated this prologue info. */
|
||
struct gdbarch *gdbarch;
|
||
|
||
/* The offset from the frame base to the stack pointer --- always
|
||
zero or negative.
|
||
|
||
Calling this a "size" is a bit misleading, but given that the
|
||
stack grows downwards, using offsets for everything keeps one
|
||
from going completely sign-crazy: you never change anything's
|
||
sign for an ADD instruction; always change the second operand's
|
||
sign for a SUB instruction; and everything takes care of
|
||
itself. */
|
||
int frame_size;
|
||
|
||
/* Non-zero if this function has initialized the frame pointer from
|
||
the stack pointer, zero otherwise. */
|
||
int has_frame_ptr;
|
||
|
||
/* If has_frame_ptr is non-zero, this is the offset from the frame
|
||
base to where the frame pointer points. This is always zero or
|
||
negative. */
|
||
int frame_ptr_offset;
|
||
|
||
/* The address of the first instruction at which the frame has been
|
||
set up and the arguments are where the debug info says they are
|
||
--- as best as we can tell. */
|
||
CORE_ADDR prologue_end;
|
||
|
||
/* reg_offset[R] is the offset from the CFA at which register R is
|
||
saved, or 1 if register R has not been saved. (Real values are
|
||
always zero or negative.) */
|
||
int reg_offset[MEP_NUM_REGS];
|
||
};
|
||
|
||
/* Return non-zero if VALUE is an incoming argument register. */
|
||
|
||
static int
|
||
is_arg_reg (pv_t value)
|
||
{
|
||
return (value.kind == pvk_register
|
||
&& MEP_R1_REGNUM <= value.reg && value.reg <= MEP_R4_REGNUM
|
||
&& value.k == 0);
|
||
}
|
||
|
||
/* Return non-zero if a store of REG's current value VALUE to ADDR is
|
||
probably spilling an argument register to its stack slot in STACK.
|
||
Such instructions should be included in the prologue, if possible.
|
||
|
||
The store is a spill if:
|
||
- the value being stored is REG's original value;
|
||
- the value has not already been stored somewhere in STACK; and
|
||
- ADDR is a stack slot's address (e.g., relative to the original
|
||
value of the SP). */
|
||
static int
|
||
is_arg_spill (struct gdbarch *gdbarch, pv_t value, pv_t addr,
|
||
struct pv_area *stack)
|
||
{
|
||
return (is_arg_reg (value)
|
||
&& pv_is_register (addr, MEP_SP_REGNUM)
|
||
&& ! stack->find_reg (gdbarch, value.reg, 0));
|
||
}
|
||
|
||
|
||
/* Function for finding saved registers in a 'struct pv_area'; we pass
|
||
this to pv_area::scan.
|
||
|
||
If VALUE is a saved register, ADDR says it was saved at a constant
|
||
offset from the frame base, and SIZE indicates that the whole
|
||
register was saved, record its offset in RESULT_UNTYPED. */
|
||
static void
|
||
check_for_saved (void *result_untyped, pv_t addr, CORE_ADDR size, pv_t value)
|
||
{
|
||
struct mep_prologue *result = (struct mep_prologue *) result_untyped;
|
||
|
||
if (value.kind == pvk_register
|
||
&& value.k == 0
|
||
&& pv_is_register (addr, MEP_SP_REGNUM)
|
||
&& size == register_size (result->gdbarch, value.reg))
|
||
result->reg_offset[value.reg] = addr.k;
|
||
}
|
||
|
||
|
||
/* Analyze a prologue starting at START_PC, going no further than
|
||
LIMIT_PC. Fill in RESULT as appropriate. */
|
||
static void
|
||
mep_analyze_prologue (struct gdbarch *gdbarch,
|
||
CORE_ADDR start_pc, CORE_ADDR limit_pc,
|
||
struct mep_prologue *result)
|
||
{
|
||
CORE_ADDR pc;
|
||
unsigned long insn;
|
||
pv_t reg[MEP_NUM_REGS];
|
||
CORE_ADDR after_last_frame_setup_insn = start_pc;
|
||
|
||
memset (result, 0, sizeof (*result));
|
||
result->gdbarch = gdbarch;
|
||
|
||
for (int rn = 0; rn < MEP_NUM_REGS; rn++)
|
||
{
|
||
reg[rn] = pv_register (rn, 0);
|
||
result->reg_offset[rn] = 1;
|
||
}
|
||
|
||
pv_area stack (MEP_SP_REGNUM, gdbarch_addr_bit (gdbarch));
|
||
|
||
pc = start_pc;
|
||
while (pc < limit_pc)
|
||
{
|
||
CORE_ADDR next_pc;
|
||
pv_t pre_insn_fp, pre_insn_sp;
|
||
|
||
next_pc = mep_get_insn (gdbarch, pc, &insn);
|
||
|
||
/* A zero return from mep_get_insn means that either we weren't
|
||
able to read the instruction from memory, or that we don't
|
||
have enough information to be able to reliably decode it. So
|
||
we'll store here and hope for the best. */
|
||
if (! next_pc)
|
||
break;
|
||
|
||
/* Note the current values of the SP and FP, so we can tell if
|
||
this instruction changed them, below. */
|
||
pre_insn_fp = reg[MEP_FP_REGNUM];
|
||
pre_insn_sp = reg[MEP_SP_REGNUM];
|
||
|
||
if (IS_ADD (insn))
|
||
{
|
||
int rn = ADD_TARGET (insn);
|
||
CORE_ADDR imm6 = ADD_OFFSET (insn);
|
||
|
||
reg[rn] = pv_add_constant (reg[rn], imm6);
|
||
}
|
||
else if (IS_ADD3_16 (insn))
|
||
{
|
||
int rn = ADD3_16_TARGET (insn);
|
||
int imm7 = ADD3_16_OFFSET (insn);
|
||
|
||
reg[rn] = pv_add_constant (reg[MEP_SP_REGNUM], imm7);
|
||
}
|
||
else if (IS_ADD3_32 (insn))
|
||
{
|
||
int rn = ADD3_32_TARGET (insn);
|
||
int rm = ADD3_32_SOURCE (insn);
|
||
int imm16 = ADD3_32_OFFSET (insn);
|
||
|
||
reg[rn] = pv_add_constant (reg[rm], imm16);
|
||
}
|
||
else if (IS_SW_REG (insn))
|
||
{
|
||
int rn = SW_REG_SOURCE (insn);
|
||
int rm = SW_REG_BASE (insn);
|
||
|
||
/* If simulating this store would require us to forget
|
||
everything we know about the stack frame in the name of
|
||
accuracy, it would be better to just quit now. */
|
||
if (stack.store_would_trash (reg[rm]))
|
||
break;
|
||
|
||
if (is_arg_spill (gdbarch, reg[rn], reg[rm], &stack))
|
||
after_last_frame_setup_insn = next_pc;
|
||
|
||
stack.store (reg[rm], 4, reg[rn]);
|
||
}
|
||
else if (IS_SW_IMMD (insn))
|
||
{
|
||
int rn = SW_IMMD_SOURCE (insn);
|
||
int offset = SW_IMMD_OFFSET (insn);
|
||
pv_t addr = pv_add_constant (reg[MEP_SP_REGNUM], offset);
|
||
|
||
/* If simulating this store would require us to forget
|
||
everything we know about the stack frame in the name of
|
||
accuracy, it would be better to just quit now. */
|
||
if (stack.store_would_trash (addr))
|
||
break;
|
||
|
||
if (is_arg_spill (gdbarch, reg[rn], addr, &stack))
|
||
after_last_frame_setup_insn = next_pc;
|
||
|
||
stack.store (addr, 4, reg[rn]);
|
||
}
|
||
else if (IS_MOV (insn))
|
||
{
|
||
int rn = MOV_TARGET (insn);
|
||
int rm = MOV_SOURCE (insn);
|
||
|
||
reg[rn] = reg[rm];
|
||
|
||
if (pv_is_register (reg[rm], rm) && is_arg_reg (reg[rm]))
|
||
after_last_frame_setup_insn = next_pc;
|
||
}
|
||
else if (IS_SB (insn) || IS_SH (insn) || IS_SW (insn))
|
||
{
|
||
int rn = SWBH_32_SOURCE (insn);
|
||
int rm = SWBH_32_BASE (insn);
|
||
int disp = SWBH_32_OFFSET (insn);
|
||
int size = (IS_SB (insn) ? 1
|
||
: IS_SH (insn) ? 2
|
||
: (gdb_assert (IS_SW (insn)), 4));
|
||
pv_t addr = pv_add_constant (reg[rm], disp);
|
||
|
||
if (stack.store_would_trash (addr))
|
||
break;
|
||
|
||
if (is_arg_spill (gdbarch, reg[rn], addr, &stack))
|
||
after_last_frame_setup_insn = next_pc;
|
||
|
||
stack.store (addr, size, reg[rn]);
|
||
}
|
||
else if (IS_LDC (insn))
|
||
{
|
||
int rn = LDC_TARGET (insn);
|
||
int cr = LDC_IMM (insn) + MEP_FIRST_CSR_REGNUM;
|
||
|
||
reg[rn] = reg[cr];
|
||
}
|
||
else if (IS_LW (insn))
|
||
{
|
||
int rn = LW_TARGET (insn);
|
||
int rm = LW_BASE (insn);
|
||
int offset = LW_OFFSET (insn);
|
||
pv_t addr = pv_add_constant (reg[rm], offset);
|
||
|
||
reg[rn] = stack.fetch (addr, 4);
|
||
}
|
||
else if (IS_BRA (insn) && BRA_DISP (insn) > 0)
|
||
{
|
||
/* When a loop appears as the first statement of a function
|
||
body, gcc 4.x will use a BRA instruction to branch to the
|
||
loop condition checking code. This BRA instruction is
|
||
marked as part of the prologue. We therefore set next_pc
|
||
to this branch target and also stop the prologue scan.
|
||
The instructions at and beyond the branch target should
|
||
no longer be associated with the prologue.
|
||
|
||
Note that we only consider forward branches here. We
|
||
presume that a forward branch is being used to skip over
|
||
a loop body.
|
||
|
||
A backwards branch is covered by the default case below.
|
||
If we were to encounter a backwards branch, that would
|
||
most likely mean that we've scanned through a loop body.
|
||
We definitely want to stop the prologue scan when this
|
||
happens and that is precisely what is done by the default
|
||
case below. */
|
||
next_pc = pc + BRA_DISP (insn);
|
||
after_last_frame_setup_insn = next_pc;
|
||
break;
|
||
}
|
||
else
|
||
/* We've hit some instruction we don't know how to simulate.
|
||
Strictly speaking, we should set every value we're
|
||
tracking to "unknown". But we'll be optimistic, assume
|
||
that we have enough information already, and stop
|
||
analysis here. */
|
||
break;
|
||
|
||
/* If this instruction changed the FP or decreased the SP (i.e.,
|
||
allocated more stack space), then this may be a good place to
|
||
declare the prologue finished. However, there are some
|
||
exceptions:
|
||
|
||
- If the instruction just changed the FP back to its original
|
||
value, then that's probably a restore instruction. The
|
||
prologue should definitely end before that.
|
||
|
||
- If the instruction increased the value of the SP (that is,
|
||
shrunk the frame), then it's probably part of a frame
|
||
teardown sequence, and the prologue should end before that. */
|
||
|
||
if (! pv_is_identical (reg[MEP_FP_REGNUM], pre_insn_fp))
|
||
{
|
||
if (! pv_is_register_k (reg[MEP_FP_REGNUM], MEP_FP_REGNUM, 0))
|
||
after_last_frame_setup_insn = next_pc;
|
||
}
|
||
else if (! pv_is_identical (reg[MEP_SP_REGNUM], pre_insn_sp))
|
||
{
|
||
/* The comparison of constants looks odd, there, because .k
|
||
is unsigned. All it really means is that the new value
|
||
is lower than it was before the instruction. */
|
||
if (pv_is_register (pre_insn_sp, MEP_SP_REGNUM)
|
||
&& pv_is_register (reg[MEP_SP_REGNUM], MEP_SP_REGNUM)
|
||
&& ((pre_insn_sp.k - reg[MEP_SP_REGNUM].k)
|
||
< (reg[MEP_SP_REGNUM].k - pre_insn_sp.k)))
|
||
after_last_frame_setup_insn = next_pc;
|
||
}
|
||
|
||
pc = next_pc;
|
||
}
|
||
|
||
/* Is the frame size (offset, really) a known constant? */
|
||
if (pv_is_register (reg[MEP_SP_REGNUM], MEP_SP_REGNUM))
|
||
result->frame_size = reg[MEP_SP_REGNUM].k;
|
||
|
||
/* Was the frame pointer initialized? */
|
||
if (pv_is_register (reg[MEP_FP_REGNUM], MEP_SP_REGNUM))
|
||
{
|
||
result->has_frame_ptr = 1;
|
||
result->frame_ptr_offset = reg[MEP_FP_REGNUM].k;
|
||
}
|
||
|
||
/* Record where all the registers were saved. */
|
||
stack.scan (check_for_saved, (void *) result);
|
||
|
||
result->prologue_end = after_last_frame_setup_insn;
|
||
}
|
||
|
||
|
||
static CORE_ADDR
|
||
mep_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
|
||
{
|
||
const char *name;
|
||
CORE_ADDR func_addr, func_end;
|
||
struct mep_prologue p;
|
||
|
||
/* Try to find the extent of the function that contains PC. */
|
||
if (! find_pc_partial_function (pc, &name, &func_addr, &func_end))
|
||
return pc;
|
||
|
||
mep_analyze_prologue (gdbarch, pc, func_end, &p);
|
||
return p.prologue_end;
|
||
}
|
||
|
||
|
||
|
||
/* Breakpoints. */
|
||
constexpr gdb_byte mep_break_insn[] = { 0x70, 0x32 };
|
||
|
||
typedef BP_MANIPULATION (mep_break_insn) mep_breakpoint;
|
||
|
||
|
||
/* Frames and frame unwinding. */
|
||
|
||
|
||
static struct mep_prologue *
|
||
mep_analyze_frame_prologue (struct frame_info *this_frame,
|
||
void **this_prologue_cache)
|
||
{
|
||
if (! *this_prologue_cache)
|
||
{
|
||
CORE_ADDR func_start, stop_addr;
|
||
|
||
*this_prologue_cache
|
||
= FRAME_OBSTACK_ZALLOC (struct mep_prologue);
|
||
|
||
func_start = get_frame_func (this_frame);
|
||
stop_addr = get_frame_pc (this_frame);
|
||
|
||
/* If we couldn't find any function containing the PC, then
|
||
just initialize the prologue cache, but don't do anything. */
|
||
if (! func_start)
|
||
stop_addr = func_start;
|
||
|
||
mep_analyze_prologue (get_frame_arch (this_frame),
|
||
func_start, stop_addr,
|
||
(struct mep_prologue *) *this_prologue_cache);
|
||
}
|
||
|
||
return (struct mep_prologue *) *this_prologue_cache;
|
||
}
|
||
|
||
|
||
/* Given the next frame and a prologue cache, return this frame's
|
||
base. */
|
||
static CORE_ADDR
|
||
mep_frame_base (struct frame_info *this_frame,
|
||
void **this_prologue_cache)
|
||
{
|
||
struct mep_prologue *p
|
||
= mep_analyze_frame_prologue (this_frame, this_prologue_cache);
|
||
|
||
/* In functions that use alloca, the distance between the stack
|
||
pointer and the frame base varies dynamically, so we can't use
|
||
the SP plus static information like prologue analysis to find the
|
||
frame base. However, such functions must have a frame pointer,
|
||
to be able to restore the SP on exit. So whenever we do have a
|
||
frame pointer, use that to find the base. */
|
||
if (p->has_frame_ptr)
|
||
{
|
||
CORE_ADDR fp
|
||
= get_frame_register_unsigned (this_frame, MEP_FP_REGNUM);
|
||
return fp - p->frame_ptr_offset;
|
||
}
|
||
else
|
||
{
|
||
CORE_ADDR sp
|
||
= get_frame_register_unsigned (this_frame, MEP_SP_REGNUM);
|
||
return sp - p->frame_size;
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
mep_frame_this_id (struct frame_info *this_frame,
|
||
void **this_prologue_cache,
|
||
struct frame_id *this_id)
|
||
{
|
||
*this_id = frame_id_build (mep_frame_base (this_frame, this_prologue_cache),
|
||
get_frame_func (this_frame));
|
||
}
|
||
|
||
|
||
static struct value *
|
||
mep_frame_prev_register (struct frame_info *this_frame,
|
||
void **this_prologue_cache, int regnum)
|
||
{
|
||
struct mep_prologue *p
|
||
= mep_analyze_frame_prologue (this_frame, this_prologue_cache);
|
||
|
||
/* There are a number of complications in unwinding registers on the
|
||
MeP, having to do with core functions calling VLIW functions and
|
||
vice versa.
|
||
|
||
The least significant bit of the link register, LP.LTOM, is the
|
||
VLIW mode toggle bit: it's set if a core function called a VLIW
|
||
function, or vice versa, and clear when the caller and callee
|
||
were both in the same mode.
|
||
|
||
So, if we're asked to unwind the PC, then we really want to
|
||
unwind the LP and clear the least significant bit. (Real return
|
||
addresses are always even.) And if we want to unwind the program
|
||
status word (PSW), we need to toggle PSW.OM if LP.LTOM is set.
|
||
|
||
Tweaking the register values we return in this way means that the
|
||
bits in BUFFERP[] are not the same as the bits you'd find at
|
||
ADDRP in the inferior, so we make sure lvalp is not_lval when we
|
||
do this. */
|
||
if (regnum == MEP_PC_REGNUM)
|
||
{
|
||
struct value *value;
|
||
CORE_ADDR lp;
|
||
value = mep_frame_prev_register (this_frame, this_prologue_cache,
|
||
MEP_LP_REGNUM);
|
||
lp = value_as_long (value);
|
||
release_value (value);
|
||
|
||
return frame_unwind_got_constant (this_frame, regnum, lp & ~1);
|
||
}
|
||
else
|
||
{
|
||
CORE_ADDR frame_base = mep_frame_base (this_frame, this_prologue_cache);
|
||
struct value *value;
|
||
|
||
/* Our caller's SP is our frame base. */
|
||
if (regnum == MEP_SP_REGNUM)
|
||
return frame_unwind_got_constant (this_frame, regnum, frame_base);
|
||
|
||
/* If prologue analysis says we saved this register somewhere,
|
||
return a description of the stack slot holding it. */
|
||
if (p->reg_offset[regnum] != 1)
|
||
value = frame_unwind_got_memory (this_frame, regnum,
|
||
frame_base + p->reg_offset[regnum]);
|
||
|
||
/* Otherwise, presume we haven't changed the value of this
|
||
register, and get it from the next frame. */
|
||
else
|
||
value = frame_unwind_got_register (this_frame, regnum, regnum);
|
||
|
||
/* If we need to toggle the operating mode, do so. */
|
||
if (regnum == MEP_PSW_REGNUM)
|
||
{
|
||
CORE_ADDR psw, lp;
|
||
|
||
psw = value_as_long (value);
|
||
release_value (value);
|
||
|
||
/* Get the LP's value, too. */
|
||
value = get_frame_register_value (this_frame, MEP_LP_REGNUM);
|
||
lp = value_as_long (value);
|
||
release_value (value);
|
||
|
||
/* If LP.LTOM is set, then toggle PSW.OM. */
|
||
if (lp & 0x1)
|
||
psw ^= 0x1000;
|
||
|
||
return frame_unwind_got_constant (this_frame, regnum, psw);
|
||
}
|
||
|
||
return value;
|
||
}
|
||
}
|
||
|
||
|
||
static const struct frame_unwind mep_frame_unwind = {
|
||
NORMAL_FRAME,
|
||
default_frame_unwind_stop_reason,
|
||
mep_frame_this_id,
|
||
mep_frame_prev_register,
|
||
NULL,
|
||
default_frame_sniffer
|
||
};
|
||
|
||
|
||
/* Return values. */
|
||
|
||
|
||
static int
|
||
mep_use_struct_convention (struct type *type)
|
||
{
|
||
return (TYPE_LENGTH (type) > MEP_GPR_SIZE);
|
||
}
|
||
|
||
|
||
static void
|
||
mep_extract_return_value (struct gdbarch *arch,
|
||
struct type *type,
|
||
struct regcache *regcache,
|
||
gdb_byte *valbuf)
|
||
{
|
||
int byte_order = gdbarch_byte_order (arch);
|
||
|
||
/* Values that don't occupy a full register appear at the less
|
||
significant end of the value. This is the offset to where the
|
||
value starts. */
|
||
int offset;
|
||
|
||
/* Return values > MEP_GPR_SIZE bytes are returned in memory,
|
||
pointed to by R0. */
|
||
gdb_assert (TYPE_LENGTH (type) <= MEP_GPR_SIZE);
|
||
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
offset = MEP_GPR_SIZE - TYPE_LENGTH (type);
|
||
else
|
||
offset = 0;
|
||
|
||
/* Return values that do fit in a single register are returned in R0. */
|
||
regcache->cooked_read_part (MEP_R0_REGNUM, offset, TYPE_LENGTH (type),
|
||
valbuf);
|
||
}
|
||
|
||
|
||
static void
|
||
mep_store_return_value (struct gdbarch *arch,
|
||
struct type *type,
|
||
struct regcache *regcache,
|
||
const gdb_byte *valbuf)
|
||
{
|
||
int byte_order = gdbarch_byte_order (arch);
|
||
|
||
/* Values that fit in a single register go in R0. */
|
||
if (TYPE_LENGTH (type) <= MEP_GPR_SIZE)
|
||
{
|
||
/* Values that don't occupy a full register appear at the least
|
||
significant end of the value. This is the offset to where the
|
||
value starts. */
|
||
int offset;
|
||
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
offset = MEP_GPR_SIZE - TYPE_LENGTH (type);
|
||
else
|
||
offset = 0;
|
||
|
||
regcache->cooked_write_part (MEP_R0_REGNUM, offset, TYPE_LENGTH (type),
|
||
valbuf);
|
||
}
|
||
|
||
/* Return values larger than a single register are returned in
|
||
memory, pointed to by R0. Unfortunately, we can't count on R0
|
||
pointing to the return buffer, so we raise an error here. */
|
||
else
|
||
error (_("\
|
||
GDB cannot set return values larger than four bytes; the Media Processor's\n\
|
||
calling conventions do not provide enough information to do this.\n\
|
||
Try using the 'return' command with no argument."));
|
||
}
|
||
|
||
static enum return_value_convention
|
||
mep_return_value (struct gdbarch *gdbarch, struct value *function,
|
||
struct type *type, struct regcache *regcache,
|
||
gdb_byte *readbuf, const gdb_byte *writebuf)
|
||
{
|
||
if (mep_use_struct_convention (type))
|
||
{
|
||
if (readbuf)
|
||
{
|
||
ULONGEST addr;
|
||
/* Although the address of the struct buffer gets passed in R1, it's
|
||
returned in R0. Fetch R0's value and then read the memory
|
||
at that address. */
|
||
regcache_raw_read_unsigned (regcache, MEP_R0_REGNUM, &addr);
|
||
read_memory (addr, readbuf, TYPE_LENGTH (type));
|
||
}
|
||
if (writebuf)
|
||
{
|
||
/* Return values larger than a single register are returned in
|
||
memory, pointed to by R0. Unfortunately, we can't count on R0
|
||
pointing to the return buffer, so we raise an error here. */
|
||
error (_("\
|
||
GDB cannot set return values larger than four bytes; the Media Processor's\n\
|
||
calling conventions do not provide enough information to do this.\n\
|
||
Try using the 'return' command with no argument."));
|
||
}
|
||
return RETURN_VALUE_ABI_RETURNS_ADDRESS;
|
||
}
|
||
|
||
if (readbuf)
|
||
mep_extract_return_value (gdbarch, type, regcache, readbuf);
|
||
if (writebuf)
|
||
mep_store_return_value (gdbarch, type, regcache, writebuf);
|
||
|
||
return RETURN_VALUE_REGISTER_CONVENTION;
|
||
}
|
||
|
||
|
||
/* Inferior calls. */
|
||
|
||
|
||
static CORE_ADDR
|
||
mep_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
|
||
{
|
||
/* Require word alignment. */
|
||
return sp & -4;
|
||
}
|
||
|
||
|
||
/* From "lang_spec2.txt":
|
||
|
||
4.2 Calling conventions
|
||
|
||
4.2.1 Core register conventions
|
||
|
||
- Parameters should be evaluated from left to right, and they
|
||
should be held in $1,$2,$3,$4 in order. The fifth parameter or
|
||
after should be held in the stack. If the size is larger than 4
|
||
bytes in the first four parameters, the pointer should be held in
|
||
the registers instead. If the size is larger than 4 bytes in the
|
||
fifth parameter or after, the pointer should be held in the stack.
|
||
|
||
- Return value of a function should be held in register $0. If the
|
||
size of return value is larger than 4 bytes, $1 should hold the
|
||
pointer pointing memory that would hold the return value. In this
|
||
case, the first parameter should be held in $2, the second one in
|
||
$3, and the third one in $4, and the forth parameter or after
|
||
should be held in the stack.
|
||
|
||
[This doesn't say so, but arguments shorter than four bytes are
|
||
passed in the least significant end of a four-byte word when
|
||
they're passed on the stack.] */
|
||
|
||
|
||
/* Traverse the list of ARGC arguments ARGV; for every ARGV[i] too
|
||
large to fit in a register, save it on the stack, and place its
|
||
address in COPY[i]. SP is the initial stack pointer; return the
|
||
new stack pointer. */
|
||
static CORE_ADDR
|
||
push_large_arguments (CORE_ADDR sp, int argc, struct value **argv,
|
||
CORE_ADDR copy[])
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < argc; i++)
|
||
{
|
||
unsigned arg_len = TYPE_LENGTH (value_type (argv[i]));
|
||
|
||
if (arg_len > MEP_GPR_SIZE)
|
||
{
|
||
/* Reserve space for the copy, and then round the SP down, to
|
||
make sure it's all aligned properly. */
|
||
sp = (sp - arg_len) & -4;
|
||
write_memory (sp, value_contents (argv[i]), arg_len);
|
||
copy[i] = sp;
|
||
}
|
||
}
|
||
|
||
return sp;
|
||
}
|
||
|
||
|
||
static CORE_ADDR
|
||
mep_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
||
struct regcache *regcache, CORE_ADDR bp_addr,
|
||
int argc, struct value **argv, CORE_ADDR sp,
|
||
function_call_return_method return_method,
|
||
CORE_ADDR struct_addr)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
CORE_ADDR *copy = (CORE_ADDR *) alloca (argc * sizeof (copy[0]));
|
||
int i;
|
||
|
||
/* The number of the next register available to hold an argument. */
|
||
int arg_reg;
|
||
|
||
/* The address of the next stack slot available to hold an argument. */
|
||
CORE_ADDR arg_stack;
|
||
|
||
/* The address of the end of the stack area for arguments. This is
|
||
just for error checking. */
|
||
CORE_ADDR arg_stack_end;
|
||
|
||
sp = push_large_arguments (sp, argc, argv, copy);
|
||
|
||
/* Reserve space for the stack arguments, if any. */
|
||
arg_stack_end = sp;
|
||
if (argc + (struct_addr ? 1 : 0) > 4)
|
||
sp -= ((argc + (struct_addr ? 1 : 0)) - 4) * MEP_GPR_SIZE;
|
||
|
||
arg_reg = MEP_R1_REGNUM;
|
||
arg_stack = sp;
|
||
|
||
/* If we're returning a structure by value, push the pointer to the
|
||
buffer as the first argument. */
|
||
if (return_method == return_method_struct)
|
||
{
|
||
regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr);
|
||
arg_reg++;
|
||
}
|
||
|
||
for (i = 0; i < argc; i++)
|
||
{
|
||
ULONGEST value;
|
||
|
||
/* Arguments that fit in a GPR get expanded to fill the GPR. */
|
||
if (TYPE_LENGTH (value_type (argv[i])) <= MEP_GPR_SIZE)
|
||
value = extract_unsigned_integer (value_contents (argv[i]),
|
||
TYPE_LENGTH (value_type (argv[i])),
|
||
byte_order);
|
||
|
||
/* Arguments too large to fit in a GPR get copied to the stack,
|
||
and we pass a pointer to the copy. */
|
||
else
|
||
value = copy[i];
|
||
|
||
/* We use $1 -- $4 for passing arguments, then use the stack. */
|
||
if (arg_reg <= MEP_R4_REGNUM)
|
||
{
|
||
regcache_cooked_write_unsigned (regcache, arg_reg, value);
|
||
arg_reg++;
|
||
}
|
||
else
|
||
{
|
||
gdb_byte buf[MEP_GPR_SIZE];
|
||
store_unsigned_integer (buf, MEP_GPR_SIZE, byte_order, value);
|
||
write_memory (arg_stack, buf, MEP_GPR_SIZE);
|
||
arg_stack += MEP_GPR_SIZE;
|
||
}
|
||
}
|
||
|
||
gdb_assert (arg_stack <= arg_stack_end);
|
||
|
||
/* Set the return address. */
|
||
regcache_cooked_write_unsigned (regcache, MEP_LP_REGNUM, bp_addr);
|
||
|
||
/* Update the stack pointer. */
|
||
regcache_cooked_write_unsigned (regcache, MEP_SP_REGNUM, sp);
|
||
|
||
return sp;
|
||
}
|
||
|
||
|
||
/* Initialization. */
|
||
|
||
|
||
static struct gdbarch *
|
||
mep_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||
{
|
||
struct gdbarch *gdbarch;
|
||
struct gdbarch_tdep *tdep;
|
||
|
||
/* Which me_module are we building a gdbarch object for? */
|
||
CONFIG_ATTR me_module;
|
||
|
||
/* If we have a BFD in hand, figure out which me_module it was built
|
||
for. Otherwise, use the no-particular-me_module code. */
|
||
if (info.abfd)
|
||
{
|
||
/* The way to get the me_module code depends on the object file
|
||
format. At the moment, we only know how to handle ELF. */
|
||
if (bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
|
||
{
|
||
int flag = elf_elfheader (info.abfd)->e_flags & EF_MEP_INDEX_MASK;
|
||
me_module = (CONFIG_ATTR) flag;
|
||
}
|
||
else
|
||
me_module = CONFIG_NONE;
|
||
}
|
||
else
|
||
me_module = CONFIG_NONE;
|
||
|
||
/* If we're setting the architecture from a file, check the
|
||
endianness of the file against that of the me_module. */
|
||
if (info.abfd)
|
||
{
|
||
/* The negations on either side make the comparison treat all
|
||
non-zero (true) values as equal. */
|
||
if (! bfd_big_endian (info.abfd) != ! me_module_big_endian (me_module))
|
||
{
|
||
const char *module_name = me_module_name (me_module);
|
||
const char *module_endianness
|
||
= me_module_big_endian (me_module) ? "big" : "little";
|
||
const char *file_name = bfd_get_filename (info.abfd);
|
||
const char *file_endianness
|
||
= bfd_big_endian (info.abfd) ? "big" : "little";
|
||
|
||
fputc_unfiltered ('\n', gdb_stderr);
|
||
if (module_name)
|
||
warning (_("the MeP module '%s' is %s-endian, but the executable\n"
|
||
"%s is %s-endian."),
|
||
module_name, module_endianness,
|
||
file_name, file_endianness);
|
||
else
|
||
warning (_("the selected MeP module is %s-endian, but the "
|
||
"executable\n"
|
||
"%s is %s-endian."),
|
||
module_endianness, file_name, file_endianness);
|
||
}
|
||
}
|
||
|
||
/* Find a candidate among the list of architectures we've created
|
||
already. info->bfd_arch_info needs to match, but we also want
|
||
the right me_module: the ELF header's e_flags field needs to
|
||
match as well. */
|
||
for (arches = gdbarch_list_lookup_by_info (arches, &info);
|
||
arches != NULL;
|
||
arches = gdbarch_list_lookup_by_info (arches->next, &info))
|
||
if (gdbarch_tdep (arches->gdbarch)->me_module == me_module)
|
||
return arches->gdbarch;
|
||
|
||
tdep = XCNEW (struct gdbarch_tdep);
|
||
gdbarch = gdbarch_alloc (&info, tdep);
|
||
|
||
/* Get a CGEN CPU descriptor for this architecture. */
|
||
{
|
||
const char *mach_name = info.bfd_arch_info->printable_name;
|
||
enum cgen_endian endian = (info.byte_order == BFD_ENDIAN_BIG
|
||
? CGEN_ENDIAN_BIG
|
||
: CGEN_ENDIAN_LITTLE);
|
||
|
||
tdep->cpu_desc = mep_cgen_cpu_open (CGEN_CPU_OPEN_BFDMACH, mach_name,
|
||
CGEN_CPU_OPEN_ENDIAN, endian,
|
||
CGEN_CPU_OPEN_END);
|
||
}
|
||
|
||
tdep->me_module = me_module;
|
||
|
||
/* Register set. */
|
||
set_gdbarch_num_regs (gdbarch, MEP_NUM_RAW_REGS);
|
||
set_gdbarch_pc_regnum (gdbarch, MEP_PC_REGNUM);
|
||
set_gdbarch_sp_regnum (gdbarch, MEP_SP_REGNUM);
|
||
set_gdbarch_register_name (gdbarch, mep_register_name);
|
||
set_gdbarch_register_type (gdbarch, mep_register_type);
|
||
set_gdbarch_num_pseudo_regs (gdbarch, MEP_NUM_PSEUDO_REGS);
|
||
set_gdbarch_pseudo_register_read (gdbarch, mep_pseudo_register_read);
|
||
set_gdbarch_pseudo_register_write (gdbarch, mep_pseudo_register_write);
|
||
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
|
||
set_gdbarch_stab_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
|
||
|
||
set_gdbarch_register_reggroup_p (gdbarch, mep_register_reggroup_p);
|
||
reggroup_add (gdbarch, all_reggroup);
|
||
reggroup_add (gdbarch, general_reggroup);
|
||
reggroup_add (gdbarch, save_reggroup);
|
||
reggroup_add (gdbarch, restore_reggroup);
|
||
reggroup_add (gdbarch, mep_csr_reggroup);
|
||
reggroup_add (gdbarch, mep_cr_reggroup);
|
||
reggroup_add (gdbarch, mep_ccr_reggroup);
|
||
|
||
/* Disassembly. */
|
||
set_gdbarch_print_insn (gdbarch, mep_gdb_print_insn);
|
||
|
||
/* Breakpoints. */
|
||
set_gdbarch_breakpoint_kind_from_pc (gdbarch, mep_breakpoint::kind_from_pc);
|
||
set_gdbarch_sw_breakpoint_from_kind (gdbarch, mep_breakpoint::bp_from_kind);
|
||
set_gdbarch_decr_pc_after_break (gdbarch, 0);
|
||
set_gdbarch_skip_prologue (gdbarch, mep_skip_prologue);
|
||
|
||
/* Frames and frame unwinding. */
|
||
frame_unwind_append_unwinder (gdbarch, &mep_frame_unwind);
|
||
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
|
||
set_gdbarch_frame_args_skip (gdbarch, 0);
|
||
|
||
/* Return values. */
|
||
set_gdbarch_return_value (gdbarch, mep_return_value);
|
||
|
||
/* Inferior function calls. */
|
||
set_gdbarch_frame_align (gdbarch, mep_frame_align);
|
||
set_gdbarch_push_dummy_call (gdbarch, mep_push_dummy_call);
|
||
|
||
return gdbarch;
|
||
}
|
||
|
||
void _initialize_mep_tdep ();
|
||
void
|
||
_initialize_mep_tdep ()
|
||
{
|
||
mep_csr_reggroup = reggroup_new ("csr", USER_REGGROUP);
|
||
mep_cr_reggroup = reggroup_new ("cr", USER_REGGROUP);
|
||
mep_ccr_reggroup = reggroup_new ("ccr", USER_REGGROUP);
|
||
|
||
register_gdbarch_init (bfd_arch_mep, mep_gdbarch_init);
|
||
|
||
mep_init_pseudoregister_maps ();
|
||
}
|