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
This commit is contained in:
parent
d2d668fbbb
commit
7c20948114
|
@ -1,3 +1,72 @@
|
|||
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>
|
||||
|
||||
* ipa-cp.c (ipa_value_from_jfunc): Make global.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Not included in avr.c since this requires C front end. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
|
@ -27,8 +28,17 @@
|
|||
#include "cpplib.h"
|
||||
#include "tree.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. */
|
||||
|
||||
|
@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile)
|
|||
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
|
||||
easily query if or if not a specific builtin
|
||||
is available. */
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
F: caller (via __FUNCTION__)
|
||||
P: Pass name and number
|
||||
?: Print caller, current function and pass info
|
||||
!: Ditto, but only print if in a pass with static pass number,
|
||||
else return.
|
||||
|
||||
== same as printf ==
|
||||
|
||||
|
@ -318,12 +320,13 @@ avr_log_set_avr_log (void)
|
|||
|| NULL != strstr (str, "," #S ",") \
|
||||
|| 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 (legitimize_address);
|
||||
SET_DUMP_DETAIL (legitimize_reload_address);
|
||||
SET_DUMP_DETAIL (constraints);
|
||||
SET_DUMP_DETAIL (address_cost);
|
||||
SET_DUMP_DETAIL (progmem);
|
||||
SET_DUMP_DETAIL (rtx_costs);
|
||||
|
||||
#undef SET_DUMP_DETAIL
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to);
|
|||
extern int avr_simple_epilogue (void);
|
||||
extern int avr_hard_regno_rename_ok (unsigned int, unsigned int);
|
||||
extern rtx avr_return_addr_rtx (int count, rtx tem);
|
||||
extern void avr_register_target_pragmas (void);
|
||||
extern bool avr_accumulate_outgoing_args (void);
|
||||
|
||||
#ifdef TREE_CODE
|
||||
|
@ -47,7 +48,6 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
|
|||
#endif /* TREE_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_movhi (rtx insn, rtx operands[], 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
|
|||
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_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 */
|
||||
|
||||
#ifdef REAL_VALUE_TYPE
|
||||
|
@ -139,12 +141,13 @@ extern void avr_log_set_avr_log (void);
|
|||
|
||||
typedef struct
|
||||
{
|
||||
unsigned rtx_costs :1;
|
||||
unsigned address_cost :1;
|
||||
unsigned constraints :1;
|
||||
unsigned legitimate_address_p :1;
|
||||
unsigned legitimize_address :1;
|
||||
unsigned legitimize_reload_address :1;
|
||||
unsigned constraints :1;
|
||||
unsigned address_cost :1;
|
||||
unsigned progmem :1;
|
||||
unsigned rtx_costs :1;
|
||||
} avr_log_t;
|
||||
|
||||
extern avr_log_t avr_log;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "tree.h"
|
||||
#include "output.h"
|
||||
#include "expr.h"
|
||||
#include "c-family/c-common.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "obstack.h"
|
||||
#include "function.h"
|
||||
|
@ -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. */
|
||||
#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)) */
|
||||
static GTY(()) rtx tmp_reg_rtx;
|
||||
|
||||
/* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
|
||||
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"} */
|
||||
static const char *const avr_regnames[] = REGISTER_NAMES;
|
||||
|
||||
|
@ -172,9 +182,6 @@ bool avr_need_copy_data_p = false;
|
|||
#undef TARGET_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
|
||||
#define TARGET_RETURN_IN_MEMORY avr_return_in_memory
|
||||
|
||||
|
@ -189,9 +196,6 @@ bool avr_need_copy_data_p = false;
|
|||
#undef TARGET_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
|
||||
#define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p
|
||||
#undef TARGET_CAN_ELIMINATE
|
||||
|
@ -221,6 +225,24 @@ bool avr_need_copy_data_p = false;
|
|||
#undef TARGET_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.
|
||||
|
@ -295,6 +317,8 @@ avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask)
|
|||
static void
|
||||
avr_option_override (void)
|
||||
{
|
||||
int regno;
|
||||
|
||||
flag_delete_null_pointer_checks = 0;
|
||||
|
||||
/* caller-save.c looks for call-clobbered hard registers that are assigned
|
||||
|
@ -323,8 +347,14 @@ avr_option_override (void)
|
|||
avr_current_arch = &avr_arch_types[avr_current_device->arch];
|
||||
avr_extra_arch_macro = avr_current_device->macro;
|
||||
|
||||
tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO);
|
||||
zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
|
||||
for (regno = 0; regno < 32; 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;
|
||||
|
||||
|
@ -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
|
||||
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)
|
|||
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
|
||||
memory address for an operand of mode MODE */
|
||||
|
||||
|
@ -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
|
||||
************************************************************************/
|
||||
|
||||
/* 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 *
|
||||
output_movqi (rtx insn, rtx operands[], int *l)
|
||||
{
|
||||
|
@ -2185,6 +2465,12 @@ output_movqi (rtx insn, rtx operands[], int *l)
|
|||
rtx src = operands[1];
|
||||
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)
|
||||
l = &dummy;
|
||||
|
||||
|
@ -2235,6 +2521,12 @@ output_movhi (rtx insn, rtx operands[], int *l)
|
|||
rtx src = operands[1];
|
||||
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)
|
||||
l = &dummy;
|
||||
|
||||
|
@ -2848,6 +3140,12 @@ output_movsisf (rtx insn, rtx operands[], int *l)
|
|||
rtx src = operands[1];
|
||||
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)
|
||||
l = &dummy;
|
||||
|
||||
|
@ -3167,6 +3465,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen)
|
|||
rtx dest = op[0];
|
||||
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 (src, VOIDmode)) /* mov r,r */
|
||||
|
@ -3764,9 +4068,10 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
|
|||
if (len)
|
||||
*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 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[],
|
|||
/* No scratch register available, use one from LD_REGS (saved in
|
||||
__tmp_reg__) that doesn't overlap with registers to shift. */
|
||||
|
||||
op[3] = gen_rtx_REG (QImode,
|
||||
((true_regnum (operands[0]) - 1) & 15) + 16);
|
||||
op[3] = all_regs_rtx[((REGNO (operands[0]) - 1) & 15) + 16];
|
||||
op[4] = tmp_reg_rtx;
|
||||
saved_in_tmp = 1;
|
||||
|
||||
|
@ -6208,8 +6512,15 @@ avr_attribute_table[] =
|
|||
{ 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
|
||||
avr_progmem_p (tree decl, tree attributes)
|
||||
|
@ -6219,11 +6530,15 @@ avr_progmem_p (tree decl, tree attributes)
|
|||
if (TREE_CODE (decl) != VAR_DECL)
|
||||
return 0;
|
||||
|
||||
if (NULL_TREE
|
||||
!= lookup_attribute ("progmem", attributes))
|
||||
if (avr_decl_pgm_p (decl))
|
||||
return 1;
|
||||
|
||||
a=decl;
|
||||
if (NULL_TREE
|
||||
!= lookup_attribute ("progmem", attributes))
|
||||
return -1;
|
||||
|
||||
a = decl;
|
||||
|
||||
do
|
||||
a = TREE_TYPE(a);
|
||||
while (TREE_CODE (a) == ARRAY_TYPE);
|
||||
|
@ -6232,16 +6547,123 @@ avr_progmem_p (tree decl, tree attributes)
|
|||
return 0;
|
||||
|
||||
if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a)))
|
||||
return 1;
|
||||
return -1;
|
||||
|
||||
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. */
|
||||
|
||||
static void
|
||||
avr_insert_attributes (tree node, tree *attributes)
|
||||
{
|
||||
avr_pgm_check_var_decl (node);
|
||||
|
||||
if (TREE_CODE (node) == VAR_DECL
|
||||
&& (TREE_STATIC (node) || DECL_EXTERNAL (node))
|
||||
&& avr_progmem_p (node, *attributes))
|
||||
|
@ -6258,11 +6680,20 @@ avr_insert_attributes (tree node, tree *attributes)
|
|||
if (error_mark_node == node0)
|
||||
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"
|
||||
" read-only section by means of %<__attribute__((progmem))%>",
|
||||
node);
|
||||
" read-only section by means of %qs", node, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6462,6 +6893,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
|
|||
&& avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
|
||||
{
|
||||
flags &= ~SECTION_WRITE;
|
||||
flags &= ~SECTION_BSS;
|
||||
flags |= AVR_SECTION_PROGMEM;
|
||||
}
|
||||
|
||||
|
@ -8049,10 +8481,14 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
|
|||
|
||||
reg_class_t
|
||||
avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
|
||||
addr_space_t as ATTRIBUTE_UNUSED,
|
||||
RTX_CODE outer_code,
|
||||
addr_space_t as, RTX_CODE outer_code,
|
||||
RTX_CODE index_code ATTRIBUTE_UNUSED)
|
||||
{
|
||||
if (!ADDR_SPACE_GENERIC_P (as))
|
||||
{
|
||||
return POINTER_Z_REGS;
|
||||
}
|
||||
|
||||
if (!avr_strict_X)
|
||||
return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
|
||||
|
||||
|
@ -8071,6 +8507,27 @@ avr_regno_mode_code_ok_for_base_p (int regno,
|
|||
{
|
||||
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
|
||||
&& (regno == REG_X
|
||||
|| regno == REG_Y
|
||||
|
@ -8122,9 +8579,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
|
|||
int clobber_val = 1234;
|
||||
bool cooked_clobber_p = false;
|
||||
bool set_p = false;
|
||||
unsigned int n;
|
||||
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)
|
||||
&& CONSTANT_P (src));
|
||||
|
@ -8138,7 +8594,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
|
|||
if (REGNO (dest) < 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
|
||||
|
@ -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. */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -8165,7 +8621,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
|
|||
{
|
||||
int ldreg_p;
|
||||
bool done_byte = false;
|
||||
unsigned int j;
|
||||
int j;
|
||||
rtx xop[3];
|
||||
|
||||
/* Crop the n-th destination byte. */
|
||||
|
@ -8595,6 +9051,150 @@ avr_case_values_threshold (void)
|
|||
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 */
|
||||
|
||||
static void
|
||||
|
|
|
@ -391,6 +391,14 @@ typedef struct avr_args {
|
|||
|
||||
#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 DATA_SECTION_ASM_OP "\t.data"
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
(REG_Z 30)
|
||||
(REG_W 24)
|
||||
(REG_SP 32)
|
||||
(LPM_REGNO 0) ; implicit target register of LPM
|
||||
(TMP_REGNO 0) ; temporary register r0
|
||||
(ZERO_REGNO 1) ; zero register r1
|
||||
|
||||
|
@ -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"
|
||||
;; "movhi"
|
||||
|
@ -327,12 +361,25 @@
|
|||
(match_operand:MOVMODE 1 "general_operand" ""))]
|
||||
""
|
||||
{
|
||||
/* One of the ops has to be in a register. */
|
||||
if (!register_operand (operands[0], <MODE>mode)
|
||||
&& !(register_operand (operands[1], <MODE>mode)
|
||||
|| CONST0_RTX (<MODE>mode) == operands[1]))
|
||||
rtx dest = operands[0];
|
||||
rtx src = 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] = copy_to_mode_reg (<MODE>mode, operands[1]);
|
||||
operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
|
||||
}
|
||||
|
||||
if (avr_load_libgcc_p (src))
|
||||
{
|
||||
/* For the small devices, do loads per libgcc call. */
|
||||
emit_insn (gen_load<mode>_libgcc (dest, src));
|
||||
DONE;
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -346,11 +393,14 @@
|
|||
;; so this may still be a win for registers live across function calls.
|
||||
|
||||
(define_insn "movqi_insn"
|
||||
[(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"))]
|
||||
"(register_operand (operands[0],QImode)
|
||||
|| register_operand (operands[1], QImode) || const0_rtx == operands[1])"
|
||||
"* return output_movqi (insn, operands, NULL);"
|
||||
[(set (match_operand:QI 0 "nonimmediate_operand" "=r ,d,Qm,r ,q,r,*r")
|
||||
(match_operand:QI 1 "nox_general_operand" "rL,i,rL,Qm,r,q,i"))]
|
||||
"register_operand (operands[0], QImode)
|
||||
|| register_operand (operands[1], QImode)
|
||||
|| const0_rtx == operands[1]"
|
||||
{
|
||||
return output_movqi (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "1,1,5,5,1,1,4")
|
||||
(set_attr "adjust_len" "mov8")
|
||||
(set_attr "cc" "none,none,clobber,clobber,none,none,clobber")])
|
||||
|
@ -425,11 +475,14 @@
|
|||
(set_attr "cc" "none")])
|
||||
|
||||
(define_insn "*movhi"
|
||||
[(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"))]
|
||||
"(register_operand (operands[0],HImode)
|
||||
|| register_operand (operands[1],HImode) || const0_rtx == operands[1])"
|
||||
"* return output_movhi (insn, operands, NULL);"
|
||||
[(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m ,d,*r,q,r")
|
||||
(match_operand:HI 1 "nox_general_operand" "r,L,m,rL,i,i ,r,q"))]
|
||||
"register_operand (operands[0], HImode)
|
||||
|| register_operand (operands[1], HImode)
|
||||
|| const0_rtx == operands[1]"
|
||||
{
|
||||
return output_movhi (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "2,2,6,7,2,6,5,2")
|
||||
(set_attr "adjust_len" "mov16")
|
||||
(set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")])
|
||||
|
@ -462,6 +515,40 @@
|
|||
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)
|
||||
|
||||
|
@ -492,7 +579,7 @@
|
|||
|
||||
(define_insn "*movpsi"
|
||||
[(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[1], PSImode)
|
||||
|| const0_rtx == operands[1]"
|
||||
|
@ -532,10 +619,11 @@
|
|||
|
||||
|
||||
(define_insn "*movsi"
|
||||
[(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"))]
|
||||
"(register_operand (operands[0],SImode)
|
||||
|| register_operand (operands[1],SImode) || const0_rtx == operands[1])"
|
||||
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
|
||||
(match_operand:SI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))]
|
||||
"register_operand (operands[0], SImode)
|
||||
|| register_operand (operands[1], SImode)
|
||||
|| const0_rtx == operands[1]"
|
||||
{
|
||||
return output_movsisf (insn, operands, NULL);
|
||||
}
|
||||
|
@ -547,8 +635,8 @@
|
|||
;; move floating point numbers (32 bit)
|
||||
|
||||
(define_insn "*movsf"
|
||||
[(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"))]
|
||||
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
|
||||
(match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))]
|
||||
"register_operand (operands[0], SFmode)
|
||||
|| register_operand (operands[1], SFmode)
|
||||
|| operands[1] == CONST0_RTX (SFmode)"
|
||||
|
@ -599,7 +687,7 @@
|
|||
enum machine_mode mode;
|
||||
rtx label = gen_label_rtx ();
|
||||
rtx loop_reg;
|
||||
rtx jump;
|
||||
rtx jump, src;
|
||||
|
||||
/* Copy pointers into new psuedos - they will be changed. */
|
||||
rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
|
||||
|
@ -608,6 +696,9 @@
|
|||
/* Create rtx for tmp register - we use this as scratch. */
|
||||
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)
|
||||
FAIL;
|
||||
|
||||
|
@ -628,7 +719,9 @@
|
|||
emit_label (label);
|
||||
|
||||
/* 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));
|
||||
|
||||
/* Move to mem and inc pointer. */
|
||||
|
@ -2868,10 +2961,8 @@
|
|||
|
||||
(define_expand "ashlqi3"
|
||||
[(set (match_operand:QI 0 "register_operand" "")
|
||||
(ashift:QI (match_operand:QI 1 "register_operand" "")
|
||||
(match_operand:QI 2 "general_operand" "")))]
|
||||
""
|
||||
"")
|
||||
(ashift:QI (match_operand:QI 1 "register_operand" "")
|
||||
(match_operand:QI 2 "nop_general_operand" "")))])
|
||||
|
||||
(define_split ; ashlqi3_const4
|
||||
[(set (match_operand:QI 0 "d_register_operand" "")
|
||||
|
@ -2903,21 +2994,25 @@
|
|||
"")
|
||||
|
||||
(define_insn "*ashlqi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashlqi")
|
||||
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
|
||||
|
||||
(define_insn "ashlhi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashlhi")
|
||||
(set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
|
||||
|
@ -3005,11 +3100,13 @@
|
|||
|
||||
|
||||
(define_insn "ashlsi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashlsi")
|
||||
(set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
|
||||
|
@ -3063,11 +3160,13 @@
|
|||
|
||||
(define_insn "*ashlhi3_const"
|
||||
[(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")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
(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")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return ashlhi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return ashlhi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,2,2,4,10")
|
||||
(set_attr "adjust_len" "ashlhi")
|
||||
(set_attr "cc" "none,set_n,clobber,set_n,clobber")])
|
||||
|
@ -3084,11 +3183,13 @@
|
|||
|
||||
(define_insn "*ashlsi3_const"
|
||||
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
|
||||
(ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
(ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return ashlsi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return ashlsi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,4,4,10")
|
||||
(set_attr "adjust_len" "ashlsi")
|
||||
(set_attr "cc" "none,set_n,clobber,clobber")])
|
||||
|
@ -3109,21 +3210,25 @@
|
|||
;; arithmetic shift right
|
||||
|
||||
(define_insn "ashrqi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashrqi")
|
||||
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")])
|
||||
|
||||
(define_insn "ashrhi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashrhi")
|
||||
(set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
|
||||
|
@ -3141,11 +3246,13 @@
|
|||
(set_attr "cc" "clobber")])
|
||||
|
||||
(define_insn "ashrsi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "ashrsi")
|
||||
(set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
|
||||
|
@ -3164,11 +3271,13 @@
|
|||
|
||||
(define_insn "*ashrhi3_const"
|
||||
[(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")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
(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")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return ashrhi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return ashrhi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,2,4,4,10")
|
||||
(set_attr "adjust_len" "ashrhi")
|
||||
(set_attr "cc" "none,clobber,set_n,clobber,clobber")])
|
||||
|
@ -3185,11 +3294,13 @@
|
|||
|
||||
(define_insn "*ashrsi3_const"
|
||||
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
|
||||
(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return ashrsi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return ashrsi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,4,4,10")
|
||||
(set_attr "adjust_len" "ashrsi")
|
||||
(set_attr "cc" "none,clobber,set_n,clobber")])
|
||||
|
@ -3198,11 +3309,9 @@
|
|||
;; logical shift right
|
||||
|
||||
(define_expand "lshrqi3"
|
||||
[(set (match_operand:QI 0 "register_operand" "")
|
||||
(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
|
||||
(match_operand:QI 2 "general_operand" "")))]
|
||||
""
|
||||
"")
|
||||
[(set (match_operand:QI 0 "register_operand" "")
|
||||
(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
|
||||
(match_operand:QI 2 "nop_general_operand" "")))])
|
||||
|
||||
(define_split ; lshrqi3_const4
|
||||
[(set (match_operand:QI 0 "d_register_operand" "")
|
||||
|
@ -3234,21 +3343,25 @@
|
|||
"")
|
||||
|
||||
(define_insn "*lshrqi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "lshrqi")
|
||||
(set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
|
||||
|
||||
(define_insn "lshrhi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "lshrhi")
|
||||
(set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
|
||||
|
@ -3266,11 +3379,13 @@
|
|||
(set_attr "cc" "clobber")])
|
||||
|
||||
(define_insn "lshrsi3"
|
||||
[(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")
|
||||
(match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))]
|
||||
[(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")
|
||||
(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 "adjust_len" "lshrsi")
|
||||
(set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
|
||||
|
@ -3324,11 +3439,13 @@
|
|||
|
||||
(define_insn "*lshrhi3_const"
|
||||
[(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")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
(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")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return lshrhi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return lshrhi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,2,2,4,10")
|
||||
(set_attr "adjust_len" "lshrhi")
|
||||
(set_attr "cc" "none,clobber,clobber,clobber,clobber")])
|
||||
|
@ -3345,11 +3462,13 @@
|
|||
|
||||
(define_insn "*lshrsi3_const"
|
||||
[(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
|
||||
(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0")
|
||||
(match_operand:QI 2 "const_int_operand" "L,P,O,n")))
|
||||
(clobber (match_scratch:QI 3 "=X,X,X,&d"))]
|
||||
"reload_completed"
|
||||
"* return lshrsi3_out (insn, operands, NULL);"
|
||||
{
|
||||
return lshrsi3_out (insn, operands, NULL);
|
||||
}
|
||||
[(set_attr "length" "0,4,4,10")
|
||||
(set_attr "adjust_len" "lshrsi")
|
||||
(set_attr "cc" "none,clobber,clobber,clobber")])
|
||||
|
@ -3615,6 +3734,27 @@
|
|||
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"
|
||||
[(set (match_operand:SI 0 "register_operand" "=r")
|
||||
(zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))]
|
||||
|
|
|
@ -57,6 +57,17 @@
|
|||
(and (match_code "const_int")
|
||||
(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.
|
||||
(define_predicate "const0_operand"
|
||||
(and (match_code "const_int,const_double")
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
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>
|
||||
|
||||
* config.host (hppa*64*-*-hpux11*): Remove pa/t-stublib64 from
|
||||
|
|
|
@ -1181,6 +1181,52 @@ DEFUN __tablejump_elpm__
|
|||
ENDF __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
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ LIB1ASMFUNCS = \
|
|||
_cleanup \
|
||||
_tablejump \
|
||||
_tablejump_elpm \
|
||||
_load_3 _load_4 \
|
||||
_copy_data \
|
||||
_clear_bss \
|
||||
_ctors \
|
||||
|
|
Loading…
Reference in New Issue