From: Nick Clifton Date: Tue, 27 Aug 2002 20:01:17 +0000 (+0000) Subject: config.gcc: Add v850e-*-* target. X-Git-Tag: releases/gcc-3.3.0~3142 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b43783196fe7e18c741444ccbb94ad6c7920814b;p=thirdparty%2Fgcc.git config.gcc: Add v850e-*-* target. 2002-08-27 Nick Clifton Catherine Moore Jim Wilson * config.gcc: Add v850e-*-* target. Add --with-cpu= support for v850. * config/v850/lib1funcs.asm: Add v850e callt functions. * config/v850/v850.h: Add support for v850e target. * config/v850/v850.c: Add functions to support v850e target. * config/v850/v850-protos.h: Add prototypes for new functions in v850.c. * config/v850/v850.md: Add patterns for v850e instructions. * doc/invoke.texi: Document new v850e command line switches. Co-Authored-By: Catherine Moore Co-Authored-By: Jim Wilson From-SVN: r56612 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 83d64bb4af2a..ec6d938aad80 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2002-08-27 Nick Clifton + Catherine Moore + Jim Wilson + + * config.gcc: Add v850e-*-* target. + Add --with-cpu= support for v850. + * config/v850/lib1funcs.asm: Add v850e callt functions. + * config/v850/v850.h: Add support for v850e target. + * config/v850/v850.c: Add functions to support v850e target. + * config/v850/v850-protos.h: Add prototypes for new functions in v850.c. + * config/v850/v850.md: Add patterns for v850e instructions. + * doc/invoke.texi: Document new v850e command line switches. + Tue Aug 27 18:30:47 2002 J"orn Rennecke Aldy Hernandez diff --git a/gcc/config.gcc b/gcc/config.gcc index 8432cf56d40d..145d2cf0064f 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -2643,6 +2643,21 @@ v850-*-rtems*) c_target_objs="v850-c.o" cxx_target_objs="v850-c.o" ;; +v850e-*-*) + target_cpu_default="TARGET_CPU_v850e" + tm_file="dbxelf.h elfos.h svr4.h v850/v850.h" + tm_p_file=v850/v850-protos.h + tmake_file=v850/t-v850 + md_file=v850/v850.md + out_file=v850/v850.c + if test x$stabs = xyes + then + tm_file="${tm_file} dbx.h" + fi + use_collect2=no + c_target_objs="v850-c.o" + cxx_target_objs="v850-c.o" + ;; v850-*-*) target_cpu_default="TARGET_CPU_generic" tm_file="dbxelf.h elfos.h svr4.h ${tm_file}" @@ -2952,6 +2967,22 @@ sparc*-*-*) ;; esac ;; +v850*-*-*) + case "x$with_cpu" in + x) + ;; + v850e) + target_cpu_default2="TARGET_CPU_$with_cpu" + ;; + *) + if test x$pass2done = xyes + then + echo "Unknown cpu used with --with-cpu=$with_cpu" 1>&2 + exit 1 + fi + ;; + esac + ;; esac if test "$target_cpu_default2" != "" diff --git a/gcc/config/v850/lib1funcs.asm b/gcc/config/v850/lib1funcs.asm index 621ba60a4868..fb241d4bea2d 100644 --- a/gcc/config/v850/lib1funcs.asm +++ b/gcc/config/v850/lib1funcs.asm @@ -1,5 +1,5 @@ /* libgcc routines for NEC V850. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc. This file is part of GNU CC. @@ -1114,7 +1114,7 @@ __return_r29_r31: .type __save_r31,@function /* Allocate space and save register 31 on the stack */ /* Also allocate space for the argument save area */ - /* Called via: jalr __save_r29_r31,r10 */ + /* Called via: jalr __save_r31,r10 */ __save_r31: addi -20,sp,sp st.w r31,16[sp] @@ -1130,7 +1130,7 @@ __return_r31: ld.w 16[sp],r31 addi 20,sp,sp jmp [r31] - .size __return_r29_r31,.-__return_r29_r31 + .size __return_r31,.-__return_r31 #endif /* L_save_31c */ #ifdef L_save_varargs @@ -1267,3 +1267,378 @@ __restore_all_interrupt: jmp [r10] .size __restore_all_interrupt,.-__restore_all_interrupt #endif /* L_save_all_interrupt */ + + +#if defined __v850e__ +#ifdef L_callt_save_r2_r29 + /* Put these functions into the call table area. */ + .call_table_text + + /* Allocate space and save registers 2, 20 .. 29 on the stack. */ + /* Called via: callt ctoff(__callt_save_r2_r29). */ + .align 2 +.L_save_r2_r29: + add -4, sp + st.w r2, 0[sp] + prepare {r20 - r29}, 0 + ctret + + /* Restore saved registers, deallocate stack and return to the user. */ + /* Called via: callt ctoff(__callt_return_r2_r29). */ + .align 2 +.L_return_r2_r29: + dispose 0, {r20-r29} + ld.w 0[sp], r2 + add 4, sp + jmp [r31] + + /* Place the offsets of the start of these routines into the call table. */ + .call_table_data + + .global __callt_save_r2_r29 + .type __callt_save_r2_r29,@function +__callt_save_r2_r29: .short ctoff(.L_save_r2_r29) + + .global __callt_return_r2_r29 + .type __callt_return_r2_r29,@function +__callt_return_r2_r29: .short ctoff(.L_return_r2_r29) + +#endif /* L_callt_save_r2_r29 */ + +#ifdef L_callt_save_r2_r31 + /* Put these functions into the call table area. */ + .call_table_text + + /* Allocate space and save registers 2 and 20 .. 29, 31 on the stack. */ + /* Also allocate space for the argument save area. */ + /* Called via: callt ctoff(__callt_save_r2_r31). */ + .align 2 +.L_save_r2_r31: + add -4, sp + st.w r2, 0[sp] + prepare {r20 - r29, r31}, 4 + ctret + + /* Restore saved registers, deallocate stack and return to the user. */ + /* Called via: callt ctoff(__callt_return_r2_r31). */ + .align 2 +.L_return_r2_r31: + dispose 4, {r20 - r29, r31} + ld.w 0[sp], r2 + addi 4, sp, sp + jmp [r31] + + /* Place the offsets of the start of these routines into the call table. */ + .call_table_data + + .global __callt_save_r2_r31 + .type __callt_save_r2_r31,@function +__callt_save_r2_r31: .short ctoff(.L_save_r2_r31) + + .global __callt_return_r2_r31 + .type __callt_return_r2_r31,@function +__callt_return_r2_r31: .short ctoff(.L_return_r2_r31) + +#endif /* L_callt_save_r2_r31 */ + + +#ifdef L_callt_save_r6_r9 + /* Put these functions into the call table area. */ + .call_table_text + + /* Save registers r6 - r9 onto the stack in the space reserved for them. + Use by variable argument functions. + Called via: callt ctoff(__callt_save_r6_r9). */ + .align 2 +.L_save_r6_r9: + mov ep,r1 + mov sp,ep + sst.w r6,0[ep] + sst.w r7,4[ep] + sst.w r8,8[ep] + sst.w r9,12[ep] + mov r1,ep + ctret + + /* Place the offsets of the start of this routines into the call table. */ + .call_table_data + + .global __callt_save_r6_r9 + .type __callt_save_r6_r9,@function +__callt_save_r6_r9: .short ctoff(.L_save_r6_r9) +#endif /* L_callt_save_r6_r9 */ + + +#ifdef L_callt_save_interrupt + /* Put this functions into the call table area */ + .call_table_text + + /* Save registers r1, ep, gp, r10 on stack and load up with expected values. */ + /* Called via: callt ctoff(__callt_save_interrupt). */ + .align 2 +.L_save_interrupt: + /* SP has already been moved before callt ctoff(_save_interrupt). */ + /* addi -24, sp, sp */ + st.w ep, 0[sp] + st.w gp, 4[sp] + st.w r1, 8[sp] + /* R10 has alread been saved bofore callt ctoff(_save_interrupt). */ + /* st.w r10, 12[sp] */ + mov hilo(__ep),ep + mov hilo(__gp),gp + ctret + + /* Place the offsets of the start of the routine into the call table. */ + .call_table_data + .global __callt_save_interrupt + .type __callt_save_interrupt,@function +__callt_save_interrupt: .short ctoff(.L_save_interrupt) + + .call_table_text + + /* Restore saved registers, deallocate stack and return from the interrupt. */ + /* Called via: callt ctoff(__callt_restore_itnerrupt). */ + .text + .align 2 + .globl __return_interrupt + .type __return_interrupt,@function +.L_return_interrupt: + ld.w 20[sp], r1 + ldsr r1, ctpsw + ld.w 16[sp], r1 + ldsr r1, ctpc + ld.w 12[sp], r10 + ld.w 8[sp], r1 + ld.w 4[sp], gp + ld.w 0[sp], ep + addi 24, sp, sp + reti + + /* Place the offsets of the start of the routine into the call table. */ + .call_table_data + + .global __callt_return_interrupt + .type __callt_return_interrupt,@function +__callt_return_interrupt: .short ctoff(.L_return_interrupt) + +#endif /* L_callt_save_interrupt */ + +#ifdef L_callt_save_all_interrupt + /* Put this functions into the call table area. */ + .call_table_text + + /* Save all registers except for those saved in __save_interrupt. */ + /* Allocate enough stack for all of the registers & 16 bytes of space. */ + /* Called via: callt ctoff(__callt_save_all_interrupt). */ + .align 2 +.L_save_all_interrupt: + addi -60, sp, sp + mov ep, r1 + mov sp, ep + sst.w r2, 56[ep] + sst.w r5, 52[ep] + sst.w r6, 48[ep] + sst.w r7, 44[ep] + sst.w r8, 40[ep] + sst.w r9, 36[ep] + sst.w r11, 32[ep] + sst.w r12, 28[ep] + sst.w r13, 24[ep] + sst.w r14, 20[ep] + sst.w r15, 16[ep] + sst.w r16, 12[ep] + sst.w r17, 8[ep] + sst.w r18, 4[ep] + sst.w r19, 0[ep] + mov r1, ep + + prepare {r20 - r29, r31}, 4 + ctret + + /* Restore all registers saved in __save_all_interrupt. */ + /* & deallocate the stack space. */ + /* Called via: callt ctoff(__callt_restore_all_interrupt). */ + .align 2 +.L_restore_all_interrupt: + dispose 4, {r20 - r29, r31} + + mov ep, r1 + mov sp, ep + sld.w 0 [ep], r19 + sld.w 4 [ep], r18 + sld.w 8 [ep], r17 + sld.w 12[ep], r16 + sld.w 16[ep], r15 + sld.w 20[ep], r14 + sld.w 24[ep], r13 + sld.w 28[ep], r12 + sld.w 32[ep], r11 + sld.w 36[ep], r9 + sld.w 40[ep], r8 + sld.w 44[ep], r7 + sld.w 48[ep], r6 + sld.w 52[ep], r5 + sld.w 56[ep], r2 + mov r1, ep + addi 60, sp, sp + ctret + + /* Place the offsets of the start of these routines into the call table. */ + .call_table_data + + .global __callt_save_all_interrupt + .type __callt_save_all_interrupt,@function +__callt_save_all_interrupt: .short ctoff(.L_save_all_interrupt) + + .global __callt_restore_all_interrupt + .type __callt_restore_all_interrupt,@function +__callt_restore_all_interrupt: .short ctoff(.L_restore_all_interrupt) + +#endif /* L_callt_save_all_interrupt */ + + +#define MAKE_CALLT_FUNCS( START ) \ + .call_table_text ;\ + .align 2 ;\ + /* Allocate space and save registers START .. r29 on the stack. */ ;\ + /* Called via: callt ctoff(__callt_save_START_r29). */ ;\ +.L_save_##START##_r29: ;\ + prepare { START - r29 }, 0 ;\ + ctret ;\ + ;\ + /* Restore saved registers, deallocate stack and return. */ ;\ + /* Called via: callt ctoff(__return_START_r29) */ ;\ + .align 2 ;\ +.L_return_##START##_r29: ;\ + dispose 0, { START - r29 }, r31 ;\ + ;\ + /* Place the offsets of the start of these funcs into the call table. */;\ + .call_table_data ;\ + ;\ + .global __callt_save_##START##_r29 ;\ + .type __callt_save_##START##_r29,@function ;\ +__callt_save_##START##_r29: .short ctoff(.L_save_##START##_r29 ) ;\ + ;\ + .global __callt_return_##START##_r29 ;\ + .type __callt_return_##START##_r29,@function ;\ +__callt_return_##START##_r29: .short ctoff(.L_return_##START##_r29 ) + + +#define MAKE_CALLT_CFUNCS( START ) \ + .call_table_text ;\ + .align 2 ;\ + /* Allocate space and save registers START .. r31 on the stack. */ ;\ + /* Called via: callt ctoff(__callt_save_START_r31c). */ ;\ +.L_save_##START##_r31c: ;\ + prepare { START - r29, r31}, 4 ;\ + ctret ;\ + ;\ + /* Restore saved registers, deallocate stack and return. */ ;\ + /* Called via: callt ctoff(__return_START_r31c). */ ;\ + .align 2 ;\ +.L_return_##START##_r31c: ;\ + dispose 4, { START - r29, r31}, r31 ;\ + ;\ + /* Place the offsets of the start of these funcs into the call table. */;\ + .call_table_data ;\ + ;\ + .global __callt_save_##START##_r31c ;\ + .type __callt_save_##START##_r31c,@function ;\ +__callt_save_##START##_r31c: .short ctoff(.L_save_##START##_r31c ) ;\ + ;\ + .global __callt_return_##START##_r31c ;\ + .type __callt_return_##START##_r31c,@function ;\ +__callt_return_##START##_r31c: .short ctoff(.L_return_##START##_r31c ) + + +#ifdef L_callt_save_20 + MAKE_CALLT_FUNCS (r20) +#endif +#ifdef L_callt_save_21 + MAKE_CALLT_FUNCS (r21) +#endif +#ifdef L_callt_save_22 + MAKE_CALLT_FUNCS (r22) +#endif +#ifdef L_callt_save_23 + MAKE_CALLT_FUNCS (r23) +#endif +#ifdef L_callt_save_24 + MAKE_CALLT_FUNCS (r24) +#endif +#ifdef L_callt_save_25 + MAKE_CALLT_FUNCS (r25) +#endif +#ifdef L_callt_save_26 + MAKE_CALLT_FUNCS (r26) +#endif +#ifdef L_callt_save_27 + MAKE_CALLT_FUNCS (r27) +#endif +#ifdef L_callt_save_28 + MAKE_CALLT_FUNCS (r28) +#endif +#ifdef L_callt_save_29 + MAKE_CALLT_FUNCS (r29) +#endif + +#ifdef L_callt_save_20c + MAKE_CALLT_CFUNCS (r20) +#endif +#ifdef L_callt_save_21c + MAKE_CALLT_CFUNCS (r21) +#endif +#ifdef L_callt_save_22c + MAKE_CALLT_CFUNCS (r22) +#endif +#ifdef L_callt_save_23c + MAKE_CALLT_CFUNCS (r23) +#endif +#ifdef L_callt_save_24c + MAKE_CALLT_CFUNCS (r24) +#endif +#ifdef L_callt_save_25c + MAKE_CALLT_CFUNCS (r25) +#endif +#ifdef L_callt_save_26c + MAKE_CALLT_CFUNCS (r26) +#endif +#ifdef L_callt_save_27c + MAKE_CALLT_CFUNCS (r27) +#endif +#ifdef L_callt_save_28c + MAKE_CALLT_CFUNCS (r28) +#endif +#ifdef L_callt_save_29c + MAKE_CALLT_CFUNCS (r29) +#endif + + +#ifdef L_callt_save_31c + .call_table_text + .align 2 + /* Allocate space and save register r31 on the stack. */ + /* Called via: callt ctoff(__callt_save_r31c). */ +.L_callt_save_r31c: + prepare {r31}, 4 + ctret + + /* Restore saved registers, deallocate stack and return. */ + /* Called via: callt ctoff(__return_r31c). */ + .align 2 +.L_callt_return_r31c: + dispose 4, {r31}, r31 + + /* Place the offsets of the start of these funcs into the call table. */ + .call_table_data + + .global __callt_save_r31c + .type __callt_save_r31c,@function +__callt_save_r31c: .short ctoff(.L_callt_save_r31c) + + .global __callt_return_r31c + .type __callt_return_r31c,@function +__callt_return_r31c: .short ctoff(.L_callt_return_r31c) +#endif + +#endif /* __v850e__ */ diff --git a/gcc/config/v850/t-v850 b/gcc/config/v850/t-v850 index 2452969f267d..2030dc30d3e0 100644 --- a/gcc/config/v850/t-v850 +++ b/gcc/config/v850/t-v850 @@ -29,7 +29,34 @@ LIB1ASMFUNCS = _mulsi3 \ _save_31c \ _save_varargs \ _save_interrupt \ - _save_all_interrupt + _save_all_interrupt \ + _callt_save_20 \ + _callt_save_21 \ + _callt_save_22 \ + _callt_save_23 \ + _callt_save_24 \ + _callt_save_25 \ + _callt_save_26 \ + _callt_save_27 \ + _callt_save_28 \ + _callt_save_29 \ + _callt_save_20c \ + _callt_save_21c \ + _callt_save_22c \ + _callt_save_23c \ + _callt_save_24c \ + _callt_save_25c \ + _callt_save_26c \ + _callt_save_27c \ + _callt_save_28c \ + _callt_save_29c \ + _callt_save_31c \ + _callt_save_varargs \ + _callt_save_interrupt \ + _callt_save_all_interrupt \ + _callt_save_r2_r29 \ + _callt_save_r2_r31 \ + _callt_save_r6_r9 # We want fine grained libraries, so use the new code to build the # floating point emulation libraries. @@ -50,6 +77,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c cat $(srcdir)/config/fp-bit.c >> fp-bit.c TCFLAGS = -Wa,-mwarn-signed-overflow -Wa,-mwarn-unsigned-overflow +# Create non-target specific versions of the libraries +TCFLAGS += -mno-app-regs -msmall-sld -mv850 -D__v850e__ -Wa,-mv850any v850-c.o: $(srcdir)/config/v850/v850-c.c $(RTL_H) $(TREE_H) $(CONFIG_H) $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< diff --git a/gcc/config/v850/v850-protos.h b/gcc/config/v850/v850-protos.h index 05b740adfd03..ed0a565eefbe 100644 --- a/gcc/config/v850/v850-protos.h +++ b/gcc/config/v850/v850-protos.h @@ -53,6 +53,10 @@ extern void notice_update_cc PARAMS ((rtx, rtx)); extern char * construct_save_jarl PARAMS ((rtx)); extern char * construct_restore_jr PARAMS ((rtx)); #ifdef HAVE_MACHINE_MODES +extern char * construct_dispose_instruction PARAMS ((rtx)); +extern char * construct_prepare_instruction PARAMS ((rtx)); +extern int pattern_is_ok_for_prepare PARAMS ((rtx, Mmode)); +extern int pattern_is_ok_for_dispose PARAMS ((rtx, Mmode)); extern int ep_memory_operand PARAMS ((rtx, Mmode, int)); extern int reg_or_0_operand PARAMS ((rtx, Mmode)); extern int reg_or_int5_operand PARAMS ((rtx, Mmode)); diff --git a/gcc/config/v850/v850.c b/gcc/config/v850/v850.c index 80096781ae67..3190839e8eb5 100644 --- a/gcc/config/v850/v850.c +++ b/gcc/config/v850/v850.c @@ -142,6 +142,13 @@ override_options () } } } + + /* Make sure that the US_BIT_SET mask has been correctly initialised. */ + if ((target_flags & MASK_US_MASK_SET) == 0) + { + target_flags |= MASK_US_MASK_SET; + target_flags &= ~MASK_US_BIT_SET; + } } @@ -176,6 +183,9 @@ function_arg (cum, mode, type, named) else size = GET_MODE_SIZE (mode); + if (size < 1) + return 0; + if (type) align = TYPE_ALIGN (type) / BITS_PER_UNIT; else @@ -696,6 +706,38 @@ print_operand_address (file, addr) } } +/* When assemble_integer is used to emit the offsets for a switch + table it can encounter (TRUNCATE:HI (MINUS:SI (LABEL_REF:SI) (LABEL_REF:SI))). + output_addr_const will normally barf at this, but it is OK to omit + the truncate and just emit the difference of the two labels. The + .hword directive will automatically handle the truncation for us. + + Returns 1 if rtx was handled, 0 otherwise. */ + +int +v850_output_addr_const_extra (file, x) + FILE * file; + rtx x; +{ + if (GET_CODE (x) != TRUNCATE) + return 0; + + x = XEXP (x, 0); + + /* We must also handle the case where the switch table was passed a + constant value and so has been collapsed. In this case the first + label will have been deleted. In such a case it is OK to emit + nothing, since the table will not be used. + (cf gcc.c-torture/compile/990801-1.c). */ + if (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (x, 0), 0)) == CODE_LABEL + && INSN_DELETED_P (XEXP (XEXP (x, 0), 0))) + return 1; + + output_addr_const (file, x); + return 1; +} /* Return appropriate code to load up a 1, 2, or 4 integer/floating point value. */ @@ -716,16 +758,19 @@ output_move_single (operands) { HOST_WIDE_INT value = INTVAL (src); - if (CONST_OK_FOR_J (value)) /* signed 5 bit immediate */ + if (CONST_OK_FOR_J (value)) /* Signed 5 bit immediate. */ return "mov %1,%0"; - else if (CONST_OK_FOR_K (value)) /* signed 16 bit immediate */ + else if (CONST_OK_FOR_K (value)) /* Signed 16 bit immediate. */ return "movea lo(%1),%.,%0"; - else if (CONST_OK_FOR_L (value)) /* upper 16 bits were set */ + else if (CONST_OK_FOR_L (value)) /* Upper 16 bits were set. */ return "movhi hi(%1),%.,%0"; - else /* random constant */ + /* A random constant. */ + else if (TARGET_V850E) + return "mov %1,%0"; + else return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0"; } @@ -734,16 +779,21 @@ output_move_single (operands) HOST_WIDE_INT high, low; const_double_split (src, &high, &low); - if (CONST_OK_FOR_J (high)) /* signed 5 bit immediate */ + + if (CONST_OK_FOR_J (high)) /* Signed 5 bit immediate. */ return "mov %F1,%0"; - else if (CONST_OK_FOR_K (high)) /* signed 16 bit immediate */ + else if (CONST_OK_FOR_K (high)) /* Signed 16 bit immediate. */ return "movea lo(%F1),%.,%0"; - else if (CONST_OK_FOR_L (high)) /* upper 16 bits were set */ + else if (CONST_OK_FOR_L (high)) /* Upper 16 bits were set. */ return "movhi hi(%F1),%.,%0"; - else /* random constant */ + /* A random constant. */ + else if (TARGET_V850E) + return "mov %F1,%0"; + + else return "movhi hi(%F1),%.,%0\n\tmovea lo(%F1),%0,%0"; } @@ -757,7 +807,10 @@ output_move_single (operands) || GET_CODE (src) == SYMBOL_REF || GET_CODE (src) == CONST) { - return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0"; + if (TARGET_V850E) + return "mov hilo(%1),%0"; + else + return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0"; } else if (GET_CODE (src) == HIGH) @@ -881,11 +934,25 @@ ep_memory_offset (mode, unsignedp) switch (mode) { case QImode: - max_offset = (1 << 7); + if (TARGET_SMALL_SLD) + max_offset = (1 << 4); + else if (TARGET_V850E + && ( ( unsignedp && ! TARGET_US_BIT_SET) + || (! unsignedp && TARGET_US_BIT_SET))) + max_offset = (1 << 4); + else + max_offset = (1 << 7); break; case HImode: - max_offset = (1 << 8); + if (TARGET_SMALL_SLD) + max_offset = (1 << 5); + else if (TARGET_V850E + && ( ( unsignedp && ! TARGET_US_BIT_SET) + || (! unsignedp && TARGET_US_BIT_SET))) + max_offset = (1 << 5); + else + max_offset = (1 << 8); break; case SImode: @@ -985,6 +1052,32 @@ reg_or_int5_operand (op, mode) return register_operand (op, mode); } +/* Return true if OP is either a register or a signed nine bit integer. */ + +int +reg_or_int9_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT) + return CONST_OK_FOR_O (INTVAL (op)); + + return register_operand (op, mode); +} + +/* Return true if OP is either a register or a const integer. */ + +int +reg_or_const_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT) + return TRUE; + + return register_operand (op, mode); +} + /* Return true if OP is a valid call operand. */ int @@ -1129,6 +1222,16 @@ Saved %d bytes (%d uses of register %s) in function %s, starting as insn %d, end else if (GET_CODE (SET_SRC (pattern)) == MEM) p_mem = &SET_SRC (pattern); + else if (GET_CODE (SET_SRC (pattern)) == SIGN_EXTEND + && GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM) + p_mem = &XEXP (SET_SRC (pattern), 0); + + else if (GET_CODE (SET_SRC (pattern)) == ZERO_EXTEND + && GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM) + { + p_mem = &XEXP (SET_SRC (pattern), 0); + unsignedp = TRUE; + } else p_mem = (rtx *)0; @@ -1278,6 +1381,16 @@ void v850_reorg (start_insn) else if (GET_CODE (src) == MEM) mem = src; + else if (GET_CODE (src) == SIGN_EXTEND + && GET_CODE (XEXP (src, 0)) == MEM) + mem = XEXP (src, 0); + + else if (GET_CODE (src) == ZERO_EXTEND + && GET_CODE (XEXP (src, 0)) == MEM) + { + mem = XEXP (src, 0); + unsignedp = TRUE; + } else mem = NULL_RTX; @@ -1531,8 +1644,11 @@ expand_prologue () /* Save/setup global registers for interrupt functions right now. */ if (interrupt_handler) { + if (TARGET_V850E && ! TARGET_DISABLE_CALLT) + emit_insn (gen_callt_save_interrupt ()); + else emit_insn (gen_save_interrupt ()); - + actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE; if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0) @@ -1544,7 +1660,10 @@ expand_prologue () { if (TARGET_PROLOG_FUNCTION) { - emit_insn (gen_save_r6_r9 ()); + if (TARGET_V850E && ! TARGET_DISABLE_CALLT) + emit_insn (gen_save_r6_r9_v850e ()); + else + emit_insn (gen_save_r6_r9 ()); } else { @@ -1656,7 +1775,10 @@ Saved %d bytes via prologue function (%d vs. %d) for function %s\n", /* Special case interrupt functions that save all registers for a call. */ if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0) { - emit_insn (gen_save_all_interrupt ()); + if (TARGET_V850E && ! TARGET_DISABLE_CALLT) + emit_insn (gen_callt_save_all_interrupt ()); + else + emit_insn (gen_save_all_interrupt ()); } else { @@ -1888,7 +2010,10 @@ Saved %d bytes via epilogue function (%d vs. %d) in function %s\n", for a call. */ if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0) { - emit_insn (gen_restore_all_interrupt ()); + if (TARGET_V850E && ! TARGET_DISABLE_CALLT) + emit_insn (gen_callt_restore_all_interrupt ()); + else + emit_insn (gen_restore_all_interrupt ()); } else { @@ -1926,7 +2051,12 @@ Saved %d bytes via epilogue function (%d vs. %d) in function %s\n", /* And return or use reti for interrupt handlers. */ if (interrupt_handler) - emit_jump_insn (gen_restore_interrupt ()); + { + if (TARGET_V850E && ! TARGET_DISABLE_CALLT) + emit_insn (gen_callt_return_interrupt ()); + else + emit_jump_insn (gen_return_interrupt ()); + } else if (actual_fsize) emit_jump_insn (gen_return_internal ()); else @@ -2233,10 +2363,9 @@ register_is_ok_for_epilogue (op, mode) rtx op; enum machine_mode ATTRIBUTE_UNUSED mode; { - /* The save/restore routines can only cope with registers 2, and 20 - 31 */ - return (GET_CODE (op) == REG) - && (((REGNO (op) >= 20) && REGNO (op) <= 31) - || REGNO (op) == 2); + /* The save/restore routines can only cope with registers 20 - 31. */ + return ((GET_CODE (op) == REG) + && (((REGNO (op) >= 20) && REGNO (op) <= 31))); } /* Return non-zero if the given RTX is suitable for collapsing into @@ -2817,6 +2946,392 @@ v850_insert_attributes (decl, attr_ptr) } } } + +/* Return non-zero if the given RTX is suitable + for collapsing into a DISPOSE instruction. */ + +int +pattern_is_ok_for_dispose (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + int i; + + /* If there are no registers to restore then + the dispose instruction is not suitable. */ + if (count <= 2) + return 0; + + /* The pattern matching has already established that we are performing a + function epilogue and that we are popping at least one register. We must + now check the remaining entries in the vector to make sure that they are + also register pops. There is no good reason why there should ever be + anything else in this vector, but being paranoid always helps... + + The test below performs the C equivalent of this machine description + pattern match: + + (set (match_operand:SI n "register_is_ok_for_epilogue" "r") + (mem:SI (plus:SI (reg:SI 3) + (match_operand:SI n "immediate_operand" "i")))) + */ + + for (i = 3; i < count; i++) + { + rtx vector_element = XVECEXP (op, 0, i); + rtx dest; + rtx src; + rtx plus; + + if (GET_CODE (vector_element) != SET) + return 0; + + dest = SET_DEST (vector_element); + src = SET_SRC (vector_element); + + if ( GET_CODE (dest) != REG + || GET_MODE (dest) != SImode + || ! register_is_ok_for_epilogue (dest, SImode) + || GET_CODE (src) != MEM + || GET_MODE (src) != SImode) + return 0; + + plus = XEXP (src, 0); + + if ( GET_CODE (plus) != PLUS + || GET_CODE (XEXP (plus, 0)) != REG + || GET_MODE (XEXP (plus, 0)) != SImode + || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM + || GET_CODE (XEXP (plus, 1)) != CONST_INT) + return 0; + } + + return 1; +} + +/* Construct a DISPOSE instruction that is the equivalent of + the given RTX. We have already verified that this should + be possible. */ + +char * +construct_dispose_instruction (op) + rtx op; +{ + int count = XVECLEN (op, 0); + int stack_bytes; + unsigned long int mask; + int i; + static char buff[ 100 ]; /* XXX */ + int use_callt = 0; + + if (count <= 2) + { + error ("Bogus DISPOSE construction: %d\n", count); + return NULL; + } + + /* Work out how many bytes to pop off the + stack before retrieving registers. */ + if (GET_CODE (XVECEXP (op, 0, 1)) != SET) + abort (); + if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != PLUS) + abort (); + if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) != CONST_INT) + abort (); + + stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)); + + /* Each pop will remove 4 bytes from the stack... */ + stack_bytes -= (count - 2) * 4; + + /* Make sure that the amount we are popping + will fit into the DISPOSE instruction. */ + if (stack_bytes > 128) + { + error ("Too much stack space to dispose of: %d", stack_bytes); + return NULL; + } + + /* Now compute the bit mask of registers to push. */ + mask = 0; + + for (i = 2; i < count; i++) + { + rtx vector_element = XVECEXP (op, 0, i); + + if (GET_CODE (vector_element) != SET) + abort (); + if (GET_CODE (SET_DEST (vector_element)) != REG) + abort (); + if (! register_is_ok_for_epilogue (SET_DEST (vector_element), SImode)) + abort (); + + if (REGNO (SET_DEST (vector_element)) == 2) + use_callt = 1; + else + mask |= 1 << REGNO (SET_DEST (vector_element)); + } + + if (! TARGET_DISABLE_CALLT + && (use_callt || stack_bytes == 0 || stack_bytes == 16)) + { + if (use_callt) + { + sprintf (buff, "callt ctoff(__callt_return_r2_r%d)", (mask & (1 << 31)) ? 31 : 29); + return buff; + } + else + { + for (i = 20; i < 32; i++) + if (mask & (1 << i)) + break; + + if (i == 31) + sprintf (buff, "callt ctoff(__callt_return_r31c)"); + else + sprintf (buff, "callt ctoff(__callt_return_r%d_r%d%s)", + i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : ""); + } + } + else + { + static char regs [100]; /* XXX */ + int done_one; + + /* Generate the DISPOSE instruction. Note we could just issue the + bit mask as a number as the assembler can cope with this, but for + the sake of our readers we turn it into a textual description. */ + regs[0] = 0; + done_one = 0; + + for (i = 20; i < 32; i++) + { + if (mask & (1 << i)) + { + int first; + + if (done_one) + strcat (regs, ", "); + else + done_one = 1; + + first = i; + strcat (regs, reg_names[ first ]); + + for (i++; i < 32; i++) + if ((mask & (1 << i)) == 0) + break; + + if (i > first + 1) + { + strcat (regs, " - "); + strcat (regs, reg_names[ i - 1 ] ); + } + } + } + + sprintf (buff, "dispose %d {%s}, r31", stack_bytes / 4, regs); + } + + return buff; +} + +/* Return non-zero if the given RTX is suitable + for collapsing into a PREPARE instruction. */ + +int +pattern_is_ok_for_prepare (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + int i; + + /* If there are no registers to restore then the prepare instruction + is not suitable. */ + if (count <= 1) + return 0; + + /* The pattern matching has already established that we are adjusting the + stack and pushing at least one register. We must now check that the + remaining entries in the vector to make sure that they are also register + pushes. + + The test below performs the C equivalent of this machine description + pattern match: + + (set (mem:SI (plus:SI (reg:SI 3) + (match_operand:SI 2 "immediate_operand" "i"))) + (match_operand:SI 3 "register_is_ok_for_epilogue" "r")) + + */ + + for (i = 2; i < count; i++) + { + rtx vector_element = XVECEXP (op, 0, i); + rtx dest; + rtx src; + rtx plus; + + if (GET_CODE (vector_element) != SET) + return 0; + + dest = SET_DEST (vector_element); + src = SET_SRC (vector_element); + + if ( GET_CODE (dest) != MEM + || GET_MODE (dest) != SImode + || GET_CODE (src) != REG + || GET_MODE (src) != SImode + || ! register_is_ok_for_epilogue (src, SImode) + ) + return 0; + + plus = XEXP (dest, 0); + + if ( GET_CODE (plus) != PLUS + || GET_CODE (XEXP (plus, 0)) != REG + || GET_MODE (XEXP (plus, 0)) != SImode + || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM + || GET_CODE (XEXP (plus, 1)) != CONST_INT) + return 0; + + /* If the register is being pushed somewhere other than the stack + space just aquired by the first operand then abandon this quest. + Note: the test is <= becuase both values are negative. */ + if (INTVAL (XEXP (plus, 1)) + <= INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1))) + return 0; + } + + return 1; +} + +/* Construct a PREPARE instruction that is the equivalent of + the given RTL. We have already verified that this should + be possible. */ + +char * +construct_prepare_instruction (op) + rtx op; +{ + int count = XVECLEN (op, 0); + int stack_bytes; + unsigned long int mask; + int i; + static char buff[ 100 ]; /* XXX */ + int use_callt = 0; + + if (count <= 1) + { + error ("Bogus PREPEARE construction: %d\n", count); + return NULL; + } + + /* Work out how many bytes to push onto + the stack after storing the registers. */ + if (GET_CODE (XVECEXP (op, 0, 0)) != SET) + abort (); + if (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != PLUS) + abort (); + if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) != CONST_INT) + abort (); + + stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)); + + /* Each push will put 4 bytes from the stack. */ + stack_bytes += (count - 1) * 4; + + /* Make sure that the amount we are popping + will fit into the DISPOSE instruction. */ + if (stack_bytes < -128) + { + error ("Too much stack space to prepare: %d", stack_bytes); + return NULL; + } + + /* Now compute the bit mask of registers to push. */ + mask = 0; + for (i = 1; i < count; i++) + { + rtx vector_element = XVECEXP (op, 0, i); + + if (GET_CODE (vector_element) != SET) + abort (); + if (GET_CODE (SET_SRC (vector_element)) != REG) + abort (); + if (! register_is_ok_for_epilogue (SET_SRC (vector_element), SImode)) + abort (); + + if (REGNO (SET_SRC (vector_element)) == 2) + use_callt = 1; + else + mask |= 1 << REGNO (SET_SRC (vector_element)); + } + + if ((! TARGET_DISABLE_CALLT) + && (use_callt || stack_bytes == 0 || stack_bytes == -16)) + { + if (use_callt) + { + sprintf (buff, "callt ctoff(__callt_save_r2_r%d)", (mask & (1 << 31)) ? 31 : 29 ); + return buff; + } + + for (i = 20; i < 32; i++) + if (mask & (1 << i)) + break; + + if (i == 31) + sprintf (buff, "callt ctoff(__callt_save_r31c)"); + else + sprintf (buff, "callt ctoff(__callt_save_r%d_r%d%s)", + i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : ""); + } + else + { + static char regs [100]; /* XXX */ + int done_one; + + + /* Generate the PREPARE instruction. Note we could just issue the + bit mask as a number as the assembler can cope with this, but for + the sake of our readers we turn it into a textual description. */ + regs[0] = 0; + done_one = 0; + + for (i = 20; i < 32; i++) + { + if (mask & (1 << i)) + { + int first; + + if (done_one) + strcat (regs, ", "); + else + done_one = 1; + + first = i; + strcat (regs, reg_names[ first ]); + + for (i++; i < 32; i++) + if ((mask & (1 << i)) == 0) + break; + + if (i > first + 1) + { + strcat (regs, " - "); + strcat (regs, reg_names[ i - 1 ] ); + } + } + } + + sprintf (buff, "prepare {%s}, %d", regs, (- stack_bytes) / 4); + } + + return buff; +} /* Implement `va_arg'. */ diff --git a/gcc/config/v850/v850.h b/gcc/config/v850/v850.h index aff42cb319e4..4d89c982f938 100644 --- a/gcc/config/v850/v850.h +++ b/gcc/config/v850/v850.h @@ -31,8 +31,8 @@ Boston, MA 02111-1307, USA. */ #undef STARTFILE_SPEC #undef ASM_SPEC - #define TARGET_CPU_generic 1 +#define TARGET_CPU_v850e 2 #ifndef TARGET_CPU_DEFAULT #define TARGET_CPU_DEFAULT TARGET_CPU_generic @@ -43,9 +43,22 @@ Boston, MA 02111-1307, USA. */ #define SUBTARGET_CPP_SPEC "%{!mv*:-D__v850__}" #define TARGET_VERSION fprintf (stderr, " (NEC V850)"); +/* Choose which processor will be the default. + We must pass a -mv850xx option to the assembler if no explicit -mv* option + is given, because the assembler's processor default may not be correct. */ +#if TARGET_CPU_DEFAULT == TARGET_CPU_v850e +#undef MASK_DEFAULT +#define MASK_DEFAULT MASK_V850E +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "%{!mv*:-mv850e}" +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "%{!mv*:-D__v850e__}" +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (NEC V850E)"); +#endif #define ASM_SPEC "%{mv*:-mv%*}" -#define CPP_SPEC "%{mv850ea:-D__v850ea__} %{mv850e:-D__v850e__} %{mv850:-D__v850__} %(subtarget_cpp_spec)" +#define CPP_SPEC "%{mv850e:-D__v850e__} %{mv850:-D__v850__} %(subtarget_cpp_spec)" #define EXTRA_SPECS \ { "subtarget_asm_spec", SUBTARGET_ASM_SPEC }, \ @@ -67,8 +80,15 @@ extern int target_flags; #define MASK_CPU 0x00000030 #define MASK_V850 0x00000010 +#define MASK_V850E 0x00000020 +#define MASK_SMALL_SLD 0x00000040 #define MASK_BIG_SWITCH 0x00000100 +#define MASK_NO_APP_REGS 0x00000200 +#define MASK_DISABLE_CALLT 0x00000400 + +#define MASK_US_BIT_SET 0x00001000 +#define MASK_US_MASK_SET 0x00002000 /* Macros used in the machine description to test the flags. */ @@ -107,8 +127,23 @@ extern int target_flags; /* Whether to emit 2 byte per entry or 4 byte per entry switch tables. */ #define TARGET_BIG_SWITCH (target_flags & MASK_BIG_SWITCH) -/* General debug flag */ -#define TARGET_DEBUG (target_flags & MASK_DEBUG) +/* General debug flag. */ +#define TARGET_DEBUG (target_flags & MASK_DEBUG) +#define TARGET_V850E ((target_flags & MASK_V850E) == MASK_V850E) + +#define TARGET_US_BIT_SET (target_flags & MASK_US_BIT_SET) + +/* Whether to assume that the SLD.B and SLD.H instructions only have small + displacement fields, thus allowing the generated code to run on any of + the V850 range of processors. */ +#define TARGET_SMALL_SLD (target_flags & MASK_SMALL_SLD) + +/* True if callt will not be used for function prolog & epilog. */ +#define TARGET_DISABLE_CALLT (target_flags & MASK_DISABLE_CALLT) + +/* False if r2 and r5 can be used by the compiler. True if r2 + and r5 are to be fixed registers (for compatibility with GHS). */ +#define TARGET_NO_APP_REGS (target_flags & MASK_NO_APP_REGS) /* Macro to define tables used to set the flags. This is a list in braces of pairs in braces, @@ -134,6 +169,19 @@ extern int target_flags; { "v850", MASK_V850, \ N_("Compile for the v850 processor") }, \ { "v850", -(MASK_V850 ^ MASK_CPU), "" }, \ + { "v850e", MASK_V850E, N_("Compile for v850e processor") }, \ + { "v850e", -(MASK_V850E ^ MASK_CPU), "" }, /* Make sure that the other bits are cleared. */ \ + { "small-sld", MASK_SMALL_SLD, N_("Enable the use of the short load instructions") }, \ + { "no-small-sld", -MASK_SMALL_SLD, "" }, \ + { "disable-callt", MASK_DISABLE_CALLT, \ + N_("Do not use the callt instruction") }, \ + { "no-disable-callt", -MASK_DISABLE_CALLT, "" }, \ + { "US-bit-set", (MASK_US_BIT_SET | MASK_US_MASK_SET), "" }, \ + { "no-US-bit-set", -MASK_US_BIT_SET, "" }, \ + { "no-US-bit-set", MASK_US_MASK_SET, "" }, \ + { "app-regs", -MASK_NO_APP_REGS, "" }, \ + { "no-app-regs", MASK_NO_APP_REGS, \ + N_("Do not use registers r2 and r5") }, \ { "big-switch", MASK_BIG_SWITCH, \ N_("Use 4 byte entries in switch tables") },\ { "", MASK_DEFAULT, ""}} @@ -327,6 +375,17 @@ extern struct small_memory_info small_memory[(int)SMALL_MEMORY_max]; 0, 1, 3, 4, 5, 30, 32, 33 /* fixed registers */ \ } +/* If TARGET_NO_APP_REGS is not defined then add r2 and r5 to + the pool of fixed registers. See PR 14505. */ +#define CONDITIONAL_REGISTER_USAGE \ +{ \ + if (TARGET_NO_APP_REGS) \ + { \ + fixed_regs[2] = 1; call_used_regs[2] = 1; \ + fixed_regs[5] = 1; call_used_regs[5] = 1; \ + } \ +} + /* Return number of consecutive hard regs needed starting at reg REGNO to hold something of mode MODE. @@ -457,11 +516,11 @@ enum reg_class #define CONST_OK_FOR_M(VALUE) ((unsigned)(VALUE) < 0x10000) /* 5 bit unsigned immediate in shift instructions */ #define CONST_OK_FOR_N(VALUE) ((unsigned) (VALUE) <= 31) +/* 9 bit signed immediate for word multiply instruction. */ +#define CONST_OK_FOR_O(VALUE) ((unsigned) (VALUE) + 0x100 < 0x200) -#define CONST_OK_FOR_O(VALUE) 0 #define CONST_OK_FOR_P(VALUE) 0 - #define CONST_OK_FOR_LETTER_P(VALUE, C) \ ((C) == 'I' ? CONST_OK_FOR_I (VALUE) : \ (C) == 'J' ? CONST_OK_FOR_J (VALUE) : \ @@ -851,7 +910,7 @@ extern int current_function_anonymous_args; ((C) == 'Q' ? ep_memory_operand (OP, GET_MODE (OP), 0) \ : (C) == 'R' ? special_symbolref_operand (OP, VOIDmode) \ : (C) == 'S' ? (GET_CODE (OP) == SYMBOL_REF && ! ZDA_NAME_P (XSTR (OP, 0))) \ - : (C) == 'T' ? 0 \ + : (C) == 'T' ? ep_memory_operand(OP,GET_MODE(OP),TRUE) \ : (C) == 'U' ? ((GET_CODE (OP) == SYMBOL_REF && ZDA_NAME_P (XSTR (OP, 0))) \ || (GET_CODE (OP) == CONST \ && GET_CODE (XEXP (OP, 0)) == PLUS \ @@ -1136,17 +1195,9 @@ zbss_section () \ #undef USER_LABEL_PREFIX #define USER_LABEL_PREFIX "_" -/* When assemble_integer is used to emit the offsets for a switch - table it can encounter (TRUNCATE:HI (MINUS:SI (LABEL_REF:SI) (LABEL_REF:SI))). - output_addr_const will normally barf at this, but it is OK to omit - the truncate and just emit the difference of the two labels. The - .hword directive will automatically handle the truncation for us. */ - -#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL) \ - if (GET_CODE (x) == TRUNCATE) \ - output_addr_const (FILE, XEXP (X, 0)); \ - else \ - goto FAIL; +#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL) \ + if (! v850_output_addr_const_extra (FILE, X)) \ + goto FAIL /* This says how to output the assembler to define a global uninitialized but not common symbol. */ @@ -1243,10 +1294,12 @@ zbss_section () \ /* This is how to output an element of a case-vector that is relative. */ -#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ - fprintf (FILE, "\t%s .L%d-.L%d\n", \ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t%s %s.L%d-.L%d%s\n", \ (TARGET_BIG_SWITCH ? ".long" : ".short"), \ - VALUE, REL) + (! TARGET_BIG_SWITCH && TARGET_V850E ? "(" : ""), \ + VALUE, REL, \ + (! TARGET_BIG_SWITCH && TARGET_V850E ? ")>>1" : "")) #define ASM_OUTPUT_ALIGN(FILE, LOG) \ if ((LOG) != 0) \ @@ -1426,6 +1479,9 @@ extern union tree_node * GHS_current_section_names [(int) COUNT_OF_GHS_SECTION_K { "pattern_is_ok_for_prologue", { PARALLEL }}, \ { "pattern_is_ok_for_epilogue", { PARALLEL }}, \ { "register_is_ok_for_epilogue",{ REG }}, \ +{ "pattern_is_ok_for_dispose", { PARALLEL }}, \ +{ "pattern_is_ok_for_prepare", { PARALLEL }}, \ +{ "register_is_ok_for_dispose", { REG }}, \ { "not_power_of_two_operand", { CONST_INT }}, #endif /* ! GCC_V850_H */ diff --git a/gcc/config/v850/v850.md b/gcc/config/v850/v850.md index 354dc111ba4f..75d8662fdc9c 100644 --- a/gcc/config/v850/v850.md +++ b/gcc/config/v850/v850.md @@ -1,5 +1,5 @@ ;; GCC machine description for NEC V850 -;; Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. +;; Copyright (C) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. ;; Contributed by Jeff Law (law@cygnus.com). ;; This file is part of GNU CC. @@ -153,6 +153,7 @@ must be done with HIGH & LO_SUM patterns. */ if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != HIGH + && ! TARGET_V850E && !special_symbolref_operand (operands[1], VOIDmode) && !(GET_CODE (operands[1]) == CONST_INT && (CONST_OK_FOR_J (INTVAL (operands[1])) @@ -174,6 +175,24 @@ } }") +;; This is the same as the following pattern, except that it includes +;; support for arbitrary 32 bit immediates. + +;; ??? This always loads addresses using hilo. If the only use of this address +;; was in a load/store, then we would get smaller code if we only loaded the +;; upper part with hi, and then put the lower part in the load/store insn. + +(define_insn "*movsi_internal_v850e" + [(set (match_operand:SI 0 "general_operand" "=r,r,r,r,Q,r,r,m,m,r") + (match_operand:SI 1 "general_operand" "Jr,K,L,Q,Ir,m,R,r,I,i"))] + "TARGET_V850E + && (register_operand (operands[0], SImode) + || reg_or_0_operand (operands[1], SImode))" + "* return output_move_single (operands);" + [(set_attr "length" "2,4,4,2,2,4,4,4,4,6") + (set_attr "cc" "none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit") + (set_attr "type" "other,other,other,load,other,load,other,other,other,other")]) + (define_insn "*movsi_internal" [(set (match_operand:SI 0 "general_operand" "=r,r,r,r,Q,r,r,m,m") (match_operand:SI 1 "movsi_source_operand" "Jr,K,L,Q,Ir,m,R,r,I"))] @@ -377,6 +396,94 @@ (set_attr "cc" "none_0hit,none_0hit") (set_attr "type" "mult")]) +;; ??? The scheduling info is probably wrong. + +;; ??? This instruction can also generate the 32 bit highpart, but using it +;; may increase code size counter to the desired result. + +;; ??? This instructions can also give a DImode result. + +;; ??? There is unsigned version, but it matters only for the DImode/highpart +;; results. + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "register_operand" "%0") + ;; %redact changeone /reg_or_int9/ 'register' unless nec-no-copyright + (match_operand:SI 2 "reg_or_int9_operand" "rO")))] + "TARGET_V850E" + "mul %2,%1,%." + [(set_attr "length" "4") + (set_attr "cc" "none_0hit") + (set_attr "type" "mult")]) + +;; ---------------------------------------------------------------------- +;; DIVIDE INSTRUCTIONS +;; ---------------------------------------------------------------------- + +;; ??? These insns do set the Z/N condition codes, except that they are based +;; on only one of the two results, so it doesn't seem to make sense to use +;; them. + +;; ??? The scheduling info is probably wrong. + +(define_insn "divmodsi4" + [(set (match_operand:SI 0 "register_operand" "=r") + (div:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mod:SI (match_dup 1) + (match_dup 2)))] + "TARGET_V850E" + "div %2,%0,%3" + [(set_attr "length" "4") + (set_attr "cc" "clobber") + (set_attr "type" "other")]) + +(define_insn "udivmodsi4" + [(set (match_operand:SI 0 "register_operand" "=r") + (udiv:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (umod:SI (match_dup 1) + (match_dup 2)))] + "TARGET_V850E" + "divu %2,%0,%3" + [(set_attr "length" "4") + (set_attr "cc" "clobber") + (set_attr "type" "other")]) + +;; ??? There is a 2 byte instruction for generating only the quotient. +;; However, it isn't clear how to compute the length field correctly. + +(define_insn "divmodhi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (div:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:HI 2 "register_operand" "r"))) + (set (match_operand:HI 3 "register_operand" "=r") + (mod:HI (match_dup 1) + (match_dup 2)))] + "TARGET_V850E" + "divh %2,%0,%3" + [(set_attr "length" "4") + (set_attr "cc" "clobber") + (set_attr "type" "other")]) + +;; Half-words are sign-extended by default, so we must zero extend to a word +;; here before doing the divide. + +(define_insn "udivmodhi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (udiv:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:HI 2 "register_operand" "r"))) + (set (match_operand:HI 3 "register_operand" "=r") + (umod:HI (match_dup 1) + (match_dup 2)))] + "TARGET_V850E" + "zxh %0 ; divhu %2,%0,%3" + [(set_attr "length" "4") + (set_attr "cc" "clobber") + (set_attr "type" "other")]) ;; ---------------------------------------------------------------------- ;; AND INSTRUCTIONS @@ -734,6 +841,245 @@ [(set_attr "length" "4") (set_attr "cc" "none_0hit")]) +;; ---------------------------------------------------------------------- +;; CONDITIONAL MOVE INSTRUCTIONS +;; ---------------------------------------------------------------------- + +;; Instructions using cc0 aren't allowed to have input reloads, so we must +;; hide the fact that this instruction uses cc0. We do so by including the +;; compare instruction inside it. + +;; ??? This is very ugly. The right way to do this is to modify cmpsi so +;; that it doesn't emit RTL, and then modify the bcc/scc patterns so that +;; they emit RTL for the compare instruction. Unfortunately, this requires +;; lots of changes that will be hard to sanitise. So for now, cmpsi still +;; emits RTL, and I get the compare operands here from the previous insn. + +(define_expand "movsicc" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_dup 4) (match_dup 5)]) + ;; %redact changeone /const/ 'int5' unless nec-no-copyright + (match_operand:SI 2 "reg_or_const_operand" "rJ") + ;; %redact changeone /const/ '0' unless nec-no-copyright + (match_operand:SI 3 "reg_or_const_operand" "rI")))] + "TARGET_V850E" + " +{ + rtx insn = get_last_insn_anywhere (); + + if ( (GET_CODE (operands[2]) == CONST_INT + && GET_CODE (operands[3]) == CONST_INT)) + { + int o2 = INTVAL (operands[2]); + int o3 = INTVAL (operands[3]); + + if (o2 == 1 && o3 == 0) + FAIL; /* setf */ + if (o3 == 1 && o2 == 0) + FAIL; /* setf */ + if (o2 == 0 && (o3 < -16 || o3 > 15) && exact_log2 (o3) >= 0) + FAIL; /* setf + shift */ + if (o3 == 0 && (o2 < -16 || o2 > 15) && exact_log2 (o2) >=0) + FAIL; /* setf + shift */ + if (o2 != 0) + operands[2] = copy_to_mode_reg (SImode, operands[2]); + if (o3 !=0 ) + operands[3] = copy_to_mode_reg (SImode, operands[3]); + } + else + { + if (GET_CODE (operands[2]) != REG) + operands[2] = copy_to_mode_reg (SImode,operands[2]); + if (GET_CODE (operands[3]) != REG) + operands[3] = copy_to_mode_reg (SImode, operands[3]); + } + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == cc0_rtx) + { + rtx src = SET_SRC (PATTERN (insn)); + + if (GET_CODE (src) == COMPARE) + { + operands[4] = XEXP (src, 0); + operands[5] = XEXP (src, 1); + } + else if (GET_CODE (src) == REG + || GET_CODE (src) == SUBREG) + { + operands[4] = src; + operands[5] = const0_rtx; + } + else + abort (); + } + else + abort (); +}") + +;; ??? Clobbering the condition codes is overkill. + +;; ??? We sometimes emit an unnecessary compare instruction because the +;; condition codes may have already been set by an earlier instruction, +;; but we have no code here to avoid the compare if it is unnecessary. + +(define_insn "*movsicc_normal" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_operand:SI 4 "register_operand" "r") + (match_operand:SI 5 "reg_or_int5_operand" "rJ")]) + (match_operand:SI 2 "reg_or_int5_operand" "rJ") + (match_operand:SI 3 "reg_or_0_operand" "rI")))] + "TARGET_V850E" + "cmp %5,%4 ; cmov %c1,%2,%z3,%0" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +(define_insn "*movsicc_reversed" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_operand:SI 4 "register_operand" "r") + (match_operand:SI 5 "reg_or_int5_operand" "rJ")]) + (match_operand:SI 2 "reg_or_0_operand" "rI") + (match_operand:SI 3 "reg_or_int5_operand" "rJ")))] + "TARGET_V850E" + "cmp %5,%4 ; cmov %C1,%3,%z2,%0" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +(define_insn "*movsicc_tst1" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(zero_extract:SI + (match_operand:QI 2 "memory_operand" "m") + (const_int 1) + (match_operand 3 "const_int_operand" "n")) + (const_int 0)]) + (match_operand:SI 4 "reg_or_int5_operand" "rJ") + (match_operand:SI 5 "reg_or_0_operand" "rI")))] + "TARGET_V850E" + "tst1 %3,%2 ; cmov %c1,%4,%z5,%0" + [(set_attr "length" "8") + (set_attr "cc" "clobber")]) + +(define_insn "*movsicc_tst1_reversed" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(zero_extract:SI + (match_operand:QI 2 "memory_operand" "m") + (const_int 1) + (match_operand 3 "const_int_operand" "n")) + (const_int 0)]) + (match_operand:SI 4 "reg_or_0_operand" "rI") + (match_operand:SI 5 "reg_or_int5_operand" "rJ")))] + "TARGET_V850E" + "tst1 %3,%2 ; cmov %C1,%5,%z4,%0" + [(set_attr "length" "8") + (set_attr "cc" "clobber")]) + +;; Matching for sasf requires combining 4 instructions, so we provide a +;; dummy pattern to match the first 3, which will always be turned into the +;; second pattern by subsequent combining. As above, we must include the +;; comparison to avoid input reloads in an insn using cc0. + +(define_insn "*sasf_1" + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operator 1 "comparison_operator" [(cc0) (const_int 0)]) + (ashift:SI (match_operand:SI 2 "register_operand" "") + (const_int 1))))] + "TARGET_V850E" + "* abort ();") + +(define_insn "*sasf_2" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI + (match_operator 1 "comparison_operator" + [(match_operand:SI 3 "register_operand" "r") + (match_operand:SI 4 "reg_or_int5_operand" "rJ")]) + (ashift:SI (match_operand:SI 2 "register_operand" "0") + (const_int 1))))] + "TARGET_V850E" + "cmp %4,%3 ; sasf %c1,%0" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_operand:SI 4 "register_operand" "") + (match_operand:SI 5 "reg_or_int5_operand" "")]) + (match_operand:SI 2 "const_int_operand" "") + (match_operand:SI 3 "const_int_operand" "")))] + "TARGET_V850E + && ((INTVAL (operands[2]) ^ INTVAL (operands[3])) == 1) + && ((INTVAL (operands[2]) + INTVAL (operands[3])) != 1) + && (GET_CODE (operands[5]) == CONST_INT + || REGNO (operands[0]) != REGNO (operands[5])) + && REGNO (operands[0]) != REGNO (operands[4])" + [(set (match_dup 0) (match_dup 6)) + (set (match_dup 0) + (ior:SI (match_op_dup 7 [(match_dup 4) (match_dup 5)]) + (ashift:SI (match_dup 0) (const_int 1))))] + " +{ + operands[6] = GEN_INT (INTVAL (operands[2]) >> 1); + if (INTVAL (operands[2]) & 0x1) + operands[7] = operands[1]; + else + operands[7] = gen_rtx (reverse_condition (GET_CODE (operands[1])), + GET_MODE (operands[1]), XEXP (operands[1], 0), + XEXP (operands[1], 1)); +}") +;; --------------------------------------------------------------------- +;; BYTE SWAP INSTRUCTIONS +;; --------------------------------------------------------------------- + +(define_expand "rotlhi3" + [(set (match_operand:HI 0 "register_operand" "") + (rotate:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "const_int_operand" "")))] + "TARGET_V850E" + " +{ + if (INTVAL (operands[2]) != 8) + FAIL; +}") + +(define_insn "*rotlhi3_8" + [(set (match_operand:HI 0 "register_operand" "=r") + (rotate:HI (match_operand:HI 1 "register_operand" "r") + (const_int 8)))] + "TARGET_V850E" + "bsh %1,%0" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_expand "rotlsi3" + [(set (match_operand:SI 0 "register_operand" "") + (rotate:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_V850E" + " +{ + if (INTVAL (operands[2]) != 16) + FAIL; +}") + +(define_insn "*rotlsi3_16" + [(set (match_operand:SI 0 "register_operand" "=r") + (rotate:SI (match_operand:SI 1 "register_operand" "r") + (const_int 16)))] + "TARGET_V850E" + "hsw %1,%0" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) ;; ---------------------------------------------------------------------- ;; JUMP INSTRUCTIONS @@ -921,6 +1267,20 @@ [(set_attr "length" "2") (set_attr "cc" "none")]) +(define_insn "switch" + [(set (pc) + (plus:SI + (sign_extend:SI + (mem:HI + (plus:SI (ashift:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1)) + (label_ref (match_operand 1 "" ""))))) + (label_ref (match_dup 1))))] + "TARGET_V850E" + "switch %0" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + (define_expand "casesi" [(match_operand:SI 0 "register_operand" "") (match_operand:SI 1 "register_operand" "") @@ -940,6 +1300,12 @@ /* Branch to the default label if out of range of the table. */ emit_jump_insn (gen_bgtu (operands[4])); + if (! TARGET_BIG_SWITCH && TARGET_V850E) + { + emit_jump_insn (gen_switch (reg, operands[3])); + DONE; + } + /* Shift index for the table array access. */ emit_insn (gen_ashlsi3 (reg, reg, GEN_INT (TARGET_BIG_SWITCH ? 2 : 1))); /* Load the table address into a pseudo. */ @@ -1084,6 +1450,18 @@ ;; EXTEND INSTRUCTIONS ;; ---------------------------------------------------------------------- +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (zero_extend:SI + (match_operand:HI 1 "nonimmediate_operand" "0,r,T,m")))] + "TARGET_V850E" + "@ + zxh %0 + andi 65535,%1,%0 + sld.hu %1,%0 + ld.hu %1,%0" + [(set_attr "length" "2,4,2,4") + (set_attr "cc" "none_0hit,set_znv,none_0hit,none_0hit")]) (define_insn "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "=r") @@ -1094,6 +1472,18 @@ [(set_attr "length" "4") (set_attr "cc" "set_znv")]) +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (zero_extend:SI + (match_operand:QI 1 "nonimmediate_operand" "0,r,T,m")))] + "TARGET_V850E" + "@ + zxb %0 + andi 255,%1,%0 + sld.bu %1,%0 + ld.bu %1,%0" + [(set_attr "length" "2,4,2,4") + (set_attr "cc" "none_0hit,set_znv,none_0hit,none_0hit")]) (define_insn "zero_extendqisi2" [(set (match_operand:SI 0 "register_operand" "=r") @@ -1106,6 +1496,18 @@ ;;- sign extension instructions +;; ??? The extendhisi2 pattern should not emit shifts for v850e? + +(define_insn "*extendhisi_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,Q,m")))] + "TARGET_V850E" + "@ + sxh %0 + sld.h %1,%0 + ld.h %1,%0" + [(set_attr "length" "2,2,4") + (set_attr "cc" "none_0hit,none_0hit,none_0hit")]) ;; ??? This is missing a sign extend from memory pattern to match the ld.h ;; instruction. @@ -1124,6 +1526,18 @@ operands[2] = gen_reg_rtx (SImode); }") +;; ??? The extendqisi2 pattern should not emit shifts for v850e? + +(define_insn "*extendqisi_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,Q,m")))] + "TARGET_V850E" + "@ + sxb %0 + sld.b %1,%0 + ld.b %1,%0" + [(set_attr "length" "2,2,4") + (set_attr "cc" "none_0hit,none_0hit,none_0hit")]) ;; ??? This is missing a sign extend from memory pattern to match the ld.b ;; instruction. @@ -1229,6 +1643,21 @@ ;; RTXs. These RTXs will then be turned into a suitable call to a worker ;; function. +;; +;; Actually, convert the RTXs into a PREPARE instruction. +;; +(define_insn "" + [(match_parallel 0 "pattern_is_ok_for_prepare" + [(set (reg:SI 3) + (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) + (set (mem:SI (plus:SI (reg:SI 3) + (match_operand:SI 2 "immediate_operand" "i"))) + (match_operand:SI 3 "register_is_ok_for_epilogue" "r"))])] + "TARGET_PROLOG_FUNCTION && TARGET_V850E" + "* return construct_prepare_instruction (operands[0]); + " + [(set_attr "length" "4") + (set_attr "cc" "none")]) (define_insn "" [(match_parallel 0 "pattern_is_ok_for_prologue" @@ -1245,6 +1674,23 @@ (const_string "4"))) (set_attr "cc" "clobber")]) +;; +;; Actually, turn the RTXs into a DISPOSE instruction. +;; +(define_insn "" + [(match_parallel 0 "pattern_is_ok_for_dispose" + [(return) + (set (reg:SI 3) + (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) + (set (match_operand:SI 2 "register_is_ok_for_epilogue" "=r") + (mem:SI (plus:SI (reg:SI 3) + (match_operand:SI 3 "immediate_operand" "i"))))])] + "TARGET_PROLOG_FUNCTION && TARGET_V850E" + "* return construct_dispose_instruction (operands[0]); + " + [(set_attr "length" "4") + (set_attr "cc" "none")]) + ;; This pattern will match a return RTX followed by any number of pop RTXs ;; and possible a stack adjustment as well. These RTXs will be turned into ;; a suitable call to a worker function. @@ -1266,31 +1712,95 @@ (set_attr "cc" "clobber")]) ;; Initialize an interrupt function. Do not depend on TARGET_PROLOG_FUNCTION. +(define_insn "callt_save_interrupt" + [(unspec_volatile [(const_int 0)] 2)] + "TARGET_V850E && !TARGET_DISABLE_CALLT" + ;; The CALLT instruction stores the next address of CALLT to CTPC register + ;; without saving its previous value. So if the interrupt handler + ;; or its caller could possibily execute the CALLT insn, save_interrupt + ;; MUST NOT be called via CALLT. + "* +{ + output_asm_insn (\"addi -24, sp, sp\", operands); + output_asm_insn (\"st.w r10, 12[sp]\", operands); + output_asm_insn (\"stsr ctpc, r10\", operands); + output_asm_insn (\"st.w r10, 16[sp]\", operands); + output_asm_insn (\"stsr ctpsw, r10\", operands); + output_asm_insn (\"st.w r10, 20[sp]\", operands); + output_asm_insn (\"callt ctoff(__callt_save_interrupt)\", operands); + return \"\"; +}" + [(set_attr "length" "26") + (set_attr "cc" "none")]) + +(define_insn "callt_return_interrupt" + [(unspec_volatile [(const_int 0)] 3)] + "TARGET_V850E && !TARGET_DISABLE_CALLT" + "callt ctoff(__callt_return_interrupt)" + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + (define_insn "save_interrupt" [(set (reg:SI 3) (plus:SI (reg:SI 3) (const_int -16))) - (set (mem:SI (reg:SI 3)) (reg:SI 30)) - (set (mem:SI (plus:SI (reg:SI 3) (const_int -4))) (reg:SI 10)) - (set (mem:SI (plus:SI (reg:SI 3) (const_int -8))) (reg:SI 4)) - (set (mem:SI (plus:SI (reg:SI 3) (const_int -12))) (reg:SI 1))] - "TARGET_V850 && ! TARGET_LONG_CALLS" - "add -16, sp ; st.w r10, 12[sp] ; jarl __save_interrupt, r10" - [(set_attr "length" "12") + (set (mem:SI (plus:SI (reg:SI 3) (const_int -16))) (reg:SI 30)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int -12))) (reg:SI 4)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int -8))) (reg:SI 1)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int -4))) (reg:SI 10))] + "" + "* +{ + if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) + return \"add -16,sp\;st.w r10,12[sp]\;jarl __save_interrupt,r10\"; + else + { + output_asm_insn (\"add -16, sp\", operands); + output_asm_insn (\"st.w r10, 12[sp]\", operands); + output_asm_insn (\"st.w ep, 0[sp]\", operands); + output_asm_insn (\"st.w gp, 4[sp]\", operands); + output_asm_insn (\"st.w r1, 8[sp]\", operands); + output_asm_insn (\"movhi hi(__ep), r0, ep\", operands); + output_asm_insn (\"movea lo(__ep), ep, ep\", operands); + output_asm_insn (\"movhi hi(__gp), r0, gp\", operands); + output_asm_insn (\"movea lo(__gp), gp, gp\", operands); + return \"\"; + } +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_LONG_CALLS") (const_int 0)) + (const_int 10) + (const_int 34))) (set_attr "cc" "clobber")]) - + ;; Restore r1, r4, r10, and return from the interrupt -(define_insn "restore_interrupt" +(define_insn "return_interrupt" [(return) - (set (reg:SI 3) (plus:SI (reg:SI 3) (const_int 16))) - (set (reg:SI 30) (mem:SI (plus:SI (reg:SI 3) (const_int 12)))) - (set (reg:SI 10) (mem:SI (plus:SI (reg:SI 3) (const_int 8)))) - (set (reg:SI 4) (mem:SI (plus:SI (reg:SI 3) (const_int 4)))) - (set (reg:SI 1) (mem:SI (reg:SI 3)))] + (set (reg:SI 3) (plus:SI (reg:SI 3) (const_int 16))) + (set (reg:SI 10) (mem:SI (plus:SI (reg:SI 3) (const_int 12)))) + (set (reg:SI 1) (mem:SI (plus:SI (reg:SI 3) (const_int 8)))) + (set (reg:SI 4) (mem:SI (plus:SI (reg:SI 3) (const_int 4)))) + (set (reg:SI 30) (mem:SI (reg:SI 3)))] "" - "jr __return_interrupt" - [(set_attr "length" "4") + "* +{ + if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) + return \"jr __return_interrupt\"; + else + { + output_asm_insn (\"ld.w 0[sp], ep\", operands); + output_asm_insn (\"ld.w 4[sp], gp\", operands); + output_asm_insn (\"ld.w 8[sp], r1\", operands); + output_asm_insn (\"ld.w 12[sp], r10\", operands); + output_asm_insn (\"addi 16, sp, sp\", operands); + output_asm_insn (\"reti\", operands); + return \"\"; + } +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_LONG_CALLS") (const_int 0)) + (const_int 4) + (const_int 24))) (set_attr "cc" "clobber")]) - ;; Save all registers except for the registers saved in save_interrupt when ;; an interrupt function makes a call. ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and @@ -1298,21 +1808,130 @@ ;; This is needed because the rest of the compiler is not ready to handle ;; insns this complicated. +(define_insn "callt_save_all_interrupt" + [(unspec_volatile [(const_int 0)] 0)] + "TARGET_V850E && !TARGET_DISABLE_CALLT" + "callt ctoff(__callt_save_all_interrupt)" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + (define_insn "save_all_interrupt" + [(unspec_volatile [(const_int 0)] 0)] + "" + "* +{ + if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) + return \"jarl __save_all_interrupt,r10\"; + + output_asm_insn (\"addi -120, sp, sp\", operands); + output_asm_insn (\"mov ep, r1\", operands); + output_asm_insn (\"mov sp, ep\", operands); + output_asm_insn (\"sst.w r31, 116[ep]\", operands); + output_asm_insn (\"sst.w r2, 112[ep]\", operands); + output_asm_insn (\"sst.w gp, 108[ep]\", operands); + output_asm_insn (\"sst.w r6, 104[ep]\", operands); + output_asm_insn (\"sst.w r7, 100[ep]\", operands); + output_asm_insn (\"sst.w r8, 96[ep]\", operands); + output_asm_insn (\"sst.w r9, 92[ep]\", operands); + output_asm_insn (\"sst.w r11, 88[ep]\", operands); + output_asm_insn (\"sst.w r12, 84[ep]\", operands); + output_asm_insn (\"sst.w r13, 80[ep]\", operands); + output_asm_insn (\"sst.w r14, 76[ep]\", operands); + output_asm_insn (\"sst.w r15, 72[ep]\", operands); + output_asm_insn (\"sst.w r16, 68[ep]\", operands); + output_asm_insn (\"sst.w r17, 64[ep]\", operands); + output_asm_insn (\"sst.w r18, 60[ep]\", operands); + output_asm_insn (\"sst.w r19, 56[ep]\", operands); + output_asm_insn (\"sst.w r20, 52[ep]\", operands); + output_asm_insn (\"sst.w r21, 48[ep]\", operands); + output_asm_insn (\"sst.w r22, 44[ep]\", operands); + output_asm_insn (\"sst.w r23, 40[ep]\", operands); + output_asm_insn (\"sst.w r24, 36[ep]\", operands); + output_asm_insn (\"sst.w r25, 32[ep]\", operands); + output_asm_insn (\"sst.w r26, 28[ep]\", operands); + output_asm_insn (\"sst.w r27, 24[ep]\", operands); + output_asm_insn (\"sst.w r28, 20[ep]\", operands); + output_asm_insn (\"sst.w r29, 16[ep]\", operands); + output_asm_insn (\"mov r1, ep\", operands); + return \"\"; +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_LONG_CALLS") (const_int 0)) + (const_int 4) + (const_int 62) + )) + (set_attr "cc" "clobber")]) + +(define_insn "_save_all_interrupt" [(unspec_volatile [(const_int 0)] 0)] "TARGET_V850 && ! TARGET_LONG_CALLS" "jarl __save_all_interrupt,r10" [(set_attr "length" "4") (set_attr "cc" "clobber")]) - ;; Restore all registers saved when an interrupt function makes a call. ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and ;; all of memory. This blocks insns from being moved across this point. ;; This is needed because the rest of the compiler is not ready to handle ;; insns this complicated. +(define_insn "callt_restore_all_interrupt" + [(unspec_volatile [(const_int 0)] 1)] + "TARGET_V850E && !TARGET_DISABLE_CALLT" + "callt ctoff(__callt_restore_all_interrupt)" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + (define_insn "restore_all_interrupt" + [(unspec_volatile [(const_int 0)] 1)] + "" + "* +{ + if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) + return \"jarl __restore_all_interrupt,r10\"; + else + { + output_asm_insn (\"mov ep, r1\", operands); + output_asm_insn (\"mov sp, ep\", operands); + output_asm_insn (\"sld.w 116[ep], r31\", operands); + output_asm_insn (\"sld.w 112[ep], r2\", operands); + output_asm_insn (\"sld.w 108[ep], gp\", operands); + output_asm_insn (\"sld.w 104[ep], r6\", operands); + output_asm_insn (\"sld.w 100[ep], r7\", operands); + output_asm_insn (\"sld.w 96[ep], r8\", operands); + output_asm_insn (\"sld.w 92[ep], r9\", operands); + output_asm_insn (\"sld.w 88[ep], r11\", operands); + output_asm_insn (\"sld.w 84[ep], r12\", operands); + output_asm_insn (\"sld.w 80[ep], r13\", operands); + output_asm_insn (\"sld.w 76[ep], r14\", operands); + output_asm_insn (\"sld.w 72[ep], r15\", operands); + output_asm_insn (\"sld.w 68[ep], r16\", operands); + output_asm_insn (\"sld.w 64[ep], r17\", operands); + output_asm_insn (\"sld.w 60[ep], r18\", operands); + output_asm_insn (\"sld.w 56[ep], r19\", operands); + output_asm_insn (\"sld.w 52[ep], r20\", operands); + output_asm_insn (\"sld.w 48[ep], r21\", operands); + output_asm_insn (\"sld.w 44[ep], r22\", operands); + output_asm_insn (\"sld.w 40[ep], r23\", operands); + output_asm_insn (\"sld.w 36[ep], r24\", operands); + output_asm_insn (\"sld.w 32[ep], r25\", operands); + output_asm_insn (\"sld.w 28[ep], r26\", operands); + output_asm_insn (\"sld.w 24[ep], r27\", operands); + output_asm_insn (\"sld.w 20[ep], r28\", operands); + output_asm_insn (\"sld.w 16[ep], r29\", operands); + output_asm_insn (\"mov r1, ep\", operands); + output_asm_insn (\"addi 120, sp, sp\", operands); + return \"\"; + } +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_LONG_CALLS") (const_int 0)) + (const_int 4) + (const_int 62) + )) + (set_attr "cc" "clobber")]) + +(define_insn "_restore_all_interrupt" [(unspec_volatile [(const_int 0)] 1)] "TARGET_V850 && ! TARGET_LONG_CALLS" "jarl __restore_all_interrupt,r10" @@ -1320,6 +1939,17 @@ (set_attr "cc" "clobber")]) ;; Save r6-r9 for a variable argument function +(define_insn "save_r6_r9_v850e" + [(set (mem:SI (reg:SI 3)) (reg:SI 6)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int 4))) (reg:SI 7)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int 8))) (reg:SI 8)) + (set (mem:SI (plus:SI (reg:SI 3) (const_int 12))) (reg:SI 9)) + ] + "TARGET_PROLOG_FUNCTION && TARGET_V850E && !TARGET_DISABLE_CALLT" + "callt ctoff(__callt_save_r6_r9)" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + (define_insn "save_r6_r9" [(set (mem:SI (reg:SI 3)) (reg:SI 6)) (set (mem:SI (plus:SI (reg:SI 3) (const_int 4))) (reg:SI 7)) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d2fc0f74d0e7..1ce61bd451d9 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -577,6 +577,9 @@ in the following sections. -mlong-calls -mno-long-calls -mep -mno-ep @gol -mprolog-function -mno-prolog-function -mspace @gol -mtda=@var{n} -msda=@var{n} -mzda=@var{n} @gol +-mapp-regs -mno-app-regs @gol +-mdisable-callt -mno-disable-callt @gol +-mv850e @gol -mv850 -mbig-switch} @emph{NS32K Options} @@ -8682,6 +8685,34 @@ Specify that the target processor is the V850. Generate code suitable for big switch tables. Use this option only if the assembler/linker complain about out of range branches within a switch table. + +@item -mapp-regs +@opindex -mapp-regs +This option will cause r2 and r5 to be used in the code generated by +the compiler. This setting is the default. + +@item -mno-app-regs +@opindex -mno-app-regs +This option will cause r2 and r5 to be treated as fixed registers. + +@item -mv850e +@opindex -mv850e +Specify that the target processor is the V850E. The preprocessor +constant @samp{__v850e__} will be defined if this option is used. + +If neither @option{-mv850} nor @option{-mv850e} are defined +then a default target processor will be chosen and the relevant +@samp{__v850*__} preprocessor constant will be defined. + +The preprocessor constants @samp{__v850} and @samp{__v851__} are always +defined, regardless of which processor variant is the target. + +@item -mdisable-callt +@opindex -mdisable-callt +This option will suppress generation of the CALLT instruction for the +v850e flavors of the v850 architecture. The default is +@option{-mno-disable-callt} which allows the CALLT instruction to be used. + @end table @node ARC Options