re PR c/87795 (Excessive alignment permitted for functions and labels)

* config/pdp11/constraints.md: Add "Z" series constraints for use
	with pre-dec and post-inc addressing.
	* config/pdp11/pdp11-protos.m (expand_block_move): Delete.
	(pdp11_expand_operands): Add int argument (word count).
	(pdp11_sp_frame_offset): Delete.
	(pdp11_cmp_length): New function.
	(pushpop_regeq): New function.
	* config/pdp11/pdp11.c (TARGET_STACK_PROTECT_RUNTIME_ENABLED_P):
	Add hook.
	(pdp11_expand_prologue, pdp11_expand_epilogue): Rewrite for new
	frame layout.
	(pdp11_initial_elimination_offset): Ditto.
	(pdp11_expand_operands): Add word count argument.  Bugfixes.
	(output_move_multiple): Change how pointer adjustment is done.
	(pdp11_gen_int_label): Correct format.
	(output_ascii): Ditto.
	(pdp11_asm_output_var): Add code for DEC assembler case.
	(pdp11_asm_print_operand): Bugfix for CONST_DOUBLE holding integer
	value.
	(legitimate_const_double_p): Ditto.
	(pdp11_register_move_cost): Adjust for new register classes.
	(pdp11_regno_reg_class): Ditto.
	(expand_block_move): Delete.
	(pushpop_regeq): New function.
	(pdp11_legitimate_address_p): Bugfix in check for constant
	offset.
	(pdp11_sp_frame_offset): Delete.
	(pdp11_reg_save_size): New helper function for new frame layout.
	(output_addr_const_pdp11): Remove CONST_DOUBLE case.
	(pdp11_expand_shift): Bugfix in check for constant shift count.
	(pdp11_shift_length): Ditto.
	(pdp11_assemble_shift): Copy input to pdp11_expand_operands.
	(pdp11_cmp_length): New function.
	* config/pdp11/pdp11.h (TARGET_CPU_CPP_BUILTINS): Add macros for
	some compile options.
	(FIXED_REGISTERS): Remove HARD_FRAME_POINTER_REGNUM.
	(CALL_USED_REGISTERS): Ditto.
	(ELIMINABLE_REGS): Ditto.
	(REGISTER_NAMES): Ditto.
	(reg_class): Add classes NOTR0_REG through NOTSP_REG for use by Z
	constraints.
	(REG_CLASS_NAMES): Ditto.
	(REG_CLASS_CONTENTS): Ditto.  Also remove
	HARD_FRAME_POINTER_REGNUM.
	(CPU_REG_CLASS): New macro.
	(CLASS_MAX_NREGS): Adjust for new register classes.
	(FUNCTION_PROFILER): Make no-op.
	(may_call_alloca): Remove unused declaration.
	(ASM_OUTPUT_ALIGN): Add workaround for PR87795.
	(ASM_OUTPUT_SKIP): Fix format.
	* config/pdp11/pdp11.md (unspecv): Add UNSPECV_MOVMEM.
	(HARD_FRAME_POINTER_REGNUM): Remove.
	(return): Delete.
	(*rts): Rename.  Remove epilogue related checks.
	(cmpsi, cmpdi): New insn.
	(cbranch<mode>4): Change to apply to SI and DI modes as well.
	(mov<mode>): Change constraints to enforce that push/pop
	destination cannot use the same register as source.
	(*mov<mode><cc_cc>): Ditto.
	(movmemhi, movmemhi1, movmemhi_nocc): Change to expand block move
	at assembly output rather than as RTL expander.
	(zero_extendqihi2): Bugfix in check for same registers.
	(adddi3_nocc): Bugfix in check for constant operand.
	(addsi3_nocc): Ditto.
	(subdi3_nocc): Ditto.
	(subsi3_nocc): Ditto.
	(negdi2_nocc): Copy input to pdp11_expand_operands.
	(negsi2_nocc): Ditto.
	(bswap2_nocc): Ditto.
	* config/pdp11/pdp11.opt (mlra): Fix documentation.
	* config/pdp11/t-pdp11: Use -Os.

From-SVN: r265932
This commit is contained in:
Paul Koning 2018-11-08 13:56:58 -05:00 committed by Paul Koning
parent d4f680c672
commit 442fcea74d
8 changed files with 607 additions and 370 deletions

View file

@ -1,3 +1,77 @@
2018-11-08 Paul Koning <ni1d@arrl.net>
* config/pdp11/constraints.md: Add "Z" series constraints for use
with pre-dec and post-inc addressing.
* config/pdp11/pdp11-protos.m (expand_block_move): Delete.
(pdp11_expand_operands): Add int argument (word count).
(pdp11_sp_frame_offset): Delete.
(pdp11_cmp_length): New function.
(pushpop_regeq): New function.
* config/pdp11/pdp11.c (TARGET_STACK_PROTECT_RUNTIME_ENABLED_P):
Add hook.
(pdp11_expand_prologue, pdp11_expand_epilogue): Rewrite for new
frame layout.
(pdp11_initial_elimination_offset): Ditto.
(pdp11_expand_operands): Add word count argument. Bugfixes.
(output_move_multiple): Change how pointer adjustment is done.
(pdp11_gen_int_label): Correct format.
(output_ascii): Ditto.
(pdp11_asm_output_var): Add code for DEC assembler case.
(pdp11_asm_print_operand): Bugfix for CONST_DOUBLE holding integer
value.
(legitimate_const_double_p): Ditto.
(pdp11_register_move_cost): Adjust for new register classes.
(pdp11_regno_reg_class): Ditto.
(expand_block_move): Delete.
(pushpop_regeq): New function.
(pdp11_legitimate_address_p): Bugfix in check for constant
offset.
(pdp11_sp_frame_offset): Delete.
(pdp11_reg_save_size): New helper function for new frame layout.
(output_addr_const_pdp11): Remove CONST_DOUBLE case.
(pdp11_expand_shift): Bugfix in check for constant shift count.
(pdp11_shift_length): Ditto.
(pdp11_assemble_shift): Copy input to pdp11_expand_operands.
(pdp11_cmp_length): New function.
* config/pdp11/pdp11.h (TARGET_CPU_CPP_BUILTINS): Add macros for
some compile options.
(FIXED_REGISTERS): Remove HARD_FRAME_POINTER_REGNUM.
(CALL_USED_REGISTERS): Ditto.
(ELIMINABLE_REGS): Ditto.
(REGISTER_NAMES): Ditto.
(reg_class): Add classes NOTR0_REG through NOTSP_REG for use by Z
constraints.
(REG_CLASS_NAMES): Ditto.
(REG_CLASS_CONTENTS): Ditto. Also remove
HARD_FRAME_POINTER_REGNUM.
(CPU_REG_CLASS): New macro.
(CLASS_MAX_NREGS): Adjust for new register classes.
(FUNCTION_PROFILER): Make no-op.
(may_call_alloca): Remove unused declaration.
(ASM_OUTPUT_ALIGN): Add workaround for PR87795.
(ASM_OUTPUT_SKIP): Fix format.
* config/pdp11/pdp11.md (unspecv): Add UNSPECV_MOVMEM.
(HARD_FRAME_POINTER_REGNUM): Remove.
(return): Delete.
(*rts): Rename. Remove epilogue related checks.
(cmpsi, cmpdi): New insn.
(cbranch<mode>4): Change to apply to SI and DI modes as well.
(mov<mode>): Change constraints to enforce that push/pop
destination cannot use the same register as source.
(*mov<mode><cc_cc>): Ditto.
(movmemhi, movmemhi1, movmemhi_nocc): Change to expand block move
at assembly output rather than as RTL expander.
(zero_extendqihi2): Bugfix in check for same registers.
(adddi3_nocc): Bugfix in check for constant operand.
(addsi3_nocc): Ditto.
(subdi3_nocc): Ditto.
(subsi3_nocc): Ditto.
(negdi2_nocc): Copy input to pdp11_expand_operands.
(negsi2_nocc): Ditto.
(bswap2_nocc): Ditto.
* config/pdp11/pdp11.opt (mlra): Fix documentation.
* config/pdp11/t-pdp11: Use -Os.
2018-11-08 Richard Earnshaw <rearnsha@arm.com>
* config/arm/parsecpu.awk (/alias/): New parsing rule.

View file

@ -88,3 +88,32 @@
(match_test "memory_address_p (GET_MODE (op), XEXP (op, 0))
&& no_side_effect_operand (op, GET_MODE (op))")))
;; What follows is a set of constraints used to prevent the generation
;; of insns that have a register as source, and an auto-increment or
;; auto-decrement memory reference as the destination where the register
;; is the same as the source. On the PDP11, such instructions are not
;; implemented consistently across the models and often do something
;; different from what the RTL intends.
(define_register_constraint "Z0" "NOTR0_REG" "Register other than 0")
(define_register_constraint "Z1" "NOTR1_REG" "Register other than 1")
(define_register_constraint "Z2" "NOTR2_REG" "Register other than 2")
(define_register_constraint "Z3" "NOTR3_REG" "Register other than 3")
(define_register_constraint "Z4" "NOTR4_REG" "Register other than 4")
(define_register_constraint "Z5" "NOTR5_REG" "Register other than 5")
(define_register_constraint "Z6" "NOTSP_REG"
"Register other than stack pointer (register 6)")
(define_memory_constraint "Za" "R0 push/pop"
(match_test "pushpop_regeq (op, 0)"))
(define_memory_constraint "Zb" "R1 push/pop"
(match_test "pushpop_regeq (op, 1)"))
(define_memory_constraint "Zc" "R2 push/pop"
(match_test "pushpop_regeq (op, 2)"))
(define_memory_constraint "Zd" "R3 push/pop"
(match_test "pushpop_regeq (op, 3)"))
(define_memory_constraint "Ze" "R4 push/pop"
(match_test "pushpop_regeq (op, 4)"))
(define_memory_constraint "Zf" "R5 push/pop"
(match_test "pushpop_regeq (op, 5)"))
(define_memory_constraint "Zg" "SP push/pop"
(match_test "pushpop_regeq (op, 6)"))

View file

@ -26,14 +26,12 @@ extern int legitimate_const_double_p (rtx);
extern void notice_update_cc_on_set (rtx, rtx);
extern void output_addr_const_pdp11 (FILE *, rtx);
extern const char *output_move_multiple (rtx *);
extern void expand_block_move (rtx *);
extern const char *output_jump (rtx *, int, int);
extern void print_operand_address (FILE *, rtx);
typedef enum { no_action, dec_before, inc_after } pdp11_action;
typedef enum { little, either, big } pdp11_partorder;
extern bool pdp11_expand_operands (rtx *, rtx [][2], int,
extern bool pdp11_expand_operands (rtx *, rtx [][2], int, int,
pdp11_action *, pdp11_partorder);
extern int pdp11_sp_frame_offset (void);
extern int pdp11_initial_elimination_offset (int, int);
extern enum reg_class pdp11_regno_reg_class (int);
extern bool pdp11_fixed_cc_regs (unsigned int *, unsigned int *);
@ -42,6 +40,8 @@ extern bool pdp11_expand_shift (rtx *, rtx (*) (rtx, rtx, rtx),
rtx (*) (rtx, rtx, rtx));
extern const char * pdp11_assemble_shift (rtx *, machine_mode, int);
extern int pdp11_shift_length (rtx *, machine_mode, int, bool);
extern int pdp11_cmp_length (rtx *, int);
extern bool pushpop_regeq (rtx, int);
extern bool pdp11_small_shift (int);
#endif /* RTX_CODE */

View file

@ -304,6 +304,9 @@ static bool pdp11_scalar_mode_supported_p (scalar_mode);
#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
#undef TARGET_STACK_PROTECT_RUNTIME_ENABLED_P
#define TARGET_STACK_PROTECT_RUNTIME_ENABLED_P hook_bool_void_false
/* A helper function to determine if REGNO should be saved in the
current function's stack frame. */
@ -316,6 +319,13 @@ pdp11_saved_regno (unsigned regno)
/* Expand the function prologue. */
/* Frame layout, from high to low memory (stack push order):
return address (from jsr instruction)
saved CPU registers, lowest number first
saved FPU registers, lowest number first, always 64 bit mode
*** frame pointer points here ***
local variables
alloca storage if any. */
void
pdp11_expand_prologue (void)
{
@ -331,31 +341,9 @@ pdp11_expand_prologue (void)
emit_insn (gen_seti ());
}
if (frame_pointer_needed)
{
x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (Pmode, x);
emit_move_insn (x, hard_frame_pointer_rtx);
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
}
/* Make frame. */
if (fsize)
{
emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (-fsize)));
/* Prevent frame references via the frame pointer from being
scheduled before the frame is allocated. */
if (frame_pointer_needed)
emit_insn (gen_blockage ());
}
/* Save CPU registers. */
for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++)
if (pdp11_saved_regno (regno)
&& (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
if (pdp11_saved_regno (regno))
{
x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (Pmode, x);
@ -383,25 +371,21 @@ pdp11_expand_prologue (void)
x = gen_frame_mem (DFmode, x);
emit_move_insn (x, via_ac);
}
if (frame_pointer_needed)
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
/* Make local variable space. */
if (fsize)
emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (-fsize)));
}
/* The function epilogue should not depend on the current stack pointer!
It should use the frame pointer only. This is mandatory because
of alloca; we also take advantage of it to omit stack adjustments
before returning. */
/* Maybe we can make leaf functions faster by switching to the
second register file - this way we don't have to save regs!
leaf functions are ~ 50% of all functions (dynamically!)
set/clear bit 11 (dec. 2048) of status word for switching register files -
but how can we do this? the pdp11/45 manual says bit may only
be set (p.24), but not cleared!
switching to kernel is probably more expensive, so we'll leave it
like this and not use the second set of registers...
maybe as option if you want to generate code for kernel mode? */
/* Generate epilogue. This uses the frame pointer to pop the local
variables and any alloca data off the stack. If there is no alloca
and frame pointer elimination hasn't been disabled, there is no
frame pointer and the local variables are popped by adjusting the
stack pointer instead. */
void
pdp11_expand_epilogue (void)
@ -410,6 +394,20 @@ pdp11_expand_epilogue (void)
unsigned regno;
rtx x, reg, via_ac = NULL;
/* Deallocate the local variables. */
if (fsize)
{
if (frame_pointer_needed)
{
/* We can deallocate the frame with a single move. */
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
}
else
emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (fsize)));
}
/* Restore the FPU registers. */
if (pdp11_saved_regno (AC4_REGNUM) || pdp11_saved_regno (AC5_REGNUM))
{
/* Find a temporary with which to restore AC4/5. */
@ -421,109 +419,33 @@ pdp11_expand_epilogue (void)
}
}
/* If possible, restore registers via pops. */
if (!frame_pointer_needed || crtl->sp_is_unchanging)
{
/* Restore registers via pops. */
/* Restore registers via pops. */
for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--)
if (pdp11_saved_regno (regno))
for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--)
if (pdp11_saved_regno (regno))
{
x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (DFmode, x);
reg = gen_rtx_REG (DFmode, regno);
if (LOAD_FPU_REG_P (regno))
emit_move_insn (reg, x);
else
{
x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (DFmode, x);
reg = gen_rtx_REG (DFmode, regno);
if (LOAD_FPU_REG_P (regno))
emit_move_insn (reg, x);
else
{
emit_move_insn (via_ac, x);
emit_move_insn (reg, via_ac);
}
emit_move_insn (via_ac, x);
emit_move_insn (reg, via_ac);
}
}
for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
if (pdp11_saved_regno (regno)
&& (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
{
x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (Pmode, x);
emit_move_insn (gen_rtx_REG (Pmode, regno), x);
}
}
else
{
/* Restore registers via moves. */
/* ??? If more than a few registers need to be restored, it's smaller
to generate a pointer through which we can emit pops. Consider
that moves cost 2*NREG words and pops cost NREG+3 words. This
means that the crossover is NREG=3.
for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
if (pdp11_saved_regno (regno))
{
x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (Pmode, x);
emit_move_insn (gen_rtx_REG (Pmode, regno), x);
}
Possible registers to use are:
(1) The first call-saved general register. This register will
be restored with the last pop.
(2) R1, if it's not used as a return register.
(3) FP itself. This option may result in +4 words, since we
may need two add imm,rn instructions instead of just one.
This also has the downside that we're not representing
the unwind info in any way, so during the epilogue the
debugger may get lost. */
HOST_WIDE_INT ofs = -pdp11_sp_frame_offset ();
for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--)
if (pdp11_saved_regno (regno))
{
x = plus_constant (Pmode, hard_frame_pointer_rtx, ofs);
x = gen_frame_mem (DFmode, x);
reg = gen_rtx_REG (DFmode, regno);
if (LOAD_FPU_REG_P (regno))
emit_move_insn (reg, x);
else
{
emit_move_insn (via_ac, x);
emit_move_insn (reg, via_ac);
}
ofs += 8;
}
for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
if (pdp11_saved_regno (regno)
&& (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
{
x = plus_constant (Pmode, hard_frame_pointer_rtx, ofs);
x = gen_frame_mem (Pmode, x);
emit_move_insn (gen_rtx_REG (Pmode, regno), x);
ofs += 2;
}
}
/* Deallocate the stack frame. */
if (fsize)
{
/* Prevent frame references via any pointer from being
scheduled after the frame is deallocated. */
emit_insn (gen_blockage ());
if (frame_pointer_needed)
{
/* We can deallocate the frame with a single move. */
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
}
else
emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (fsize)));
}
if (frame_pointer_needed)
{
x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
x = gen_frame_mem (Pmode, x);
emit_move_insn (hard_frame_pointer_rtx, x);
}
emit_jump_insn (gen_return ());
emit_jump_insn (gen_rtspc ());
}
/* Return the best assembler insn template
@ -539,21 +461,23 @@ singlemove_string (rtx *operands)
/* Expand multi-word operands (SImode or DImode) into the 2 or 4
corresponding HImode operands. The number of operands is given
as the third argument, and the required order of the parts as
the fourth argument. */
corresponding HImode operands. The number of operands is given as
the third argument, the word count for the mode as the fourth
argument, and the required order of parts as the sixth argument.
The word count is explicit because sometimes we're asked to compare
two constants, both of which have mode VOIDmode, so we can't always
rely on the input operand mode to imply the operand size. */
bool
pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount,
pdp11_expand_operands (rtx *operands, rtx exops[][2],
int opcount, int words,
pdp11_action *action, pdp11_partorder order)
{
int words, op, w, i, sh;
int op, w, i, sh;
pdp11_partorder useorder;
bool sameoff = false;
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype;
long sval[2];
words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
/* If either piece order is accepted and one is pre-decrement
while the other is post-increment, set order to be high order
word first. That will force the pre-decrement to be turned
@ -566,19 +490,16 @@ pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount,
useorder = either;
if (opcount == 2)
{
if (!REG_P (operands[0]) && !REG_P (operands[1]) &&
!(CONSTANT_P (operands[1]) ||
GET_CODE (operands[1]) == CONST_DOUBLE) &&
if (GET_CODE (operands[0]) == MEM &&
GET_CODE (operands[1]) == MEM &&
((GET_CODE (XEXP (operands[0], 0)) == POST_INC &&
GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ||
(GET_CODE (XEXP (operands[0], 0)) == PRE_DEC &&
GET_CODE (XEXP (operands[1], 0)) == POST_INC)))
useorder = big;
else if ((!REG_P (operands[0]) &&
else if ((GET_CODE (operands[0]) == MEM &&
GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ||
(!REG_P (operands[1]) &&
!(CONSTANT_P (operands[1]) ||
GET_CODE (operands[1]) == CONST_DOUBLE) &&
(GET_CODE (operands[1]) == MEM &&
GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))
useorder = little;
else if (REG_P (operands[0]) && REG_P (operands[1]) &&
@ -615,7 +536,7 @@ pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount,
/* First classify the operand. */
if (REG_P (operands[op]))
optype = REGOP;
else if (CONSTANT_P (operands[op])
else if (CONST_INT_P (operands[op])
|| GET_CODE (operands[op]) == CONST_DOUBLE)
optype = CNSTOP;
else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC)
@ -663,8 +584,11 @@ pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount,
}
if (GET_CODE (operands[op]) == CONST_DOUBLE)
REAL_VALUE_TO_TARGET_DOUBLE
(*CONST_DOUBLE_REAL_VALUE (operands[op]), sval);
{
gcc_assert (GET_MODE (operands[op]) != VOIDmode);
REAL_VALUE_TO_TARGET_DOUBLE
(*CONST_DOUBLE_REAL_VALUE (operands[op]), sval);
}
for (i = 0; i < words; i++)
{
@ -707,24 +631,31 @@ pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount,
const char *
output_move_multiple (rtx *operands)
{
rtx inops[2];
rtx exops[4][2];
rtx adjops[2];
pdp11_action action[2];
int i, words;
words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
adjops[1] = gen_rtx_CONST_INT (HImode, words * 2);
pdp11_expand_operands (operands, exops, 2, action, either);
inops[0] = operands[0];
inops[1] = operands[1];
pdp11_expand_operands (inops, exops, 2, words, action, either);
/* Check for explicit decrement before. */
if (action[0] == dec_before)
{
operands[0] = XEXP (operands[0], 0);
output_asm_insn ("sub\t%#4,%0", operands);
adjops[0] = XEXP (XEXP (operands[0], 0), 0);
output_asm_insn ("sub\t%1,%0", adjops);
}
if (action[1] == dec_before)
{
operands[1] = XEXP (operands[1], 0);
output_asm_insn ("sub\t%#4,%1", operands);
adjops[0] = XEXP (XEXP (operands[1], 0), 0);
output_asm_insn ("sub\t%1,%0", adjops);
}
/* Do the words. */
@ -734,13 +665,13 @@ output_move_multiple (rtx *operands)
/* Check for increment after. */
if (action[0] == inc_after)
{
operands[0] = XEXP (operands[0], 0);
output_asm_insn ("add\t%#4,%0", operands);
adjops[0] = XEXP (XEXP (operands[0], 0), 0);
output_asm_insn ("add\t%1,%0", adjops);
}
if (action[1] == inc_after)
{
operands[1] = XEXP (operands[1], 0);
output_asm_insn ("add\t%#4,%1", operands);
adjops[0] = XEXP (XEXP (operands[1], 0), 0);
output_asm_insn ("add\t%1,%0", adjops);
}
return "";
@ -752,9 +683,9 @@ pdp11_gen_int_label (char *label, const char *prefix, int num)
{
if (TARGET_DEC_ASM)
/* +1 because GCC numbers labels starting at zero. */
sprintf (label, "*%lu$", num + 1);
sprintf (label, "*%u$", num + 1);
else
sprintf (label, "*%s_%lu", prefix, num);
sprintf (label, "*%s_%u", prefix, num);
}
/* Output an ascii string. */
@ -780,7 +711,7 @@ output_ascii (FILE *file, const char *p, int size)
{
if (delim)
putc ('"', file);
fprintf (file, "<%o%>", c);
fprintf (file, "<%o>", c);
delim = false;
}
else
@ -815,15 +746,30 @@ pdp11_asm_output_var (FILE *file, const char *name, int size,
{
if (align > 8)
fprintf (file, "\t.even\n");
if (global)
if (TARGET_DEC_ASM)
{
fprintf (file, ".globl ");
assemble_name (file, name);
if (global)
fputs ("::", file);
else
fputs (":", file);
if (align > 8)
fprintf (file, "\t.blkw\t%o\n", (size & 0xffff) / 2);
else
fprintf (file, "\t.blkb\t%o\n", size & 0xffff);
}
fprintf (file, "\n");
assemble_name (file, name);
fputs (":", file);
ASM_OUTPUT_SKIP (file, size);
else
{
if (global)
{
fprintf (file, ".globl ");
assemble_name (file, name);
}
fprintf (file, "\n");
assemble_name (file, name);
fputs (":", file);
ASM_OUTPUT_SKIP (file, size);
}
}
/* Special format operators handled here:
@ -855,7 +801,7 @@ pdp11_asm_print_operand (FILE *file, rtx x, int code)
fprintf (file, "%s", reg_names[REGNO (x)]);
else if (GET_CODE (x) == MEM)
output_address (GET_MODE (x), XEXP (x, 0));
else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != SImode)
else if (GET_CODE (x) == CONST_DOUBLE && FLOAT_MODE_P (GET_MODE (x)))
{
REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (x), sval);
if (TARGET_DEC_ASM)
@ -1013,8 +959,7 @@ static int
pdp11_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
reg_class_t c1, reg_class_t c2)
{
if (((c1 == MUL_REGS || c1 == GENERAL_REGS) &&
(c2 == MUL_REGS || c2 == GENERAL_REGS)))
if (CPU_REG_CLASS (c1) && CPU_REG_CLASS (c2))
return 2;
else if ((c1 >= LOAD_FPU_REGS && c1 <= FPU_REGS && c2 == LOAD_FPU_REGS) ||
(c2 >= LOAD_FPU_REGS && c2 <= FPU_REGS && c1 == LOAD_FPU_REGS))
@ -1512,50 +1457,32 @@ no_side_effect_operand(rtx op, machine_mode mode ATTRIBUTE_UNUSED)
return FALSE;
}
/*
* expand a block move:
*
* operands[0] ... to
* operands[1] ... from
* operands[2] ... length
* operands[3] ... alignment
*/
void
expand_block_move(rtx *operands)
/* Return TRUE if op is a push or pop using the register "regno". */
bool
pushpop_regeq (rtx op, int regno)
{
rtx lb, test;
rtx fromop, toop, counter;
int count;
rtx addr;
/* False if not memory reference. */
if (GET_CODE (op) != MEM)
return FALSE;
/* Get the address of the memory reference. */
addr = XEXP (op, 0);
/* Transform BLKmode MEM reference into a (reg)+ operand. */
toop = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
toop = gen_rtx_POST_INC (Pmode, toop);
fromop = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
fromop = gen_rtx_POST_INC (Pmode, fromop);
count = INTVAL (operands[2]);
if (INTVAL (operands [3]) >= 2 && (count & 1) == 0)
{
count >>= 1;
toop = gen_rtx_MEM (HImode, toop);
fromop = gen_rtx_MEM (HImode, fromop);
}
else
{
toop = gen_rtx_MEM (QImode, toop);
fromop = gen_rtx_MEM (QImode, fromop);
}
counter = copy_to_mode_reg (HImode, gen_rtx_CONST_INT (HImode, count));
/* Label at top of loop */
lb = gen_label_rtx ();
emit_label (lb);
emit_move_insn (toop, fromop);
emit_insn (gen_subhi3 (counter, counter, const1_rtx));
test = gen_rtx_NE (HImode, counter, const0_rtx);
emit_jump_insn (gen_cbranchhi4 (test, counter, const0_rtx, lb));
if (GET_CODE (addr) == MEM)
addr = XEXP (addr, 0);
switch (GET_CODE (addr))
{
case PRE_DEC:
case POST_INC:
case PRE_MODIFY:
case POST_MODIFY:
return REGNO (XEXP (addr, 0)) == regno;
default:
return FALSE;
}
}
/* This function checks whether a real value can be encoded as
@ -1565,7 +1492,12 @@ int
legitimate_const_double_p (rtx address)
{
long sval[2];
/* If it's too big for HOST_WIDE_INT, it's definitely to big here. */
if (GET_MODE (address) == VOIDmode)
return 0;
REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (address), sval);
if ((sval[0] & 0xffff) == 0 && sval[1] == 0)
return 1;
return 0;
@ -1723,7 +1655,7 @@ pdp11_legitimate_address_p (machine_mode mode,
&& GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
&& GET_CODE (XEXP (xfoob, 0)) == REG
&& REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
&& CONSTANT_P (XEXP (xfoob, 1))
&& CONST_INT_P (XEXP (xfoob, 1))
&& INTVAL (XEXP (xfoob,1)) == -2;
case POST_MODIFY:
@ -1733,7 +1665,7 @@ pdp11_legitimate_address_p (machine_mode mode,
&& GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
&& GET_CODE (XEXP (xfoob, 0)) == REG
&& REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
&& CONSTANT_P (XEXP (xfoob, 1))
&& CONST_INT_P (XEXP (xfoob, 1))
&& INTVAL (XEXP (xfoob,1)) == 2;
case MEM:
@ -1792,16 +1724,18 @@ pdp11_legitimate_address_p (machine_mode mode,
enum reg_class
pdp11_regno_reg_class (int regno)
{
if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)
return GENERAL_REGS;
if (regno == ARG_POINTER_REGNUM)
return NOTSP_REG;
else if (regno == CC_REGNUM || regno == FCC_REGNUM)
return CC_REGS;
else if (regno > AC3_REGNUM)
return NO_LOAD_FPU_REGS;
else if (regno >= AC0_REGNUM)
return LOAD_FPU_REGS;
else if (regno & 1)
return MUL_REGS;
else if (regno == 6)
return NOTR0_REG;
else if (regno < 6)
return NOTSP_REG;
else
return GENERAL_REGS;
}
@ -1815,11 +1749,11 @@ pdp11_fixed_cc_regs (unsigned int *p1, unsigned int *p2)
return true;
}
int
pdp11_sp_frame_offset (void)
static int
pdp11_reg_save_size (void)
{
int offset = 0, regno;
offset = get_frame_size();
for (regno = 0; regno <= PC_REGNUM; regno++)
if (pdp11_saved_regno (regno))
offset += 2;
@ -1836,32 +1770,18 @@ pdp11_sp_frame_offset (void)
int
pdp11_initial_elimination_offset (int from, int to)
{
/* Get the size of the register save area. */
int spoff;
if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
return 4;
else if (from == FRAME_POINTER_REGNUM
&& to == HARD_FRAME_POINTER_REGNUM)
return 0;
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return get_frame_size ();
else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
return pdp11_reg_save_size () + 2;
else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return pdp11_reg_save_size () + 2 + get_frame_size ();
else
{
gcc_assert (to == STACK_POINTER_REGNUM);
/* Get the size of the register save area. */
spoff = pdp11_sp_frame_offset ();
if (from == FRAME_POINTER_REGNUM)
return spoff;
gcc_assert (from == ARG_POINTER_REGNUM);
/* If there is a frame pointer, that is saved too. */
if (frame_pointer_needed)
spoff += 2;
/* Account for the saved PC in the function call. */
return spoff + 2;
}
}
gcc_assert (0);
}
/* A copy of output_addr_const modified for pdp11 expression syntax.
output_addr_const also gets called for %cDIGIT and %nDIGIT, which we don't
@ -1913,21 +1833,6 @@ output_addr_const_pdp11 (FILE *file, rtx x)
output_addr_const_pdp11 (file, XEXP (x, 0));
break;
case CONST_DOUBLE:
if (GET_MODE (x) == VOIDmode)
{
/* We can use %o if the number is one word and positive. */
if (TARGET_DEC_ASM)
fprintf (file, "%o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
else
fprintf (file, "%#o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
}
else
/* We can't handle floating point constants;
PRINT_OPERAND must handle them. */
output_operand_lossage ("floating constant misused");
break;
case PLUS:
/* Some assemblers need integer constants to appear last (e.g. masm). */
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
@ -2033,7 +1938,7 @@ pdp11_expand_shift (rtx *operands, rtx (*shift_sc) (rtx, rtx, rtx),
rtx r, test;
rtx_code_label *lb;
if (CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
emit_insn ((*shift_sc) (operands[0], operands[1], operands[2]));
else if (TARGET_40_PLUS)
return false;
@ -2043,7 +1948,7 @@ pdp11_expand_shift (rtx *operands, rtx (*shift_sc) (rtx, rtx, rtx),
r = gen_reg_rtx (HImode);
emit_move_insn (operands[0], operands[1]);
emit_move_insn (r, operands[2]);
if (!CONSTANT_P (operands[2]))
if (!CONST_INT_P (operands[2]))
{
test = gen_rtx_LE (HImode, r, const0_rtx);
emit_jump_insn (gen_cbranchhi4 (test, r, const0_rtx, lb));
@ -2053,7 +1958,7 @@ pdp11_expand_shift (rtx *operands, rtx (*shift_sc) (rtx, rtx, rtx),
optimizer and it doesn't appreciate flow changes happening
while it's doing things. */
emit_insn ((*shift_base) (operands[0], operands[1], r));
if (!CONSTANT_P (operands[2]))
if (!CONST_INT_P (operands[2]))
{
emit_label (lb);
@ -2072,16 +1977,20 @@ const char *
pdp11_assemble_shift (rtx *operands, machine_mode m, int code)
{
int i, n;
rtx exops[4][2];
rtx inops[2];
rtx exops[2][2];
rtx lb[1];
pdp11_action action[2];
const bool small = CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]));
const bool small = CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]));
gcc_assert (small || !TARGET_40_PLUS);
if (m == E_SImode)
pdp11_expand_operands (operands, exops, 1, action, either);
{
inops[0] = operands[0];
pdp11_expand_operands (inops, exops, 1, 2, action, either);
}
if (!small)
{
/* Loop case, generate the top of loop label. */
@ -2179,7 +2088,7 @@ pdp11_shift_length (rtx *operands, machine_mode m, int code, bool simple_operand
/* If shifting by a small constant, the loop is unrolled by the
shift count. Otherwise, account for the size of the decrement
and branch. */
if (CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
shift_size *= INTVAL (operands[2]);
else
shift_size += 4;
@ -2191,6 +2100,39 @@ pdp11_shift_length (rtx *operands, machine_mode m, int code, bool simple_operand
return shift_size;
}
/* Return the length of 2 or 4 word integer compares. */
int
pdp11_cmp_length (rtx *operands, int words)
{
rtx inops[2];
rtx exops[4][2];
rtx lb[1];
int i, len = 0;
if (!reload_completed)
return 2;
inops[0] = operands[0];
inops[1] = operands[1];
pdp11_expand_operands (inops, exops, 2, words, NULL, big);
for (i = 0; i < words; i++)
{
len += 4; /* cmp instruction word and branch that follows. */
if (!REG_P (exops[i][0]) &&
!simple_memory_operand (exops[i][0], HImode))
len += 2; /* first operand extra word. */
if (!REG_P (exops[i][1]) &&
!simple_memory_operand (exops[i][1], HImode) &&
!(CONST_INT_P (exops[i][1]) && INTVAL (exops[i][1]) == 0))
len += 2; /* second operand extra word. */
}
/* Deduct one word because there is no branch at the end. */
return len - 2;
}
/* Prepend to CLOBBERS hard registers that are automatically clobbered
for an asm We do this for CC_REGNUM and FCC_REGNUM (on FPU target)
to maintain source compatibility with the original cc0-based

View file

@ -32,6 +32,20 @@ along with GCC; see the file COPYING3. If not see
do \
{ \
builtin_define_std ("pdp11"); \
if (TARGET_INT16) \
builtin_define_with_int_value ("__pdp11_int", 16); \
else \
builtin_define_with_int_value ("__pdp11_int", 32); \
if (TARGET_40) \
builtin_define_with_int_value ("__pdp11_model", 40); \
else if (TARGET_45) \
builtin_define_with_int_value ("__pdp11_model", 45); \
else \
builtin_define_with_int_value ("__pdp11_model", 10); \
if (TARGET_FPU) \
builtin_define ("__pdp11_fpu"); \
if (TARGET_AC0) \
builtin_define ("__pdp11_ac0"); \
} \
while (0)
@ -153,7 +167,7 @@ extern const struct real_format pdp11_d_format;
#define FIXED_REGISTERS \
{0, 0, 0, 0, 0, 0, 1, 1, \
0, 0, 0, 0, 0, 0, 1, 1, \
1, 1 }
1 }
@ -168,7 +182,7 @@ extern const struct real_format pdp11_d_format;
#define CALL_USED_REGISTERS \
{1, 1, 0, 0, 0, 0, 1, 1, \
0, 0, 0, 0, 0, 0, 1, 1, \
1, 1 }
1 }
/* Specify the registers used for certain standard purposes.
@ -211,6 +225,13 @@ CC_REGS is the condition codes (CPU and FPU)
enum reg_class
{ NO_REGS,
NOTR0_REG,
NOTR1_REG,
NOTR2_REG,
NOTR3_REG,
NOTR4_REG,
NOTR5_REG,
NOTSP_REG,
MUL_REGS,
GENERAL_REGS,
LOAD_FPU_REGS,
@ -229,6 +250,13 @@ enum reg_class
#define REG_CLASS_NAMES \
{ "NO_REGS", \
"NOTR0_REG", \
"NOTR1_REG", \
"NOTR2_REG", \
"NOTR3_REG", \
"NOTR4_REG", \
"NOTR5_REG", \
"SP_REG", \
"MUL_REGS", \
"GENERAL_REGS", \
"LOAD_FPU_REGS", \
@ -243,13 +271,20 @@ enum reg_class
#define REG_CLASS_CONTENTS \
{ {0x00000}, /* NO_REGS */ \
{0x000aa}, /* MUL_REGS */ \
{0x0c0ff}, /* GENERAL_REGS */ \
{0x000fe}, /* NOTR0_REG */ \
{0x000fd}, /* NOTR1_REG */ \
{0x000fb}, /* NOTR2_REG */ \
{0x000f7}, /* NOTR3_REG */ \
{0x000ef}, /* NOTR4_REG */ \
{0x000df}, /* NOTR5_REG */ \
{0x000bf}, /* NOTSP_REG */ \
{0x0002a}, /* MUL_REGS */ \
{0x040ff}, /* GENERAL_REGS */ \
{0x00f00}, /* LOAD_FPU_REGS */ \
{0x03000}, /* NO_LOAD_FPU_REGS */ \
{0x03f00}, /* FPU_REGS */ \
{0x30000}, /* CC_REGS */ \
{0x3ffff}} /* ALL_REGS */
{0x18000}, /* CC_REGS */ \
{0x1ffff}} /* ALL_REGS */
/* The same information, inverted:
Return the class number of the smallest class containing
@ -262,13 +297,17 @@ enum reg_class
#define INDEX_REG_CLASS GENERAL_REGS
#define BASE_REG_CLASS GENERAL_REGS
/* Return TRUE if the class is a CPU register. */
#define CPU_REG_CLASS(CLASS) \
(CLASS >= NOTR0_REG && CLASS <= GENERAL_REGS)
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. */
#define CLASS_MAX_NREGS(CLASS, MODE) \
((CLASS == GENERAL_REGS || CLASS == MUL_REGS)? \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD): \
1 \
)
(CPU_REG_CLASS (CLASS) ? \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD): \
1 \
)
/* Stack layout; function entry, exit and calling. */
@ -328,16 +367,13 @@ extern int current_first_parm_offset;
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry. */
#define FUNCTION_PROFILER(FILE, LABELNO) \
gcc_unreachable ();
#define FUNCTION_PROFILER(FILE, LABELNO)
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in
functions that have frame pointers.
No definition is equivalent to always zero. */
extern int may_call_alloca;
#define EXIT_IGNORE_STACK 1
/* Definitions for register eliminations.
@ -347,17 +383,14 @@ extern int may_call_alloca;
followed by "to". Eliminations of the same "from" register are listed
in order of preference.
There are two registers that can always be eliminated on the pdp11.
The frame pointer and the arg pointer can be replaced by either the
hard frame pointer or to the stack pointer, depending upon the
circumstances. The hard frame pointer is not used before reload and
so it is not eligible for elimination. */
There are two registers that can be eliminated on the pdp11. The
arg pointer can be replaced by the frame pointer; the frame pointer
can often be replaced by the stack pointer. */
#define ELIMINABLE_REGS \
{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
((OFFSET) = pdp11_initial_elimination_offset ((FROM), (TO)))
@ -514,8 +547,8 @@ extern int may_call_alloca;
#define REGISTER_NAMES \
{"r0", "r1", "r2", "r3", "r4", "r5", "sp", "pc", \
"ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "fp", "ap", \
"cc", "fcc" }
"ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ap", "cc", \
"fcc" }
/* Globalizing directive for a label. */
#define GLOBAL_ASM_OP "\t.globl\t"
@ -568,28 +601,22 @@ extern int may_call_alloca;
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
pdp11_output_addr_vec_elt (FILE, VALUE)
/* This is how to output an assembler line
that says to advance the location counter
to a multiple of 2**LOG bytes.
/* This is how to output an assembler line that says to advance the
location counter to a multiple of 2**LOG bytes. Only values 0 and
1 should appear, but due to PR87795 larger values (which are not
supported) can also appear. So we treat all alignment of LOG >= 1
as word (2 byte) alignment.
*/
#define ASM_OUTPUT_ALIGN(FILE,LOG) \
switch (LOG) \
{ \
case 0: \
break; \
case 1: \
fprintf (FILE, "\t.even\n"); \
break; \
default: \
gcc_unreachable (); \
}
if (LOG != 0) \
fprintf (FILE, "\t.even\n")
#define ASM_OUTPUT_SKIP(FILE,SIZE) \
if (TARGET_DEC_ASM) \
fprintf (FILE, "\t.blkb\t%ho\n", (SIZE) & 0xffff); \
fprintf (FILE, "\t.blkb\t%o\n", (SIZE) & 0xffff); \
else \
fprintf (FILE, "\t.=.+ %#ho\n", (SIZE) & 0xffff);
fprintf (FILE, "\t.=.+ %#o\n", (SIZE) & 0xffff);
/* This says how to output an assembler line
to define a global common symbol. */
@ -597,7 +624,6 @@ extern int may_call_alloca;
#define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \
pdp11_asm_output_var (FILE, NAME, SIZE, ALIGN, true)
/* This says how to output an assembler line
to define a local common symbol. */

View file

@ -26,6 +26,7 @@
UNSPECV_BLOCKAGE
UNSPECV_SETD
UNSPECV_SETI
UNSPECV_MOVMEM
])
(define_constants
@ -33,22 +34,21 @@
;; Register numbers
(R0_REGNUM 0)
(RETVAL_REGNUM 0)
(HARD_FRAME_POINTER_REGNUM 5)
(FRAME_POINTER_REGNUM 5)
(STACK_POINTER_REGNUM 6)
(PC_REGNUM 7)
(AC0_REGNUM 8)
(AC3_REGNUM 11)
(AC4_REGNUM 12)
(AC5_REGNUM 13)
;; The next two are not physical registers but are used for addressing
;; arguments.
(FRAME_POINTER_REGNUM 14)
(ARG_POINTER_REGNUM 15)
;; The next one is not a physical register but is used for
;; addressing arguments.
(ARG_POINTER_REGNUM 14)
;; Condition code registers
(CC_REGNUM 16)
(FCC_REGNUM 17)
(CC_REGNUM 15)
(FCC_REGNUM 16)
;; End of hard registers
(FIRST_PSEUDO_REGISTER 18)
(FIRST_PSEUDO_REGISTER 17)
;; Branch offset limits, as byte offsets from (pc). That is NOT
;; the same thing as "instruction address" -- it is for backward
@ -178,12 +178,7 @@
DONE;
})
(define_expand "return"
[(return)]
"reload_completed && !frame_pointer_needed && pdp11_sp_frame_offset () == 0"
"")
(define_insn "*rts"
(define_insn "rtspc"
[(return)]
""
"rts\tpc")
@ -249,6 +244,78 @@
cmp<PDPint:isfx>\t%0,%1"
[(set_attr "length" "2,2,4,4,4,6")])
;; Two word compare
(define_insn "cmpsi"
[(set (reg:CC CC_REGNUM)
(compare:CC (match_operand:SI 0 "general_operand" "rDQi")
(match_operand:SI 1 "general_operand" "rDQi")))]
""
{
rtx inops[2];
rtx exops[2][2];
rtx lb[1];
inops[0] = operands[0];
inops[1] = operands[1];
pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
lb[0] = gen_label_rtx ();
if (CONST_INT_P (exops[0][1]) && INTVAL (exops[0][1]) == 0)
output_asm_insn ("tst\t%0", exops[0]);
else
output_asm_insn ("cmp\t%0,%1", exops[0]);
output_asm_insn ("bne\t%l0", lb);
if (CONST_INT_P (exops[1][1]) && INTVAL (exops[1][1]) == 0)
output_asm_insn ("tst\t%0", exops[1]);
else
output_asm_insn ("cmp\t%0,%1", exops[1]);
output_asm_label (lb[0]);
fputs (":\n", asm_out_file);
return "";
}
[(set (attr "length")
(symbol_ref "pdp11_cmp_length (operands, 2)"))
(set_attr "base_cost" "0")])
;; Four word compare
(define_insn "cmpdi"
[(set (reg:CC CC_REGNUM)
(compare:CC (match_operand:DI 0 "general_operand" "rDQi")
(match_operand:DI 1 "general_operand" "rDQi")))]
""
{
rtx inops[4];
rtx exops[4][2];
rtx lb[1];
int i;
inops[0] = operands[0];
inops[1] = operands[1];
pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
lb[0] = gen_label_rtx ();
for (i = 0; i < 3; i++)
{
if (CONST_INT_P (exops[i][1]) && INTVAL (exops[i][1]) == 0)
output_asm_insn ("tst\t%0", exops[i]);
else
output_asm_insn ("cmp\t%0,%1", exops[i]);
output_asm_insn ("bne\t%l0", lb);
}
if (CONST_INT_P (exops[3][1]) && INTVAL (exops[3][1]) == 0)
output_asm_insn ("tst\t%0", exops[3]);
else
output_asm_insn ("cmp\t%0,%1", exops[3]);
output_asm_label (lb[0]);
fputs (":\n", asm_out_file);
return "";
}
[(set (attr "length")
(symbol_ref "pdp11_cmp_length (operands, 2)"))
(set_attr "base_cost" "0")])
;; sob instruction
;;
;; This expander has to check for mode match because the doloop pass
@ -368,8 +435,8 @@
(define_insn_and_split "cbranch<mode>4"
[(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(match_operand:PDPint 1 "general_operand" "g")
(match_operand:PDPint 2 "general_operand" "g")])
[(match_operand:QHSDint 1 "general_operand" "g")
(match_operand:QHSDint 2 "general_operand" "g")])
(label_ref (match_operand 3 "" ""))
(pc)))]
""
@ -473,12 +540,19 @@
"* return output_move_multiple (operands);"
[(set_attr "length" "4,6,8,16")])
;; That long string of "Z" constraints enforces the restriction that
;; a register source and auto increment or decrement destination must
;; not use the same register, because that case is not consistently
;; implemented across the PDP11 models.
;; TODO: the same should be applied to insn like add, but this is not
;; necessary yet because the incdec optimization pass does not apply
;; that optimization to 3-operand insns at the moment.
(define_insn "mov<mode>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))]
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Za,Zb,Zc,Zd,Ze,Zf,Zg,rD,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "RN,Z0,Z1,Z2,Z3,Z4,Z5,Z6,r,Qi,rRN,Qi"))]
""
""
[(set_attr "length" "2,4,4,6")])
[(set_attr "length" "2,2,2,2,2,2,2,2,2,4,4,6")])
;; This splits all the integer moves: DI and SI modes as well as
;; the simple machine operations.
@ -493,8 +567,8 @@
;; MOV clears V
(define_insn "*mov<mode>_<cc_cc>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Za,Zb,Zc,Zd,Ze,Zf,Zg,rD,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "RN,Z0,Z1,Z2,Z3,Z4,Z5,Z6,r,Qi,rRN,Qi"))
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
"*
@ -504,7 +578,7 @@
return \"mov<PDPint:isfx>\t%1,%0\";
}"
[(set_attr "length" "2,4,4,6")])
[(set_attr "length" "2,2,2,2,2,2,2,2,2,4,4,6")])
;; movdf has unusually complicated condition code handling, because
;; load (into float register) updates the FCC, while store (from
@ -591,18 +665,98 @@
;; Expand a block move. We turn this into a move loop.
(define_expand "movmemhi"
[(match_operand:BLK 0 "general_operand" "=g")
(match_operand:BLK 1 "general_operand" "g")
(match_operand:HI 2 "immediate_operand" "i")
(match_operand:HI 3 "immediate_operand" "i")]
[(parallel [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
(match_operand:BLK 0 "general_operand" "=g")
(match_operand:BLK 1 "general_operand" "g")
(match_operand:HI 2 "immediate_operand" "i")
(match_operand:HI 3 "immediate_operand" "i")
(clobber (mem:BLK (scratch)))
(clobber (match_dup 0))
(clobber (match_dup 1))
(clobber (match_dup 2))])]
""
"
{
if (INTVAL (operands[2]) != 0)
expand_block_move (operands);
DONE;
int count;
count = INTVAL (operands[2]);
if (count == 0)
DONE;
if (INTVAL (operands [3]) >= 2 && (count & 1) == 0)
count >>= 1;
else
operands[3] = const1_rtx;
operands[2] = copy_to_mode_reg (HImode,
gen_rtx_CONST_INT (HImode, count));
/* Load BLKmode MEM addresses into scratch registers. */
operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
}")
;; Expand a block move. We turn this into a move loop.
(define_insn_and_split "movmemhi1"
[(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
(match_operand:HI 0 "register_operand" "+r")
(match_operand:HI 1 "register_operand" "+r")
(match_operand:HI 2 "register_operand" "+r")
(match_operand:HI 3 "immediate_operand" "i")
(clobber (mem:BLK (scratch)))
(clobber (match_dup 0))
(clobber (match_dup 1))
(clobber (match_dup 2))]
""
"#"
"reload_completed"
[(parallel [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
(match_dup 0)
(match_dup 1)
(match_dup 2)
(match_dup 3)
(clobber (mem:BLK (scratch)))
(clobber (match_dup 0))
(clobber (match_dup 1))
(clobber (match_dup 2))
(clobber (reg:CC CC_REGNUM))])]
"")
(define_insn "movmemhi_nocc"
[(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
(match_operand:HI 0 "register_operand" "+r")
(match_operand:HI 1 "register_operand" "+r")
(match_operand:HI 2 "register_operand" "+r")
(match_operand:HI 3 "immediate_operand" "i")
(clobber (mem:BLK (scratch)))
(clobber (match_dup 0))
(clobber (match_dup 1))
(clobber (match_dup 2))
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
"*
{
rtx lb[2];
lb[0] = operands[2];
lb[1] = gen_label_rtx ();
output_asm_label (lb[1]);
fputs (\":\n\", asm_out_file);
if (INTVAL (operands[3]) > 1)
output_asm_insn (\"mov\t(%1)+,(%0)+\", operands);
else
output_asm_insn (\"movb\t(%1)+,(%0)+\", operands);
if (TARGET_40_PLUS)
output_asm_insn (\"sob\t%0,%l1\", lb);
else
{
output_asm_insn (\"dec\t%0\", lb);
output_asm_insn (\"bne\t%l1\", lb);
}
return \"\";
}"
[(set (attr "length")
(if_then_else (match_test "TARGET_40_PLUS")
(const_int 4)
(const_int 6)))])
;;- truncation instructions
@ -659,7 +813,8 @@
emit_move_insn (r, const0_rtx);
DONE;
}
else if (!rtx_equal_p (operands[0], operands[1]))
else if (!REG_P (operands[1]) ||
REGNO (operands[0]) != REGNO (operands[1]))
{
/* Alternatives 2 and 3 */
emit_move_insn (operands[0], const0_rtx);
@ -975,22 +1130,22 @@
inops[0] = operands[0];
inops[1] = operands[2];
pdp11_expand_operands (inops, exops, 2, NULL, either);
pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
output_asm_insn (\"add\t%1,%0\", exops[0]);
if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
{
output_asm_insn (\"add\t%1,%0\", exops[1]);
output_asm_insn (\"adc\t%0\", exops[0]);
}
if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
if (!CONST_INT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
{
output_asm_insn (\"add\t%1,%0\", exops[2]);
output_asm_insn (\"adc\t%0\", exops[1]);
output_asm_insn (\"adc\t%0\", exops[0]);
}
if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
if (!CONST_INT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
{
output_asm_insn (\"add\t%1,%0\", exops[3]);
output_asm_insn (\"adc\t%0\", exops[2]);
@ -1037,11 +1192,11 @@
inops[0] = operands[0];
inops[1] = operands[2];
pdp11_expand_operands (inops, exops, 2, NULL, either);
pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
output_asm_insn (\"add\t%1,%0\", exops[0]);
if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
{
output_asm_insn (\"add\t%1,%0\", exops[1]);
output_asm_insn (\"adc\t%0\", exops[0]);
@ -1169,22 +1324,22 @@
inops[0] = operands[0];
inops[1] = operands[2];
pdp11_expand_operands (inops, exops, 2, NULL, either);
pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
output_asm_insn (\"sub\t%1,%0\", exops[0]);
if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
{
output_asm_insn (\"sub\t%1,%0\", exops[1]);
output_asm_insn (\"sbc\t%0\", exops[0]);
}
if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
if (!CONST_INT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
{
output_asm_insn (\"sub\t%1,%0\", exops[2]);
output_asm_insn (\"sbc\t%0\", exops[1]);
output_asm_insn (\"sbc\t%0\", exops[0]);
}
if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
if (!CONST_INT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
{
output_asm_insn (\"sub\t%1,%0\", exops[3]);
output_asm_insn (\"sbc\t%0\", exops[2]);
@ -1222,11 +1377,11 @@
inops[0] = operands[0];
inops[1] = operands[2];
pdp11_expand_operands (inops, exops, 2, NULL, either);
pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
output_asm_insn (\"sub\t%1,%0\", exops[0]);
if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
{
output_asm_insn (\"sub\t%1,%0\", exops[1]);
output_asm_insn (\"sbc\t%0\", exops[0]);
@ -1702,9 +1857,11 @@
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
{
rtx inops[2];
rtx exops[4][2];
pdp11_expand_operands (operands, exops, 1, NULL, either);
inops[0] = operands[0];
pdp11_expand_operands (inops, exops, 1, 4, NULL, big);
output_asm_insn (\"com\t%0\", exops[3]);
output_asm_insn (\"com\t%0\", exops[2]);
@ -1738,9 +1895,11 @@
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
{
rtx exops[2][2];
pdp11_expand_operands (operands, exops, 1, NULL, either);
rtx inops[2];
rtx exops[4][2];
inops[0] = operands[0];
pdp11_expand_operands (inops, exops, 1, 2, NULL, big);
output_asm_insn (\"com\t%0\", exops[1]);
output_asm_insn (\"com\t%0\", exops[0]);
@ -2046,10 +2205,13 @@
(clobber (reg:CC CC_REGNUM))]
""
{
rtx inops[2];
rtx exops[2][2];
rtx t;
pdp11_expand_operands (operands, exops, 2, NULL, either);
inops[0] = operands[0];
inops[1] = operands[1];
pdp11_expand_operands (inops, exops, 2, 2, NULL, either);
t = exops[0][0];
exops[0][0] = exops[1][0];

View file

@ -68,4 +68,4 @@ Use UNIX assembler syntax.
mlra
Target Report Mask(LRA)
Use LRA register allocator
Use LRA register allocator.

View file

@ -18,6 +18,10 @@
MULTILIB_OPTIONS = msoft-float
# Optimize for space
LIBGCC2_CFLAGS = -Os
CRTSTUFF_T_CFLAGS = -Os
# Because the pdp11 POINTER_SIZE is only 16, in dwarf2out.c,
# DWARF_ARANGES_PAD_SIZE is 0, thus a loop in output_aranges that checks
# (i < (unsigned) DWARF_ARANGES_PAD_SIZE) elicits a warning that the