Commit 7c209481 by Georg-Johann Lay Committed by Georg-Johann Lay

re PR target/49868 (Implement named address space to place/access data in flash memory)

gcc/
	PR target/49868
	* config/avr/avr.h (ADDR_SPACE_PGM): New address spaces.
	(REGISTER_TARGET_PRAGMAS): New define.
	* config/avr/avr-protos.h (avr_mem_pgm_p): New.
	(avr_load_libgcc_p): New.
	(asm_output_external_libcall): Remove.
	(avr_register_target_pragmas): New.
	(avr_log_t): Add field "progmem".  Order alphabetically.
	* config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem.
	* config/avr/avr-c.c (langhooks.h): New include.
	(avr_register_target_pragmas): New function. Register address
	space __pgm.
	(avr_cpu_cpp_builtins): Add built-in define __PGM.
	* config/avr/avr.c: Include "c-family/c-common.h".
	(TARGET_LEGITIMATE_ADDRESS_P): Remove define.
	(TARGET_LEGITIMIZE_ADDRESS): Remove define.
	(TARGET_ADDR_SPACE_SUBSET_P): Define to...
	(avr_addr_space_subset_p): ...this new static function.
	(TARGET_ADDR_SPACE_CONVERT): Define to...
	(avr_addr_space_convert): ...this new static function.
	(TARGET_ADDR_SPACE_ADDRESS_MODE): Define to...
	(avr_addr_space_address_mode): ...this new static function.
	(TARGET_ADDR_SPACE_POINTER_MODE): Define to...
	(avr_addr_space_pointer_mode): ...this new static function.
	(TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to...
	(avr_addr_space_legitimate_address_p): ...this new static function.
	(TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to...
	(avr_addr_space_legitimize_address): ...this new static function.
	(avr_mode_code_base_reg_class): Handle address spaces.
	(avr_regno_mode_code_ok_for_base_p): Ditto.
	(lpm_addr_reg_rtx, lpm_reg_rtx, all_regs_rtx): New static variables.
	(avr_option_override): Initialize them.
	(output_reload_in_const): Use all_regs_rtx. Fix signedness of loop
	variables.
	(avr_pgm_segment): New static function.
	(avr_decl_pgm_p, avr_mem_pgm_p): New static functions.
	(avr_out_lpm, avr_out_lpm_no_lpmx): New static functions.
	(output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call
	avr_out_lpm to handle loads from progmem.
	(avr_load_libgcc_p): New static function.
	(avr_progmem_p): Test if decl is in flash.
	(avr_pgm_pointer_const_p): New static function.
	(avr_nonconst_pointer_addrspace): New static function.
	(avr_pgm_check_var_decl): New static function.
	(avr_insert_attributes): Use it.  Change error message to report
	cause (progmem or address space) when code wants to write to flash.
	(avr_section_type_flags): Unset section flag SECTION_BSS for
	data in progmem.
	* config/avr/predicates.md (nop_general_operand): New predicate.
	(nox_general_operand): New predicate.
	* config/avr/avr.md (LPM_REGNO): New define_constant.
	(load<mode>_libgcc): New expander.
	(*load.<mode>.libgcc): New insn.
	(mov<mode>): Handle loads from non-generic AS.
	(movmemhi): Ditto.  Propagate address space information to newly
	created MEM.
	(movqi_insn, *movhi, *movpsi, *movsi, *movsf): Change predicate #1
	to nox_general_operand.
	(ashrqi3, ashrhi3, ashrsi3): Change predicate #1 to nop_general_operand.
	(ashlqi3, *ashlqi3, ashlhi3, ashlsi3): Ditto.
	(lshrqi3, *lshrqi3, lshrhi3, lshrsi3): Ditto.
	(split-lpmx): New split.
	(*ashlhi3_const, *ashlsi3_const, *ashrhi3_const, *ashrsi3_const,
	*lshrhi3_const, *lshrsi3_const): Indent, unquote C.

libgcc/
	PR target/49868
	* config/avr/t-avr (LIB1ASMFUNCS): Add _load_3,  _load_4.
	* config/avr/lib1funcs.S (__load_3, __load_4, __xload_2): New functions.

From-SVN: r181378
parent d2d668fb
2011-11-15 Georg-Johann Lay <avr@gjlay.de>
PR target/49868
* config/avr/avr.h (ADDR_SPACE_PGM): New address spaces.
(REGISTER_TARGET_PRAGMAS): New define.
* config/avr/avr-protos.h (avr_mem_pgm_p): New.
(avr_load_libgcc_p): New.
(avr_register_target_pragmas): New.
(asm_output_external_libcall): Remove.
(avr_log_t): Add field "progmem". Order alphabetically.
* config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem.
* config/avr/avr-c.c (langhooks.h): New include.
(avr_register_target_pragmas): New function. Register address
space __pgm.
(avr_cpu_cpp_builtins): Add built-in define __PGM.
* config/avr/avr.c: Include "c-family/c-common.h".
(TARGET_LEGITIMATE_ADDRESS_P): Remove define.
(TARGET_LEGITIMIZE_ADDRESS): Remove define.
(TARGET_ADDR_SPACE_SUBSET_P): Define to...
(avr_addr_space_subset_p): ...this new static function.
(TARGET_ADDR_SPACE_CONVERT): Define to...
(avr_addr_space_convert): ...this new static function.
(TARGET_ADDR_SPACE_ADDRESS_MODE): Define to...
(avr_addr_space_address_mode): ...this new static function.
(TARGET_ADDR_SPACE_POINTER_MODE): Define to...
(avr_addr_space_pointer_mode): ...this new static function.
(TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to...
(avr_addr_space_legitimate_address_p): ...this new static function.
(TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to...
(avr_addr_space_legitimize_address): ...this new static function.
(avr_mode_code_base_reg_class): Handle address spaces.
(avr_regno_mode_code_ok_for_base_p): Ditto.
(lpm_addr_reg_rtx, lpm_reg_rtx, all_regs_rtx): New static variables.
(avr_option_override): Initialize them.
(output_reload_in_const): Use all_regs_rtx. Fix signedness of loop
variables.
(avr_pgm_segment): New static function.
(avr_decl_pgm_p, avr_mem_pgm_p): New static functions.
(avr_out_lpm, avr_out_lpm_no_lpmx): New static functions.
(output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call
avr_out_lpm to handle loads from progmem.
(avr_load_libgcc_p): New static function.
(avr_progmem_p): Test if decl is in flash.
(avr_pgm_pointer_const_p): New static function.
(avr_nonconst_pointer_addrspace): New static function.
(avr_pgm_check_var_decl): New static function.
(avr_insert_attributes): Use it. Change error message to report
cause (progmem or address space) when code wants to write to flash.
(avr_section_type_flags): Unset section flag SECTION_BSS for
data in progmem.
* config/avr/predicates.md (nop_general_operand): New predicate.
(nox_general_operand): New predicate.
* config/avr/avr.md (LPM_REGNO): New define_constant.
(load<mode>_libgcc): New expander.
(*load.<mode>.libgcc): New insn.
(mov<mode>): Handle loads from non-generic AS.
(movmemhi): Ditto. Propagate address space information to newly
created MEM.
(movqi_insn, *movhi, *movpsi, *movsi, *movsf): Change predicate #1
to nox_general_operand.
(ashrqi3, ashrhi3, ashrsi3): Change predicate #1 to nop_general_operand.
(ashlqi3, *ashlqi3, ashlhi3, ashlsi3): Ditto.
(lshrqi3, *lshrqi3, lshrhi3, lshrsi3): Ditto.
(split-lpmx): New split.
(*ashlhi3_const, *ashlsi3_const, *ashrhi3_const, *ashrsi3_const,
*lshrhi3_const, *lshrsi3_const): Indent, unquote C.
2011-11-15 Maxim Kuvyrkov <maxim@codesourcery.com> 2011-11-15 Maxim Kuvyrkov <maxim@codesourcery.com>
* ipa-cp.c (ipa_value_from_jfunc): Make global. * ipa-cp.c (ipa_value_from_jfunc): Make global.
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
along with GCC; see the file COPYING3. If not see along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
/* Not included in avr.c since this requires C front end. */
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
...@@ -27,8 +28,17 @@ ...@@ -27,8 +28,17 @@
#include "cpplib.h" #include "cpplib.h"
#include "tree.h" #include "tree.h"
#include "c-family/c-common.h" #include "c-family/c-common.h"
#include "langhooks.h"
/* Implement `REGISTER_TARGET_PRAGMAS'. */
void
avr_register_target_pragmas (void)
{
c_register_addr_space ("__pgm", ADDR_SPACE_PGM);
}
/* Not included in avr.c since this requires C front end. */
/* Worker function for TARGET_CPU_CPP_BUILTINS. */ /* Worker function for TARGET_CPU_CPP_BUILTINS. */
...@@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) ...@@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile)
cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__"); cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__");
} }
/* Define builtin macros so that the user can easily query if or if not
non-generic address spaces (and which) are supported.
This is only supported for C. For C++, a language extension is needed
(as mentioned in ISO/IEC DTR 18037; Annex F.2) which is not
implemented in GCC up to now. */
if (!strcmp (lang_hooks.name, "GNU C"))
{
cpp_define (pfile, "__PGM=__pgm");
}
/* Define builtin macros so that the user can /* Define builtin macros so that the user can
easily query if or if not a specific builtin easily query if or if not a specific builtin
is available. */ is available. */
......
...@@ -59,6 +59,8 @@ ...@@ -59,6 +59,8 @@
F: caller (via __FUNCTION__) F: caller (via __FUNCTION__)
P: Pass name and number P: Pass name and number
?: Print caller, current function and pass info ?: Print caller, current function and pass info
!: Ditto, but only print if in a pass with static pass number,
else return.
== same as printf == == same as printf ==
...@@ -318,12 +320,13 @@ avr_log_set_avr_log (void) ...@@ -318,12 +320,13 @@ avr_log_set_avr_log (void)
|| NULL != strstr (str, "," #S ",") \ || NULL != strstr (str, "," #S ",") \
|| NULL != strstr (str, ",all,")) || NULL != strstr (str, ",all,"))
SET_DUMP_DETAIL (rtx_costs); SET_DUMP_DETAIL (address_cost);
SET_DUMP_DETAIL (constraints);
SET_DUMP_DETAIL (legitimate_address_p); SET_DUMP_DETAIL (legitimate_address_p);
SET_DUMP_DETAIL (legitimize_address); SET_DUMP_DETAIL (legitimize_address);
SET_DUMP_DETAIL (legitimize_reload_address); SET_DUMP_DETAIL (legitimize_reload_address);
SET_DUMP_DETAIL (constraints); SET_DUMP_DETAIL (progmem);
SET_DUMP_DETAIL (address_cost); SET_DUMP_DETAIL (rtx_costs);
#undef SET_DUMP_DETAIL #undef SET_DUMP_DETAIL
} }
......
...@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to); ...@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to);
extern int avr_simple_epilogue (void); extern int avr_simple_epilogue (void);
extern int avr_hard_regno_rename_ok (unsigned int, unsigned int); extern int avr_hard_regno_rename_ok (unsigned int, unsigned int);
extern rtx avr_return_addr_rtx (int count, rtx tem); extern rtx avr_return_addr_rtx (int count, rtx tem);
extern void avr_register_target_pragmas (void);
extern bool avr_accumulate_outgoing_args (void); extern bool avr_accumulate_outgoing_args (void);
#ifdef TREE_CODE #ifdef TREE_CODE
...@@ -47,7 +48,6 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, ...@@ -47,7 +48,6 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
#endif /* TREE_CODE */ #endif /* TREE_CODE */
#ifdef RTX_CODE #ifdef RTX_CODE
extern void asm_output_external_libcall (FILE *file, rtx symref);
extern const char *output_movqi (rtx insn, rtx operands[], int *l); extern const char *output_movqi (rtx insn, rtx operands[], int *l);
extern const char *output_movhi (rtx insn, rtx operands[], int *l); extern const char *output_movhi (rtx insn, rtx operands[], int *l);
extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l); extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l);
...@@ -121,6 +121,8 @@ extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t ...@@ -121,6 +121,8 @@ extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t
extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE); extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
extern rtx avr_incoming_return_addr_rtx (void); extern rtx avr_incoming_return_addr_rtx (void);
extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int)); extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int));
extern bool avr_mem_pgm_p (rtx);
extern bool avr_load_libgcc_p (rtx);
#endif /* RTX_CODE */ #endif /* RTX_CODE */
#ifdef REAL_VALUE_TYPE #ifdef REAL_VALUE_TYPE
...@@ -139,12 +141,13 @@ extern void avr_log_set_avr_log (void); ...@@ -139,12 +141,13 @@ extern void avr_log_set_avr_log (void);
typedef struct typedef struct
{ {
unsigned rtx_costs :1; unsigned address_cost :1;
unsigned constraints :1;
unsigned legitimate_address_p :1; unsigned legitimate_address_p :1;
unsigned legitimize_address :1; unsigned legitimize_address :1;
unsigned legitimize_reload_address :1; unsigned legitimize_reload_address :1;
unsigned constraints :1; unsigned progmem :1;
unsigned address_cost :1; unsigned rtx_costs :1;
} avr_log_t; } avr_log_t;
extern avr_log_t avr_log; extern avr_log_t avr_log;
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "tree.h" #include "tree.h"
#include "output.h" #include "output.h"
#include "expr.h" #include "expr.h"
#include "c-family/c-common.h"
#include "diagnostic-core.h" #include "diagnostic-core.h"
#include "obstack.h" #include "obstack.h"
#include "function.h" #include "function.h"
...@@ -84,12 +85,21 @@ static bool avr_rtx_costs (rtx, int, int, int, int *, bool); ...@@ -84,12 +85,21 @@ static bool avr_rtx_costs (rtx, int, int, int, int *, bool);
/* Allocate registers from r25 to r8 for parameters for function calls. */ /* Allocate registers from r25 to r8 for parameters for function calls. */
#define FIRST_CUM_REG 26 #define FIRST_CUM_REG 26
/* Implicit target register of LPM instruction (R0) */
static GTY(()) rtx lpm_reg_rtx;
/* (Implicit) address register of LPM instruction (R31:R30 = Z) */
static GTY(()) rtx lpm_addr_reg_rtx;
/* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */ /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */
static GTY(()) rtx tmp_reg_rtx; static GTY(()) rtx tmp_reg_rtx;
/* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */ /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
static GTY(()) rtx zero_reg_rtx; static GTY(()) rtx zero_reg_rtx;
/* RTXs for all general purpose registers as QImode */
static GTY(()) rtx all_regs_rtx[32];
/* AVR register names {"r0", "r1", ..., "r31"} */ /* AVR register names {"r0", "r1", ..., "r31"} */
static const char *const avr_regnames[] = REGISTER_NAMES; static const char *const avr_regnames[] = REGISTER_NAMES;
...@@ -172,9 +182,6 @@ bool avr_need_copy_data_p = false; ...@@ -172,9 +182,6 @@ bool avr_need_copy_data_p = false;
#undef TARGET_FUNCTION_ARG_ADVANCE #undef TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address
#undef TARGET_RETURN_IN_MEMORY #undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY avr_return_in_memory #define TARGET_RETURN_IN_MEMORY avr_return_in_memory
...@@ -189,9 +196,6 @@ bool avr_need_copy_data_p = false; ...@@ -189,9 +196,6 @@ bool avr_need_copy_data_p = false;
#undef TARGET_CASE_VALUES_THRESHOLD #undef TARGET_CASE_VALUES_THRESHOLD
#define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p
#undef TARGET_FRAME_POINTER_REQUIRED #undef TARGET_FRAME_POINTER_REQUIRED
#define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p
#undef TARGET_CAN_ELIMINATE #undef TARGET_CAN_ELIMINATE
...@@ -221,6 +225,24 @@ bool avr_need_copy_data_p = false; ...@@ -221,6 +225,24 @@ bool avr_need_copy_data_p = false;
#undef TARGET_SCALAR_MODE_SUPPORTED_P #undef TARGET_SCALAR_MODE_SUPPORTED_P
#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p #define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p
#undef TARGET_ADDR_SPACE_SUBSET_P
#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p
#undef TARGET_ADDR_SPACE_CONVERT
#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert
#undef TARGET_ADDR_SPACE_ADDRESS_MODE
#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode
#undef TARGET_ADDR_SPACE_POINTER_MODE
#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode
#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p
#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
/* Custom function to replace string prefix. /* Custom function to replace string prefix.
...@@ -295,6 +317,8 @@ avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask) ...@@ -295,6 +317,8 @@ avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask)
static void static void
avr_option_override (void) avr_option_override (void)
{ {
int regno;
flag_delete_null_pointer_checks = 0; flag_delete_null_pointer_checks = 0;
/* caller-save.c looks for call-clobbered hard registers that are assigned /* caller-save.c looks for call-clobbered hard registers that are assigned
...@@ -323,8 +347,14 @@ avr_option_override (void) ...@@ -323,8 +347,14 @@ avr_option_override (void)
avr_current_arch = &avr_arch_types[avr_current_device->arch]; avr_current_arch = &avr_arch_types[avr_current_device->arch];
avr_extra_arch_macro = avr_current_device->macro; avr_extra_arch_macro = avr_current_device->macro;
tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); for (regno = 0; regno < 32; regno ++)
zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO); all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
lpm_reg_rtx = all_regs_rtx[LPM_REGNO];
tmp_reg_rtx = all_regs_rtx[TMP_REGNO];
zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
init_machine_status = avr_init_machine_status; init_machine_status = avr_init_machine_status;
...@@ -384,6 +414,28 @@ avr_scalar_mode_supported_p (enum machine_mode mode) ...@@ -384,6 +414,28 @@ avr_scalar_mode_supported_p (enum machine_mode mode)
} }
/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */
static bool
avr_decl_pgm_p (tree decl)
{
if (TREE_CODE (decl) != VAR_DECL)
return false;
return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
}
/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */
bool
avr_mem_pgm_p (rtx x)
{
return (MEM_P (x)
&& !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)));
}
/* A helper for the subsequent function attribute used to dig for /* A helper for the subsequent function attribute used to dig for
attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
...@@ -1379,6 +1431,9 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) ...@@ -1379,6 +1431,9 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
return ok; return ok;
} }
/* Former implementation of TARGET_LEGITIMIZE_ADDRESS,
now only a helper for avr_addr_space_legitimize_address. */
/* Attempts to replace X with a valid /* Attempts to replace X with a valid
memory address for an operand of mode MODE */ memory address for an operand of mode MODE */
...@@ -2177,6 +2232,231 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee) ...@@ -2177,6 +2232,231 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
/*********************************************************************** /***********************************************************************
Functions for outputting various mov's for a various modes Functions for outputting various mov's for a various modes
************************************************************************/ ************************************************************************/
/* Return true if a value of mode MODE is read from flash by
__load_* function from libgcc. */
bool
avr_load_libgcc_p (rtx op)
{
enum machine_mode mode = GET_MODE (op);
int n_bytes = GET_MODE_SIZE (mode);
return (n_bytes > 2
&& !AVR_HAVE_LPMX
&& avr_mem_pgm_p (op));
}
/* Helper function for the next function in the case where only restricted
version of LPM instruction is available. */
static const char*
avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
{
rtx dest = xop[0];
rtx addr = xop[1];
int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
int regno_dest;
regno_dest = REGNO (dest);
/* The implicit target register of LPM. */
xop[3] = lpm_reg_rtx;
switch (GET_CODE (addr))
{
default:
gcc_unreachable();
case REG:
gcc_assert (REG_Z == REGNO (addr));
switch (n_bytes)
{
default:
gcc_unreachable();
case 1:
return avr_asm_len ("lpm" CR_TAB
"mov %0,%3", xop, plen, 2);
case 2:
if (REGNO (dest) == REG_Z)
return avr_asm_len ("lpm" CR_TAB
"push %3" CR_TAB
"adiw %2,1" CR_TAB
"lpm" CR_TAB
"mov %B0,%3" CR_TAB
"pop %A0", xop, plen, 6);
else
{
avr_asm_len ("lpm" CR_TAB
"mov %A0,%3" CR_TAB
"adiw %2,1" CR_TAB
"lpm" CR_TAB
"mov %B0,%3", xop, plen, 5);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,1", xop, plen, 1);
}
break; /* 2 */
}
break; /* REG */
case POST_INC:
gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
&& n_bytes <= 2);
avr_asm_len ("lpm" CR_TAB
"mov %A0,%3" CR_TAB
"adiw %2,1", xop, plen, 3);
if (n_bytes >= 2)
avr_asm_len ("lpm" CR_TAB
"mov %B0,%3" CR_TAB
"adiw %2,1", xop, plen, 3);
break; /* POST_INC */
} /* switch CODE (addr) */
return "";
}
/* If PLEN == NULL: Ouput instructions to load a value from a memory location
OP[1] in AS1 to register OP[0].
If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
Return "". */
static const char*
avr_out_lpm (rtx insn, rtx *op, int *plen)
{
rtx xop[5];
rtx dest = op[0];
rtx src = SET_SRC (single_set (insn));
rtx addr;
int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
int regno_dest;
if (plen)
*plen = 0;
if (MEM_P (dest))
{
warning (0, "writing to address space %qs not supported",
c_addr_space_name (MEM_ADDR_SPACE (dest)));
return "";
}
addr = XEXP (src, 0);
gcc_assert (!avr_load_libgcc_p (src)
&& REG_P (dest)
&& (REG_P (addr) || POST_INC == GET_CODE (addr)));
xop[0] = dest;
xop[1] = addr;
xop[2] = lpm_addr_reg_rtx;
regno_dest = REGNO (dest);
if (!AVR_HAVE_LPMX)
{
return avr_out_lpm_no_lpmx (insn, xop, plen);
}
switch (GET_CODE (addr))
{
default:
gcc_unreachable();
case REG:
gcc_assert (REG_Z == REGNO (addr));
switch (n_bytes)
{
default:
gcc_unreachable();
case 1:
return avr_asm_len ("lpm %0,%a2", xop, plen, -1);
case 2:
if (REGNO (dest) == REG_Z)
return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
"lpm %B0,%a2" CR_TAB
"mov %A0,__tmp_reg__", xop, plen, -3);
else
{
avr_asm_len ("lpm %A0,%a2+" CR_TAB
"lpm %B0,%a2", xop, plen, -2);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,1", xop, plen, 1);
}
break; /* 2 */
case 3:
avr_asm_len ("lpm %A0,%a2+" CR_TAB
"lpm %B0,%a2+" CR_TAB
"lpm %C0,%a2", xop, plen, -3);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,2", xop, plen, 1);
break; /* 3 */
case 4:
avr_asm_len ("lpm %A0,%a2+" CR_TAB
"lpm %B0,%a2+", xop, plen, -2);
if (REGNO (dest) == REG_Z - 2)
return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
"lpm %C0,%a2" CR_TAB
"mov %D0,__tmp_reg__", xop, plen, 3);
else
{
avr_asm_len ("lpm %C0,%a2+" CR_TAB
"lpm %D0,%a2", xop, plen, 2);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,3", xop, plen, 1);
}
break; /* 4 */
} /* n_bytes */
break; /* REG */
case POST_INC:
gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
&& n_bytes <= 4);
avr_asm_len ("lpm %A0,%a2+", xop, plen, -1);
if (n_bytes >= 2) avr_asm_len ("lpm %B0,%a2+", xop, plen, 1);
if (n_bytes >= 3) avr_asm_len ("lpm %C0,%a2+", xop, plen, 1);
if (n_bytes >= 4) avr_asm_len ("lpm %D0,%a2+", xop, plen, 1);
break; /* POST_INC */
} /* switch CODE (addr) */
return "";
}
const char * const char *
output_movqi (rtx insn, rtx operands[], int *l) output_movqi (rtx insn, rtx operands[], int *l)
{ {
...@@ -2185,6 +2465,12 @@ output_movqi (rtx insn, rtx operands[], int *l) ...@@ -2185,6 +2465,12 @@ output_movqi (rtx insn, rtx operands[], int *l)
rtx src = operands[1]; rtx src = operands[1];
int *real_l = l; int *real_l = l;
if (avr_mem_pgm_p (src)
|| avr_mem_pgm_p (dest))
{
return avr_out_lpm (insn, operands, real_l);
}
if (!l) if (!l)
l = &dummy; l = &dummy;
...@@ -2235,6 +2521,12 @@ output_movhi (rtx insn, rtx operands[], int *l) ...@@ -2235,6 +2521,12 @@ output_movhi (rtx insn, rtx operands[], int *l)
rtx src = operands[1]; rtx src = operands[1];
int *real_l = l; int *real_l = l;
if (avr_mem_pgm_p (src)
|| avr_mem_pgm_p (dest))
{
return avr_out_lpm (insn, operands, real_l);
}
if (!l) if (!l)
l = &dummy; l = &dummy;
...@@ -2848,6 +3140,12 @@ output_movsisf (rtx insn, rtx operands[], int *l) ...@@ -2848,6 +3140,12 @@ output_movsisf (rtx insn, rtx operands[], int *l)
rtx src = operands[1]; rtx src = operands[1];
int *real_l = l; int *real_l = l;
if (avr_mem_pgm_p (src)
|| avr_mem_pgm_p (dest))
{
return avr_out_lpm (insn, operands, real_l);
}
if (!l) if (!l)
l = &dummy; l = &dummy;
...@@ -3167,6 +3465,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) ...@@ -3167,6 +3465,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen)
rtx dest = op[0]; rtx dest = op[0];
rtx src = op[1]; rtx src = op[1];
if (avr_mem_pgm_p (src)
|| avr_mem_pgm_p (dest))
{
return avr_out_lpm (insn, op, plen);
}
if (register_operand (dest, VOIDmode)) if (register_operand (dest, VOIDmode))
{ {
if (register_operand (src, VOIDmode)) /* mov r,r */ if (register_operand (src, VOIDmode)) /* mov r,r */
...@@ -3764,9 +4068,10 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], ...@@ -3764,9 +4068,10 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
if (len) if (len)
*len = 1; *len = 1;
if (GET_CODE (operands[2]) == CONST_INT) if (CONST_INT_P (operands[2]))
{ {
int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL
&& REG_P (operands[3]));
int count = INTVAL (operands[2]); int count = INTVAL (operands[2]);
int max_len = 10; /* If larger than this, always use a loop. */ int max_len = 10; /* If larger than this, always use a loop. */
...@@ -3819,8 +4124,7 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], ...@@ -3819,8 +4124,7 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
/* No scratch register available, use one from LD_REGS (saved in /* No scratch register available, use one from LD_REGS (saved in
__tmp_reg__) that doesn't overlap with registers to shift. */ __tmp_reg__) that doesn't overlap with registers to shift. */
op[3] = gen_rtx_REG (QImode, op[3] = all_regs_rtx[((REGNO (operands[0]) - 1) & 15) + 16];
((true_regnum (operands[0]) - 1) & 15) + 16);
op[4] = tmp_reg_rtx; op[4] = tmp_reg_rtx;
saved_in_tmp = 1; saved_in_tmp = 1;
...@@ -6208,8 +6512,15 @@ avr_attribute_table[] = ...@@ -6208,8 +6512,15 @@ avr_attribute_table[] =
{ NULL, 0, 0, false, false, false, NULL, false } { NULL, 0, 0, false, false, false, NULL, false }
}; };
/* Look for attribute `progmem' in DECL
if found return 1, otherwise 0. */ /* Look if DECL shall be placed in program memory space by
means of attribute `progmem' or some address-space qualifier.
Return non-zero if DECL is data that must end up in Flash and
zero if the data lives in RAM (.bss, .data, .rodata, ...).
Return 1 if DECL is located in 16-bit flash address-space
Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES
Return 0 otherwise */
int int
avr_progmem_p (tree decl, tree attributes) avr_progmem_p (tree decl, tree attributes)
...@@ -6219,11 +6530,15 @@ avr_progmem_p (tree decl, tree attributes) ...@@ -6219,11 +6530,15 @@ avr_progmem_p (tree decl, tree attributes)
if (TREE_CODE (decl) != VAR_DECL) if (TREE_CODE (decl) != VAR_DECL)
return 0; return 0;
if (avr_decl_pgm_p (decl))
return 1;
if (NULL_TREE if (NULL_TREE
!= lookup_attribute ("progmem", attributes)) != lookup_attribute ("progmem", attributes))
return 1; return -1;
a=decl; a = decl;
do do
a = TREE_TYPE(a); a = TREE_TYPE(a);
while (TREE_CODE (a) == ARRAY_TYPE); while (TREE_CODE (a) == ARRAY_TYPE);
...@@ -6232,16 +6547,123 @@ avr_progmem_p (tree decl, tree attributes) ...@@ -6232,16 +6547,123 @@ avr_progmem_p (tree decl, tree attributes)
return 0; return 0;
if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a))) if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a)))
return 1; return -1;
return 0; return 0;
} }
/* Scan type TYP for pointer references to address space ASn.
Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting
the AS are also declared to be CONST.
Otherwise, return the respective addres space, i.e. a value != 0. */
static addr_space_t
avr_nonconst_pointer_addrspace (tree typ)
{
while (ARRAY_TYPE == TREE_CODE (typ))
typ = TREE_TYPE (typ);
if (POINTER_TYPE_P (typ))
{
tree target = TREE_TYPE (typ);
/* Pointer to function: Test the function's return type. */
if (FUNCTION_TYPE == TREE_CODE (target))
return avr_nonconst_pointer_addrspace (TREE_TYPE (target));
/* "Ordinary" pointers... */
while (TREE_CODE (target) == ARRAY_TYPE)
target = TREE_TYPE (target);
if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
&& !TYPE_READONLY (target))
{
/* Pointers to non-generic address space must be const. */
return TYPE_ADDR_SPACE (target);
}
/* Scan pointer's target type. */
return avr_nonconst_pointer_addrspace (target);
}
return ADDR_SPACE_GENERIC;
}
/* Sanity check NODE so that all pointers targeting address space AS1
go along with CONST qualifier. Writing to this address space should
be detected and complained about as early as possible. */
static bool
avr_pgm_check_var_decl (tree node)
{
const char *reason = NULL;
addr_space_t as = ADDR_SPACE_GENERIC;
gcc_assert (as == 0);
if (avr_log.progmem)
avr_edump ("%?: %t\n", node);
switch (TREE_CODE (node))
{
default:
break;
case VAR_DECL:
if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
reason = "variable";
break;
case PARM_DECL:
if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
reason = "function parameter";
break;
case FIELD_DECL:
if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
reason = "structure field";
break;
case FUNCTION_DECL:
if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))),
as)
reason = "return type of function";
break;
case POINTER_TYPE:
if (as = avr_nonconst_pointer_addrspace (node), as)
reason = "pointer";
break;
}
if (reason)
{
if (TYPE_P (node))
error ("pointer targeting address space %qs must be const in %qT",
c_addr_space_name (as), node);
else
error ("pointer targeting address space %qs must be const in %s %q+D",
c_addr_space_name (as), reason, node);
}
return reason == NULL;
}
/* Add the section attribute if the variable is in progmem. */ /* Add the section attribute if the variable is in progmem. */
static void static void
avr_insert_attributes (tree node, tree *attributes) avr_insert_attributes (tree node, tree *attributes)
{ {
avr_pgm_check_var_decl (node);
if (TREE_CODE (node) == VAR_DECL if (TREE_CODE (node) == VAR_DECL
&& (TREE_STATIC (node) || DECL_EXTERNAL (node)) && (TREE_STATIC (node) || DECL_EXTERNAL (node))
&& avr_progmem_p (node, *attributes)) && avr_progmem_p (node, *attributes))
...@@ -6258,11 +6680,20 @@ avr_insert_attributes (tree node, tree *attributes) ...@@ -6258,11 +6680,20 @@ avr_insert_attributes (tree node, tree *attributes)
if (error_mark_node == node0) if (error_mark_node == node0)
return; return;
if (!TYPE_READONLY (node0)) if (!TYPE_READONLY (node0)
&& !TREE_READONLY (node))
{ {
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node));
const char *reason = "__attribute__((progmem))";
if (!ADDR_SPACE_GENERIC_P (as))
reason = c_addr_space_name (as);
if (avr_log.progmem)
avr_edump ("\n%?: %t\n%t\n", node, node0);
error ("variable %q+D must be const in order to be put into" error ("variable %q+D must be const in order to be put into"
" read-only section by means of %<__attribute__((progmem))%>", " read-only section by means of %qs", node, reason);
node);
} }
} }
} }
...@@ -6462,6 +6893,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc) ...@@ -6462,6 +6893,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
&& avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
{ {
flags &= ~SECTION_WRITE; flags &= ~SECTION_WRITE;
flags &= ~SECTION_BSS;
flags |= AVR_SECTION_PROGMEM; flags |= AVR_SECTION_PROGMEM;
} }
...@@ -8049,10 +8481,14 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode) ...@@ -8049,10 +8481,14 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
reg_class_t reg_class_t
avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED, avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
addr_space_t as ATTRIBUTE_UNUSED, addr_space_t as, RTX_CODE outer_code,
RTX_CODE outer_code,
RTX_CODE index_code ATTRIBUTE_UNUSED) RTX_CODE index_code ATTRIBUTE_UNUSED)
{ {
if (!ADDR_SPACE_GENERIC_P (as))
{
return POINTER_Z_REGS;
}
if (!avr_strict_X) if (!avr_strict_X)
return reload_completed ? BASE_POINTER_REGS : POINTER_REGS; return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
...@@ -8071,6 +8507,27 @@ avr_regno_mode_code_ok_for_base_p (int regno, ...@@ -8071,6 +8507,27 @@ avr_regno_mode_code_ok_for_base_p (int regno,
{ {
bool ok = false; bool ok = false;
if (!ADDR_SPACE_GENERIC_P (as))
{
if (regno < FIRST_PSEUDO_REGISTER
&& regno == REG_Z)
{
return true;
}
if (reg_renumber)
{
regno = reg_renumber[regno];
if (regno == REG_Z)
{
return true;
}
}
return false;
}
if (regno < FIRST_PSEUDO_REGISTER if (regno < FIRST_PSEUDO_REGISTER
&& (regno == REG_X && (regno == REG_X
|| regno == REG_Y || regno == REG_Y
...@@ -8122,9 +8579,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) ...@@ -8122,9 +8579,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
int clobber_val = 1234; int clobber_val = 1234;
bool cooked_clobber_p = false; bool cooked_clobber_p = false;
bool set_p = false; bool set_p = false;
unsigned int n;
enum machine_mode mode = GET_MODE (dest); enum machine_mode mode = GET_MODE (dest);
int n_bytes = GET_MODE_SIZE (mode); int n, n_bytes = GET_MODE_SIZE (mode);
gcc_assert (REG_P (dest) gcc_assert (REG_P (dest)
&& CONSTANT_P (src)); && CONSTANT_P (src));
...@@ -8138,7 +8594,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) ...@@ -8138,7 +8594,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
if (REGNO (dest) < 16 if (REGNO (dest) < 16
&& REGNO (dest) + GET_MODE_SIZE (mode) > 16) && REGNO (dest) + GET_MODE_SIZE (mode) > 16)
{ {
clobber_reg = gen_rtx_REG (QImode, REGNO (dest) + n_bytes - 1); clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1];
} }
/* We might need a clobber reg but don't have one. Look at the value to /* We might need a clobber reg but don't have one. Look at the value to
...@@ -8155,7 +8611,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) ...@@ -8155,7 +8611,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
That's cheaper than loading from constant pool. */ That's cheaper than loading from constant pool. */
cooked_clobber_p = true; cooked_clobber_p = true;
clobber_reg = gen_rtx_REG (QImode, REG_Z + 1); clobber_reg = all_regs_rtx[REG_Z + 1];
avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1); avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
} }
...@@ -8165,7 +8621,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) ...@@ -8165,7 +8621,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
{ {
int ldreg_p; int ldreg_p;
bool done_byte = false; bool done_byte = false;
unsigned int j; int j;
rtx xop[3]; rtx xop[3];
/* Crop the n-th destination byte. */ /* Crop the n-th destination byte. */
...@@ -8595,6 +9051,150 @@ avr_case_values_threshold (void) ...@@ -8595,6 +9051,150 @@ avr_case_values_threshold (void)
return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17; return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
} }
/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */
static enum machine_mode
avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED)
{
return HImode;
}
/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */
static enum machine_mode
avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
{
return HImode;
}
/* Helper for following function. */
static bool
avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
{
gcc_assert (REG_P (reg));
if (strict)
{
return REGNO (reg) == REG_Z;
}
/* Avoid combine to propagate hard regs. */
if (can_create_pseudo_p()
&& REGNO (reg) < REG_Z)
{
return false;
}
return true;
}
/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */
static bool
avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
bool strict, addr_space_t as)
{
bool ok = false;
switch (as)
{
default:
gcc_unreachable();
case ADDR_SPACE_GENERIC:
return avr_legitimate_address_p (mode, x, strict);
case ADDR_SPACE_PGM:
switch (GET_CODE (x))
{
case REG:
ok = avr_reg_ok_for_pgm_addr (x, strict);
break;
case POST_INC:
ok = (!avr_load_libgcc_p (x)
&& avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict));
break;
default:
break;
}
break; /* PGM */
}
if (avr_log.legitimate_address_p)
{
avr_edump ("\n%?: ret=%b, mode=%m strict=%d "
"reload_completed=%d reload_in_progress=%d %s:",
ok, mode, strict, reload_completed, reload_in_progress,
reg_renumber ? "(reg_renumber)" : "");
if (GET_CODE (x) == PLUS
&& REG_P (XEXP (x, 0))
&& CONST_INT_P (XEXP (x, 1))
&& IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
&& reg_renumber)
{
avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
true_regnum (XEXP (x, 0)));
}
avr_edump ("\n%r\n", x);
}
return ok;
}
/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'. */
static rtx
avr_addr_space_legitimize_address (rtx x, rtx old_x,
enum machine_mode mode, addr_space_t as)
{
if (ADDR_SPACE_GENERIC_P (as))
return avr_legitimize_address (x, old_x, mode);
if (avr_log.legitimize_address)
{
avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x);
}
return old_x;
}
/* Implement `TARGET_ADDR_SPACE_CONVERT'. */
static rtx
avr_addr_space_convert (rtx src, tree type_from, tree type_to)
{
if (avr_log.progmem)
avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
src, type_from, type_to);
return src;
}
/* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */
static bool
avr_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED,
addr_space_t superset ATTRIBUTE_UNUSED)
{
return true;
}
/* Helper for __builtin_avr_delay_cycles */ /* Helper for __builtin_avr_delay_cycles */
static void static void
......
...@@ -391,6 +391,14 @@ typedef struct avr_args { ...@@ -391,6 +391,14 @@ typedef struct avr_args {
#define NO_FUNCTION_CSE #define NO_FUNCTION_CSE
#define ADDR_SPACE_PGM 1
#define REGISTER_TARGET_PRAGMAS() \
do { \
avr_register_target_pragmas(); \
} while (0)
#define TEXT_SECTION_ASM_OP "\t.text" #define TEXT_SECTION_ASM_OP "\t.text"
#define DATA_SECTION_ASM_OP "\t.data" #define DATA_SECTION_ASM_OP "\t.data"
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
(REG_Z 30) (REG_Z 30)
(REG_W 24) (REG_W 24)
(REG_SP 32) (REG_SP 32)
(LPM_REGNO 0) ; implicit target register of LPM
(TMP_REGNO 0) ; temporary register r0 (TMP_REGNO 0) ; temporary register r0
(ZERO_REGNO 1) ; zero register r1 (ZERO_REGNO 1) ; zero register r1
...@@ -315,6 +316,39 @@ ...@@ -315,6 +316,39 @@
"") "")
;;======================================================================== ;;========================================================================
;; Move stuff around
(define_expand "load<mode>_libgcc"
[(set (match_dup 3)
(match_dup 2))
(set (reg:MOVMODE 22)
(match_operand:MOVMODE 1 "memory_operand" ""))
(set (match_operand:MOVMODE 0 "register_operand" "")
(reg:MOVMODE 22))]
"avr_load_libgcc_p (operands[1])"
{
operands[3] = gen_rtx_REG (HImode, REG_Z);
operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX);
operands[1] = replace_equiv_address (operands[1], operands[3]);
set_mem_addr_space (operands[1], ADDR_SPACE_PGM);
})
(define_insn "*load.<mode>.libgcc"
[(set (reg:MOVMODE 22)
(match_operand:MOVMODE 0 "memory_operand" "m,m"))]
"avr_load_libgcc_p (operands[0])
&& REG_P (XEXP (operands[0], 0))
&& REG_Z == REGNO (XEXP (operands[0], 0))"
{
operands[0] = GEN_INT (GET_MODE_SIZE (<MODE>mode));
return "%~call __load_%0";
}
[(set_attr "length" "1,2")
(set_attr "isa" "rjmp,jmp")
(set_attr "cc" "clobber")])
;; General move expanders
;; "movqi" ;; "movqi"
;; "movhi" ;; "movhi"
...@@ -327,12 +361,25 @@ ...@@ -327,12 +361,25 @@
(match_operand:MOVMODE 1 "general_operand" ""))] (match_operand:MOVMODE 1 "general_operand" ""))]
"" ""
{ {
/* One of the ops has to be in a register. */ rtx dest = operands[0];
if (!register_operand (operands[0], <MODE>mode) rtx src = operands[1];
&& !(register_operand (operands[1], <MODE>mode)
|| CONST0_RTX (<MODE>mode) == operands[1])) if (avr_mem_pgm_p (dest))
DONE;
/* One of the operands has to be in a register. */
if (!register_operand (dest, <MODE>mode)
&& !(register_operand (src, <MODE>mode)
|| src == CONST0_RTX (<MODE>mode)))
{
operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
}
if (avr_load_libgcc_p (src))
{ {
operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); /* For the small devices, do loads per libgcc call. */
emit_insn (gen_load<mode>_libgcc (dest, src));
DONE;
} }
}) })
...@@ -346,11 +393,14 @@ ...@@ -346,11 +393,14 @@
;; so this may still be a win for registers live across function calls. ;; so this may still be a win for registers live across function calls.
(define_insn "movqi_insn" (define_insn "movqi_insn"
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r") [(set (match_operand:QI 0 "nonimmediate_operand" "=r ,d,Qm,r ,q,r,*r")
(match_operand:QI 1 "general_operand" "rL,i,rL,Qm,r,q,i"))] (match_operand:QI 1 "nox_general_operand" "rL,i,rL,Qm,r,q,i"))]
"(register_operand (operands[0],QImode) "register_operand (operands[0], QImode)
|| register_operand (operands[1], QImode) || const0_rtx == operands[1])" || register_operand (operands[1], QImode)
"* return output_movqi (insn, operands, NULL);" || const0_rtx == operands[1]"
{
return output_movqi (insn, operands, NULL);
}
[(set_attr "length" "1,1,5,5,1,1,4") [(set_attr "length" "1,1,5,5,1,1,4")
(set_attr "adjust_len" "mov8") (set_attr "adjust_len" "mov8")
(set_attr "cc" "none,none,clobber,clobber,none,none,clobber")]) (set_attr "cc" "none,none,clobber,clobber,none,none,clobber")])
...@@ -425,11 +475,14 @@ ...@@ -425,11 +475,14 @@
(set_attr "cc" "none")]) (set_attr "cc" "none")])
(define_insn "*movhi" (define_insn "*movhi"
[(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m,d,*r,q,r") [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m ,d,*r,q,r")
(match_operand:HI 1 "general_operand" "r,L,m,rL,i,i,r,q"))] (match_operand:HI 1 "nox_general_operand" "r,L,m,rL,i,i ,r,q"))]
"(register_operand (operands[0],HImode) "register_operand (operands[0], HImode)
|| register_operand (operands[1],HImode) || const0_rtx == operands[1])" || register_operand (operands[1], HImode)
"* return output_movhi (insn, operands, NULL);" || const0_rtx == operands[1]"
{
return output_movhi (insn, operands, NULL);
}
[(set_attr "length" "2,2,6,7,2,6,5,2") [(set_attr "length" "2,2,6,7,2,6,5,2")
(set_attr "adjust_len" "mov16") (set_attr "adjust_len" "mov16")
(set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")]) (set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")])
...@@ -462,6 +515,40 @@ ...@@ -462,6 +515,40 @@
operands[5] = gen_rtx_REG (HImode, REGNO (operands[3])); operands[5] = gen_rtx_REG (HImode, REGNO (operands[3]));
}) })
;; For LPM loads from AS1 we split
;; R = *Z
;; to
;; R = *Z++
;; Z = Z - sizeof (R)
;;
;; so that the second instruction can be optimized out.
(define_split ; "split-lpmx"
[(set (match_operand:HISI 0 "register_operand" "")
(match_operand:HISI 1 "memory_operand" ""))]
"reload_completed
&& AVR_HAVE_LPMX"
[(set (match_dup 0)
(match_dup 2))
(set (match_dup 3)
(plus:HI (match_dup 3)
(match_dup 4)))]
{
rtx addr = XEXP (operands[1], 0);
if (!avr_mem_pgm_p (operands[1])
|| !REG_P (addr)
|| reg_overlap_mentioned_p (addr, operands[0]))
{
FAIL;
}
operands[2] = replace_equiv_address (operands[1],
gen_rtx_POST_INC (Pmode, addr));
operands[3] = addr;
operands[4] = gen_int_mode (-GET_MODE_SIZE (<MODE>mode), HImode);
})
;;========================================================================== ;;==========================================================================
;; xpointer move (24 bit) ;; xpointer move (24 bit)
...@@ -492,7 +579,7 @@ ...@@ -492,7 +579,7 @@
(define_insn "*movpsi" (define_insn "*movpsi"
[(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
(match_operand:PSI 1 "general_operand" "r,L,Qm,rL,i ,i"))] (match_operand:PSI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))]
"register_operand (operands[0], PSImode) "register_operand (operands[0], PSImode)
|| register_operand (operands[1], PSImode) || register_operand (operands[1], PSImode)
|| const0_rtx == operands[1]" || const0_rtx == operands[1]"
...@@ -532,10 +619,11 @@ ...@@ -532,10 +619,11 @@
(define_insn "*movsi" (define_insn "*movsi"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
(match_operand:SI 1 "general_operand" "r,L,Qm,rL,i,i"))] (match_operand:SI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))]
"(register_operand (operands[0],SImode) "register_operand (operands[0], SImode)
|| register_operand (operands[1],SImode) || const0_rtx == operands[1])" || register_operand (operands[1], SImode)
|| const0_rtx == operands[1]"
{ {
return output_movsisf (insn, operands, NULL); return output_movsisf (insn, operands, NULL);
} }
...@@ -547,8 +635,8 @@ ...@@ -547,8 +635,8 @@
;; move floating point numbers (32 bit) ;; move floating point numbers (32 bit)
(define_insn "*movsf" (define_insn "*movsf"
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
(match_operand:SF 1 "general_operand" "r,G,Qm,rG,F,F"))] (match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))]
"register_operand (operands[0], SFmode) "register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode) || register_operand (operands[1], SFmode)
|| operands[1] == CONST0_RTX (SFmode)" || operands[1] == CONST0_RTX (SFmode)"
...@@ -599,7 +687,7 @@ ...@@ -599,7 +687,7 @@
enum machine_mode mode; enum machine_mode mode;
rtx label = gen_label_rtx (); rtx label = gen_label_rtx ();
rtx loop_reg; rtx loop_reg;
rtx jump; rtx jump, src;
/* Copy pointers into new psuedos - they will be changed. */ /* Copy pointers into new psuedos - they will be changed. */
rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
...@@ -608,6 +696,9 @@ ...@@ -608,6 +696,9 @@
/* Create rtx for tmp register - we use this as scratch. */ /* Create rtx for tmp register - we use this as scratch. */
rtx tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); rtx tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO);
if (avr_mem_pgm_p (operands[0]))
DONE;
if (GET_CODE (operands[2]) != CONST_INT) if (GET_CODE (operands[2]) != CONST_INT)
FAIL; FAIL;
...@@ -628,7 +719,9 @@ ...@@ -628,7 +719,9 @@
emit_label (label); emit_label (label);
/* Move one byte into scratch and inc pointer. */ /* Move one byte into scratch and inc pointer. */
emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, addr1)); src = gen_rtx_MEM (QImode, addr1);
set_mem_addr_space (src, MEM_ADDR_SPACE (operands[1]));
emit_move_insn (tmp_reg_rtx, src);
emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx)); emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx));
/* Move to mem and inc pointer. */ /* Move to mem and inc pointer. */
...@@ -2868,10 +2961,8 @@ ...@@ -2868,10 +2961,8 @@
(define_expand "ashlqi3" (define_expand "ashlqi3"
[(set (match_operand:QI 0 "register_operand" "") [(set (match_operand:QI 0 "register_operand" "")
(ashift:QI (match_operand:QI 1 "register_operand" "") (ashift:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))] (match_operand:QI 2 "nop_general_operand" "")))])
""
"")
(define_split ; ashlqi3_const4 (define_split ; ashlqi3_const4
[(set (match_operand:QI 0 "d_register_operand" "") [(set (match_operand:QI 0 "d_register_operand" "")
...@@ -2903,21 +2994,25 @@ ...@@ -2903,21 +2994,25 @@
"") "")
(define_insn "*ashlqi3" (define_insn "*ashlqi3"
[(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r")
(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))]
"" ""
"* return ashlqi3_out (insn, operands, NULL);" {
return ashlqi3_out (insn, operands, NULL);
}
[(set_attr "length" "5,0,1,2,4,6,9") [(set_attr "length" "5,0,1,2,4,6,9")
(set_attr "adjust_len" "ashlqi") (set_attr "adjust_len" "ashlqi")
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
(define_insn "ashlhi3" (define_insn "ashlhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r")
(ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") (ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return ashlhi3_out (insn, operands, NULL);" {
return ashlhi3_out (insn, operands, NULL);
}
[(set_attr "length" "6,0,2,2,4,10,10") [(set_attr "length" "6,0,2,2,4,10,10")
(set_attr "adjust_len" "ashlhi") (set_attr "adjust_len" "ashlhi")
(set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
...@@ -3005,11 +3100,13 @@ ...@@ -3005,11 +3100,13 @@
(define_insn "ashlsi3" (define_insn "ashlsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r")
(ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return ashlsi3_out (insn, operands, NULL);" {
return ashlsi3_out (insn, operands, NULL);
}
[(set_attr "length" "8,0,4,4,8,10,12") [(set_attr "length" "8,0,4,4,8,10,12")
(set_attr "adjust_len" "ashlsi") (set_attr "adjust_len" "ashlsi")
(set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
...@@ -3063,11 +3160,13 @@ ...@@ -3063,11 +3160,13 @@
(define_insn "*ashlhi3_const" (define_insn "*ashlhi3_const"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
(ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") (ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return ashlhi3_out (insn, operands, NULL);" {
return ashlhi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,2,2,4,10") [(set_attr "length" "0,2,2,4,10")
(set_attr "adjust_len" "ashlhi") (set_attr "adjust_len" "ashlhi")
(set_attr "cc" "none,set_n,clobber,set_n,clobber")]) (set_attr "cc" "none,set_n,clobber,set_n,clobber")])
...@@ -3084,11 +3183,13 @@ ...@@ -3084,11 +3183,13 @@
(define_insn "*ashlsi3_const" (define_insn "*ashlsi3_const"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
(ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0") (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
(clobber (match_scratch:QI 3 "=X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return ashlsi3_out (insn, operands, NULL);" {
return ashlsi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,4,4,10") [(set_attr "length" "0,4,4,10")
(set_attr "adjust_len" "ashlsi") (set_attr "adjust_len" "ashlsi")
(set_attr "cc" "none,set_n,clobber,clobber")]) (set_attr "cc" "none,set_n,clobber,clobber")])
...@@ -3109,21 +3210,25 @@ ...@@ -3109,21 +3210,25 @@
;; arithmetic shift right ;; arithmetic shift right
(define_insn "ashrqi3" (define_insn "ashrqi3"
[(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r") [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r")
(ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0") (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0")
(match_operand:QI 2 "general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))]
"" ""
"* return ashrqi3_out (insn, operands, NULL);" {
return ashrqi3_out (insn, operands, NULL);
}
[(set_attr "length" "5,0,1,2,5,4,9") [(set_attr "length" "5,0,1,2,5,4,9")
(set_attr "adjust_len" "ashrqi") (set_attr "adjust_len" "ashrqi")
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")]) (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")])
(define_insn "ashrhi3" (define_insn "ashrhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return ashrhi3_out (insn, operands, NULL);" {
return ashrhi3_out (insn, operands, NULL);
}
[(set_attr "length" "6,0,2,4,4,10,10") [(set_attr "length" "6,0,2,4,4,10,10")
(set_attr "adjust_len" "ashrhi") (set_attr "adjust_len" "ashrhi")
(set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
...@@ -3141,11 +3246,13 @@ ...@@ -3141,11 +3246,13 @@
(set_attr "cc" "clobber")]) (set_attr "cc" "clobber")])
(define_insn "ashrsi3" (define_insn "ashrsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r")
(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return ashrsi3_out (insn, operands, NULL);" {
return ashrsi3_out (insn, operands, NULL);
}
[(set_attr "length" "8,0,4,6,8,10,12") [(set_attr "length" "8,0,4,6,8,10,12")
(set_attr "adjust_len" "ashrsi") (set_attr "adjust_len" "ashrsi")
(set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
...@@ -3164,11 +3271,13 @@ ...@@ -3164,11 +3271,13 @@
(define_insn "*ashrhi3_const" (define_insn "*ashrhi3_const"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return ashrhi3_out (insn, operands, NULL);" {
return ashrhi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,2,4,4,10") [(set_attr "length" "0,2,4,4,10")
(set_attr "adjust_len" "ashrhi") (set_attr "adjust_len" "ashrhi")
(set_attr "cc" "none,clobber,set_n,clobber,clobber")]) (set_attr "cc" "none,clobber,set_n,clobber,clobber")])
...@@ -3185,11 +3294,13 @@ ...@@ -3185,11 +3294,13 @@
(define_insn "*ashrsi3_const" (define_insn "*ashrsi3_const"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
(clobber (match_scratch:QI 3 "=X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return ashrsi3_out (insn, operands, NULL);" {
return ashrsi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,4,4,10") [(set_attr "length" "0,4,4,10")
(set_attr "adjust_len" "ashrsi") (set_attr "adjust_len" "ashrsi")
(set_attr "cc" "none,clobber,set_n,clobber")]) (set_attr "cc" "none,clobber,set_n,clobber")])
...@@ -3198,11 +3309,9 @@ ...@@ -3198,11 +3309,9 @@
;; logical shift right ;; logical shift right
(define_expand "lshrqi3" (define_expand "lshrqi3"
[(set (match_operand:QI 0 "register_operand" "") [(set (match_operand:QI 0 "register_operand" "")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "") (lshiftrt:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))] (match_operand:QI 2 "nop_general_operand" "")))])
""
"")
(define_split ; lshrqi3_const4 (define_split ; lshrqi3_const4
[(set (match_operand:QI 0 "d_register_operand" "") [(set (match_operand:QI 0 "d_register_operand" "")
...@@ -3234,21 +3343,25 @@ ...@@ -3234,21 +3343,25 @@
"") "")
(define_insn "*lshrqi3" (define_insn "*lshrqi3"
[(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))]
"" ""
"* return lshrqi3_out (insn, operands, NULL);" {
return lshrqi3_out (insn, operands, NULL);
}
[(set_attr "length" "5,0,1,2,4,6,9") [(set_attr "length" "5,0,1,2,4,6,9")
(set_attr "adjust_len" "lshrqi") (set_attr "adjust_len" "lshrqi")
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
(define_insn "lshrhi3" (define_insn "lshrhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r")
(lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return lshrhi3_out (insn, operands, NULL);" {
return lshrhi3_out (insn, operands, NULL);
}
[(set_attr "length" "6,0,2,2,4,10,10") [(set_attr "length" "6,0,2,2,4,10,10")
(set_attr "adjust_len" "lshrhi") (set_attr "adjust_len" "lshrhi")
(set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
...@@ -3266,11 +3379,13 @@ ...@@ -3266,11 +3379,13 @@
(set_attr "cc" "clobber")]) (set_attr "cc" "clobber")])
(define_insn "lshrsi3" (define_insn "lshrsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r")
(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
"" ""
"* return lshrsi3_out (insn, operands, NULL);" {
return lshrsi3_out (insn, operands, NULL);
}
[(set_attr "length" "8,0,4,4,8,10,12") [(set_attr "length" "8,0,4,4,8,10,12")
(set_attr "adjust_len" "lshrsi") (set_attr "adjust_len" "lshrsi")
(set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
...@@ -3324,11 +3439,13 @@ ...@@ -3324,11 +3439,13 @@
(define_insn "*lshrhi3_const" (define_insn "*lshrhi3_const"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
(lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return lshrhi3_out (insn, operands, NULL);" {
return lshrhi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,2,2,4,10") [(set_attr "length" "0,2,2,4,10")
(set_attr "adjust_len" "lshrhi") (set_attr "adjust_len" "lshrhi")
(set_attr "cc" "none,clobber,clobber,clobber,clobber")]) (set_attr "cc" "none,clobber,clobber,clobber,clobber")])
...@@ -3345,11 +3462,13 @@ ...@@ -3345,11 +3462,13 @@
(define_insn "*lshrsi3_const" (define_insn "*lshrsi3_const"
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r") [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
(match_operand:QI 2 "const_int_operand" "L,P,O,n"))) (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
(clobber (match_scratch:QI 3 "=X,X,X,&d"))] (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
"reload_completed" "reload_completed"
"* return lshrsi3_out (insn, operands, NULL);" {
return lshrsi3_out (insn, operands, NULL);
}
[(set_attr "length" "0,4,4,10") [(set_attr "length" "0,4,4,10")
(set_attr "adjust_len" "lshrsi") (set_attr "adjust_len" "lshrsi")
(set_attr "cc" "none,clobber,clobber,clobber")]) (set_attr "cc" "none,clobber,clobber,clobber")])
...@@ -3615,6 +3734,27 @@ ...@@ -3615,6 +3734,27 @@
operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
}) })
(define_insn_and_split "n_extendhipsi2"
[(set (match_operand:PSI 0 "register_operand" "=r,r,d,r")
(lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n")
(match_operand:HI 2 "register_operand" "r,r,r,r")))
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
""
"#"
"reload_completed"
[(set (match_dup 4) (match_dup 2))
(set (match_dup 3) (match_dup 6))
; no-op move in the case where no scratch is needed
(set (match_dup 5) (match_dup 3))]
{
operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0);
operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
operands[6] = operands[1];
if (GET_CODE (operands[3]) == SCRATCH)
operands[3] = operands[5];
})
(define_insn_and_split "zero_extendhisi2" (define_insn_and_split "zero_extendhisi2"
[(set (match_operand:SI 0 "register_operand" "=r") [(set (match_operand:SI 0 "register_operand" "=r")
(zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))] (zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))]
......
...@@ -57,6 +57,17 @@ ...@@ -57,6 +57,17 @@
(and (match_code "const_int") (and (match_code "const_int")
(match_test "IN_RANGE((INTVAL (op)), 0x20, (0x60 - GET_MODE_SIZE(mode)))"))) (match_test "IN_RANGE((INTVAL (op)), 0x20, (0x60 - GET_MODE_SIZE(mode)))")))
;; Return 1 if OP is a general operand not in program memory
(define_predicate "nop_general_operand"
(and (match_operand 0 "general_operand")
(match_test "!avr_mem_pgm_p (op)")))
;; Return 1 if OP is an "ordinary" general operand, i.e. a general
;; operand whose load is not handled by a libgcc call.
(define_predicate "nox_general_operand"
(and (match_operand 0 "general_operand")
(match_test "!avr_load_libgcc_p (op)")))
;; Return 1 if OP is the zero constant for MODE. ;; Return 1 if OP is the zero constant for MODE.
(define_predicate "const0_operand" (define_predicate "const0_operand"
(and (match_code "const_int,const_double") (and (match_code "const_int,const_double")
......
2011-11-15 Georg-Johann Lay <avr@gjlay.de>
PR target/49868
* config/avr/t-avr (LIB1ASMFUNCS): Add _load_3, _load_4.
* config/avr/lib1funcs.S (__load_3, __load_4, __xload_2): New functions.
2011-11-13 John David Anglin <dave.anglin@nrc-cnrc.gc.ca> 2011-11-13 John David Anglin <dave.anglin@nrc-cnrc.gc.ca>
* config.host (hppa*64*-*-hpux11*): Remove pa/t-stublib64 from * config.host (hppa*64*-*-hpux11*): Remove pa/t-stublib64 from
......
...@@ -1181,6 +1181,52 @@ DEFUN __tablejump_elpm__ ...@@ -1181,6 +1181,52 @@ DEFUN __tablejump_elpm__
ENDF __tablejump_elpm__ ENDF __tablejump_elpm__
#endif /* defined (L_tablejump_elpm) */ #endif /* defined (L_tablejump_elpm) */
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loading n bytes from Flash; n = 3,4
;; R22... = Flash[Z]
;; Clobbers: __tmp_reg__
#if (defined (L_load_3) \
|| defined (L_load_4)) \
&& !defined (__AVR_HAVE_LPMX__)
;; Destination
#define D0 22
#define D1 D0+1
#define D2 D0+2
#define D3 D0+3
.macro .load dest, n
lpm
mov \dest, r0
.if \dest != D0+\n-1
adiw r30, 1
.else
sbiw r30, \n-1
.endif
.endm
#if defined (L_load_3)
DEFUN __load_3
push D3
XCALL __load_4
pop D3
ret
ENDF __load_3
#endif /* L_load_3 */
#if defined (L_load_4)
DEFUN __load_4
.load D0, 4
.load D1, 4
.load D2, 4
.load D3, 4
ret
ENDF __load_4
#endif /* L_load_4 */
#endif /* L_load_3 || L_load_3 */
.section .text.libgcc.builtins, "ax", @progbits .section .text.libgcc.builtins, "ax", @progbits
......
...@@ -21,6 +21,7 @@ LIB1ASMFUNCS = \ ...@@ -21,6 +21,7 @@ LIB1ASMFUNCS = \
_cleanup \ _cleanup \
_tablejump \ _tablejump \
_tablejump_elpm \ _tablejump_elpm \
_load_3 _load_4 \
_copy_data \ _copy_data \
_clear_bss \ _clear_bss \
_ctors \ _ctors \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment