[rl78] Various fixes and tweaks
diff mbox

Message ID 201501170258.t0H2wL7U004033@greed.delorie.com
State New
Headers show

Commit Message

DJ Delorie Jan. 17, 2015, 2:58 a.m. UTC
Various RL78-specific fixes and tweaks wrt volatiles and addressing
modes.  Committed.

	* config/rl78/rl78-real.md (addqi3_real): Allow volatiles.
	(addhi3_real): Likewise.  Fix [HL+0] syntax.
	(subqi3_real): Likewise.
	(subhi3_real): Likewise.
	(cbranchqi4_real): Likewise.  Allow saddr,#imm.
	(cbranchhi4_real): Likewise.
	(cbranchhi4_real_inverted): Likewise.
	(cbranchsi4_real_lt): Likewise.
	(cbranchsi4_real_ge): Likewise.
	(cbranchsi4_real_ge): Likewise.
	* config/rl78/rl78-virt.md (add<mode>3_virt): Likewise.
	(sub<mode>3_virt): Likewise.
	(cbranchqi4_virt): Likewise.
	(cbranchhi4_virt): Likewise.
	* config/rl78/rl78.c (rl78_print_operand_1): 'p' modifier means
	always use '[reg+imm]' even when imm is zero.
	* config/rl78/predicates.md (rl78_volatile_memory_operand): New.
	(rl78_general_operand): New.
	(rl78_nonimmediate_operand): New.
	(rl78_nonfar_operand): Use them.
	(rl78_nonfar_nonimm_operand): Likewise.
	(rl78_stack_based_mem): Fix.
	* config/rl78/constraints.md (Ibqi): New.
	(IBqi): New.
	(Wsa): New.
	(Wsf): New.
	(Cs1): Fix.
	* config/rl78/rl78-expand.md (andqi3): Accept volatiles.
	(iorqi3): Likewise.
	(xorqi3): Likewise.
	* config/rl78/rl78-protos.h (rl78_sfr_p): New.

        * config/rl78/constrains (Qs8): New constraint.
        * config/rl78/rl78.c (rl78_flags_already_set): New function.
        * config/rl78/rl78-protos.h (rl78_flags_already_set): New prototype.
        * config/rl78/rl78-real.md (update_Z): New attribute.
        Update patterns to set it.
        (cbranchqi4_real): Call rl78_flags_already_set() to determine if a
        shorter compare and branch sequence can be used.
        (cbranchhi4_real): Likewise.
        (cbranchhi4_real_inverted): Likewise.

	* config/rl78/predicates.md (uword_operand): Allow symbol_refs.
	* config/rl78/rl78-c.c (rl78_register_pragmas): Register __near
	address space.
	* config/rl78/rl78.c (rl78_get_name_encoding): New.
	(rl78_option_override): Allow -mes0 only if C.
	(characterize_address): Support subregs of symbol_refs.
	(rl78_addr_space_address_mode): Move.  Add __near.
	(rl78_far_p): Likewise.
	(rl78_addr_space_pointer_mode): Likewise.
	(rl78_as_legitimate_address): Likewise.
	(rl78_addr_space_subset_p): Likewise.
	(rl78_addr_space_convert): Likewise.
	(rl78_print_operand_1): Support 16-bit addressing of 32-bit
	symbols with -mes0.
	(transcode_memory_rtx): Don't copy ES if -mes0.  Allow symbol[BC]
	addressing.
	(rl78_alloc_physical_registers_op1): Change logic to prefer
	symbol[BC] addressing.
	(frodata_section): New.
	(rl78_asm_init_sections): Initialize it.
	(rl78_select_section): Put __far readonly symbols in .frodata.
	(rl78_make_type_far): New.
	(rl78_insert_attributes): Force all readonly symbols to be __far when -mes0.
	(rl78_asm_out_integer): New.
	* config/rl78/rl78.h (ADDR_SPACE_NEAR): New.
	* config/rl78/rl78.opt (-mes0): New.

	* config/rl78/rl78.h (ASM_OUTPUT_LABELREF): New.
	(ASM_OUTPUT_ALIGNED_DECL_COMMON): New.
	(ASM_OUTPUT_ALIGNED_DECL_LOCAL): New.
	* config/rl78/rl78-protos.h (rl78_output_labelref): New.
	(rl78_saddr_p): New.
	(rl78_output_aligned_common): New.
	* config/rl78/rl78.c (rl78_output_symbol_ref): Strip encodings.
	(rl78_handle_saddr_attribute): New.
	(rl78_handle_naked_attribute): New.
	(rl78_attribute_table): Add saddr.
	(rl78_print_operand_1): Don't print '!' on saddr operands.
	(rl78_print_operand_1): Strip encodings.
	(rl78_sfr_p): New.
	(rl78_strip_name_encoding): New.
	(rl78_attrlist_to_encoding): New.
	(rl78_encode_section_info): New.
	(rl78_asm_init_sections): New.
	(rl78_select_section): New.
	(rl78_output_labelref): New.
	(rl78_output_aligned_common): New.
	(rl78_asm_out_integer): New.
	(rl78_asm_ctor_dtor): New.
	(rl78_asm_constructor): New.
	(rl78_asm_destructor): New.

	* config/rl78/rl78-real.md (movqi_es): Rename to movqi_to_es.
	* config/rl78/rl78.c (rl78_expand_epilogue): Update.
	(transcode_memory_rtx): Update.
	(rl78_expand_epilogue): Use A_REG instead of 0.

Patch
diff mbox

Index: config/rl78/predicates.md
===================================================================
--- config/rl78/predicates.md	(revision 219790)
+++ config/rl78/predicates.md	(working copy)
@@ -15,24 +15,40 @@ 
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GCC; see the file COPYING3.  If not see
 ;; <http://www.gnu.org/licenses/>.
 
-(define_predicate "rl78_any_operand"
+
+(define_predicate "rl78_volatile_memory_operand"
+  (and (match_code "mem")
+       (match_test ("memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0), MEM_ADDR_SPACE (op))")))
+)
+
+; TRUE for any valid general operand.  We do this because
+; general_operand refuses to match volatile memory refs.
+
+(define_predicate "rl78_general_operand"
   (ior (match_operand 0 "general_operand")
-       (match_code "mem,const_int,const_double,reg"))
+       (match_operand 0 "rl78_volatile_memory_operand"))
+)
+
+; Likewise for nonimmediate_operand.
+
+(define_predicate "rl78_nonimmediate_operand"
+  (ior (match_operand 0 "nonimmediate_operand")
+       (match_operand 0 "rl78_volatile_memory_operand"))
 )
 
 (define_predicate "rl78_nonfar_operand"
-  (and (match_operand 0 "general_operand")
+  (and (match_operand 0 "rl78_general_operand")
        (not (match_test "rl78_far_p (op)")))
 )
 
 (define_predicate "rl78_nonfar_nonimm_operand"
-  (and (match_operand 0 "nonimmediate_operand")
+  (and (match_operand 0 "rl78_nonimmediate_operand")
        (not (match_test "rl78_far_p (op)")))
 )
 
 (define_predicate "rl78_near_mem_operand"
   (and (match_code "mem")
        (match_test "!rl78_far_p (op) && rl78_as_legitimate_address (VOIDmode, XEXP (op, 0), true, ADDR_SPACE_GENERIC)"))
@@ -44,15 +60,20 @@ 
 
 (define_predicate "rl78_24_operand"
   (and (match_code "const_int")
        (match_test "INTVAL (op) == 2 || INTVAL (op) == 4")))
 
 (define_predicate "uword_operand"
-  (ior (match_code "const")
-       (and (match_code "const_int")
-	    (match_test "IN_RANGE (INTVAL (op), 0, 65536)"))))
+  (ior (ior (ior (match_code "const")
+		 (and (match_code "const_int")
+		      (match_test "IN_RANGE (INTVAL (op), 0, 65536)")))
+	    (and (match_code "subreg")
+		 (ior (match_code "symbol_ref" "0")
+		      (match_code "const" "0"))))
+       (match_code "symbol_ref")
+       ))
 
 (define_predicate "rl78_cmp_operator_signed"
   (match_code "gt,ge,lt,le"))
 (define_predicate "rl78_cmp_operator_real"
   (match_code "eq,ne,gtu,ltu,geu,leu"))
 (define_predicate "rl78_cmp_operator"
@@ -70,7 +91,9 @@ 
   (and (match_code "mem")
        (ior (and (match_code "reg" "0")
 		 (match_test "REGNO (XEXP (op, 0)) == SP_REG"))
 	    (and (match_code "plus" "0")
 		 (and (match_code "reg" "00")
 		      (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == SP_REG")
-		      (match_code "const_int" "01"))))))
+		      (and (match_code "const_int" "01")
+			   (match_test "IN_RANGE (INTVAL (XEXP (XEXP (op, 0), 1)), 0, 256 - GET_MODE_SIZE (GET_MODE (op)))"))
+			   )))))
Index: config/rl78/rl78.h
===================================================================
--- config/rl78/rl78.h	(revision 219790)
+++ config/rl78/rl78.h	(working copy)
@@ -131,13 +131,14 @@ 
 
 #define MOVE_MAX 			2
 #define STARTING_FRAME_OFFSET		0
 
 #define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC)   1
 
-#define ADDR_SPACE_FAR	1
+#define ADDR_SPACE_NEAR			1
+#define ADDR_SPACE_FAR			2
 
 #define HAVE_PRE_DECCREMENT		0
 #define HAVE_POST_INCREMENT		0
 
 #define MOVE_RATIO(SPEED) 		((SPEED) ? 24 : 16)
 #define SLOW_BYTE_ACCESS		0
@@ -238,12 +239,14 @@  enum reg_class
   "V_REGS",						\
   "GR_REGS",						\
   "PSWREG",						\
   "ALL_REGS"						\
 }
 
+/* Note that no class may include the second register in $fp, because
+   we treat $fp as a single HImode register.  */
 #define REG_CLASS_CONTENTS				\
 {							\
   { 0x00000000, 0x00000000 },	/* No registers,  */		\
   { 0x00000001, 0x00000000 }, \
   { 0x00000002, 0x00000000 }, \
   { 0x00000003, 0x00000000 }, \
@@ -421,12 +424,22 @@  typedef unsigned int CUMULATIVE_ARGS;
    the tablejump insn.  */
 
 #define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
   fprintf (FILE, "\t.long .L%d - 1b\n", VALUE)
 
 
+#define ASM_OUTPUT_SYMBOL_REF(FILE, SYM) rl78_output_symbol_ref ((FILE), (SYM))
+
+#define ASM_OUTPUT_LABELREF(FILE, SYM) rl78_output_labelref ((FILE), (SYM))
+
+#define ASM_OUTPUT_ALIGNED_DECL_COMMON(STREAM, DECL, NAME, SIZE, ALIGNMENT) \
+	rl78_output_aligned_common (STREAM, DECL, NAME, SIZE, ALIGNMENT, 1)
+
+#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(STREAM, DECL, NAME, SIZE, ALIGNMENT) \
+	rl78_output_aligned_common (STREAM, DECL, NAME, SIZE, ALIGNMENT, 0)
+
 #define ASM_OUTPUT_ALIGN(STREAM, LOG)		\
   do						\
     {						\
       if ((LOG) == 0)				\
         break;					\
       fprintf (STREAM, "\t.balign %d\n", 1 << (LOG));	\
Index: config/rl78/constraints.md
===================================================================
--- config/rl78/constraints.md	(revision 219790)
+++ config/rl78/constraints.md	(working copy)
@@ -108,12 +108,23 @@ 
 (define_constraint "ISqi"
   "@internal
    Integer constant with bit 7 set."
   (and (match_code "const_int")
        (match_test "(ival & 0x80) != 0")))
 
+(define_constraint "Ibqi"
+  "@internal
+   Integer constant with one bit in 0..7 set."
+  (and (match_code "const_int")
+       (match_test "(ival & 0xff) && (exact_log2 (ival & 0xff) >= 0)")))
+(define_constraint "IBqi"
+  "@internal
+   Integer constant with one bit in 0..7 clear."
+  (and (match_code "const_int")
+       (match_test "(~ival & 0xff) && (exact_log2 (~ival & 0xff) >= 0)")))
+
 (define_constraint "J"
   "Integer constant in the range -255 @dots{} 0"
   (and (match_code "const_int")
        (match_test "IN_RANGE (ival, -255, 0)")))
 
 (define_constraint "K"
@@ -339,27 +350,41 @@ 
        (ior
 	(and (match_code "reg" "0")
 	     (match_test "REGNO (XEXP (op, 0)) == SP_REG"))
 	(and (match_code "plus" "0")
 	     (and (and (match_code "reg" "00")
 		       (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == SP_REG"))
-		       (match_test "ubyte_operand (XEXP (XEXP (op, 0), 1), VOIDmode)"))))
+		       (and (match_code "const_int" "01")
+		            (match_test "IN_RANGE (INTVAL (XEXP (XEXP (op, 0), 1)), 0, 256 - GET_MODE_SIZE (GET_MODE (op)))")))))
        )
   )
+
 (define_memory_constraint "Ws1"
   "es:word8[SP]"
   (match_test "(rl78_es_addr (op) && satisfies_constraint_Cs1 (rl78_es_base (op)))
                || satisfies_constraint_Cs1 (op)")
   )
 
 (define_memory_constraint "Wfr"
   "ES/CS far pointer"
   (and (match_code "mem")
        (match_test "rl78_far_p (op)"))
   )
 
+(define_memory_constraint "Wsa"
+  "any SADDR memory access"
+  (and (match_code "mem")
+       (match_test "rl78_saddr_p (op)"))
+)
+
+(define_memory_constraint "Wsf"
+  "any SFR memory access"
+  (and (match_code "mem")
+       (match_test "rl78_sfr_p (op)"))
+)
+
 (define_memory_constraint "Y"
   "any near legitimate memory access"
   (and (match_code "mem")
        (match_test "!rl78_far_p (op) && rl78_as_legitimate_address (VOIDmode, XEXP (op, 0), true, ADDR_SPACE_GENERIC)"))
 )
 
@@ -381,6 +406,12 @@ 
   "built-in compare types"
   (match_code "eq,ne,gtu,ltu,geu,leu"))
 
 (define_memory_constraint "Qsc"
   "synthetic compares"
   (match_code "gt,lt,ge,le"))
+
+(define_constraint "Qs8"
+  "Integer constant computed from (SUBREG (SYMREF))."
+  (and (match_code "subreg")
+       (match_test "GET_CODE (XEXP (op, 0)) == SYMBOL_REF"))
+)
Index: config/rl78/rl78-protos.h
===================================================================
--- config/rl78/rl78-protos.h	(revision 219790)
+++ config/rl78/rl78-protos.h	(working copy)
@@ -42,6 +42,14 @@  void		rl78_register_pragmas (void);
 bool		rl78_regno_mode_code_ok_for_base_p (int, machine_mode, addr_space_t, int, int);
 void		rl78_setup_peep_movhi (rtx *);
 bool		rl78_virt_insns_ok (void);
 
 bool		rl78_es_addr (rtx);
 rtx		rl78_es_base (rtx);
+
+bool		rl78_flags_already_set (rtx, rtx);
+void		rl78_output_symbol_ref (FILE *, rtx);
+void		rl78_output_labelref (FILE *, const char *);
+int		rl78_saddr_p (rtx x);
+int		rl78_sfr_p (rtx x);
+void		rl78_output_aligned_common (FILE *, tree, const char *,
+					    int, int, int);
Index: config/rl78/rl78-expand.md
===================================================================
--- config/rl78/rl78-expand.md	(revision 219790)
+++ config/rl78/rl78-expand.md	(working copy)
@@ -147,35 +147,35 @@ 
                  (zero_extend:HI (match_operand:QI 2 "register_operand"))))]
   "!TARGET_G10"
   ""
 )
 
 (define_expand "andqi3"
-  [(set (match_operand:QI         0 "nonimmediate_operand")
-	(and:QI (match_operand:QI 1 "general_operand")
-		(match_operand:QI 2 "general_operand")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand")
+	(and:QI (match_operand:QI 1 "rl78_general_operand")
+		(match_operand:QI 2 "rl78_general_operand")))
    ]
   ""
   "if (rl78_force_nonfar_3 (operands, gen_andqi3))
      DONE;"
 )
 
 (define_expand "iorqi3"
-  [(set (match_operand:QI         0 "nonimmediate_operand")
-	(ior:QI (match_operand:QI 1 "general_operand")
-		(match_operand:QI 2 "general_operand")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand")
+	(ior:QI (match_operand:QI 1 "rl78_general_operand")
+		(match_operand:QI 2 "rl78_general_operand")))
    ]
   ""
   "if (rl78_force_nonfar_3 (operands, gen_iorqi3))
      DONE;"
 )
 
 (define_expand "xorqi3"
-  [(set (match_operand:QI         0 "nonimmediate_operand")
-	(xor:QI (match_operand:QI 1 "general_operand")
-		(match_operand:QI 2 "general_operand")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand")
+	(xor:QI (match_operand:QI 1 "rl78_general_operand")
+		(match_operand:QI 2 "rl78_general_operand")))
    ]
   ""
   "if (rl78_force_nonfar_3 (operands, gen_xorqi3))
      DONE;"
 )
 
Index: config/rl78/rl78-real.md
===================================================================
--- config/rl78/rl78-real.md	(revision 219790)
+++ config/rl78/rl78-real.md	(working copy)
@@ -24,15 +24,23 @@ 
 ;; allow virtual registers in their predicates - the reorg pass that
 ;; allocates physical registers uses the constraints to select
 ;; registers, but insns with virtual registers MUST match one of these
 ;; patterns - other than the constraints - so that the operand info is
 ;; properly set up for the alloc pass.
 
+;; This attribute reflects how the insn alters the Z flag,
+;; based upon the value of the it's output.  The default is NO
+;; for no change, but other possibilities are UPDATE_Z if it changes
+;; the Z flag and CLOBBER if the state of the flag is indeterminate.
+;; The CY and AC flags are not set in the same way as the Z flag, so
+;; their values are not tracked.
+(define_attr "update_Z" "no,update_Z,clobber" (const_string "no"))
+
 ;;---------- Moving ------------------------
 
-(define_insn "movqi_es"
+(define_insn "movqi_to_es"
   [(set (reg:QI ES_REG)
 	(match_operand:QI 0 "register_operand" "a"))]
   ""
   "mov\tes, %0"
 )
 
@@ -48,29 +56,30 @@ 
 	(match_operand:QI 0 "register_operand" "a"))]
   ""
   "mov\tcs, %0"
 )
 
 (define_insn "*movqi_real"
-  [(set (match_operand:QI 0 "nonimmediate_operand" "=g,RaxbcWab,RaxbcWab,a,                          bcx,R, WabWd2WhlWh1WhbWbcWs1v, bcx")
-	(match_operand    1 "general_operand"      "0,K,        M,       RInt8sJvWabWdeWd2WhlWh1WhbWbcWs1,Wab,aInt8J,a,                      R"))]
+  [(set (match_operand:QI 0 "rl78_nonimmediate_operand" "=g,RaxbcWab,RaxbcWab,a,                               bcx,R,     WabWd2WhlWh1WhbWbcWs1v, bcx,WsaWsf")
+	(match_operand    1 "rl78_general_operand"      "0,K,        M,       RInt8sJvWabWdeWd2WhlWh1WhbWbcWs1,Wab,aInt8J,a,                      R,  i"))]
   "rl78_real_insns_ok ()"
   "@
    ; mov\t%0, %1
    oneb\t%0
    clrb\t%0
    mov\t%0, %1
    mov\t%0, %1
    mov\t%0, %1
    mov\t%0, %1
-   mov\t%0, %S1"
+   mov\t%0, %S1
+   mov\t%0, %1"
 )
 
 (define_insn "*movhi_real"
-  [(set (match_operand:HI 0 "nonimmediate_operand" "=g,AB,AB,RSv,A,BDTvSWabWd2WdeWhlWh1WbcWs1, BDT,ABDT,v")
-	(match_operand:HI 1 "general_operand"      " 0,K, M, i,  BDTvSWabWd2WdeWh1WhlWbcWs1,A, BDT,vS,  ABDT"))]
+  [(set (match_operand:HI 0 "rl78_nonimmediate_operand" "=g,AB,AB,RSv,A,BDTvSWabWd2WdeWhlWh1WbcWs1, BDT,ABDT,v")
+	(match_operand:HI 1 "rl78_general_operand"      " 0,K, M, i,  BDTvSWabWd2WdeWh1WhlWbcWs1,A, BDT,vS,  ABDT"))]
   "rl78_real_insns_ok ()"
   "@
    ; movw\t%0, %1
    onew\t%0
    clrw\t%0
    movw\t%0, %1
@@ -101,66 +110,72 @@ 
    sarw\t%0, 8"
   )
 
 ;;---------- Arithmetic ------------------------
 
 (define_insn "*addqi3_real"
-  [(set (match_operand:QI          0 "nonimmediate_operand"  "=rvWabWhlWh1,rvWabWhlWh1,a,*bcdehl")
-	(plus:QI (match_operand:QI 1 "general_operand"  "%0,0,0,0")
-		 (match_operand:QI 2 "general_operand" "K,L,RWhlWh1Wabi,a")))
+  [(set (match_operand:QI          0 "rl78_nonimmediate_operand"  "=rvWabWhlWh1,rvWabWhlWh1,a,*bcdehl,Wsa")
+	(plus:QI (match_operand:QI 1 "rl78_general_operand"  "%0,0,0,0,0")
+		 (match_operand:QI 2 "rl78_general_operand" "K,L,RWhlWh1Wabi,a,i")))
    ]
   "rl78_real_insns_ok ()"
   "@
     inc\t%0
     dec\t%0
     add\t%0, %2
+    add\t%0, %2
     add\t%0, %2"
+  [(set (attr "update_Z") (const_string "update_Z"))]
 )
 
 (define_insn "*addhi3_real"
-  [(set (match_operand:HI          0 "nonimmediate_operand"  "=vABDTWh1Wab,vABDTWh1Wab,v,v,A,S,S,A")
-	(plus:HI (match_operand:HI 1 "general_operand"  "%0,0,0,0,0,0,0,S")
-		 (match_operand:HI 2 "general_operand" "K,L,N,O,RWh1WhlWabiv,Int8,J,Ri")))
+  [(set (match_operand:HI          0 "rl78_nonimmediate_operand"  "=vABDTWh1Wab,vABDTWh1Wab,v,v,A,S,S,A")
+	(plus:HI (match_operand:HI 1 "rl78_general_operand"  "%0,0,0,0,0,0,0,S")
+		 (match_operand:HI 2 "" "K,L,N,O,RWh1WhlWabiv,Int8Qs8,J,Ri")))
    ]
   "rl78_real_insns_ok ()"
   "@
-   incw\t%0
-   decw\t%0
+   incw\t%p0
+   decw\t%p0
    incw\t%0 \;incw\t%0
    decw\t%0 \;decw\t%0
    addw\t%0, %p2
    addw\t%0, %2
    subw\t%0, %m2
    movw\t%0, %1 \;addw\t%0, %2"
+  [(set_attr "update_Z" "*,*,*,*,update_Z,update_Z,update_Z,update_Z")]
 )
 
 (define_insn "*addqihi3a_real"
   [(set (match_operand:HI                          0 "register_operand" "=r")
 	(plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand"  "r"))
 		 (match_operand:HI                 2 "register_operand"  "0")))
    ]
   "rl78_real_insns_ok ()"
   "add\t%q0, %q1 \;addc\t%Q0, #0"
+  [(set (attr "update_Z") (const_string "update_Z"))]
 )
 
 (define_insn "*subqi3_real"
   [(set (match_operand:QI           0 "nonimmediate_operand"  "=a,R,v")
 	(minus:QI (match_operand:QI 1 "general_operand"  "0,0,0")
-		  (match_operand:QI 2 "general_operand" "RiWabWhbWh1Whl,a,i")))
+		  (match_operand:QI 2 "rl78_general_operand" "RiWabWhbWh1Whl,a,i")))
    ]
   "rl78_real_insns_ok ()"
   "sub\t%0, %2"
+  [(set (attr "update_Z") (const_string "update_Z"))]
 )
 
 (define_insn "*subhi3_real"
   [(set (match_operand:HI           0 "nonimmediate_operand"  "=A,S")
 	(minus:HI (match_operand:HI 1 "general_operand"  "0,0")
-		  (match_operand:HI 2 "general_operand" "iBDTWabWh1v,i")))
+		  (match_operand:HI 2 "rl78_general_operand" "iBDTWabWh1v,i")))
    ]
   "rl78_real_insns_ok ()"
   "subw\t%0, %2"
+  [(set (attr "update_Z") (const_string "update_Z"))]
 )
 
 (define_insn "*umulhi3_shift_real"
   [(set (match_operand:HI 0 "register_operand" "=A,A")
         (mult:HI (match_operand:HI 1 "rl78_nonfar_operand" "0,0")
                  (match_operand:HI 2 "rl78_24_operand" "N,i")))]
@@ -176,36 +191,47 @@ 
                  (zero_extend:HI (match_operand:QI 2 "general_operand" "x"))))]
   "rl78_real_insns_ok () && !TARGET_G10"
   "mulu\t%2"
 )
 
 (define_insn "*andqi3_real"
-  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
-	(and:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
-		(match_operand:QI 2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand"  "=Wsf,A,R,vWsa")
+	(and:QI (match_operand:QI 1 "rl78_general_operand"       "%0,0,0,0")
+		(match_operand:QI 2 "rl78_general_operand"       "IBqi,iRvWabWhbWh1Whl,A,i")))
    ]
   "rl78_real_insns_ok ()"
-  "and\t%0, %2"
+  "@
+   clr1\t%0.%B2
+   and\t%0, %2
+   and\t%0, %2
+   and\t%0, %2"
+  [(set_attr "update_Z" "*,update_Z,update_Z,update_Z")]
 )
 
 (define_insn "*iorqi3_real"
-  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
-	(ior:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
-		(match_operand:QI 2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand"  "=Wsf,A,R,vWsa")
+	(ior:QI (match_operand:QI 1 "rl78_general_operand"       "%0,0,0,0")
+		(match_operand:QI 2 "rl78_general_operand"       "Ibqi,iRvWabWhbWh1Whl,A,i")))
    ]
   "rl78_real_insns_ok ()"
-  "or\t%0, %2"
+  "@
+   set1\t%0.%B2
+   or\t%0, %2
+   or\t%0, %2
+   or\t%0, %2"
+  [(set_attr "update_Z" "*,update_Z,update_Z,update_Z")]
 )
 
 (define_insn "*xorqi3_real"
-  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
-	(xor:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
-		(match_operand    2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+  [(set (match_operand:QI         0 "rl78_nonimmediate_operand"  "=A,R,vWsa")
+	(xor:QI (match_operand:QI 1 "rl78_general_operand"       "%0,0,0")
+		(match_operand    2 "rl78_general_operand"       "iRvWabWhbWh1Whl,A,i")))
    ]
   "rl78_real_insns_ok ()"
   "xor\t%0, %2"
+  [(set (attr "update_Z") (const_string "update_Z"))]
 )
 
 ;;---------- Shifts ------------------------
 
 (define_insn "*ashlqi3_real"
   [(set (match_operand:QI            0 "nonimmediate_operand"  "=abc,a,a")
@@ -214,24 +240,26 @@ 
    ]
   "rl78_real_insns_ok ()"
   "@
    shl\t%0, %u2
    cmp0 %2\; bz $2f\; 1: shl\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: shl\t%0, 1 \;dec %2 \;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 (define_insn "*ashlhi3_real"
   [(set (match_operand:HI            0 "nonimmediate_operand"  "=AB,A,A")
 	(ashift:HI (match_operand:HI 1 "general_operand"  "0,0,0")
 		   (match_operand:QI 2 "general_operand" "P,bc,dehl")))
    ]
   "rl78_real_insns_ok ()"
   "@
    shlw\t%0, %u2
    cmp0 %2\; bz $2f\; 1: shlw\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: shlw\t%0, 1 \;dec %2 \;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 ;;----------
 
 (define_insn "*ashrqi3_real"
   [(set (match_operand:QI              0 "nonimmediate_operand"  "=abc,a,a")
@@ -240,24 +268,26 @@ 
    ]
   "rl78_real_insns_ok ()"
   "@
    sar\t%0, %u2
    cmp0 %2\; bz $2f\; 1: sar\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: sar\t%0, 1\;dec %2 \;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 (define_insn "*ashrhi3_real"
   [(set (match_operand:HI              0 "nonimmediate_operand"  "=AB,A,A")
 	(ashiftrt:HI (match_operand:HI 1 "general_operand"  "0,0,0")
 		     (match_operand:QI 2 "general_operand" "P,bc,dehl")))
    ]
   "rl78_real_insns_ok ()"
   "@
    sarw\t%0, %u2
    cmp0 %2\; bz $2f\; 1: sarw\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: sarw\t%0, 1\;dec %2\;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 ;;----------
 
 (define_insn "*lshrqi3_real"
   [(set (match_operand:QI              0 "nonimmediate_operand"  "=abc,a,a")
@@ -266,24 +296,26 @@ 
    ]
   "rl78_real_insns_ok ()"
   "@
    shr\t%0, %u2
    cmp0 %2\; bz $2f\; 1: shr\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: shr\t%0, 1\;dec %2\;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 (define_insn "*lshrhi3_real"
   [(set (match_operand:HI              0 "nonimmediate_operand"  "=AB,A,A")
 	(lshiftrt:HI (match_operand:HI 1 "general_operand"  "0,0,0")
 		     (match_operand:QI 2 "general_operand" "P,bc,dehl")))
    ]
   "rl78_real_insns_ok ()"
   "@
    shrw\t%0, %u2
    cmp0 %2\; bz $2f\; 1: shrw\t%0, 1 \;dec %2 \;bnz $1b\;2:
    inc %2\;dec %2\;bz $2f\;1: shrw\t%0, 1\;dec %2\;bnz $1b\;2:"
+  [(set_attr "update_Z" "*,clobber,clobber")]
 )
 
 ;;---------- Branching ------------------------
 
 (define_insn "*indirect_jump_real"
   [(set (pc)
@@ -304,118 +336,156 @@ 
   [(call (match_operand:HI 0 "memory_operand" "Wab,Wca")
 	 (match_operand 1 "" ""))]
   "rl78_real_insns_ok ()"
   "@
    call\t!!%A0
    call\t%A0"
+  [(set (attr "update_Z") (const_string "clobber"))]
   )
 
 (define_insn "*call_value_real"
   [(set (match_operand 0 "register_operand" "=v,v")
 	(call (match_operand:HI 1 "memory_operand" "Wab,Wca")
 	      (match_operand 2 "" "")))]
   "rl78_real_insns_ok ()"
   "@
    call\t!!%A1
    call\t%A1"
+  [(set (attr "update_Z") (const_string "clobber"))]
   )
 
 (define_insn "*cbranchqi4_real_signed"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_signed"
-			      [(match_operand:QI 1 "general_operand" "A,A,A")
-			       (match_operand:QI 2 "general_operand" "ISqi,i,v")])
+			      [(match_operand:QI 1 "general_operand" "A,A,A,A,Wsa")
+			       (match_operand:QI 2 "general_operand" "M,ISqi,i,v,i")])
               (label_ref (match_operand 3 "" ""))
 	      (pc)))]
   "rl78_real_insns_ok ()"
-  "@
-   cmp\t%1, %2 \;xor1 CY,%1.7\;not1 CY\;sk%C0 \;br\t!!%3
-   cmp\t%1, %2 \;xor1 CY,%1.7\;sk%C0 \;br\t!!%3
-   cmp\t%1, %2 \;xor1 CY,%1.7\;xor1 CY,%2.7\;sk%C0 \;br\t!!%3"
+  {
+    gcc_assert (GET_CODE (operands[0]) != EQ && GET_CODE (operands[0]) != NE);
+
+    switch (which_alternative)
+    {
+    case 0: return "cmp0\t%1\; xor1\tCY, %1.7\; sk%C0\; br\t!!%3";
+    case 1: return "cmp\t%1, %2\; xor1\tCY, %1.7\; not1\tCY\; sk%C0\; br\t!!%3";
+    case 4:
+    case 2: return "cmp\t%1, %2\; xor1\tCY, %1.7\; sk%C0\; br\t!!%3";
+    case 3: return "cmp\t%1, %2\; xor1\tCY, %1.7\; xor1\tCY, %2.7\; sk%C0\; br\t!!%3";
+    default: gcc_unreachable ();
+    }
+  }   
+  [(set (attr "update_Z") (const_string "clobber"))] ;; FIXME: flags are set based on %1 vs %2
   )
 
 (define_insn "*cbranchqi4_real"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_real"
-			      [(match_operand:QI 1 "general_operand" "Wabvaxbc,a,              v,bcdehl")
-			       (match_operand:QI 2 "general_operand" "M,       irvWabWhlWh1Whb,i,a")])
+			      [(match_operand:QI 1 "rl78_general_operand" "Wabvaxbc,a,              vWsaWab,bcdehl")
+			       (match_operand:QI 2 "rl78_general_operand" "M,       irvWabWhlWh1Whb,i,a")])
               (label_ref (match_operand 3 "" ""))
 	      (pc)))]
   "rl78_real_insns_ok ()"
-  "@
-   cmp0\t%1 \;sk%C0 \;br\t!!%3
-   cmp\t%1, %2 \;sk%C0 \;br\t!!%3
-   cmp\t%1, %2 \;sk%C0 \;br\t!!%3
-   cmp\t%1, %2 \;sk%C0 \;br\t!!%3"
+  {
+    if (which_alternative == 0)
+      {
+        if (rl78_flags_already_set (operands[0], operands[1]))
+          return "sk%C0\; br\t!!%3\; # zero-comparison eliminated";
+	else
+	  return "cmp0\t%1\; sk%C0\; br\t!!%3";
+      }
+    return "cmp\t%1, %2\; sk%C0\; br\t!!%3";
+  }
+  [(set (attr "update_Z") (const_string "clobber"))] ;; FIXME: alt 0: flags are set based on %1 vs %2
   )
 
 (define_insn "*cbranchhi4_real_signed"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_signed"
 			      [(match_operand:HI 1 "general_operand" "A,A,A,vR")
 			       (match_operand:HI 2 "general_operand" "IShi,i,v,1")])
               (label_ref (match_operand 3))
 	      (pc)))]
   "rl78_real_insns_ok ()"
   "@
-   cmpw\t%1, %2 \;xor1 CY,%Q1.7\;not1 CY\;sk%C0 \;br\t!!%3
-   cmpw\t%1, %2 \;xor1 CY,%Q1.7\;sk%C0 \;br\t!!%3
-   cmpw\t%1, %2 \;xor1 CY,%Q1.7\;xor1 CY,%Q2.7\;sk%C0 \;br\t!!%3
+   cmpw\t%1, %2\; xor1\tCY, %Q1.7\; not1\tCY\; sk%C0\; br\t!!%3
+   cmpw\t%1, %2\; xor1\tCY, %Q1.7\; sk%C0\; br\t!!%3
+   cmpw\t%1, %2\; xor1\tCY, %Q1.7\; xor1\tCY, %Q2.7\; sk%C0\; br\t!!%3
    %z0\t!!%3"
+  [(set_attr "update_Z" "clobber,clobber,clobber,*")]
   )
 
 (define_insn "cbranchhi4_real"
   [(set (pc) (if_then_else
 	      (match_operator                    0 "rl78_cmp_operator_real"
-			      [(match_operand:HI 1 "general_operand" "A,vR")
-			       (match_operand:HI 2 "general_operand" "iBDTvWabWhlWh1,1")])
+			      [(match_operand:HI 1 "general_operand" "A,A,vR")
+			       (match_operand:HI 2 "rl78_general_operand" "M,iBDTvWabWhlWh1,1")])
               (label_ref (match_operand          3 "" ""))
 	      (pc)))]
   "rl78_real_insns_ok ()"
-  "@
-  cmpw\t%1, %2 \;sk%C0 \;br\t!!%3
-  %z0\t!!%3"
+  {
+    switch (which_alternative)
+      {
+      case 0:
+        if (rl78_flags_already_set (operands[0], operands[1]))
+	  return "sk%C0\; br\t!!%3\; # cmpw eliminated";
+	/* else fall through.  */
+      case 1:
+	return "cmpw\t%1, %2\; sk%C0\; br\t!!%3";
+      case 2:
+        return "%z0\t!!%3";
+      default:
+        gcc_unreachable ();
+      }
+  }
+  [(set (attr "update_Z") (const_string "clobber"))] ;; FIXME: Z might be set based on %1 vs %2
   )
 
 (define_insn "cbranchhi4_real_inverted"  
   [(set (pc) (if_then_else
 	      (match_operator                    0 "rl78_cmp_operator_real"
-			      [(match_operand:HI 1 "general_operand" "A")
-			       (match_operand:HI 2 "general_operand" "iBDTvWabWhlWh1")])
+			      [(match_operand:HI 1 "general_operand" "A,A")
+			       (match_operand:HI 2 "rl78_general_operand" "M,iBDTvWabWhlWh1")])
 	      (pc)
               (label_ref (match_operand          3 "" ""))))]
   "rl78_real_insns_ok ()"
-  "cmpw\t%1, %2 \;sk%C0 \;br\t!!%3"
+  {
+    if (which_alternative == 0 && rl78_flags_already_set (operands[0], operands[1]))
+      return "sk%C0\; br\t!!%3\; # inverted cmpw eliminated";
+    else
+      return "cmpw\t%1, %2\; sk%C0\; br\t!!%3";
+  }
+  [(set (attr "update_Z") (const_string "clobber"))] ;; FIXME: flags are set based on %1 vs %2
   )
 
 (define_insn "*cbranchsi4_real_lt"
   [(set (pc) (if_then_else
-	      (lt (match_operand:SI 0 "general_operand" "U,vWabWhlWh1")
+	      (lt (match_operand:SI 0 "rl78_general_operand" "U,vWabWhlWh1")
 		  (const_int 0))
               (label_ref (match_operand 1 "" ""))
 	      (pc)))
    (clobber (reg:HI AX_REG))
    ]
   "rl78_real_insns_ok ()"
   "@
-   mov a, %E0 \;mov1 CY,a.7 \;sknc \;br\t!!%1
-   mov1 CY,%E0.7 \;sknc \;br\t!!%1"
+   mov\ta, %E0\; mov1\tCY, a.7\; sknc\; br\t!!%1
+   mov1\tCY, %E0.7\; sknc\; br\t!!%1"
   )
 
 (define_insn "*cbranchsi4_real_ge"
   [(set (pc) (if_then_else
-	      (ge (match_operand:SI 0 "general_operand" "U,vWabWhlWh1")
+	      (ge (match_operand:SI 0 "rl78_general_operand" "U,vWabWhlWh1")
 		  (const_int 0))
               (label_ref (match_operand 1 "" ""))
 	      (pc)))
    (clobber (reg:HI AX_REG))
    ]
   "rl78_real_insns_ok ()"
   "@
-   mov a, %E0 \;mov1 CY,a.7 \;skc \;br\t!!%1
-   mov1 CY,%E0.7 \;skc \;br\t!!%1"
+   mov\ta, %E0\; mov1\tCY, a.7\; skc\; br\t!!%1
+   mov1\tCY, %E0.7\; skc\; br\t!!%1"
   )
 
 (define_insn "*cbranchsi4_real_signed"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_signed"
 			      [(match_operand:SI 1 "general_operand"   "vU,vU,vU,i,i")
@@ -423,30 +493,32 @@ 
               (label_ref (match_operand 3 "" ""))
 	      (pc)))
    (clobber (reg:HI AX_REG))
    ]
   "rl78_real_insns_ok ()"
   "@
-   movw ax,%H1 \;cmpw  ax, %H2 \;xor1 CY,a.7\;not1 CY\;      movw ax,%h1 \;sknz \;cmpw  ax, %h2 \;sk%C0 \;br\t!!%3
-   movw ax,%H1 \;cmpw  ax, %H2 \;xor1 CY,a.7\;               movw ax,%h1 \;sknz \;cmpw  ax, %h2 \;sk%C0 \;br\t!!%3
-   movw ax,%H1 \;cmpw  ax, %H2 \;xor1 CY,a.7\;xor1 CY,%E2.7\;movw ax,%h1 \;sknz \;cmpw  ax, %h2 \;sk%C0 \;br\t!!%3
-   movw ax, %H1\; cmpw  ax, %H2\; xor1 CY, a.7\; not1 CY\; movw ax, %h1 \;sknz\; cmpw  ax, %h2 \;sk%0 \;br\t!!%3
-   movw ax, %H1\; cmpw  ax, %H2\; xor1 CY, a.7\; movw ax, %h1\; sknz\; cmpw ax, %h2\; sk%0\; br\t!!%3"
+   movw\tax, %H1\; cmpw\tax, %H2\; xor1\tCY, a.7\; not1\tCY\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%C0\; br\t!!%3
+   movw\tax, %H1\; cmpw\tax, %H2\; xor1\tCY, a.7\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%C0\; br\t!!%3
+   movw\tax, %H1\; cmpw\tax, %H2\; xor1\tCY, a.7\; xor1\tCY, %E2.7\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%C0\; br\t!!%3
+   movw\tax, %H1\; cmpw\tax, %H2\; xor1\tCY, a.7\; not1\tCY\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%0\; br\t!!%3
+   movw\tax, %H1\; cmpw\tax, %H2\; xor1\tCY, a.7\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%0\; br\t!!%3"
+  [(set (attr "update_Z") (const_string "clobber"))]
   )
 
 (define_insn "*cbranchsi4_real"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_real"
 			      [(match_operand:SI 1 "general_operand" "vUi")
 			       (match_operand:SI 2 "general_operand" "iWhlWh1v")])
               (label_ref (match_operand 3 "" ""))
 	      (pc)))
    (clobber (reg:HI AX_REG))
    ]
   "rl78_real_insns_ok ()"
-  "movw ax,%H1 \;cmpw  ax, %H2 \;movw ax,%h1 \;sknz \;cmpw  ax, %h2 \;sk%C0 \;br\t!!%3"
+  "movw\tax, %H1\; cmpw\tax, %H2\; movw\tax, %h1\; sknz\; cmpw\tax, %h2\; sk%C0\; br\t!!%3"
+  [(set (attr "update_Z") (const_string "clobber"))]
   )
 
 ;; Peephole to match:
 ;;
 ;;     (set (mem (sp)) (ax))
 ;;     (set (ax) (mem (sp)))
@@ -477,23 +549,25 @@ 
 			       (match_operand 0 "immediate_operand" "n"))
 			  (const_int 0))
 		      (label_ref (match_operand 1 "" ""))
 		      (pc)))]
   ""
   "bf\tA.%B0, $%1"
+  [(set (attr "update_Z") (const_string "clobber"))]
 )
 
 (define_insn "bt"
   [(set (pc)
 	(if_then_else (ne (and (reg:QI A_REG)
 			       (match_operand 0 "immediate_operand" "n"))
 			  (const_int 0))
 		      (label_ref (match_operand 1 "" ""))
 		      (pc)))]
   ""
   "bt\tA.%B0, $%1"
+  [(set (attr "update_Z") (const_string "clobber"))]
 )
 
 ;; NOTE: These peepholes are fragile.  They rely upon GCC generating
 ;; a specific sequence on insns, based upon examination of test code.
 ;; Improvements to GCC or using code other than the test code can result
 ;; in the peephole not matching and the optimization being missed.
@@ -562,7 +636,8 @@ 
   [(set (match_operand:HI                 0 "register_operand"  "=A")
 	(and:HI (neg:HI (match_operand:HI 1 "register_operand"  "0"))
 		(match_operand:HI         2 "immediate_operand" "n")))
    ]
   "rl78_real_insns_ok ()"
   "xor a, #0xff @ xch a, x @ xor a, #0xff @ xch a, x @ addw ax, #1 @ and a, %Q2 @ xch a, x @ and a, %q2 @ xch a, x"
+  [(set (attr "update_Z") (const_string "clobber"))]
 )
Index: config/rl78/rl78-c.c
===================================================================
--- config/rl78/rl78-c.c	(revision 219790)
+++ config/rl78/rl78-c.c	(working copy)
@@ -36,8 +36,9 @@ 
 #include "rl78-protos.h"
 
 /* Implements REGISTER_TARGET_PRAGMAS.  */
 void
 rl78_register_pragmas (void)
 {
+  c_register_addr_space ("__near", ADDR_SPACE_NEAR);
   c_register_addr_space ("__far", ADDR_SPACE_FAR);
 }
Index: config/rl78/rl78-virt.md
===================================================================
--- config/rl78/rl78-virt.md	(revision 219790)
+++ config/rl78/rl78-virt.md	(working copy)
@@ -85,22 +85,22 @@ 
 
 ;;---------- Arithmetic ------------------------
 
 (define_insn "*add<mode>3_virt"
   [(set (match_operand:QHI           0 "rl78_nonfar_nonimm_operand" "=vY,S")
 	(plus:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "viY,0")
-		  (match_operand:QHI 2 "general_operand" "vim,i")))
+		  (match_operand:QHI 2 "rl78_general_operand" "vim,i")))
    ]
   "rl78_virt_insns_ok ()"
   "v.add\t%0, %1, %2"
 )
 
 (define_insn "*sub<mode>3_virt"
   [(set (match_operand:QHI            0 "rl78_nonfar_nonimm_operand" "=vm,S")
 	(minus:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "vim,0")
-		   (match_operand:QHI 2 "general_operand" "vim,i")))
+		   (match_operand:QHI 2 "rl78_general_operand" "vim,i")))
    ]
   "rl78_virt_insns_ok ()"
   "v.sub\t%0, %1, %2"
 )
 
 (define_insn "*umulhi3_shift_virt"
@@ -121,31 +121,31 @@ 
   [(set_attr "valloc" "umul")]
 )
 
 (define_insn "*andqi3_virt"
   [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=vm")
 	(and:QI (match_operand:QI 1 "rl78_nonfar_operand" "vim")
-		(match_operand:QI 2 "general_operand" "vim")))
+		(match_operand:QI 2 "rl78_general_operand" "vim")))
    ]
   "rl78_virt_insns_ok ()"
   "v.and\t%0, %1, %2"
 )
 
 (define_insn "*iorqi3_virt"
   [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=vm")
 	(ior:QI (match_operand:QI 1 "rl78_nonfar_operand" "vim")
-		(match_operand:QI 2 "general_operand" "vim")))
+		(match_operand:QI 2 "rl78_general_operand" "vim")))
    ]
   "rl78_virt_insns_ok ()"
   "v.or\t%0, %1, %2"
 )
 
-(define_insn "*xor3_virt"
+(define_insn "*xorqi3_virt"
   [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=v,vm,m")
 	(xor:QI (match_operand:QI 1 "rl78_nonfar_operand" "%0,vm,vm")
-		(match_operand    2 "general_operand" "i,vm,vim")))
+		(match_operand    2 "rl78_general_operand" "i,vm,vim")))
    ]
   "rl78_virt_insns_ok ()"
   "v.xor\t%0, %1, %2"
 )
 
 ;;---------- Shifts ------------------------
@@ -340,14 +340,14 @@ 
   [(set_attr "valloc" "cmp")]
   )
 
 (define_insn "*cbranchqi4_virt"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_real"
-			      [(match_operand:QI 1 "general_operand" "vim")
-			       (match_operand:QI 2 "general_operand" "vim")])
+			      [(match_operand:QI 1 "rl78_general_operand" "vim")
+			       (match_operand:QI 2 "rl78_general_operand" "vim")])
               (label_ref (match_operand 3 "" ""))
 	      (pc)))]
   "rl78_virt_insns_ok ()"
   "v.cmp\t%1, %2\\n\tv.b%C0\t%3"
   [(set_attr "valloc" "cmp")]
   )
@@ -364,14 +364,14 @@ 
   [(set_attr "valloc" "cmp")]
   )
 
 (define_insn "*cbranchhi4_virt"
   [(set (pc) (if_then_else
 	      (match_operator 0 "rl78_cmp_operator_real"
-			      [(match_operand:HI 1 "general_operand" "vim")
-			       (match_operand:HI 2 "general_operand" "vim")])
+			      [(match_operand:HI 1 "rl78_general_operand" "vim")
+			       (match_operand:HI 2 "rl78_general_operand" "vim")])
               (label_ref (match_operand 3 "" ""))
 	      (pc)))]
   "rl78_virt_insns_ok ()"
   "v.cmpw\t%1, %2\\n\tv.b%C0\t%3"
   [(set_attr "valloc" "cmp")]
   )
Index: config/rl78/rl78.c
===================================================================
--- config/rl78/rl78.c	(revision 219790)
+++ config/rl78/rl78.c	(working copy)
@@ -82,16 +82,20 @@ 
 #include "dumpfile.h"
 #include "tree-pass.h"
 #include "context.h"
 #include "tm-constrs.h" /* for satisfies_constraint_*().  */
 #include "insn-flags.h" /* for gen_*().  */
 #include "builtins.h"
+#include "stringpool.h"
 
 static inline bool is_interrupt_func (const_tree decl);
 static inline bool is_brk_interrupt_func (const_tree decl);
 static void rl78_reorg (void);
+static const char *rl78_strip_name_encoding (const char *);
+static const char *rl78_strip_nonasm_name_encoding (const char *);
+static section * rl78_select_section (tree, int, unsigned HOST_WIDE_INT);
 
 
 /* Debugging statements are tagged with DEBUG0 only so that they can
    be easily enabled individually, by replacing the '0' with '1' as
    needed.  */
 #define DEBUG0 0
@@ -315,12 +319,35 @@  rl78_asm_file_start (void)
     };
 
   register_pass (& rl78_devirt_info);
   register_pass (& rl78_move_elim_info);
 }
 
+void
+rl78_output_symbol_ref (FILE * file, rtx sym)
+{
+  tree type = SYMBOL_REF_DECL (sym);
+  const char *str = XSTR (sym, 0);
+
+  if (str[0] == '*')
+    {
+      fputs (str + 1, file);
+    }
+  else
+    {
+      str = rl78_strip_nonasm_name_encoding (str);
+      if (type && TREE_CODE (type) == FUNCTION_DECL)
+	{
+	  fprintf (file, "%%code(");
+	  assemble_name (file, str);
+	  fprintf (file, ")");
+	}
+      else
+	assemble_name (file, str);
+    }
+}
 
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE		rl78_option_override
 
 static void
 rl78_option_override (void)
@@ -335,12 +362,19 @@  rl78_option_override (void)
     {
       int i;
 
       for (i = 24; i < 32; i++)
 	fixed_regs[i] = 0;
     }
+
+  if (TARGET_ES0
+      && strcmp (lang_hooks.name, "GNU C")
+      /* Compiling with -flto results in a language of GNU GIMPLE being used... */
+      && strcmp (lang_hooks.name, "GNU GIMPLE"))
+    /* Address spaces are currently only supported by C.  */
+    error ("-mes0 can only be used with C");
 }
 
 /* Most registers are 8 bits.  Some are 16 bits because, for example,
    gcc doesn't like dealing with $FP as a register pair (the second
    half of $fp is also 2 to keep reload happy wrt register pairs, but
    no register class includes it).  This table maps register numbers
@@ -692,25 +726,72 @@  rl78_handle_func_attribute (tree * node,
 
   /* FIXME: We ought to check that the interrupt and exception
      handler attributes have been applied to void functions.  */
   return NULL_TREE;
 }
 
+/* Check "naked" attributes.  */
+static tree
+rl78_handle_naked_attribute (tree * node,
+			     tree   name ATTRIBUTE_UNUSED,
+			     tree   args,
+			     int    flags ATTRIBUTE_UNUSED,
+			     bool * no_add_attrs)
+{
+  gcc_assert (DECL_P (* node));
+  gcc_assert (args == NULL_TREE);
+
+  if (TREE_CODE (* node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "naked attribute only applies to functions");
+      * no_add_attrs = true;
+    }
+
+  /* Disable warnings about this function - eg reaching the end without
+     seeing a return statement - because the programmer is doing things
+     that gcc does not know about.  */
+  TREE_NO_WARNING (* node) = 1;
+
+  return NULL_TREE;
+}
+
+/* Check "saddr" attributes.  */
+static tree
+rl78_handle_saddr_attribute (tree * node,
+			     tree   name,
+			     tree   args ATTRIBUTE_UNUSED,
+			     int    flags ATTRIBUTE_UNUSED,
+			     bool * no_add_attrs)
+{
+  gcc_assert (DECL_P (* node));
+
+  if (TREE_CODE (* node) == FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute doesn't apply to functions",
+	       name);
+      * no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 #undef  TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE		rl78_attribute_table
 
 /* Table of RL78-specific attributes.  */
 const struct attribute_spec rl78_attribute_table[] =
 {
   /* Name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
      affects_type_identity.  */
   { "interrupt",      0, 0, true, false, false, rl78_handle_func_attribute,
     false },
   { "brk_interrupt",  0, 0, true, false, false, rl78_handle_func_attribute,
     false },
-  { "naked",          0, 0, true, false, false, rl78_handle_func_attribute,
+  { "naked",          0, 0, true, false, false, rl78_handle_naked_attribute,
+    false },
+  { "saddr",          0, 0, true, false, false, rl78_handle_saddr_attribute,
     false },
   { NULL,             0, 0, false, false, false, NULL, false }
 };
 
 
 
@@ -745,12 +826,24 @@  characterize_address (rtx x, rtx *base, 
 
   if (GET_CODE (x) == PLUS)
     {
       *base = XEXP (x, 0);
       x = XEXP (x, 1);
 
+      if (GET_CODE (*base) == SUBREG)
+	{
+	  if (GET_MODE (*base) == HImode
+	      && GET_MODE (XEXP (*base, 0)) == SImode
+	      && GET_CODE (XEXP (*base, 0)) == REG)
+	    {
+	      /* This is a throw-away rtx just to tell everyone
+		 else what effective register we're using.  */
+	      *base = gen_rtx_REG (HImode, REGNO (XEXP (*base, 0)));
+	    }
+	}
+
       if (GET_CODE (*base) != REG
 	  && GET_CODE (x) == REG)
 	{
 	  rtx tmp = *base;
 	  *base = x;
 	  x = tmp;
@@ -778,12 +871,24 @@  characterize_address (rtx x, rtx *base, 
 	}
       /* fall through */
     case MEM:
     case REG:
       return false;
 
+    case SUBREG:
+      switch (GET_CODE (XEXP (x, 0)))
+	{
+	case CONST:
+	case SYMBOL_REF:
+	case CONST_INT:
+	  *addend = x;
+	  return true;
+	default:
+	  return false;
+	}
+
     case CONST:
     case SYMBOL_REF:
     case CONST_INT:
       *addend = x;
       return true;
 
@@ -825,24 +930,45 @@  rl78_hl_b_c_addr_p (rtx op)
 
   return true;
 }
 
 #define REG_IS(r, regno) (((r) == (regno)) || ((r) >= FIRST_PSEUDO_REGISTER && !(strict)))
 
+/* Return the appropriate mode for a named address address.  */
+
+#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE rl78_addr_space_address_mode
+
+static enum machine_mode
+rl78_addr_space_address_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    case ADDR_SPACE_GENERIC:
+      return HImode;
+    case ADDR_SPACE_NEAR:
+      return HImode;
+    case ADDR_SPACE_FAR:
+      return SImode;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Used in various constraints and predicates to match operands in the
    "far" address space.  */
 int
 rl78_far_p (rtx x)
 {
   if (! MEM_P (x))
     return 0;
 #if DEBUG0
   fprintf (stderr, "\033[35mrl78_far_p: "); debug_rtx (x);
   fprintf (stderr, " = %d\033[0m\n", MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR);
 #endif
-  return MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR;
+  return GET_MODE_BITSIZE (rl78_addr_space_address_mode (MEM_ADDR_SPACE (x))) == 32;
 }
 
 /* Return the appropriate mode for a named address pointer.  */
 #undef  TARGET_ADDR_SPACE_POINTER_MODE
 #define TARGET_ADDR_SPACE_POINTER_MODE rl78_addr_space_pointer_mode
 
@@ -850,12 +976,14 @@  static machine_mode
 rl78_addr_space_pointer_mode (addr_space_t addrspace)
 {
   switch (addrspace)
     {
     case ADDR_SPACE_GENERIC:
       return HImode;
+    case ADDR_SPACE_NEAR:
+      return HImode;
     case ADDR_SPACE_FAR:
       return SImode;
     default:
       gcc_unreachable ();
     }
 }
@@ -867,29 +995,12 @@  rl78_addr_space_pointer_mode (addr_space
 static bool
 rl78_valid_pointer_mode (machine_mode m)
 {
   return (m == HImode || m == SImode);
 }
 
-/* Return the appropriate mode for a named address address.  */
-#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
-#define TARGET_ADDR_SPACE_ADDRESS_MODE rl78_addr_space_address_mode
-
-static machine_mode
-rl78_addr_space_address_mode (addr_space_t addrspace)
-{
-  switch (addrspace)
-    {
-    case ADDR_SPACE_GENERIC:
-      return HImode;
-    case ADDR_SPACE_FAR:
-      return SImode;
-    default:
-      gcc_unreachable ();
-    }
-}
 
 #undef  TARGET_LEGITIMATE_CONSTANT_P
 #define TARGET_LEGITIMATE_CONSTANT_P		rl78_is_legitimate_constant
 
 static bool
 rl78_is_legitimate_constant (machine_mode mode ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED)
@@ -903,32 +1014,34 @@  rl78_is_legitimate_constant (machine_mod
 bool
 rl78_as_legitimate_address (machine_mode mode ATTRIBUTE_UNUSED, rtx x,
 			    bool strict ATTRIBUTE_UNUSED, addr_space_t as ATTRIBUTE_UNUSED)
 {
   rtx base, index, addend;
   bool is_far_addr = false;
+  int as_bits;
+
+  as_bits = GET_MODE_BITSIZE (rl78_addr_space_address_mode (as));
 
   if (GET_CODE (x) == UNSPEC
       && XINT (x, 1) == UNS_ES_ADDR)
     {
       x = XVECEXP (x, 0, 1);
       is_far_addr = true;
     }
 
-  if (as == ADDR_SPACE_GENERIC
-      && (GET_MODE (x) == SImode || is_far_addr))
+  if (as_bits == 16 && is_far_addr)
     return false;
 
   if (! characterize_address (x, &base, &index, &addend))
     return false;
 
   /* We can't extract the high/low portions of a PLUS address
      involving a register during devirtualization, so make sure all
      such __far addresses do not have addends.  This forces GCC to do
      the sum separately.  */
-  if (addend && base && as == ADDR_SPACE_FAR)
+  if (addend && base && as_bits == 32 && GET_MODE (base) == SImode)
     return false;
 
   if (base && index)
     {
       int ir = REGNO (index);
       int br = REGNO (base);
@@ -953,55 +1066,69 @@  rl78_as_legitimate_address (machine_mode
 #undef  TARGET_ADDR_SPACE_SUBSET_P
 #define TARGET_ADDR_SPACE_SUBSET_P rl78_addr_space_subset_p
 
 static bool
 rl78_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
 {
-  gcc_assert (subset == ADDR_SPACE_GENERIC || subset == ADDR_SPACE_FAR);
-  gcc_assert (superset == ADDR_SPACE_GENERIC || superset == ADDR_SPACE_FAR);
+  int subset_bits;
+  int superset_bits;
 
-  if (subset == superset)
-    return true;
+  subset_bits = GET_MODE_BITSIZE (rl78_addr_space_address_mode (subset));
+  superset_bits = GET_MODE_BITSIZE (rl78_addr_space_address_mode (superset));
 
-  else
-    return (subset == ADDR_SPACE_GENERIC && superset == ADDR_SPACE_FAR);
+  return (subset_bits <= superset_bits);
 }
 
 #undef  TARGET_ADDR_SPACE_CONVERT
 #define TARGET_ADDR_SPACE_CONVERT rl78_addr_space_convert
 
 /* Convert from one address space to another.  */
 static rtx
 rl78_addr_space_convert (rtx op, tree from_type, tree to_type)
 {
   addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
   addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
   rtx result;
+  int to_bits;
+  int from_bits;
 
-  gcc_assert (from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_FAR);
-  gcc_assert (to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_FAR);
+  to_bits = GET_MODE_BITSIZE (rl78_addr_space_address_mode (to_as));
+  from_bits = GET_MODE_BITSIZE (rl78_addr_space_address_mode (from_as));
 
-  if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_FAR)
+  if (to_bits < from_bits)
     {
+      rtx tmp;
       /* This is unpredictable, as we're truncating off usable address
 	 bits.  */
 
+      warning (OPT_Waddress, "converting far pointer to near pointer");
       result = gen_reg_rtx (HImode);
-      emit_move_insn (result, simplify_subreg (HImode, op, SImode, 0));
+      if (GET_CODE (op) == SYMBOL_REF
+	  || (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER))
+	tmp = gen_rtx_raw_SUBREG (HImode, op, 0);
+      else
+	tmp = simplify_subreg (HImode, op, SImode, 0);
+      gcc_assert (tmp != NULL_RTX);
+      emit_move_insn (result, tmp);
       return result;
     }
-  else if (to_as == ADDR_SPACE_FAR && from_as == ADDR_SPACE_GENERIC)
+  else if (to_bits > from_bits)
     {
       /* This always works.  */
       result = gen_reg_rtx (SImode);
       emit_move_insn (rl78_subreg (HImode, result, SImode, 0), op);
-      emit_move_insn (rl78_subreg (HImode, result, SImode, 2), const0_rtx);
+      if (TREE_CODE (from_type) == POINTER_TYPE
+	  && TREE_CODE (TREE_TYPE (from_type)) == FUNCTION_TYPE)
+	emit_move_insn (rl78_subreg (HImode, result, SImode, 2), const0_rtx);
+      else
+	emit_move_insn (rl78_subreg (HImode, result, SImode, 2), GEN_INT (0x0f));
       return result;
     }
   else
-    gcc_unreachable ();
+    return op;
+  gcc_unreachable ();
 }
 
 /* Implements REGNO_MODE_CODE_OK_FOR_BASE_P.  */
 bool
 rl78_regno_mode_code_ok_for_base_p (int regno, machine_mode mode ATTRIBUTE_UNUSED,
 				    addr_space_t address_space ATTRIBUTE_UNUSED,
@@ -1126,25 +1253,26 @@  rl78_expand_prologue (void)
   for (i = 0; i < 16; i++)
     if (cfun->machine->need_to_push [i])
       {
 	if (TARGET_G10)
 	  {
 	    if (i != 0)
-	      emit_move_insn (gen_rtx_REG (HImode, 0), gen_rtx_REG (HImode, i * 2));
-	    F (emit_insn (gen_push (gen_rtx_REG (HImode, 0))));
+	      emit_move_insn (gen_rtx_REG (HImode, AX_REG), gen_rtx_REG (HImode, i * 2));
+	    F (emit_insn (gen_push (gen_rtx_REG (HImode, AX_REG))));
 	  }
 	else
 	  {
-	    int need_bank = i/4;
+	    int need_bank = i / 4;
 
 	    if (need_bank != rb)
 	      {
 		emit_insn (gen_sel_rb (GEN_INT (need_bank)));
 		rb = need_bank;
 	      }
-	    F (emit_insn (gen_push (gen_rtx_REG (HImode, i*2))));
+	    F (emit_insn (gen_push (gen_rtx_REG (HImode, i * 2))));
+
 	  }
       }
 
   if (rb != 0)
     emit_insn (gen_sel_rb (GEN_INT (0)));
 
@@ -1202,23 +1330,23 @@  rl78_expand_epilogue (void)
 	}
     }
 
   if (is_interrupt_func (cfun->decl) && cfun->machine->uses_es)
     {
       emit_insn (gen_pop (gen_rtx_REG (HImode, AX_REG)));
-      emit_insn (gen_movqi_es (gen_rtx_REG (QImode, A_REG)));
+      emit_insn (gen_movqi_to_es (gen_rtx_REG (QImode, A_REG)));
     }
 
   for (i = 15; i >= 0; i--)
     if (cfun->machine->need_to_push [i])
       {
 	rtx dest = gen_rtx_REG (HImode, i * 2);
 
 	if (TARGET_G10)
 	  {
-	    rtx ax = gen_rtx_REG (HImode, 0);
+	    rtx ax = gen_rtx_REG (HImode, AX_REG);
 
 	    emit_insn (gen_pop (ax));
 	    if (i != 0)
 	      {
 		emit_move_insn (dest, ax);
 		/* Generate a USE of the pop'd register so that DCE will not eliminate the move.  */
@@ -1412,13 +1540,14 @@  rl78_print_operand_1 (FILE * file, rtx o
 	rl78_print_operand_1 (file, XEXP (op, 0), letter);
       else
 	{
 	  if (rl78_far_p (op))
 	    {
 	      fprintf (file, "es:");
-	      op = gen_rtx_MEM (GET_MODE (op), XVECEXP (XEXP (op, 0), 0, 1));
+	      if (GET_CODE (XEXP (op, 0)) == UNSPEC)
+		op = gen_rtx_MEM (GET_MODE (op), XVECEXP (XEXP (op, 0), 0, 1));
 	    }
 	  if (letter == 'H')
 	    {
 	      op = adjust_address (op, HImode, 2);
 	      letter = 0;
 	    }
@@ -1446,28 +1575,32 @@  rl78_print_operand_1 (FILE * file, rtx o
 	    {
 	      op = adjust_address (op, QImode, 3);
 	      letter = 0;
 	    }
 	  if (CONSTANT_P (XEXP (op, 0)))
 	    {
-	      fprintf (file, "!");
+	      if (!rl78_saddr_p (op))
+		fprintf (file, "!");
 	      rl78_print_operand_1 (file, XEXP (op, 0), letter);
 	    }
 	  else if (GET_CODE (XEXP (op, 0)) == PLUS
 		   && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF)
 	    {
-	      fprintf (file, "!");
+	      if (!rl78_saddr_p (op))
+		fprintf (file, "!");
 	      rl78_print_operand_1 (file, XEXP (op, 0), letter);
 	    }
 	  else if (GET_CODE (XEXP (op, 0)) == PLUS
 		   && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
 		   && REGNO (XEXP (XEXP (op, 0), 0)) == 2)
 	    {
 	      rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 1), 'u');
 	      fprintf (file, "[");
 	      rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 0), 0);
+	      if (letter == 'p' && GET_CODE (XEXP (op, 0)) == REG)
+		fprintf (file, "+0");
 	      fprintf (file, "]");
 	    }
 	  else
 	    {
 	      fprintf (file, "[");
 	      rl78_print_operand_1 (file, XEXP (op, 0), letter);
@@ -1510,13 +1643,21 @@  rl78_print_operand_1 (FILE * file, rtx o
 	fprintf (file, "%ld", INTVAL (op) & 0xff);
       else if (letter == 'h')
 	fprintf (file, "%ld", INTVAL (op) & 0xffff);
       else if (letter == 'e')
 	fprintf (file, "%ld", (INTVAL (op) >> 16) & 0xff);
       else if (letter == 'B')
-	fprintf (file, "%d", exact_log2 (INTVAL (op)));
+	{
+	  int ival = INTVAL (op);
+	  if (ival == -128)
+	    ival = 0x80;
+	  if (exact_log2 (ival) >= 0)
+	    fprintf (file, "%d", exact_log2 (ival));
+	  else
+	    fprintf (file, "%d", exact_log2 (~ival & 0xff));
+	}
       else if (letter == 'E')
 	fprintf (file, "%ld", (INTVAL (op) >> 24) & 0xff);
       else if (letter == 'm')
 	fprintf (file, "%ld", - INTVAL (op));
       else if (letter == 's')
 	fprintf (file, "%ld", INTVAL (op) % 8);
@@ -1594,12 +1735,33 @@  rl78_print_operand_1 (FILE * file, rtx o
 	  rl78_print_operand_1 (file, XEXP (op, 1), letter);
 	}
       if (need_paren)
 	fprintf (file, ")");
       break;
 
+    case SUBREG:
+      if (GET_MODE (op) == HImode
+	  && SUBREG_BYTE (op) == 0)
+	{
+	  fprintf (file, "%%lo16(");
+	  rl78_print_operand_1 (file, SUBREG_REG (op), 0);
+	  fprintf (file, ")");
+	}
+      else if (GET_MODE (op) == HImode
+	       && SUBREG_BYTE (op) == 2)
+	{
+	  fprintf (file, "%%hi16(");
+	  rl78_print_operand_1 (file, SUBREG_REG (op), 0);
+	  fprintf (file, ")");
+	}
+      else
+	{
+	  fprintf (file, "(%s)", GET_RTX_NAME (GET_CODE (op)));
+	}
+      break;
+
     case SYMBOL_REF:
       need_paren = 0;
       if (letter == 'H')
 	{
 	  fprintf (file, "%%hi16(");
 	  need_paren = 1;
@@ -1617,13 +1779,20 @@  rl78_print_operand_1 (FILE * file, rtx o
 	  need_paren = 1;
 	  letter = 0;
 	}
       if (letter == 'q' || letter == 'Q')
 	output_operand_lossage ("q/Q modifiers invalid for symbol references");
 
-      output_addr_const (file, op);
+      if (SYMBOL_REF_DECL (op) && TREE_CODE (SYMBOL_REF_DECL (op)) == FUNCTION_DECL)
+	{
+	  fprintf (file, "%%code(");
+	  assemble_name (file, rl78_strip_nonasm_name_encoding (XSTR (op, 0)));
+	  fprintf (file, ")");
+	}
+      else
+        assemble_name (file, rl78_strip_nonasm_name_encoding (XSTR (op, 0)));
       if (need_paren)
 	fprintf (file, ")");
       break;
 
     case CODE_LABEL:
     case LABEL_REF:
@@ -2485,94 +2654,93 @@  transcode_memory_rtx (rtx m, rtx newbase
 
   if (GET_MODE (XEXP (m, 0)) == SImode)
     {
       rtx new_m;
       rtx seg = rl78_hi8 (XEXP (m, 0));
 
-#if DEBUG_ALLOC
-      fprintf (stderr, "setting ES:\n");
-      debug_rtx(seg);
-#endif
-      emit_insn_before (EM (gen_movqi (A, seg)), before);
-      emit_insn_before (EM (gen_movqi_es (A)), before);
+      if (!TARGET_ES0)
+	{
+          emit_insn_before (EM (gen_movqi (A, seg)), before);
+          emit_insn_before (EM (gen_movqi_to_es (A)), before);
+        }
+
       record_content (A, NULL_RTX);
 
       new_m = gen_rtx_MEM (GET_MODE (m), rl78_lo16 (XEXP (m, 0)));
       MEM_COPY_ATTRIBUTES (new_m, m);
       m = new_m;
       need_es = 1;
     }
 
   characterize_address (XEXP (m, 0), & base, & index, & addendr);
   gcc_assert (index == NULL_RTX);
 
-#if DEBUG_ALLOC
-  fprintf (stderr, "\033[33m"); debug_rtx (m); fprintf (stderr, "\033[0m");
-  debug_rtx (base);
-#endif
   if (base == NULL_RTX)
     return m;
 
   if (addendr && GET_CODE (addendr) == CONST_INT)
     addend = INTVAL (addendr);
 
   gcc_assert (REG_P (base));
   gcc_assert (REG_P (newbase));
 
+  int limit = 256 - GET_MODE_SIZE (GET_MODE (m));
+
   if (REGNO (base) == SP_REG)
     {
-      if (addend >= 0 && addend  <= 255)
+      if (addend >= 0 && addend  <= limit)
 	return m;
     }
 
   /* BASE should be a virtual register.  We copy it to NEWBASE.  If
      the addend is out of range for DE/HL, we use AX to compute the full
      address.  */
 
   if (addend < 0
-      || (addend > 255 && REGNO (newbase) != 2)
-      || (addendr && GET_CODE (addendr) != CONST_INT))
+      || (addend > limit && REGNO (newbase) != BC_REG)
+      || (addendr
+	  && (GET_CODE (addendr) != CONST_INT)
+	  && ((REGNO (newbase) != BC_REG))
+	  ))
     {
       /* mov ax, vreg
 	 add ax, #imm
 	 mov hl, ax	*/
       EM (emit_insn_before (gen_movhi (AX, base), before));
       EM (emit_insn_before (gen_addhi3 (AX, AX, addendr), before));
       EM (emit_insn_before (gen_movhi (newbase, AX), before));
       record_content (AX, NULL_RTX);
       record_content (newbase, NULL_RTX);
 
       base = newbase;
       addend = 0;
+      addendr = 0;
     }
   else
     {
       base = gen_and_emit_move (newbase, base, before, true);
     }
 
   if (addend)
     {
       record_content (base, NULL_RTX);
       base = gen_rtx_PLUS (HImode, base, GEN_INT (addend));
     }
+  else if (addendr)
+    {
+      record_content (base, NULL_RTX);
+      base = gen_rtx_PLUS (HImode, base, addendr);
+    }
 
-#if DEBUG_ALLOC
-  fprintf (stderr, "\033[33m");
-  debug_rtx (m);
-#endif
   if (need_es)
     {
       m = change_address (m, GET_MODE (m), gen_es_addr (base));
       cfun->machine->uses_es = true;
     }
   else
     m = change_address (m, GET_MODE (m), base);
-#if DEBUG_ALLOC
-  debug_rtx (m);
-  fprintf (stderr, "\033[0m");
-#endif
   return m;
 }
 
 /* Copy SRC to accumulator (A or AX), placing any generated insns
    before BEFORE.  Returns accumulator RTX.  */
 static rtx
@@ -2721,13 +2889,19 @@  rl78_alloc_physical_registers_op1 (rtx_i
       OP (1) = transcode_memory_rtx (OP (1), DE, insn);
     }
   else
     {
       /* If necessary, load the operands into BC and HL.
 	 Check to see if we already have OP (0) in HL
-	 and if so, swap the order.  */
+	 and if so, swap the order.
+
+	 It is tempting to perform this optimization when OP(0) does
+	 not hold a MEM, but this leads to bigger code in general.
+	 The problem is that if OP(1) holds a MEM then swapping it
+	 into BC means a BC-relative load is used and these 3 bytes
+	 long vs 1 byte for an HL load.  */
       if (MEM_P (OP (0))
 	  && already_contains (HL, XEXP (OP (0), 0)))
 	{
 	  OP (0) = transcode_memory_rtx (OP (0), HL, insn);
 	  OP (1) = transcode_memory_rtx (OP (1), BC, insn);
 	}
@@ -3808,19 +3982,428 @@  static bool rl78_rtx_costs (rtx   x,
 	}
     }
   return false;
 }
 
 
+
+
+static GTY(()) section * saddr_section;
+static GTY(()) section * frodata_section;
+
+int
+rl78_saddr_p (rtx x)
+{
+  const char * c;
+
+  if (MEM_P (x))
+    x = XEXP (x, 0);
+  if (GET_CODE (x) == PLUS)
+    x = XEXP (x, 0);
+  if (GET_CODE (x) != SYMBOL_REF)
+    return 0;
+
+  c = XSTR (x, 0);
+  if (memcmp (c, "@s.", 3) == 0)
+    return 1;
+
+  return 0;
+}
+
+int
+rl78_sfr_p (rtx x)
+{
+  if (MEM_P (x))
+    x = XEXP (x, 0);
+  if (GET_CODE (x) != CONST_INT)
+    return 0;
+
+  if ((INTVAL (x) & 0xFF00) != 0xFF00)
+    return 0;
+
+  return 1;
+}
+
+#undef  TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING rl78_strip_name_encoding
+
+static const char *
+rl78_strip_name_encoding (const char * sym)
+{
+  while (1)
+    {
+      if (*sym == '*')
+	sym++;
+      else if (*sym == '@' && sym[2] == '.')
+	sym += 3;
+      else
+	return sym;
+    }
+}
+
+/* Like rl78_strip_name_encoding, but does not strip leading asterisks.  This
+   is important if the stripped name is going to be passed to assemble_name()
+   as that handles asterisk prefixed names in a special manner.  */
+
+static const char *
+rl78_strip_nonasm_name_encoding (const char * sym)
+{
+  while (1)
+    {
+      if (*sym == '@' && sym[2] == '.')
+	sym += 3;
+      else
+	return sym;
+    }
+}
+
+
+static int
+rl78_attrlist_to_encoding (tree list, tree decl ATTRIBUTE_UNUSED)
+{
+  while (list)
+    {
+      if (is_attribute_p ("saddr", TREE_PURPOSE (list)))
+	return 's';
+      list = TREE_CHAIN (list);
+    }
+
+  return 0;
+}
+
+#define RL78_ATTRIBUTES(decl)				\
+  (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl)		\
+                : DECL_ATTRIBUTES (decl)		\
+                  ? (DECL_ATTRIBUTES (decl))		\
+		  : TYPE_ATTRIBUTES (TREE_TYPE (decl))
+
+#undef  TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO rl78_encode_section_info
+
+static void
+rl78_encode_section_info (tree decl, rtx rtl, int first)
+{
+  rtx rtlname;
+  const char * oldname;
+  char encoding;
+  char * newname;
+  tree idp;
+  tree type;
+  tree rl78_attributes;
+
+  if (!first)
+    return;
+
+  rtlname = XEXP (rtl, 0);
+
+  if (GET_CODE (rtlname) == SYMBOL_REF)
+    oldname = XSTR (rtlname, 0);
+  else if (GET_CODE (rtlname) == MEM
+	   && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
+    oldname = XSTR (XEXP (rtlname, 0), 0);
+  else
+    gcc_unreachable ();
+
+  type = TREE_TYPE (decl);
+  if (type == error_mark_node)
+    return;
+  if (! DECL_P (decl))
+    return;
+  rl78_attributes = RL78_ATTRIBUTES (decl);
+
+  encoding = rl78_attrlist_to_encoding (rl78_attributes, decl);
+
+  if (encoding)
+    {
+      newname = (char *) alloca (strlen (oldname) + 4);
+      sprintf (newname, "@%c.%s", encoding, oldname);
+      idp = get_identifier (newname);
+      XEXP (rtl, 0) =
+	gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
+      SYMBOL_REF_WEAK (XEXP (rtl, 0)) = DECL_WEAK (decl);
+      SET_SYMBOL_REF_DECL (XEXP (rtl, 0), decl);
+    }
+}
+
+#undef  TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS 	rl78_asm_init_sections
+
+static void
+rl78_asm_init_sections (void)
+{
+  saddr_section
+    = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
+			   "\t.section .saddr,\"aw\",@progbits");
+  frodata_section
+    = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
+			   "\t.section .frodata,\"aw\",@progbits");
+}
+
+#undef  TARGET_ASM_SELECT_SECTION
+#define TARGET_ASM_SELECT_SECTION	rl78_select_section
+
+static section *
+rl78_select_section (tree decl,
+		     int reloc ATTRIBUTE_UNUSED,
+		     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
+{
+  int readonly = 1;
+
+  switch (TREE_CODE (decl))
+    {
+    case VAR_DECL:
+      if (!TREE_READONLY (decl)
+	  || TREE_SIDE_EFFECTS (decl)
+	  || !DECL_INITIAL (decl)
+	  || (DECL_INITIAL (decl) != error_mark_node
+	      && !TREE_CONSTANT (DECL_INITIAL (decl))))
+	readonly = 0;
+      break;
+    case CONSTRUCTOR:
+      if (! TREE_CONSTANT (decl))
+	readonly = 0;
+      break;
+
+    default:
+      break;
+    }
+
+  if (TREE_CODE (decl) == VAR_DECL)
+    {
+      const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+
+      if (name[0] == '@' && name[2] == '.')
+	switch (name[1])
+	  {
+	  case 's':
+	    return saddr_section;
+	  }
+
+      if (TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_FAR
+	  && readonly)
+	{
+	  return frodata_section;
+	}
+    }
+
+  if (readonly)
+    return readonly_data_section;
+
+  return data_section;
+}
+
+void
+rl78_output_labelref (FILE *file, const char *str)
+{
+  const char *str2;
+
+  str2 = targetm.strip_name_encoding (str);
+  if (str2[0] != '.')
+    fputs (user_label_prefix, file);
+  fputs (str2, file);
+}
+
+void
+rl78_output_aligned_common (FILE *stream,
+			    tree decl ATTRIBUTE_UNUSED,
+			    const char *name,
+			    int size, int align, int global)
+{
+  /* We intentionally don't use rl78_section_tag() here.  */
+  if (name[0] == '@' && name[2] == '.')
+    {
+      const char *sec = 0;
+      switch (name[1])
+	{
+	case 's':
+	  switch_to_section (saddr_section);
+	  sec = ".saddr";
+	  break;
+	}
+      if (sec)
+	{
+	  const char *name2;
+	  int p2align = 0;
+
+	  while (align > BITS_PER_UNIT)
+	    {
+	      align /= 2;
+	      p2align ++;
+	    }
+	  name2 = targetm.strip_name_encoding (name);
+	  if (global)
+	    fprintf (stream, "\t.global\t_%s\n", name2);
+	  fprintf (stream, "\t.p2align %d\n", p2align);
+	  fprintf (stream, "\t.type\t_%s,@object\n", name2);
+	  fprintf (stream, "\t.size\t_%s,%d\n", name2, size);
+	  fprintf (stream, "_%s:\n\t.zero\t%d\n", name2, size);
+	  return;
+	}
+    }
+
+  if (!global)
+    {
+      fprintf (stream, "\t.local\t");
+      assemble_name (stream, name);
+      fprintf (stream, "\n");
+    }
+  fprintf (stream, "\t.comm\t");
+  assemble_name (stream, name);
+  fprintf (stream, ",%u,%u\n", size, align / BITS_PER_UNIT);
+}
+
+#undef  TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES rl78_insert_attributes
+
+static void
+rl78_insert_attributes (tree decl, tree *attributes ATTRIBUTE_UNUSED)
+{
+  if (TARGET_ES0
+      && TREE_CODE (decl) == VAR_DECL
+      && TREE_READONLY (decl)
+      && TREE_ADDRESSABLE (decl)
+      && TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_GENERIC)
+    {
+      tree type = TREE_TYPE (decl);
+      tree attr = TYPE_ATTRIBUTES (type);
+      int q = TYPE_QUALS_NO_ADDR_SPACE (type) | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_FAR);
+      
+      TREE_TYPE (decl) = build_type_attribute_qual_variant (type, attr, q);
+    }
+}
+
+#undef  TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER rl78_asm_out_integer
+
+static bool
+rl78_asm_out_integer (rtx x, unsigned int size, int aligned_p)
+{
+  if (default_assemble_integer (x, size, aligned_p))
+    return true;
+
+  if (size == 4)
+    {
+      assemble_integer_with_op (".long\t", x);
+      return true;
+    }
+
+  return false;
+}
+ 
+
 #undef  TARGET_UNWIND_WORD_MODE
 #define TARGET_UNWIND_WORD_MODE rl78_unwind_word_mode
 
 static machine_mode
 rl78_unwind_word_mode (void)
 {
   return HImode;
 }
 
-
+#ifndef USE_COLLECT2
+#undef  TARGET_ASM_CONSTRUCTOR
+#define TARGET_ASM_CONSTRUCTOR rl78_asm_constructor
+#undef  TARGET_ASM_DESTRUCTOR
+#define TARGET_ASM_DESTRUCTOR  rl78_asm_destructor
+
+static void
+rl78_asm_ctor_dtor (rtx symbol, int priority, bool is_ctor)
+{
+  section *sec;
+
+  if (priority != DEFAULT_INIT_PRIORITY)
+    {
+      /* This section of the function is based upon code copied
+	 from: gcc/varasm.c:get_cdtor_priority_section().  */
+      char buf[16];
+
+      sprintf (buf, "%s.%.5u", is_ctor ? ".ctors" : ".dtors",
+	       MAX_INIT_PRIORITY - priority);
+      sec = get_section (buf, 0, NULL);
+    }
+  else
+    sec = is_ctor ? ctors_section : dtors_section;
+
+  assemble_addr_to_section (symbol, sec);
+}
+
+static void
+rl78_asm_constructor (rtx symbol, int priority)
+{
+  rl78_asm_ctor_dtor (symbol, priority, true);
+}
+
+static void
+rl78_asm_destructor (rtx symbol, int priority)
+{
+  rl78_asm_ctor_dtor (symbol, priority, false);
+}
+#endif /* ! USE_COLLECT2 */
+
+/* Scan backwards through the insn chain looking to see if the flags
+   have been set for a comparison of OP against OPERAND.  Start with
+   the insn *before* the current insn.  */
+
+bool
+rl78_flags_already_set (rtx op, rtx operand)
+{
+  /* We only track the Z flag.  */
+  if (GET_CODE (op) != EQ && GET_CODE (op) != NE)
+    return false;
+
+  /* This should not happen, but let's be paranoid.  */
+  if (current_output_insn == NULL_RTX)
+    return false;
+
+  rtx_insn *insn;
+  bool res = false;
+
+  for (insn = prev_nonnote_nondebug_insn (current_output_insn);
+       insn != NULL_RTX;
+       insn = prev_nonnote_nondebug_insn (insn))
+    {
+      if (LABEL_P (insn))
+	break;
+      
+      if (! INSN_P (insn))
+	continue;
+
+      /* Make sure that the insn can be recognized.  */
+      if (recog_memoized (insn) == -1)
+	continue;
+
+      enum attr_update_Z updated = get_attr_update_Z (insn);
+
+      rtx set = single_set (insn);
+      bool must_break = (set != NULL_RTX && rtx_equal_p (operand, SET_DEST (set)));
+
+      switch (updated)
+	{
+	case UPDATE_Z_NO:
+	  break;
+	case UPDATE_Z_CLOBBER:
+	  must_break = true;
+	  break;
+	case UPDATE_Z_UPDATE_Z:
+	  res = must_break;
+	  must_break = true;
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (must_break)
+	break;
+    }
+
+  /* We have to re-recognize the current insn as the call(s) to
+     get_attr_update_Z() above will have overwritten the recog_data cache.  */
+  recog_memoized (current_output_insn);
+  cleanup_subreg_operands (current_output_insn);
+  constrain_operands_cached (current_output_insn, 1);
+
+  return res;
+}
+ 
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-rl78.h"
Index: config/rl78/rl78.opt
===================================================================
--- config/rl78/rl78.opt	(revision 219790)
+++ config/rl78/rl78.opt	(working copy)
@@ -50,6 +50,10 @@  mrelax
 Target Report Optimization
 Enable assembler and linker relaxation.  Enabled by default at -Os.
 
 mg10
 Target Mask(G10) Report
 Target the RL78/G10 series
+
+mes0
+Target Mask(ES0)
+Assume ES is zero throughout program execution, use ES: for read-only data.