This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [Patch]: PR49868: Named address space support for AVR


Georg-Johann Lay schrieb:

> This patch adds named address space support to read data from flash (aka.
> progmem) to target AVR.
> 
> The patch has two parts:
> 
> The first part is a repost of Ulrich's work from
>    http://gcc.gnu.org/ml/gcc/2011-08/msg00131.html
> with the needed changes to ./gcc and ./gcc/doc
> 
> This patch is needed because the target hooks MODE_CODE_BASE_REG_CLASS and
> REGNO_MODE_CODE_OK_FOR_BASE_P don't distinguish between different address
> spaces.  Ulrich's patch adds respective support to these hooks.
> 
> The second part is the AVR dependent part that adds __pgm as address space
> qualifier for address space AS1.
> 
> The AVR part is just the worker code.  If there is agreement that AS support
> for AVR is okay in principle and Ulrich's work will go into GCC, I will supply
> test programs and updates to the user manual, of course.
> 
> The major drawbacks of the current AS implementation are:
> 
> - It works only for C.
>   For C++, a language extension would be needed as indicated in
>      ISO/IEC DTR 18037
>      Annex F - C++ Compatibility and Migration issues
>      F.2 Multiple Address Spaces Support
> 
> - Register allocation does not a good job. AS1 can only be addressed
>   byte-wise by one single address register (Z) as per *Z or *Z++.

This flaw from register allocator are filed as PR50775 now.

> The AVR part does several things:
> 
> - It locates data in AS1 into appropriate section, i.e. somewhere in
>   .progmem
> 
> - It does early sanity checks to ensure that __pgm is always accompanied
>   with const so that writing to AS1 in not possible.
> 
> - It prints LPM instructions to access flash memory.

The attached patch is an update merge so that it fits without conflicts.

The patch requires Ulrich's works which is still in review
   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50775

The regression tests run with this patch and the new ChangeLog enttry si
written as if Ulrich's patch was applied.

Tests pass without regression.

Besides the update to a nor up-to-date SVN version, the patch sets a built-in
define __PGM so that it is easy for users to test if or if not the feature is
available.

Documentation and test cases will follow in separate patch.

Ok for trunk after Ulrich's work has been approved?

Johann

	PR target/49868
	* config/avr/avr.h (ADDR_SPACE_PGM): New define for address space AS1.
	(REGISTER_TARGET_PRAGMAS): New define.
	* config/avr/avr-protos.h (avr_mem_pgm_p): New prototype.
	(avr_register_target_pragmas): New prototype.
	(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 AS1 as "__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 AS1.
	(avr_regno_mode_code_ok_for_base_p): Handle AS1.
	(lpm_addr_reg_rtx, lpm_reg_rtx): New static GTYed variables.
	(avr_decl_pgm_p): New static function.
	(avr_mem_pgm_p): New function.
	(avr_asm_len): Return always "" instead of void.
	(avr_out_lpm_no_lpmx): New static function.
	(avr_out_lpm): New static function.
	(output_movqi, output_movhi, output_movsisf): Call avr_out_lpm to
	handle loads from progmem.
	(avr_progmem_p): Test if decl is in AS1.
	(avr_pgm_pointer_const_p): New static function.
	(avr_pgm_check_var_decl): New static function.
	(avr_insert_attributes): Use it.  Change error message to report
	cause (progmem or AS1) when code wants to write to AS1.
	(avr_section_type_flags): Unset section flag SECTION_BSS for
	data in progmem.
	* config/avr/avr.md (LPM_REGNO): New define_constants.
	(movqi, movhi, movsi, movsf): Skip if code would write to AS1.
	(movmemhi): Ditto.  Propagate address space information to newly
	created MEM.
	(split-lpmx): New split.
Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 180605)
+++ config/avr/avr.md	(working copy)
@@ -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.  */
Index: config/avr/avr-log.c
===================================================================
--- config/avr/avr-log.c	(revision 180605)
+++ config/avr/avr-log.c	(working copy)
@@ -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
     }
Index: config/avr/avr-c.c
===================================================================
--- config/avr/avr-c.c	(revision 180605)
+++ config/avr/avr-c.c	(working copy)
@@ -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. */
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 180605)
+++ config/avr/avr-protos.h	(working copy)
@@ -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;
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 180605)
+++ config/avr/avr.c	(working copy)
@@ -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
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 180605)
+++ config/avr/avr.h	(working copy)
@@ -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"

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]