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:
Georg-Johann Lay 2011-11-15 09:01:46 +00:00 committed by Georg-Johann Lay
parent d2d668fbbb
commit 7c20948114
11 changed files with 1037 additions and 129 deletions

View File

@ -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.

View File

@ -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. */

View File

@ -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
}

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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")))]

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -21,6 +21,7 @@ LIB1ASMFUNCS = \
_cleanup \
_tablejump \
_tablejump_elpm \
_load_3 _load_4 \
_copy_data \
_clear_bss \
_ctors \