Commit 2b835d68 by Richard Earnshaw

(arm_gen_constant): New function.

(arm_split_constant): Split most of the functionality into
arm_gen_constant.  Try to decide which way of handling the constant
is optimal for the target processor.
(arm_prgmode): New enum.
(target_{cpu,fpe}_name, arm_fast_multiply, arm_arch4): New variables.
(all_procs): New table describing processors and capabilities.
(arm_override_options): New function.
(arm_return_in_memory): New function.
(arm_rtx_costs): Adjust the multiply costs to cope with processors
with fast multiplication instructions.
(output_move_double): Use the ldm/stm variants more efficiently.
Delete cases that can no-longer occur.
(output_return_instruction, output_func_epilogue): Use TARGET_APCS_32,
not TARGET_6 for determining the type of return instruction to emit.
(final_prescan_insn case CALL_INSN): Use TARGET_APCS_32, not TARGET_6
to determine condition preservation.
({symbol,label}_mentioned_p): New functions.
(add_constant, dump_table, fixit, find_barrier, broken_move): New
support functions for handling constant spilling.
(arm_reorg): New constant spilling pass, for putting unhandlable
constants into the rtl where we can load them efficiently.
(output_load_symbol): Delete.
(strings_fpa): Use a form which is common to both GAS and ARMASM.
(output_return_instruction, output_func_epilogue): Call
assemble_external_libcall, before trying to generate an abort call
in the assembler.
	(arm_asm_output_label): Call ARM_OUTPUT_LABEL, rather than assuming
that labels are followed by a colon.
(aof_text_section, aof_add_import, aof_delete_import,
aof_dump_imports): New functions to support ARMASM assembler
generation.

From-SVN: r10680
parent 9fb7806b
...@@ -50,6 +50,8 @@ extern void arm_increase_location (); ...@@ -50,6 +50,8 @@ extern void arm_increase_location ();
HOST_WIDE_INT int_log2 PROTO ((HOST_WIDE_INT)); HOST_WIDE_INT int_log2 PROTO ((HOST_WIDE_INT));
static int get_prologue_size PROTO ((void)); static int get_prologue_size PROTO ((void));
static int arm_gen_constant PROTO ((enum rtx_code, enum machine_mode,
HOST_WIDE_INT, rtx, rtx, int, int));
/* Define the information needed to generate branch insns. This is /* Define the information needed to generate branch insns. This is
stored from the compare operation. */ stored from the compare operation. */
...@@ -63,6 +65,18 @@ enum processor_type arm_cpu; ...@@ -63,6 +65,18 @@ enum processor_type arm_cpu;
/* What type of floating point are we compiling for? */ /* What type of floating point are we compiling for? */
enum floating_point_type arm_fpu; enum floating_point_type arm_fpu;
/* What program mode is the cpu running in? 26-bit mode or 32-bit mode */
enum prog_mode_type arm_prgmode;
char *target_cpu_name = ARM_CPU_NAME;
char *target_fpe_name = NULL;
/* Nonzero if this is an "M" variant of the processor. */
int arm_fast_multiply = 0;
/* Nonzero if this chip support the ARM Architecture 4 extensions */
int arm_arch4 = 0;
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
must report the mode of the memory reference from PRINT_OPERAND to must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */ PRINT_OPERAND_ADDRESS. */
...@@ -96,6 +110,8 @@ static struct label_offset *offset_table[LABEL_HASH_SIZE]; ...@@ -96,6 +110,8 @@ static struct label_offset *offset_table[LABEL_HASH_SIZE];
static int return_used_this_function; static int return_used_this_function;
static int arm_constant_limit = 3;
/* For an explanation of these variables, see final_prescan_insn below. */ /* For an explanation of these variables, see final_prescan_insn below. */
int arm_ccfsm_state; int arm_ccfsm_state;
int arm_current_cc; int arm_current_cc;
...@@ -110,6 +126,168 @@ char *arm_condition_codes[] = ...@@ -110,6 +126,168 @@ char *arm_condition_codes[] =
}; };
#define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1) #define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1)
/* Initialization code */
#define FL_CO_PROC 0x01 /* Has external co-processor bus */
#define FL_FAST_MULT 0x02 /* Fast multiply */
#define FL_MODE26 0x04 /* 26-bit mode support */
#define FL_MODE32 0x08 /* 32-bit mode support */
#define FL_ARCH4 0x10 /* Architecture rel 4 */
#define FL_THUMB 0x20 /* Thumb aware */
struct processors
{
char *name;
enum processor_type type;
unsigned int flags;
};
/* Not all of these give usefully different compilation alternatives,
but there is no simple way of generalizing them. */
static struct processors all_procs[] =
{
{"arm2", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26},
{"arm250", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26},
{"arm3", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26},
{"arm6", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm60", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm600", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm610", PROCESSOR_ARM6, FL_MODE32 | FL_MODE26},
{"arm620", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm7", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm70", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm7d", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm7di", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm7dm", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32
| FL_MODE26)},
{"arm7dmi", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32
| FL_MODE26)},
{"arm700", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm700i", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26},
{"arm710", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26},
{"arm710c", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26},
{"arm7500", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26},
{"arm7tdmi", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32
| FL_ARCH4 | FL_THUMB)},
{NULL, 0, 0}
};
/* Fix up any incompatible options that the user has specified.
This has now turned into a maze. */
void
arm_override_options ()
{
int arm_thumb_aware = 0;
if (write_symbols != NO_DEBUG && flag_omit_frame_pointer)
warning ("-g with -fomit-frame-pointer may not give sensible debugging");
if (TARGET_POKE_FUNCTION_NAME)
target_flags |= ARM_FLAG_APCS_FRAME;
if (TARGET_6)
{
warning ("Option '-m6' deprecated. Use: '-mapcs-32' or -mcpu-<proc>");
target_flags |= ARM_FLAG_APCS_32;
arm_cpu = PROCESSOR_ARM6;
}
if (TARGET_3)
{
warning ("Option '-m3' deprecated. Use: '-mapcs-26' or -mcpu-<proc>");
target_flags &= ~ARM_FLAG_APCS_32;
arm_cpu = PROCESSOR_ARM2;
}
if ((TARGET_3 || TARGET_6) && target_cpu_name != NULL)
fatal ("Incompatible mix of old and new options. -m%d and -mcpu-%s",
TARGET_3 ? 3 : 6, target_cpu_name);
if (TARGET_APCS_REENT && flag_pic)
fatal ("-fpic and -mapcs-reent are incompatible");
if (TARGET_APCS_REENT)
warning ("APCS reentrant code not supported. Ignored");
if (flag_pic)
warning ("Position independent code not supported. Ignored");
if (TARGET_APCS_FLOAT)
warning ("Passing floating point arguments in fp regs not yet supported");
if (TARGET_APCS_STACK && ! TARGET_APCS)
{
warning ("-mapcs-stack-check incompatible with -mno-apcs-frame");
target_flags |= ARM_FLAG_APCS_FRAME;
}
arm_cpu = TARGET_6 ? PROCESSOR_ARM6: PROCESSOR_ARM2;
arm_fpu = FP_HARD;
if (target_cpu_name != NULL)
{
char *c = target_cpu_name;
struct processors *proc;
/* Match against the supported types. */
for (proc = all_procs; proc->name != NULL; proc++)
{
if (strcmp (proc->name, c) == 0)
break;
}
if (proc->name)
{
arm_cpu = proc->type;
/* Default value for floating point code... if no co-processor
bus, then schedule for emulated floating point. Otherwise,
assume the user has an FPA, unless overridden with -mfpe-... */
if (proc->flags & FL_CO_PROC == 0)
arm_fpu = FP_SOFT3;
else
arm_fpu = FP_HARD;
arm_fast_multiply = (proc->flags & FL_FAST_MULT) != 0;
arm_arch4 = (proc->flags & FL_ARCH4) != 0;
arm_thumb_aware = (proc->flags & FL_THUMB) != 0;
/* Processors with a load delay slot can load constants faster,
from the pool than it takes to construct them, so reduce the
complexity of the constant that we will try to generate
inline. */
}
else
fatal ("Unrecognized cpu type: %s", target_cpu_name);
}
if (target_fpe_name)
{
if (strcmp (target_fpe_name, "2") == 0)
arm_fpu = FP_SOFT2;
else if (strcmp (target_fpe_name, "3") == 0)
arm_fpu = FP_SOFT3;
else
fatal ("Invalid floating point emulation option: -mfpe-%s",
target_fpe_name);
}
if (TARGET_THUMB_INTERWORK && ! arm_thumb_aware)
{
warning ("This processor variant does not support Thumb interworking");
target_flags &= ~ARM_FLAG_THUMB;
}
if (TARGET_FPE && arm_fpu != FP_HARD)
arm_fpu = FP_SOFT2;
/* For arm2/3 there is no need to do any scheduling if there is only
a floating point emulator, or we are doing software floating-point. */
if ((TARGET_SOFT_FLOAT || arm_fpu != FP_HARD) && arm_cpu == PROCESSOR_ARM2)
flag_schedule_insns = flag_schedule_insns_after_reload = 0;
arm_prog_mode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26;
}
/* Return 1 if it is possible to return using a single instruction */ /* Return 1 if it is possible to return using a single instruction */
...@@ -191,7 +369,8 @@ const_ok_for_op (i, code, mode) ...@@ -191,7 +369,8 @@ const_ok_for_op (i, code, mode)
VAL is the integer to operate on; VAL is the integer to operate on;
SOURCE is the other operand (a register, or a null-pointer for SET); SOURCE is the other operand (a register, or a null-pointer for SET);
SUBTARGETS means it is safe to create scratch registers if that will SUBTARGETS means it is safe to create scratch registers if that will
either produce a simpler sequence, or we will want to cse the values. */ either produce a simpler sequence, or we will want to cse the values.
Return value is the number of insns emitted. */
int int
arm_split_constant (code, mode, val, target, source, subtargets) arm_split_constant (code, mode, val, target, source, subtargets)
...@@ -202,6 +381,55 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -202,6 +381,55 @@ arm_split_constant (code, mode, val, target, source, subtargets)
rtx source; rtx source;
int subtargets; int subtargets;
{ {
if (subtargets || code == SET
|| (GET_CODE (target) == REG && GET_CODE (source) == REG
&& REGNO (target) != REGNO (source)))
{
rtx temp;
if (arm_gen_constant (code, mode, val, target, source, 1, 0)
> arm_constant_limit + (code != SET))
{
if (code == SET)
{
/* Currently SET is the only monadic value for CODE, all
the rest are diadic. */
emit_insn (gen_rtx (SET, VOIDmode, target, GEN_INT (val)));
return 1;
}
else
{
rtx temp = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx (SET, VOIDmode, temp, GEN_INT (val)));
/* For MINUS, the value is subtracted from, since we never
have subtraction of a constant. */
if (code == MINUS)
emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (code, mode, temp, source)));
else
emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (code, mode, source, temp)));
return 2;
}
}
}
return arm_gen_constant (code, mode, val, target, source, subtargets, 1);
}
/* As above, but extra parameter GENERATE which, if clear, suppresses
RTL generation. */
int
arm_gen_constant (code, mode, val, target, source, subtargets, generate)
enum rtx_code code;
enum machine_mode mode;
HOST_WIDE_INT val;
rtx target;
rtx source;
int subtargets;
int generate;
{
int can_add = 0; int can_add = 0;
int can_invert = 0; int can_invert = 0;
int can_negate = 0; int can_negate = 0;
...@@ -237,15 +465,17 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -237,15 +465,17 @@ arm_split_constant (code, mode, val, target, source, subtargets)
case IOR: case IOR:
if (remainder == 0xffffffff) if (remainder == 0xffffffff)
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
GEN_INT (ARM_SIGN_EXTEND (val)))); emit_insn (gen_rtx (SET, VOIDmode, target,
GEN_INT (ARM_SIGN_EXTEND (val))));
return 1; return 1;
} }
if (remainder == 0) if (remainder == 0)
{ {
if (reload_completed && rtx_equal_p (target, source)) if (reload_completed && rtx_equal_p (target, source))
return 0; return 0;
emit_insn (gen_rtx (SET, VOIDmode, target, source)); if (generate)
emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1; return 1;
} }
break; break;
...@@ -253,14 +483,16 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -253,14 +483,16 @@ arm_split_constant (code, mode, val, target, source, subtargets)
case AND: case AND:
if (remainder == 0) if (remainder == 0)
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, const0_rtx)); if (generate)
emit_insn (gen_rtx (SET, VOIDmode, target, const0_rtx));
return 1; return 1;
} }
if (remainder == 0xffffffff) if (remainder == 0xffffffff)
{ {
if (reload_completed && rtx_equal_p (target, source)) if (reload_completed && rtx_equal_p (target, source))
return 0; return 0;
emit_insn (gen_rtx (SET, VOIDmode, target, source)); if (generate)
emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1; return 1;
} }
can_invert = 1; can_invert = 1;
...@@ -271,13 +503,15 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -271,13 +503,15 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{ {
if (reload_completed && rtx_equal_p (target, source)) if (reload_completed && rtx_equal_p (target, source))
return 0; return 0;
emit_insn (gen_rtx (SET, VOIDmode, target, source)); if (generate)
emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1; return 1;
} }
if (remainder == 0xffffffff) if (remainder == 0xffffffff)
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
gen_rtx (NOT, mode, source))); emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (NOT, mode, source)));
return 1; return 1;
} }
...@@ -289,14 +523,16 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -289,14 +523,16 @@ arm_split_constant (code, mode, val, target, source, subtargets)
passed as (source + (-val)). */ passed as (source + (-val)). */
if (remainder == 0) if (remainder == 0)
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
gen_rtx (NEG, mode, source))); emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (NEG, mode, source)));
return 1; return 1;
} }
if (const_ok_for_arm (val)) if (const_ok_for_arm (val))
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
gen_rtx (MINUS, mode, GEN_INT (val), source))); emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (MINUS, mode, GEN_INT (val), source)));
return 1; return 1;
} }
can_negate = 1; can_negate = 1;
...@@ -312,9 +548,11 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -312,9 +548,11 @@ arm_split_constant (code, mode, val, target, source, subtargets)
|| (can_negate_initial && const_ok_for_arm (-val)) || (can_negate_initial && const_ok_for_arm (-val))
|| (can_invert && const_ok_for_arm (~val))) || (can_invert && const_ok_for_arm (~val)))
{ {
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
(source ? gen_rtx (code, mode, source, emit_insn (gen_rtx (SET, VOIDmode, target,
GEN_INT (val)) : GEN_INT (val)))); (source ? gen_rtx (code, mode, source,
GEN_INT (val))
: GEN_INT (val))));
return 1; return 1;
} }
...@@ -366,10 +604,14 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -366,10 +604,14 @@ arm_split_constant (code, mode, val, target, source, subtargets)
(temp1 = ARM_SIGN_EXTEND (remainder (temp1 = ARM_SIGN_EXTEND (remainder
<< (set_sign_bit_copies - 1)))) << (set_sign_bit_copies - 1))))
{ {
new_src = subtargets ? gen_reg_rtx (mode) : target; if (generate)
emit_insn (gen_rtx (SET, VOIDmode, new_src, GEN_INT (temp1))); {
emit_insn (gen_ashrsi3 (target, new_src, new_src = subtargets ? gen_reg_rtx (mode) : target;
GEN_INT (set_sign_bit_copies - 1))); emit_insn (gen_rtx (SET, VOIDmode, new_src,
GEN_INT (temp1)));
emit_insn (gen_ashrsi3 (target, new_src,
GEN_INT (set_sign_bit_copies - 1)));
}
return 2; return 2;
} }
/* For an inverted constant, we will need to set the low bits, /* For an inverted constant, we will need to set the low bits,
...@@ -377,10 +619,14 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -377,10 +619,14 @@ arm_split_constant (code, mode, val, target, source, subtargets)
temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; temp1 |= (1 << (set_sign_bit_copies - 1)) - 1;
if (const_ok_for_arm (~temp1)) if (const_ok_for_arm (~temp1))
{ {
new_src = subtargets ? gen_reg_rtx (mode) : target; if (generate)
emit_insn (gen_rtx (SET, VOIDmode, new_src, GEN_INT (temp1))); {
emit_insn (gen_ashrsi3 (target, new_src, new_src = subtargets ? gen_reg_rtx (mode) : target;
GEN_INT (set_sign_bit_copies - 1))); emit_insn (gen_rtx (SET, VOIDmode, new_src,
GEN_INT (temp1)));
emit_insn (gen_ashrsi3 (target, new_src,
GEN_INT (set_sign_bit_copies - 1)));
}
return 2; return 2;
} }
} }
...@@ -401,18 +647,18 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -401,18 +647,18 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder)
&& ! const_ok_for_arm (temp2)) && ! const_ok_for_arm (temp2))
{ {
insns insns = arm_gen_constant (code, mode, temp2,
= arm_split_constant (code, mode, temp2, new_src = (subtargets
(new_src ? gen_reg_rtx (mode)
= subtargets ? gen_reg_rtx (mode) : target),
: target), source, subtargets, generate);
source, subtargets);
source = new_src; source = new_src;
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
gen_rtx (IOR, mode, emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (ASHIFT, mode, source, gen_rtx (IOR, mode,
GEN_INT (i)), gen_rtx (ASHIFT, mode, source,
source))); GEN_INT (i)),
source)));
return insns + 1; return insns + 1;
} }
} }
...@@ -423,18 +669,18 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -423,18 +669,18 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (((temp1 | (temp1 >> i)) == remainder) if (((temp1 | (temp1 >> i)) == remainder)
&& ! const_ok_for_arm (temp1)) && ! const_ok_for_arm (temp1))
{ {
insns insns = arm_gen_constant (code, mode, temp1,
= arm_split_constant (code, mode, temp1, new_src = (subtargets
(new_src ? gen_reg_rtx (mode)
= subtargets ? gen_reg_rtx (mode) : target),
: target), source, subtargets, generate);
source, subtargets);
source = new_src; source = new_src;
emit_insn (gen_rtx (SET, VOIDmode, target, if (generate)
gen_rtx (IOR, mode, emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (LSHIFTRT, mode, source, gen_rtx (IOR, mode,
GEN_INT (i)), gen_rtx (LSHIFTRT, mode,
source))); source, GEN_INT (i)),
source)));
return insns + 1; return insns + 1;
} }
} }
...@@ -451,12 +697,15 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -451,12 +697,15 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{ {
if (const_ok_for_arm (ARM_SIGN_EXTEND (~ val))) if (const_ok_for_arm (ARM_SIGN_EXTEND (~ val)))
{ {
rtx sub = subtargets ? gen_reg_rtx (mode) : target; if (generate)
{
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx (SET, VOIDmode, sub, emit_insn (gen_rtx (SET, VOIDmode, sub,
GEN_INT (ARM_SIGN_EXTEND (~ val)))); GEN_INT (ARM_SIGN_EXTEND (~ val))));
emit_insn (gen_rtx (SET, VOIDmode, target, emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (code, mode, source, sub))); gen_rtx (code, mode, source, sub)));
}
return 2; return 2;
} }
} }
...@@ -467,49 +716,59 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -467,49 +716,59 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (set_sign_bit_copies > 8 if (set_sign_bit_copies > 8
&& (val & (-1 << (32 - set_sign_bit_copies))) == val) && (val & (-1 << (32 - set_sign_bit_copies))) == val)
{ {
rtx sub = subtargets ? gen_reg_rtx (mode) : target; if (generate)
rtx shift = GEN_INT (set_sign_bit_copies); {
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx (SET, VOIDmode, sub, rtx shift = GEN_INT (set_sign_bit_copies);
gen_rtx (NOT, mode,
gen_rtx (ASHIFT, mode, source, emit_insn (gen_rtx (SET, VOIDmode, sub,
shift)))); gen_rtx (NOT, mode,
emit_insn (gen_rtx (SET, VOIDmode, target, gen_rtx (ASHIFT, mode, source,
gen_rtx (NOT, mode, shift))));
gen_rtx (LSHIFTRT, mode, sub, emit_insn (gen_rtx (SET, VOIDmode, target,
shift)))); gen_rtx (NOT, mode,
gen_rtx (LSHIFTRT, mode, sub,
shift))));
}
return 2; return 2;
} }
if (set_zero_bit_copies > 8 if (set_zero_bit_copies > 8
&& (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder) && (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder)
{ {
rtx sub = subtargets ? gen_reg_rtx (mode) : target; if (generate)
rtx shift = GEN_INT (set_zero_bit_copies); {
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx (SET, VOIDmode, sub, rtx shift = GEN_INT (set_zero_bit_copies);
gen_rtx (NOT, mode,
gen_rtx (LSHIFTRT, mode, source, emit_insn (gen_rtx (SET, VOIDmode, sub,
shift)))); gen_rtx (NOT, mode,
emit_insn (gen_rtx (SET, VOIDmode, target, gen_rtx (LSHIFTRT, mode, source,
gen_rtx (NOT, mode, shift))));
gen_rtx (ASHIFT, mode, sub, emit_insn (gen_rtx (SET, VOIDmode, target,
shift)))); gen_rtx (NOT, mode,
gen_rtx (ASHIFT, mode, sub,
shift))));
}
return 2; return 2;
} }
if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~ val))) if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~ val)))
{ {
rtx sub = subtargets ? gen_reg_rtx (mode) : target; if (generate)
emit_insn (gen_rtx (SET, VOIDmode, sub, {
gen_rtx (NOT, mode, source))); rtx sub = subtargets ? gen_reg_rtx (mode) : target;
source = sub; emit_insn (gen_rtx (SET, VOIDmode, sub,
if (subtargets) gen_rtx (NOT, mode, source)));
sub = gen_reg_rtx (mode); source = sub;
emit_insn (gen_rtx (SET, VOIDmode, sub, if (subtargets)
gen_rtx (AND, mode, source, GEN_INT (temp1)))); sub = gen_reg_rtx (mode);
emit_insn (gen_rtx (SET, VOIDmode, target, emit_insn (gen_rtx (SET, VOIDmode, sub,
gen_rtx (NOT, mode, sub))); gen_rtx (AND, mode, source,
GEN_INT (temp1))));
emit_insn (gen_rtx (SET, VOIDmode, target,
gen_rtx (NOT, mode, sub)));
}
return 3; return 3;
} }
break; break;
...@@ -522,19 +781,30 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -522,19 +781,30 @@ arm_split_constant (code, mode, val, target, source, subtargets)
<< (32 - clear_sign_bit_copies)) << (32 - clear_sign_bit_copies))
& 0xffffffff); & 0xffffffff);
rtx new_source; rtx new_source;
rtx shift = GEN_INT (clear_sign_bit_copies); rtx shift;
if ((remainder | shift_mask) != 0xffffffff) if ((remainder | shift_mask) != 0xffffffff)
{ {
if (generate)
{
new_source = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_source, source, subtargets, 1);
source = new_source;
}
else
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_source, source, subtargets, 0);
}
if (generate)
{
shift = GEN_INT (clear_sign_bit_copies);
new_source = subtargets ? gen_reg_rtx (mode) : target; new_source = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_split_constant (AND, mode, remainder | shift_mask, emit_insn (gen_ashlsi3 (new_source, source, shift));
new_source, source, subtargets); emit_insn (gen_lshrsi3 (target, new_source, shift));
source = new_source;
} }
new_source = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_ashlsi3 (new_source, source, shift));
emit_insn (gen_lshrsi3 (target, new_source, shift));
return insns + 2; return insns + 2;
} }
...@@ -542,19 +812,30 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -542,19 +812,30 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{ {
HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1; HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1;
rtx new_source; rtx new_source;
rtx shift = GEN_INT (clear_zero_bit_copies); rtx shift;
if ((remainder | shift_mask) != 0xffffffff) if ((remainder | shift_mask) != 0xffffffff)
{ {
if (generate)
{
new_source = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_source, source, subtargets, 1);
source = new_source;
}
else
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_source, source, subtargets, 0);
}
if (generate)
{
shift = GEN_INT (clear_zero_bit_copies);
new_source = subtargets ? gen_reg_rtx (mode) : target; new_source = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_split_constant (AND, mode, remainder | shift_mask, emit_insn (gen_lshrsi3 (new_source, source, shift));
new_source, source, subtargets); emit_insn (gen_ashlsi3 (target, new_source, shift));
source = new_source;
} }
new_source = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_lshrsi3 (new_source, source, shift));
emit_insn (gen_ashlsi3 (target, new_source, shift));
return insns + 2; return insns + 2;
} }
...@@ -629,32 +910,40 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -629,32 +910,40 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (code == SET) if (code == SET)
{ {
emit_insn (gen_rtx (SET, VOIDmode, if (generate)
new_src = (subtargets ? gen_reg_rtx (mode) emit_insn (gen_rtx (SET, VOIDmode,
: target), new_src = (subtargets
GEN_INT (can_invert ? ~temp1 : temp1))); ? gen_reg_rtx (mode)
: target),
GEN_INT (can_invert ? ~temp1 : temp1)));
can_invert = 0; can_invert = 0;
code = PLUS; code = PLUS;
} }
else if (code == MINUS) else if (code == MINUS)
{ {
emit_insn (gen_rtx (SET, VOIDmode, if (generate)
new_src = (subtargets ? gen_reg_rtx (mode) emit_insn (gen_rtx (SET, VOIDmode,
: target), new_src = (subtargets
gen_rtx (code, mode, GEN_INT (temp1), ? gen_reg_rtx (mode)
source))); : target),
gen_rtx (code, mode, GEN_INT (temp1),
source)));
code = PLUS; code = PLUS;
} }
else else
{ {
emit_insn (gen_rtx (SET, VOIDmode, if (generate)
new_src = remainder ? (subtargets emit_insn (gen_rtx (SET, VOIDmode,
? gen_reg_rtx (mode) new_src = (remainder
: target) : target, ? (subtargets
gen_rtx (code, mode, source, ? gen_reg_rtx (mode)
GEN_INT (can_invert ? ~temp1 : target)
: (can_negate : target),
? -temp1 : temp1))))); gen_rtx (code, mode, source,
GEN_INT (can_invert ? ~temp1
: (can_negate
? -temp1
: temp1)))));
} }
insns++; insns++;
...@@ -667,6 +956,46 @@ arm_split_constant (code, mode, val, target, source, subtargets) ...@@ -667,6 +956,46 @@ arm_split_constant (code, mode, val, target, source, subtargets)
return insns; return insns;
} }
/* Handle aggregates that are not laid out in a BLKmode element.
This is a sub-element of RETURN_IN_MEMORY. */
int
arm_return_in_memory (type)
tree type;
{
if (TREE_CODE (type) == RECORD_TYPE)
{
tree field;
/* For a struct, we can return in a register if every element was a
bit-field. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) != FIELD_DECL
|| ! DECL_BIT_FIELD_TYPE (field))
return 1;
return 0;
}
else if (TREE_CODE (type) == UNION_TYPE)
{
tree field;
/* Unions can be returned in registers if every element is
integral, or can be returned in an integer register. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL
|| (AGGREGATE_TYPE_P (TREE_TYPE (field))
&& RETURN_IN_MEMORY (TREE_TYPE (field)))
|| FLOAT_TYPE_P (TREE_TYPE (field)))
return 1;
}
return 0;
}
/* XXX Not sure what should be done for other aggregates, so put them in
memory. */
return 1;
}
#define REG_OR_SUBREG_REG(X) \ #define REG_OR_SUBREG_REG(X) \
(GET_CODE (X) == REG \ (GET_CODE (X) == REG \
|| (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG)) || (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG))
...@@ -814,27 +1143,35 @@ arm_rtx_costs (x, code, outer_code) ...@@ -814,27 +1143,35 @@ arm_rtx_costs (x, code, outer_code)
return 8; return 8;
case MULT: case MULT:
if (arm_fast_multiply && mode == DImode
&& (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1)))
&& (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
|| GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
return 8;
if (GET_MODE_CLASS (mode) == MODE_FLOAT if (GET_MODE_CLASS (mode) == MODE_FLOAT
|| mode == DImode) || mode == DImode)
return 30; return 30;
if (GET_CODE (XEXP (x, 1)) == CONST_INT) if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{ {
HOST_WIDE_INT i = INTVAL (XEXP (x, 1)) & 0xffffffff; unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
& (unsigned HOST_WIDE_INT) 0xffffffff);
int add_cost = const_ok_for_arm (i) ? 4 : 8; int add_cost = const_ok_for_arm (i) ? 4 : 8;
int j; int j;
int booth_unit_size = (arm_fast_multiply ? 8 : 2);
/* This will need adjusting for ARM's with fast multiplies */
for (j = 0; i && j < 32; j += 2) for (j = 0; i && j < 32; j += booth_unit_size)
{ {
i &= ~(3 << j); i >>= booth_unit_size;
add_cost += 2; add_cost += 2;
} }
return add_cost; return add_cost;
} }
return (30 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4) return ((arm_fast_multiply ? 8 : 30)
+ (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
+ (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4)); + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4));
case NEG: case NEG:
...@@ -889,15 +1226,9 @@ arm_rtx_costs (x, code, outer_code) ...@@ -889,15 +1226,9 @@ arm_rtx_costs (x, code, outer_code)
static int fpa_consts_inited = 0; static int fpa_consts_inited = 0;
char *strings_fpa[8] = { char *strings_fpa[8] = {
"0.0", "0", "1", "2", "3",
"1.0", "4", "5", "0.5", "10"
"2.0", };
"3.0",
"4.0",
"5.0",
"0.5",
"10.0"
};
static REAL_VALUE_TYPE values_fpa[8]; static REAL_VALUE_TYPE values_fpa[8];
...@@ -1306,6 +1637,64 @@ reversible_cc_register (x, mode) ...@@ -1306,6 +1637,64 @@ reversible_cc_register (x, mode)
return FALSE; return FALSE;
} }
/* Return TRUE if X references a SYMBOL_REF. */
int
symbol_mentioned_p (x)
rtx x;
{
register char *fmt;
register int i;
if (GET_CODE (x) == SYMBOL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (symbol_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
/* Return TRUE if X references a LABEL_REF. */
int
label_mentioned_p (x)
rtx x;
{
register char *fmt;
register int i;
if (GET_CODE (x) == LABEL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (label_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
enum rtx_code enum rtx_code
minmax_code (x) minmax_code (x)
rtx x; rtx x;
...@@ -1827,6 +2216,391 @@ arm_insn_not_targeted (insn) ...@@ -1827,6 +2216,391 @@ arm_insn_not_targeted (insn)
} }
/* Routines for manipulation of the constant pool. */
/* This is unashamedly hacked from the version in sh.c, since the problem is
extremely similar. */
/* Arm instructions cannot load a large constant into a register,
constants have to come from a pc relative load. The reference of a pc
relative load instruction must be less than 1k infront of the instruction.
This means that we often have to dump a constant inside a function, and
generate code to branch around it.
It is important to minimize this, since the branches will slow things
down and make things bigger.
Worst case code looks like:
ldr rn, L1
b L2
align
L1: .long value
L2:
..
ldr rn, L3
b L4
align
L3: .long value
L4:
..
We fix this by performing a scan before scheduling, which notices which
instructions need to have their operands fetched from the constant table
and builds the table.
The algorithm is:
scan, find an instruction which needs a pcrel move. Look forward, find th
last barrier which is within MAX_COUNT bytes of the requirement.
If there isn't one, make one. Process all the instructions between
the find and the barrier.
In the above example, we can tell that L3 is within 1k of L1, so
the first move can be shrunk from the 2 insn+constant sequence into
just 1 insn, and the constant moved to L3 to make:
ldr rn, L1
..
ldr rn, L3
b L4
align
L1: .long value
L3: .long value
L4:
Then the second move becomes the target for the shortening process.
*/
typedef struct
{
rtx value; /* Value in table */
HOST_WIDE_INT next_offset;
enum machine_mode mode; /* Mode of value */
} pool_node;
/* The maximum number of constants that can fit into one pool, since
the pc relative range is 0...1020 bytes and constants are at least 4
bytes long */
#define MAX_POOL_SIZE (1020/4)
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
static rtx pool_vector_label;
/* Add a constant to the pool and return its label. */
static HOST_WIDE_INT
add_constant (x, mode)
rtx x;
enum machine_mode mode;
{
int i;
rtx lab;
HOST_WIDE_INT offset;
if (mode == SImode && GET_CODE (x) == MEM && CONSTANT_P (XEXP (x, 0))
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
x = get_pool_constant (XEXP (x, 0));
#ifndef AOF_ASSEMBLER
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == 3)
x = XVECEXP (x, 0, 0);
#endif
/* First see if we've already got it */
for (i = 0; i < pool_size; i++)
{
if (GET_CODE (x) == pool_vector[i].value->code
&& mode == pool_vector[i].mode)
{
if (GET_CODE (x) == CODE_LABEL)
{
if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
continue;
}
if (rtx_equal_p (x, pool_vector[i].value))
return pool_vector[i].next_offset - GET_MODE_SIZE (mode);
}
}
/* Need a new one */
pool_vector[pool_size].next_offset = GET_MODE_SIZE (mode);
offset = 0;
if (pool_size == 0)
pool_vector_label = gen_label_rtx ();
else
pool_vector[pool_size].next_offset
+= (offset = pool_vector[pool_size - 1].next_offset);
pool_vector[pool_size].value = x;
pool_vector[pool_size].mode = mode;
pool_size++;
return offset;
}
/* Output the literal table */
static void
dump_table (scan)
rtx scan;
{
int i;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
scan = emit_label_after (pool_vector_label, scan);
for (i = 0; i < pool_size; i++)
{
pool_node *p = pool_vector + i;
switch (GET_MODE_SIZE (p->mode))
{
case 4:
scan = emit_insn_after (gen_consttable_4 (p->value), scan);
break;
case 8:
scan = emit_insn_after (gen_consttable_8 (p->value), scan);
break;
default:
abort ();
break;
}
}
scan = emit_insn_after (gen_consttable_end (), scan);
scan = emit_barrier_after (scan);
pool_size = 0;
}
/* Non zero if the src operand needs to be fixed up */
static int
fixit (src, mode, destreg)
rtx src;
enum machine_mode mode;
int destreg;
{
if (CONSTANT_P (src))
{
if (GET_CODE (src) == CONST_INT)
return (! const_ok_for_arm (INTVAL (src))
&& ! const_ok_for_arm (~INTVAL (src)));
if (GET_CODE (src) == CONST_DOUBLE)
return (GET_MODE (src) == VOIDmode
|| destreg < 16
|| (! const_double_rtx_ok_for_fpu (src)
&& ! neg_const_double_rtx_ok_for_fpu (src)));
return symbol_mentioned_p (src);
}
#ifndef AOF_ASSEMBLER
else if (GET_CODE (src) == UNSPEC && XINT (src, 1) == 3)
return 1;
#endif
else
return (mode == SImode && GET_CODE (src) == MEM
&& GET_CODE (XEXP (src, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (src, 0)));
}
/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. */
static rtx
find_barrier (from, max_count)
rtx from;
int max_count;
{
int count = 0;
rtx found_barrier = 0;
while (from && count < max_count)
{
if (GET_CODE (from) == BARRIER)
found_barrier = from;
/* Count the length of this insn */
if (GET_CODE (from) == INSN
&& GET_CODE (PATTERN (from)) == SET
&& CONSTANT_P (SET_SRC (PATTERN (from)))
&& CONSTANT_POOL_ADDRESS_P (SET_SRC (PATTERN (from))))
{
rtx src = SET_SRC (PATTERN (from));
count += 2;
}
else
count += get_attr_length (from);
from = NEXT_INSN (from);
}
if (!found_barrier)
{
/* We didn't find a barrier in time to
dump our stuff, so we'll make one */
rtx label = gen_label_rtx ();
if (from)
from = PREV_INSN (from);
else
from = get_last_insn ();
/* Walk back to be just before any jump */
while (GET_CODE (from) == JUMP_INSN
|| GET_CODE (from) == NOTE
|| GET_CODE (from) == CODE_LABEL)
from = PREV_INSN (from);
from = emit_jump_insn_after (gen_jump (label), from);
JUMP_LABEL (from) = label;
found_barrier = emit_barrier_after (from);
emit_label_after (label, found_barrier);
return found_barrier;
}
return found_barrier;
}
/* Non zero if the insn is a move instruction which needs to be fixed. */
static int
broken_move (insn)
rtx insn;
{
if (!INSN_DELETED_P (insn)
&& GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
int destreg;
enum machine_mode mode = GET_MODE (dst);
if (dst == pc_rtx)
return 0;
if (GET_CODE (dst) == REG)
destreg = REGNO (dst);
else if (GET_CODE (dst) == SUBREG && GET_CODE (SUBREG_REG (dst)) == REG)
destreg = REGNO (SUBREG_REG (dst));
return fixit (src, mode, destreg);
}
return 0;
}
void
arm_reorg (first)
rtx first;
{
rtx insn;
int count_size;
int regno;
#if 0
/* The ldr instruction can work with up to a 4k offset, and most constants
will be loaded with one of these instructions; however, the adr
instruction and the ldf instructions only work with a 1k offset. This
code needs to be rewritten to use the 4k offset when possible, and to
adjust when a 1k offset is needed. For now we just use a 1k offset
from the start. */
count_size = 4000;
/* Floating point operands can't work further than 1024 bytes from the
PC, so to make things simple we restrict all loads for such functions.
*/
if (TARGET_HARD_FLOAT)
for (regno = 16; regno < 24; regno++)
if (regs_ever_live[regno])
{
count_size = 1000;
break;
}
#else
count_size = 1000;
#endif /* 0 */
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (broken_move (insn))
{
/* This is a broken move instruction, scan ahead looking for
a barrier to stick the constant table behind */
rtx scan;
rtx barrier = find_barrier (insn, count_size);
/* Now find all the moves between the points and modify them */
for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
{
if (broken_move (scan))
{
/* This is a broken move instruction, add it to the pool */
rtx pat = PATTERN (scan);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
enum machine_mode mode = GET_MODE (dst);
HOST_WIDE_INT offset;
rtx newinsn = scan;
rtx newsrc;
rtx addr;
int scratch;
/* If this is an HImode constant load, convert it into
an SImode constant load. Since the register is always
32 bits this is safe. We have to do this, since the
load pc-relative instruction only does a 32-bit load. */
if (mode == HImode)
{
mode = SImode;
if (GET_CODE (dst) != REG)
abort ();
PUT_MODE (dst, SImode);
}
offset = add_constant (src, mode);
addr = plus_constant (gen_rtx (LABEL_REF, VOIDmode,
pool_vector_label),
offset);
/* For wide moves to integer regs we need to split the
address calculation off into a separate insn, so that
the load can then be done with a load-multiple. This is
safe, since we have already noted the length of such
insns to be 8, and we are immediately over-writing the
scratch we have grabbed with the final result. */
if (GET_MODE_SIZE (mode) > 4
&& (scratch = REGNO (dst)) < 16)
{
rtx reg = gen_rtx (REG, SImode, scratch);
newinsn = emit_insn_after (gen_movaddr (reg, addr),
newinsn);
addr = reg;
}
newsrc = gen_rtx (MEM, mode, addr);
/* Build a jump insn wrapper around the move instead
of an ordinary insn, because we want to have room for
the target label rtx in fld[7], which an ordinary
insn doesn't have. */
newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode,
dst, newsrc),
newinsn);
JUMP_LABEL (newinsn) = pool_vector_label;
/* But it's still an ordinary insn */
PUT_CODE (newinsn, INSN);
/* Kill old insn */
delete_insn (scan);
scan = newinsn;
}
}
dump_table (barrier);
insn = scan;
}
}
}
/* Routines to output assembly language. */ /* Routines to output assembly language. */
/* If the rtx is the correct value then return the string of the number. /* If the rtx is the correct value then return the string of the number.
...@@ -2150,66 +2924,132 @@ output_move_double (operands) ...@@ -2150,66 +2924,132 @@ output_move_double (operands)
switch (GET_CODE (XEXP (operands[1], 0))) switch (GET_CODE (XEXP (operands[1], 0)))
{ {
case REG: case REG:
/* Handle the simple case where address is [r, #0] more
efficient. */
output_asm_insn ("ldm%?ia\t%m1, %M0", operands); output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
break; break;
case PRE_INC: case PRE_INC:
output_asm_insn ("add%?\t%m1, %m1, #8", operands); abort (); /* Should never happen now */
output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
break; break;
case PRE_DEC: case PRE_DEC:
output_asm_insn ("sub%?\t%m1, %m1, #8", operands); output_asm_insn ("ldm%?db\t%m1!, %M0", operands);
output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
break; break;
case POST_INC: case POST_INC:
output_asm_insn ("ldm%?ia\t%m1!, %M0", operands); output_asm_insn ("ldm%?ia\t%m1!, %M0", operands);
break; break;
case POST_DEC: case POST_DEC:
output_asm_insn ("ldm%?ia\t%m1, %M0", operands); abort (); /* Should never happen now */
output_asm_insn ("sub%?\t%m1, %m1, #8", operands);
break; break;
case LABEL_REF:
case CONST:
output_asm_insn ("adr%?\t%0, %1", operands);
output_asm_insn ("ldm%?ia\t%0, %M0", operands);
break;
default: default:
otherops[1] = adj_offsettable_operand (operands[1], 4); if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1)))
/* Take care of overlapping base/data reg. */
if (reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("ldr%?\t%0, %1", otherops);
output_asm_insn ("ldr%?\t%0, %1", operands);
}
else
{ {
output_asm_insn ("ldr%?\t%0, %1", operands); otherops[0] = operands[0];
output_asm_insn ("ldr%?\t%0, %1", otherops); otherops[1] = XEXP (XEXP (operands[1], 0), 0);
otherops[2] = XEXP (XEXP (operands[1], 0), 1);
if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
{
if (GET_CODE (otherops[2]) == CONST_INT)
{
switch (INTVAL (otherops[2]))
{
case -8:
output_asm_insn ("ldm%?db\t%1, %M0", otherops);
return "";
case -4:
output_asm_insn ("ldm%?da\t%1, %M0", otherops);
return "";
case 4:
output_asm_insn ("ldm%?ib\t%1, %M0", otherops);
return "";
}
if (!(const_ok_for_arm (INTVAL (otherops[2]))))
output_asm_insn ("sub%?\t%0, %1, #%n2", otherops);
else
output_asm_insn ("add%?\t%0, %1, %2", otherops);
}
else
output_asm_insn ("add%?\t%0, %1, %2", otherops);
}
else
output_asm_insn ("sub%?\t%0, %1, %2", otherops);
return "ldm%?ia\t%0, %M0";
}
else
{
otherops[1] = adj_offsettable_operand (operands[1], 4);
/* Take care of overlapping base/data reg. */
if (reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("ldr%?\t%0, %1", otherops);
output_asm_insn ("ldr%?\t%0, %1", operands);
}
else
{
output_asm_insn ("ldr%?\t%0, %1", operands);
output_asm_insn ("ldr%?\t%0, %1", otherops);
}
} }
} }
} }
else abort(); /* Constraints should prevent this */ else
abort(); /* Constraints should prevent this */
} }
else if (code0 == MEM && code1 == REG) else if (code0 == MEM && code1 == REG)
{ {
if (REGNO (operands[1]) == 12) if (REGNO (operands[1]) == 12)
abort(); abort();
switch (GET_CODE (XEXP (operands[0], 0))) switch (GET_CODE (XEXP (operands[0], 0)))
{ {
case REG: case REG:
output_asm_insn ("stm%?ia\t%m0, %M1", operands); output_asm_insn ("stm%?ia\t%m0, %M1", operands);
break; break;
case PRE_INC: case PRE_INC:
output_asm_insn ("add%?\t%m0, %m0, #8", operands); abort (); /* Should never happen now */
output_asm_insn ("stm%?ia\t%m0, %M1", operands);
break; break;
case PRE_DEC: case PRE_DEC:
output_asm_insn ("sub%?\t%m0, %m0, #8", operands); output_asm_insn ("stm%?db\t%m0!, %M1", operands);
output_asm_insn ("stm%?ia\t%m0, %M1", operands);
break; break;
case POST_INC: case POST_INC:
output_asm_insn ("stm%?ia\t%m0!, %M1", operands); output_asm_insn ("stm%?ia\t%m0!, %M1", operands);
break; break;
case POST_DEC: case POST_DEC:
output_asm_insn ("stm%?ia\t%m0, %M1", operands); abort (); /* Should never happen now */
output_asm_insn ("sub%?\t%m0, %m0, #8", operands);
break; break;
case PLUS:
if (GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT)
{
switch (INTVAL (XEXP (XEXP (operands[0], 0), 1)))
{
case -8:
output_asm_insn ("stm%?db\t%m0, %M1", operands);
return "";
case -4:
output_asm_insn ("stm%?da\t%m0, %M1", operands);
return "";
case 4:
output_asm_insn ("stm%?ib\t%m0, %M1", operands);
return "";
}
}
/* Fall through */
default: default:
otherops[0] = adj_offsettable_operand (operands[0], 4); otherops[0] = adj_offsettable_operand (operands[0], 4);
otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1])); otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
...@@ -2217,7 +3057,8 @@ output_move_double (operands) ...@@ -2217,7 +3057,8 @@ output_move_double (operands)
output_asm_insn ("str%?\t%1, %0", otherops); output_asm_insn ("str%?\t%1, %0", otherops);
} }
} }
else abort(); /* Constraints should prevent this */ else
abort(); /* Constraints should prevent this */
return ""; return "";
} }
...@@ -2454,7 +3295,7 @@ int_log2 (power) ...@@ -2454,7 +3295,7 @@ int_log2 (power)
{ {
HOST_WIDE_INT shift = 0; HOST_WIDE_INT shift = 0;
while (((1 << shift) & power) == 0) while (((((HOST_WIDE_INT) 1) << shift) & power) == 0)
{ {
if (shift > 31) if (shift > 31)
abort (); abort ();
...@@ -2680,6 +3521,7 @@ output_return_instruction (operand, really_return) ...@@ -2680,6 +3521,7 @@ output_return_instruction (operand, really_return)
/* Otherwise, trap an attempted return by aborting. */ /* Otherwise, trap an attempted return by aborting. */
ops[0] = operand; ops[0] = operand;
ops[1] = gen_rtx (SYMBOL_REF, Pmode, "abort"); ops[1] = gen_rtx (SYMBOL_REF, Pmode, "abort");
assemble_external_libcall (ops[1]);
output_asm_insn ("bl%d0\t%a1", ops); output_asm_insn ("bl%d0\t%a1", ops);
return ""; return "";
} }
...@@ -2732,13 +3574,13 @@ output_return_instruction (operand, really_return) ...@@ -2732,13 +3574,13 @@ output_return_instruction (operand, really_return)
strcat (instr, "%|"); strcat (instr, "%|");
strcat (instr, really_return ? reg_names[15] : reg_names[14]); strcat (instr, really_return ? reg_names[15] : reg_names[14]);
} }
strcat (instr, (TARGET_6 || !really_return) ? "}" : "}^"); strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
output_asm_insn (instr, &operand); output_asm_insn (instr, &operand);
} }
else if (really_return) else if (really_return)
{ {
strcpy (instr, strcpy (instr, (TARGET_APCS_32
TARGET_6 ? "mov%?%d0\t%|pc, lr" : "mov%?%d0s\t%|pc, %|lr"); ? "mov%?%d0\t%|pc, %|lr" : "mov%?%d0s\t%|pc, %|lr"));
output_asm_insn (instr, &operand); output_asm_insn (instr, &operand);
} }
...@@ -2866,6 +3708,7 @@ output_func_epilogue (f, frame_size) ...@@ -2866,6 +3708,7 @@ output_func_epilogue (f, frame_size)
if (volatile_func) if (volatile_func)
{ {
rtx op = gen_rtx (SYMBOL_REF, Pmode, "abort"); rtx op = gen_rtx (SYMBOL_REF, Pmode, "abort");
assemble_external_libcall (op);
output_asm_insn ("bl\t%a0", &op); output_asm_insn ("bl\t%a0", &op);
code_size = 4; code_size = 4;
goto epilogue_done; goto epilogue_done;
...@@ -2891,7 +3734,7 @@ output_func_epilogue (f, frame_size) ...@@ -2891,7 +3734,7 @@ output_func_epilogue (f, frame_size)
live_regs_mask |= 0xA800; live_regs_mask |= 0xA800;
print_multi_reg (f, "ldmea\t%sfp", live_regs_mask, print_multi_reg (f, "ldmea\t%sfp", live_regs_mask,
TARGET_6 ? FALSE : TRUE); TARGET_APCS_32 ? FALSE : TRUE);
code_size += 4; code_size += 4;
} }
else else
...@@ -2914,7 +3757,7 @@ output_func_epilogue (f, frame_size) ...@@ -2914,7 +3757,7 @@ output_func_epilogue (f, frame_size)
if (current_function_pretend_args_size == 0 && regs_ever_live[14]) if (current_function_pretend_args_size == 0 && regs_ever_live[14])
{ {
print_multi_reg (f, "ldmfd\t%ssp!", live_regs_mask | 0x8000, print_multi_reg (f, "ldmfd\t%ssp!", live_regs_mask | 0x8000,
TARGET_6 ? FALSE : TRUE); TARGET_APCS_32 ? FALSE : TRUE);
code_size += 4; code_size += 4;
} }
else else
...@@ -2932,8 +3775,8 @@ output_func_epilogue (f, frame_size) ...@@ -2932,8 +3775,8 @@ output_func_epilogue (f, frame_size)
current_function_pretend_args_size); current_function_pretend_args_size);
output_add_immediate (operands); output_add_immediate (operands);
} }
fprintf (f, fprintf (f, (TARGET_APCS_32 ? "\tmov\t%spc, %slr\n"
TARGET_6 ? "\tmov\t%spc, %slr\n" : "\tmovs\t%spc, %slr\n", : "\tmovs\t%spc, %slr\n"),
REGISTER_PREFIX, REGISTER_PREFIX, f); REGISTER_PREFIX, REGISTER_PREFIX, f);
code_size += 4; code_size += 4;
} }
...@@ -3256,8 +4099,7 @@ arm_asm_output_label (stream, name) ...@@ -3256,8 +4099,7 @@ arm_asm_output_label (stream, name)
struct label_offset *cur; struct label_offset *cur;
int hash = 0; int hash = 0;
assemble_name (stream, name); ARM_OUTPUT_LABEL (stream, name);
fputs (":\n", stream);
if (! in_text_section ()) if (! in_text_section ())
return; return;
...@@ -3283,82 +4125,6 @@ arm_asm_output_label (stream, name) ...@@ -3283,82 +4125,6 @@ arm_asm_output_label (stream, name)
offset_table[hash] = cur; offset_table[hash] = cur;
} }
/* Load a symbol that is known to be in the text segment into a register.
This should never be called when not optimizing. */
char *
output_load_symbol (insn, operands)
rtx insn;
rtx *operands;
{
char *s;
char *name = XSTR (operands[1], 0);
struct label_offset *he;
int hash = 0;
int offset;
unsigned int mask, never_mask = 0xffffffff;
int shift, inst;
char buffer[100];
if (optimize == 0 || *name != '*')
abort ();
for (s = &name[1]; *s; s++)
hash += *s;
hash = hash % LABEL_HASH_SIZE;
he = offset_table[hash];
while (he && strcmp (he->name, &name[1]))
he = he->cdr;
if (!he)
abort ();
offset = (arm_text_location + insn_addresses[INSN_UID (insn)]
+ get_prologue_size () + 8 - he->offset);
if (offset < 0)
abort ();
/* When generating the instructions, we never mask out the bits that we
think will be always zero, then if a mistake has occurred somewhere, the
assembler will spot it and generate an error. */
/* If the symbol is word aligned then we might be able to reduce the
number of loads. */
shift = ((offset & 3) == 0) ? 2 : 0;
/* Clear the bits from NEVER_MASK that will be orred in with the individual
instructions. */
for (; shift < 32; shift += 8)
{
mask = 0xff << shift;
if ((offset & mask) || ((unsigned) offset) > mask)
never_mask &= ~mask;
}
inst = 8;
mask = 0xff << (shift - 32);
while (mask && (never_mask & mask) == 0)
{
if (inst == 8)
{
strcpy (buffer, "sub%?\t%0, %|pc, #(8 + . -%a1)");
if ((never_mask | mask) != 0xffffffff)
sprintf (buffer + strlen (buffer), " & 0x%x", mask | never_mask);
}
else
sprintf (buffer, "sub%%?\t%%0, %%0, #(%d + . -%%a1) & 0x%x",
inst, mask | never_mask);
output_asm_insn (buffer, operands);
mask <<= 8;
inst -= 4;
}
return "";
}
/* Output code resembling an .lcomm directive. /bin/as doesn't have this /* Output code resembling an .lcomm directive. /bin/as doesn't have this
directive hence this hack, which works by reserving some `.space' in the directive hence this hack, which works by reserving some `.space' in the
bss segment directly. bss segment directly.
...@@ -3634,11 +4400,12 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -3634,11 +4400,12 @@ final_prescan_insn (insn, opvec, noperands)
break; break;
case CALL_INSN: case CALL_INSN:
/* The arm 6xx uses full 32 bit addresses so the cc is not /* If using 32-bit addresses the cc is not preserved over
preserved over calls */ calls */
if (TARGET_6) if (TARGET_APCS_32)
fail = TRUE; fail = TRUE;
break; break;
case JUMP_INSN: case JUMP_INSN:
/* If this is an unconditional branch to the same label, succeed. /* If this is an unconditional branch to the same label, succeed.
If it is to another label, do nothing. If it is conditional, If it is to another label, do nothing. If it is conditional,
...@@ -3746,4 +4513,108 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -3746,4 +4513,108 @@ final_prescan_insn (insn, opvec, noperands)
} }
} }
/* EOF */ #ifdef AOF_ASSEMBLER
/* Special functions only needed when producing AOF syntax assembler. */
int arm_text_section_count = 1;
char *
aof_text_section (in_readonly)
int in_readonly;
{
static char buf[100];
if (in_readonly)
return "";
sprintf (buf, "\tAREA |C$$code%d|, CODE, READONLY",
arm_text_section_count++);
if (flag_pic)
strcat (buf, ", PIC, REENTRANT");
return buf;
}
static int arm_data_section_count = 1;
char *
aof_data_section ()
{
static char buf[100];
sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++);
return buf;
}
/* The AOF assembler is religiously strict about declarations of
imported and exported symbols, so that it is impossible to declare
a function as imported near the begining of the file, and then to
export it later on. It is, however, possible to delay the decision
until all the functions in the file have been compiled. To get
around this, we maintain a list of the imports and exports, and
delete from it any that are subsequently defined. At the end of
compilation we spit the remainder of the list out before the END
directive. */
struct import
{
struct import *next;
char *name;
};
static struct import *imports_list = NULL;
void
aof_add_import (name)
char *name;
{
struct import *new;
for (new = imports_list; new; new = new->next)
if (new->name == name)
return;
new = (struct import *) xmalloc (sizeof (struct import));
new->next = imports_list;
imports_list = new;
new->name = name;
}
void
aof_delete_import (name)
char *name;
{
struct import **old;
for (old = &imports_list; *old; old = & (*old)->next)
{
if ((*old)->name == name)
{
*old = (*old)->next;
return;
}
}
}
int arm_main_function = 0;
void
aof_dump_imports (f)
FILE *f;
{
/* The AOF assembler needs this to cause the startup code to be extracted
from the library. Brining in __main causes the whole thing to work
automagically. */
if (arm_main_function)
{
text_section ();
fputs ("\tIMPORT __main\n", f);
fputs ("\tDCD __main\n", f);
}
/* Now dump the remaining imports. */
while (imports_list)
{
fprintf (f, "\tIMPORT\t");
assemble_name (f, imports_list->name);
fputc ('\n', f);
imports_list = imports_list->next;
}
}
#endif /* AOF_ASSEMBLER */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment