===================================================================
@@ -42,6 +42,7 @@ (define_constants
(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
@@ -323,11 +324,17 @@ (define_expand "movqi"
[(set (match_operand:QI 0 "nonimmediate_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
- "/* One of the ops has to be in a register. */
- if (!register_operand(operand0, QImode)
- && ! (register_operand(operand1, QImode) || const0_rtx == operand1))
- operands[1] = copy_to_mode_reg(QImode, operand1);
- ")
+ {
+ if (avr_mem_pgm_p (operands[0]))
+ DONE;
+
+ /* One of the operands has to be in a register. */
+ if (!register_operand (operands[0], QImode)
+ && !(register_operand (operands[1], QImode) || const0_rtx == operands[1]))
+ {
+ operands[1] = copy_to_mode_reg (QImode, operands[1]);
+ }
+ })
(define_insn "movqi_insn"
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r")
@@ -370,15 +377,17 @@ (define_expand "movhi"
[(set (match_operand:HI 0 "nonimmediate_operand" "")
(match_operand:HI 1 "general_operand" ""))]
""
- "
-{
- /* One of the ops has to be in a register. */
- if (!register_operand(operand0, HImode)
- && !(register_operand(operand1, HImode) || const0_rtx == operands[1]))
- {
- operands[1] = copy_to_mode_reg(HImode, operand1);
- }
-}")
+ {
+ if (avr_mem_pgm_p (operands[0]))
+ DONE;
+
+ /* One of the operands has to be in a register. */
+ if (!register_operand (operands[0], HImode)
+ && !(register_operand (operands[1], HImode) || const0_rtx == operands[1]))
+ {
+ operands[1] = copy_to_mode_reg (HImode, operands[1]);
+ }
+ })
(define_insn "movhi_sp_r_irq_off"
[(set (match_operand:HI 0 "stack_register_operand" "=q")
@@ -463,6 +472,39 @@ (define_peephole2 ; movw_r
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 (ADDR_SPACE_PGM != MEM_ADDR_SPACE (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);
+ })
;;==========================================================================
;; move double word (32 bit)
@@ -470,15 +512,17 @@ (define_expand "movsi"
[(set (match_operand:SI 0 "nonimmediate_operand" "")
(match_operand:SI 1 "general_operand" ""))]
""
- "
-{
- /* One of the ops has to be in a register. */
- if (!register_operand (operand0, SImode)
- && !(register_operand (operand1, SImode) || const0_rtx == operand1))
- {
- operands[1] = copy_to_mode_reg (SImode, operand1);
- }
-}")
+ {
+ if (avr_mem_pgm_p (operands[0]))
+ DONE;
+
+ /* One of the operands has to be in a register. */
+ if (!register_operand (operands[0], SImode)
+ && !(register_operand (operands[1], SImode) || const0_rtx == operands[1]))
+ {
+ operands[1] = copy_to_mode_reg (SImode, operands[1]);
+ }
+ })
@@ -526,15 +570,17 @@ (define_expand "movsf"
[(set (match_operand:SF 0 "nonimmediate_operand" "")
(match_operand:SF 1 "general_operand" ""))]
""
- "
-{
- /* One of the ops has to be in a register. */
- if (!register_operand (operand1, SFmode)
- && !register_operand (operand0, SFmode))
- {
- operands[1] = copy_to_mode_reg (SFmode, operand1);
- }
-}")
+ {
+ if (avr_mem_pgm_p (operands[0]))
+ DONE;
+
+ /* One of the operands has to be in a register. */
+ if (!register_operand (operands[1], SFmode)
+ && !register_operand (operands[0], SFmode))
+ {
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+ }
+ })
(define_insn "*movsf"
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r")
@@ -589,7 +635,7 @@ (define_expand "movmemhi"
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));
@@ -598,6 +644,9 @@ (define_expand "movmemhi"
/* 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;
@@ -618,7 +667,9 @@ (define_expand "movmemhi"
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. */
===================================================================
@@ -318,12 +318,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
}
===================================================================
@@ -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
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 as C+ will need a language
+ extension as of 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. */
===================================================================
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offse
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);
#ifdef TREE_CODE
extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool);
@@ -107,10 +108,11 @@ extern int avr_simplify_comparison_p (en
extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
extern void out_shift_with_cnt (const char *templ, rtx insn,
rtx operands[], int *len, int t_len);
-extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, RTX_CODE, RTX_CODE);
-extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, RTX_CODE, RTX_CODE);
+extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
+extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
extern rtx avr_incoming_return_addr_rtx (void);
extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int));
+extern bool avr_mem_pgm_p (rtx);
#endif /* RTX_CODE */
#ifdef REAL_VALUE_TYPE
@@ -129,12 +131,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"
@@ -83,6 +84,12 @@ static bool avr_rtx_costs (rtx, int, int
/* 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;
@@ -171,9 +178,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
@@ -188,9 +192,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
@@ -217,6 +218,24 @@ bool avr_need_copy_data_p = false;
#undef TARGET_ASM_FUNCTION_RODATA_SECTION
#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
+#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.
@@ -319,6 +338,8 @@ avr_option_override (void)
avr_current_arch = &avr_arch_types[avr_current_device->arch];
avr_extra_arch_macro = avr_current_device->macro;
+ lpm_addr_reg_rtx = gen_rtx_REG (Pmode, REG_Z);
+ lpm_reg_rtx = gen_rtx_REG (QImode, LPM_REGNO);
tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO);
zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
@@ -369,6 +390,30 @@ avr_regno_reg_class (int r)
return ALL_REGS;
}
+
+/* Return TRUE if DECL is a VAR_DECL which is located in AS1
+ and FALSE, otherwise. */
+
+static bool
+avr_decl_pgm_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL)
+ return false;
+
+ return (ADDR_SPACE_PGM == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in AS1 and FALSE, otherwise. */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+ return (MEM_P (x)
+ && ADDR_SPACE_PGM == MEM_ADDR_SPACE (x));
+}
+
+
/* A helper for the subsequent function attribute used to dig for
attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
@@ -1156,19 +1201,19 @@ avr_cannot_modify_jumps_p (void)
/* Helper function for `avr_legitimate_address_p'. */
static inline bool
-avr_reg_ok_for_addr_p (rtx reg, addr_space_t as ATTRIBUTE_UNUSED,
+avr_reg_ok_for_addr_p (rtx reg, addr_space_t as,
RTX_CODE outer_code, bool strict)
{
return (REG_P (reg)
- && (avr_regno_mode_code_ok_for_base_p (REGNO (reg),
+ && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), as,
QImode, outer_code, UNKNOWN)
|| (!strict
&& REGNO (reg) >= FIRST_PSEUDO_REGISTER)));
}
-/* Return nonzero if X (an RTX) is a legitimate memory address on the target
- machine for a memory operand of mode MODE. */
+/* Former implementation of TARGET_LEGITIMATE_ADDRESS_P,
+ now only a helper for avr_addr_space_legitimate_address_p. */
static bool
avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
@@ -1254,6 +1299,9 @@ avr_legitimate_address_p (enum machine_m
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 */
@@ -1377,7 +1425,7 @@ avr_legitimize_reload_address (rtx *px,
/* Helper function to print assembler resp. track instruction
- sequence lengths.
+ sequence lengths. Always returns "".
If PLEN == NULL:
Output assembler code from template TPL with operands supplied
@@ -1389,7 +1437,7 @@ avr_legitimize_reload_address (rtx *px,
Don't output anything.
*/
-static void
+static const char*
avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
{
if (NULL == plen)
@@ -1403,6 +1451,8 @@ avr_asm_len (const char* tpl, rtx* opera
else
*plen += n_words;
}
+
+ return "";
}
@@ -2047,6 +2097,263 @@ avr_function_ok_for_sibcall (tree decl_c
/***********************************************************************
Functions for outputting various mov's for a various modes
************************************************************************/
+
+
+/* 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 src = xop[1];
+ rtx addr;
+ int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+ int regno_dest;
+
+ addr = XEXP (src, 0);
+ 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 */
+
+ case 4:
+ avr_asm_len ("lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %B0,%3" CR_TAB
+ "adiw %2,1", xop, plen, -6);
+
+ if (REGNO (dest) == REG_Z - 2)
+ return avr_asm_len ("lpm" CR_TAB
+ "push %3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %D0,%3" CR_TAB
+ "pop %C0", xop, plen, 6);
+ else
+ {
+ avr_asm_len ("lpm" CR_TAB
+ "mov %C0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %D0,%3", xop, plen, 5);
+
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,3", xop, plen, 1);
+ }
+
+ break; /* 4 */
+ }
+
+ break; /* REG */
+
+ case POST_INC:
+
+ gcc_assert (REG_Z == REGNO (XEXP (addr, 0)));
+
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
+
+ case 1:
+ return avr_asm_len ("lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1", xop, plen, -3);
+
+ case 2:
+ return avr_asm_len ("lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %B0,%3" CR_TAB
+ "adiw %2,1", xop, plen, -6);
+
+ case 4:
+ return avr_asm_len ("lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %B0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %C0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "lpm" CR_TAB
+ "mov %D0,%3" CR_TAB
+ "adiw %2,1", xop, plen, -12);
+ } /* switch (n_bytes) */
+ } /* 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[4];
+ rtx dest = op[0];
+ rtx src = op[1];
+ 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 (ADDR_SPACE_PGM));
+
+ return "";
+ }
+
+ gcc_assert (REG_P (dest)
+ && avr_mem_pgm_p (src));
+
+ xop[0] = op[0];
+ xop[1] = op[1];
+ xop[2] = lpm_addr_reg_rtx;
+
+ if (!AVR_HAVE_LPMX)
+ return avr_out_lpm_no_lpmx (insn, xop, plen);
+
+ addr = XEXP (src, 0);
+ regno_dest = REGNO (dest);
+
+ 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 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 */
+ }
+
+ break; /* REG */
+
+ case POST_INC:
+
+ gcc_assert (REG_Z == REGNO (XEXP (addr, 0)));
+
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
+
+ case 1:
+ return avr_asm_len ("lpm %A0,%a2+", xop, plen, -1);
+
+ case 2:
+ return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+ "lpm %B0,%a2+", xop, plen, -2);
+
+ case 4:
+ return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+ "lpm %B0,%a2+" CR_TAB
+ "lpm %C0,%a2+" CR_TAB
+ "lpm %D0,%a2+", xop, plen, -4);
+ } /* switch (n_bytes) */
+ } /* switch CODE (addr) */
+
+ return "";
+}
+
+
const char *
output_movqi (rtx insn, rtx operands[], int *l)
{
@@ -2055,6 +2362,12 @@ output_movqi (rtx insn, rtx operands[],
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;
@@ -2147,6 +2460,12 @@ output_movhi (rtx insn, rtx operands[],
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;
@@ -2760,6 +3079,12 @@ output_movsisf (rtx insn, rtx operands[]
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;
@@ -5609,6 +5934,10 @@ avr_attribute_table[] =
/* Look for attribute `progmem' in DECL
if found return 1, otherwise 0. */
+/* Return 2 if DECL is located in address space AS1.
+ Return 1 if attribute `progmem' occurs in DECL or ATTRIBUTES.
+ Return 0, otherwise. */
+
int
avr_progmem_p (tree decl, tree attributes)
{
@@ -5617,6 +5946,9 @@ avr_progmem_p (tree decl, tree attribute
if (TREE_CODE (decl) != VAR_DECL)
return 0;
+ if (avr_decl_pgm_p (decl))
+ return 2;
+
if (NULL_TREE
!= lookup_attribute ("progmem", attributes))
return 1;
@@ -5635,11 +5967,104 @@ avr_progmem_p (tree decl, tree attribute
return 0;
}
+
+/* Scan type TYP for pointer references to address space AS1.
+ Return TRUE if all pointers targeting AS1 are also declared
+ to be CONST and FALSE, otherwise. */
+
+static bool
+avr_pgm_pointer_const_p (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_pgm_pointer_const_p (TREE_TYPE (target));
+
+ /* "Ordinary" pointers... */
+
+ return ((/* ...must point to generic address space... */
+ ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+ /* ...or point to read-only locations. */
+ || TYPE_READONLY (target))
+ /* Scan pointer's target type. */
+ && avr_pgm_pointer_const_p (target));
+ }
+
+ return true;
+}
+
+
+/* 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;
+
+ if (avr_log.progmem)
+ avr_edump ("%?: %t\n", node);
+
+ switch (TREE_CODE (node))
+ {
+ default:
+ break;
+
+ case VAR_DECL:
+ if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+ reason = "variable";
+ break;
+
+ case PARM_DECL:
+ if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+ reason = "function parameter";
+ break;
+
+ case FIELD_DECL:
+ if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+ reason = "structure field";
+ break;
+
+ case FUNCTION_DECL:
+ if (!avr_pgm_pointer_const_p (TREE_TYPE (TREE_TYPE (node))))
+ reason = "return type of function";
+ break;
+
+ case POINTER_TYPE:
+ if (!avr_pgm_pointer_const_p (node))
+ reason = "pointer";
+ break;
+ }
+
+ if (reason)
+ {
+ if (TYPE_P (node))
+ error ("pointer targeting address space %qs must be const in %qT",
+ c_addr_space_name (ADDR_SPACE_PGM), node);
+ else
+ error ("pointer targeting address space %qs must be const in %s %q+D",
+ c_addr_space_name (ADDR_SPACE_PGM), 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))
@@ -5656,11 +6081,20 @@ avr_insert_attributes (tree node, tree *
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);
}
}
}
@@ -5860,6 +6294,7 @@ avr_section_type_flags (tree decl, const
&& avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
{
flags &= ~SECTION_WRITE;
+ flags &= ~SECTION_BSS;
flags |= AVR_SECTION_PROGMEM;
}
@@ -7338,9 +7773,15 @@ avr_hard_regno_mode_ok (int regno, enum
reg_class_t
avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
- RTX_CODE outer_code,
+ addr_space_t as,
+ RTX_CODE outer_code ATTRIBUTE_UNUSED,
RTX_CODE index_code ATTRIBUTE_UNUSED)
{
+ if (ADDR_SPACE_PGM == as)
+ {
+ return POINTER_Z_REGS;
+ }
+
if (!avr_strict_X)
return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
@@ -7353,11 +7794,33 @@ avr_mode_code_base_reg_class (enum machi
bool
avr_regno_mode_code_ok_for_base_p (int regno,
enum machine_mode mode ATTRIBUTE_UNUSED,
- RTX_CODE outer_code,
+ addr_space_t as,
+ RTX_CODE outer_code ATTRIBUTE_UNUSED,
RTX_CODE index_code ATTRIBUTE_UNUSED)
{
bool ok = false;
+ if (ADDR_SPACE_PGM == 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
@@ -7885,6 +8348,140 @@ 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 Pmode;
+}
+
+
+/* Implement `'. */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+ return Pmode;
+}
+
+
+/* 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 ((!reload_completed || !reload_in_progress)
+ && 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;
+
+ if (ADDR_SPACE_GENERIC_P (as))
+ return avr_legitimate_address_p (mode, x, strict);
+
+ gcc_assert (ADDR_SPACE_PGM == as);
+
+ switch (GET_CODE (x))
+ {
+ case REG:
+ ok = avr_reg_ok_for_pgm_addr (x, strict);
+ break;
+
+ case POST_INC:
+ ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
+ break;
+
+ default:
+ break;
+ }
+
+ 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 op, tree from_type ATTRIBUTE_UNUSED,
+ tree to_type ATTRIBUTE_UNUSED)
+{
+ if (avr_log.progmem)
+ avr_edump ("\n%?: op=%r\nfrom=%t\nto=%t\n", op, from_type, to_type);
+ return op;
+}
+
+
+/* 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
===================================================================
@@ -308,13 +308,13 @@ enum reg_class {
#define REGNO_REG_CLASS(R) avr_regno_reg_class(R)
-#define MODE_CODE_BASE_REG_CLASS(mode, outer_code, index_code) \
- avr_mode_code_base_reg_class (mode, outer_code, index_code)
+#define MODE_CODE_BASE_REG_CLASS(mode, address_space, outer_code, index_code) \
+ avr_mode_code_base_reg_class (mode, address_space, outer_code, index_code)
#define INDEX_REG_CLASS NO_REGS
-#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, outer_code, index_code) \
- avr_regno_mode_code_ok_for_base_p (num, mode, outer_code, index_code)
+#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, address_space, outer_code, index_code) \
+ avr_regno_mode_code_ok_for_base_p (num, mode, address_space, outer_code, index_code)
#define REGNO_OK_FOR_INDEX_P(NUM) 0
@@ -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"