aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Earnshaw <erich@gnu.org>1995-12-06 11:41:39 +0000
committerRichard Earnshaw <erich@gnu.org>1995-12-06 11:41:39 +0000
commit2b835d68114d222704505707d0315902a4201250 (patch)
treee77783f05a0d139fd6e72215a8e806a53f4ed558
parent9fb7806b3797b39b4931aa570e95ee056381dc0f (diff)
downloadgcc-2b835d68114d222704505707d0315902a4201250.zip
gcc-2b835d68114d222704505707d0315902a4201250.tar.gz
gcc-2b835d68114d222704505707d0315902a4201250.tar.bz2
(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
-rw-r--r--gcc/config/arm/arm.c1361
1 files changed, 1116 insertions, 245 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 4c39275..079e4b6 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -50,6 +50,8 @@ extern void arm_increase_location ();
HOST_WIDE_INT int_log2 PROTO ((HOST_WIDE_INT));
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
stored from the compare operation. */
@@ -63,6 +65,18 @@ enum processor_type arm_cpu;
/* What type of floating point are we compiling for? */
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
must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */
@@ -96,6 +110,8 @@ static struct label_offset *offset_table[LABEL_HASH_SIZE];
static int return_used_this_function;
+static int arm_constant_limit = 3;
+
/* For an explanation of these variables, see final_prescan_insn below. */
int arm_ccfsm_state;
int arm_current_cc;
@@ -110,6 +126,168 @@ char *arm_condition_codes[] =
};
#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 */
@@ -191,7 +369,8 @@ const_ok_for_op (i, code, mode)
VAL is the integer to operate on;
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
- 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
arm_split_constant (code, mode, val, target, source, subtargets)
@@ -202,6 +381,55 @@ arm_split_constant (code, mode, val, target, source, subtargets)
rtx source;
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_invert = 0;
int can_negate = 0;
@@ -237,15 +465,17 @@ arm_split_constant (code, mode, val, target, source, subtargets)
case IOR:
if (remainder == 0xffffffff)
{
- emit_insn (gen_rtx (SET, VOIDmode, target,
- GEN_INT (ARM_SIGN_EXTEND (val))));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ GEN_INT (ARM_SIGN_EXTEND (val))));
return 1;
}
if (remainder == 0)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
- emit_insn (gen_rtx (SET, VOIDmode, target, source));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1;
}
break;
@@ -253,14 +483,16 @@ arm_split_constant (code, mode, val, target, source, subtargets)
case AND:
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;
}
if (remainder == 0xffffffff)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
- emit_insn (gen_rtx (SET, VOIDmode, target, source));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1;
}
can_invert = 1;
@@ -271,13 +503,15 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
- emit_insn (gen_rtx (SET, VOIDmode, target, source));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target, source));
return 1;
}
if (remainder == 0xffffffff)
{
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (NOT, mode, source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (NOT, mode, source)));
return 1;
}
@@ -289,14 +523,16 @@ arm_split_constant (code, mode, val, target, source, subtargets)
passed as (source + (-val)). */
if (remainder == 0)
{
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (NEG, mode, source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (NEG, mode, source)));
return 1;
}
if (const_ok_for_arm (val))
{
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (MINUS, mode, GEN_INT (val), source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (MINUS, mode, GEN_INT (val), source)));
return 1;
}
can_negate = 1;
@@ -312,9 +548,11 @@ arm_split_constant (code, mode, val, target, source, subtargets)
|| (can_negate_initial && const_ok_for_arm (-val))
|| (can_invert && const_ok_for_arm (~val)))
{
- emit_insn (gen_rtx (SET, VOIDmode, target,
- (source ? gen_rtx (code, mode, source,
- GEN_INT (val)) : GEN_INT (val))));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ (source ? gen_rtx (code, mode, source,
+ GEN_INT (val))
+ : GEN_INT (val))));
return 1;
}
@@ -366,10 +604,14 @@ arm_split_constant (code, mode, val, target, source, subtargets)
(temp1 = ARM_SIGN_EXTEND (remainder
<< (set_sign_bit_copies - 1))))
{
- new_src = subtargets ? gen_reg_rtx (mode) : target;
- 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)));
+ if (generate)
+ {
+ new_src = subtargets ? gen_reg_rtx (mode) : target;
+ 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;
}
/* 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)
temp1 |= (1 << (set_sign_bit_copies - 1)) - 1;
if (const_ok_for_arm (~temp1))
{
- new_src = subtargets ? gen_reg_rtx (mode) : target;
- 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)));
+ if (generate)
+ {
+ new_src = subtargets ? gen_reg_rtx (mode) : target;
+ 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;
}
}
@@ -401,18 +647,18 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder)
&& ! const_ok_for_arm (temp2))
{
- insns
- = arm_split_constant (code, mode, temp2,
- (new_src
- = subtargets ? gen_reg_rtx (mode)
- : target),
- source, subtargets);
+ insns = arm_gen_constant (code, mode, temp2,
+ new_src = (subtargets
+ ? gen_reg_rtx (mode)
+ : target),
+ source, subtargets, generate);
source = new_src;
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (IOR, mode,
- gen_rtx (ASHIFT, mode, source,
- GEN_INT (i)),
- source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (IOR, mode,
+ gen_rtx (ASHIFT, mode, source,
+ GEN_INT (i)),
+ source)));
return insns + 1;
}
}
@@ -423,18 +669,18 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (((temp1 | (temp1 >> i)) == remainder)
&& ! const_ok_for_arm (temp1))
{
- insns
- = arm_split_constant (code, mode, temp1,
- (new_src
- = subtargets ? gen_reg_rtx (mode)
- : target),
- source, subtargets);
+ insns = arm_gen_constant (code, mode, temp1,
+ new_src = (subtargets
+ ? gen_reg_rtx (mode)
+ : target),
+ source, subtargets, generate);
source = new_src;
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (IOR, mode,
- gen_rtx (LSHIFTRT, mode, source,
- GEN_INT (i)),
- source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (IOR, mode,
+ gen_rtx (LSHIFTRT, mode,
+ source, GEN_INT (i)),
+ source)));
return insns + 1;
}
}
@@ -451,12 +697,15 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{
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,
- GEN_INT (ARM_SIGN_EXTEND (~ val))));
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (code, mode, source, sub)));
+ emit_insn (gen_rtx (SET, VOIDmode, sub,
+ GEN_INT (ARM_SIGN_EXTEND (~ val))));
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (code, mode, source, sub)));
+ }
return 2;
}
}
@@ -467,49 +716,59 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (set_sign_bit_copies > 8
&& (val & (-1 << (32 - set_sign_bit_copies))) == val)
{
- rtx sub = subtargets ? gen_reg_rtx (mode) : target;
- rtx shift = GEN_INT (set_sign_bit_copies);
-
- emit_insn (gen_rtx (SET, VOIDmode, sub,
- gen_rtx (NOT, mode,
- gen_rtx (ASHIFT, mode, source,
- shift))));
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (NOT, mode,
- gen_rtx (LSHIFTRT, mode, sub,
- shift))));
+ if (generate)
+ {
+ rtx sub = subtargets ? gen_reg_rtx (mode) : target;
+ rtx shift = GEN_INT (set_sign_bit_copies);
+
+ emit_insn (gen_rtx (SET, VOIDmode, sub,
+ gen_rtx (NOT, mode,
+ gen_rtx (ASHIFT, mode, source,
+ shift))));
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (NOT, mode,
+ gen_rtx (LSHIFTRT, mode, sub,
+ shift))));
+ }
return 2;
}
if (set_zero_bit_copies > 8
&& (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder)
{
- rtx sub = subtargets ? gen_reg_rtx (mode) : target;
- rtx shift = GEN_INT (set_zero_bit_copies);
-
- emit_insn (gen_rtx (SET, VOIDmode, sub,
- gen_rtx (NOT, mode,
- gen_rtx (LSHIFTRT, mode, source,
- shift))));
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (NOT, mode,
- gen_rtx (ASHIFT, mode, sub,
- shift))));
+ if (generate)
+ {
+ rtx sub = subtargets ? gen_reg_rtx (mode) : target;
+ rtx shift = GEN_INT (set_zero_bit_copies);
+
+ emit_insn (gen_rtx (SET, VOIDmode, sub,
+ gen_rtx (NOT, mode,
+ gen_rtx (LSHIFTRT, mode, source,
+ shift))));
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (NOT, mode,
+ gen_rtx (ASHIFT, mode, sub,
+ shift))));
+ }
return 2;
}
if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~ val)))
{
- rtx sub = subtargets ? gen_reg_rtx (mode) : target;
- emit_insn (gen_rtx (SET, VOIDmode, sub,
- gen_rtx (NOT, mode, source)));
- source = sub;
- if (subtargets)
- sub = gen_reg_rtx (mode);
- emit_insn (gen_rtx (SET, VOIDmode, sub,
- gen_rtx (AND, mode, source, GEN_INT (temp1))));
- emit_insn (gen_rtx (SET, VOIDmode, target,
- gen_rtx (NOT, mode, sub)));
+ if (generate)
+ {
+ rtx sub = subtargets ? gen_reg_rtx (mode) : target;
+ emit_insn (gen_rtx (SET, VOIDmode, sub,
+ gen_rtx (NOT, mode, source)));
+ source = sub;
+ if (subtargets)
+ sub = gen_reg_rtx (mode);
+ emit_insn (gen_rtx (SET, VOIDmode, sub,
+ gen_rtx (AND, mode, source,
+ GEN_INT (temp1))));
+ emit_insn (gen_rtx (SET, VOIDmode, target,
+ gen_rtx (NOT, mode, sub)));
+ }
return 3;
}
break;
@@ -522,19 +781,30 @@ arm_split_constant (code, mode, val, target, source, subtargets)
<< (32 - clear_sign_bit_copies))
& 0xffffffff);
rtx new_source;
- rtx shift = GEN_INT (clear_sign_bit_copies);
+ rtx shift;
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;
- insns = arm_split_constant (AND, mode, remainder | shift_mask,
- new_source, source, subtargets);
- source = new_source;
+ emit_insn (gen_ashlsi3 (new_source, source, shift));
+ emit_insn (gen_lshrsi3 (target, new_source, shift));
}
- 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;
}
@@ -542,19 +812,30 @@ arm_split_constant (code, mode, val, target, source, subtargets)
{
HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1;
rtx new_source;
- rtx shift = GEN_INT (clear_zero_bit_copies);
+ rtx shift;
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;
- insns = arm_split_constant (AND, mode, remainder | shift_mask,
- new_source, source, subtargets);
- source = new_source;
+ emit_insn (gen_lshrsi3 (new_source, source, shift));
+ emit_insn (gen_ashlsi3 (target, new_source, shift));
}
- 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;
}
@@ -629,32 +910,40 @@ arm_split_constant (code, mode, val, target, source, subtargets)
if (code == SET)
{
- emit_insn (gen_rtx (SET, VOIDmode,
- new_src = (subtargets ? gen_reg_rtx (mode)
- : target),
- GEN_INT (can_invert ? ~temp1 : temp1)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode,
+ new_src = (subtargets
+ ? gen_reg_rtx (mode)
+ : target),
+ GEN_INT (can_invert ? ~temp1 : temp1)));
can_invert = 0;
code = PLUS;
}
else if (code == MINUS)
{
- emit_insn (gen_rtx (SET, VOIDmode,
- new_src = (subtargets ? gen_reg_rtx (mode)
- : target),
- gen_rtx (code, mode, GEN_INT (temp1),
- source)));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode,
+ new_src = (subtargets
+ ? gen_reg_rtx (mode)
+ : target),
+ gen_rtx (code, mode, GEN_INT (temp1),
+ source)));
code = PLUS;
}
else
{
- emit_insn (gen_rtx (SET, VOIDmode,
- new_src = remainder ? (subtargets
- ? gen_reg_rtx (mode)
- : target) : target,
- gen_rtx (code, mode, source,
- GEN_INT (can_invert ? ~temp1
- : (can_negate
- ? -temp1 : temp1)))));
+ if (generate)
+ emit_insn (gen_rtx (SET, VOIDmode,
+ new_src = (remainder
+ ? (subtargets
+ ? gen_reg_rtx (mode)
+ : target)
+ : target),
+ gen_rtx (code, mode, source,
+ GEN_INT (can_invert ? ~temp1
+ : (can_negate
+ ? -temp1
+ : temp1)))));
}
insns++;
@@ -667,6 +956,46 @@ arm_split_constant (code, mode, val, target, source, subtargets)
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) \
(GET_CODE (X) == REG \
|| (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG))
@@ -814,27 +1143,35 @@ arm_rtx_costs (x, code, outer_code)
return 8;
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
|| mode == DImode)
return 30;
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 j;
-
- /* This will need adjusting for ARM's with fast multiplies */
- for (j = 0; i && j < 32; j += 2)
+ int booth_unit_size = (arm_fast_multiply ? 8 : 2);
+
+ for (j = 0; i && j < 32; j += booth_unit_size)
{
- i &= ~(3 << j);
+ i >>= booth_unit_size;
add_cost += 2;
}
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));
case NEG:
@@ -889,15 +1226,9 @@ arm_rtx_costs (x, code, outer_code)
static int fpa_consts_inited = 0;
char *strings_fpa[8] = {
- "0.0",
- "1.0",
- "2.0",
- "3.0",
- "4.0",
- "5.0",
- "0.5",
- "10.0"
- };
+ "0", "1", "2", "3",
+ "4", "5", "0.5", "10"
+};
static REAL_VALUE_TYPE values_fpa[8];
@@ -1306,6 +1637,64 @@ reversible_cc_register (x, mode)
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
minmax_code (x)
rtx x;
@@ -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. */
/* If the rtx is the correct value then return the string of the number.
@@ -2150,66 +2924,132 @@ output_move_double (operands)
switch (GET_CODE (XEXP (operands[1], 0)))
{
case REG:
- /* Handle the simple case where address is [r, #0] more
- efficient. */
output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
break;
+
case PRE_INC:
- output_asm_insn ("add%?\t%m1, %m1, #8", operands);
- output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
+ abort (); /* Should never happen now */
break;
+
case PRE_DEC:
- output_asm_insn ("sub%?\t%m1, %m1, #8", operands);
- output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
+ output_asm_insn ("ldm%?db\t%m1!, %M0", operands);
break;
+
case POST_INC:
output_asm_insn ("ldm%?ia\t%m1!, %M0", operands);
break;
+
case POST_DEC:
- output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
- output_asm_insn ("sub%?\t%m1, %m1, #8", operands);
+ abort (); /* Should never happen now */
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:
- 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
+ if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1)))
{
- output_asm_insn ("ldr%?\t%0, %1", operands);
- output_asm_insn ("ldr%?\t%0, %1", otherops);
+ otherops[0] = operands[0];
+ 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)
{
if (REGNO (operands[1]) == 12)
abort();
+
switch (GET_CODE (XEXP (operands[0], 0)))
{
case REG:
output_asm_insn ("stm%?ia\t%m0, %M1", operands);
break;
+
case PRE_INC:
- output_asm_insn ("add%?\t%m0, %m0, #8", operands);
- output_asm_insn ("stm%?ia\t%m0, %M1", operands);
+ abort (); /* Should never happen now */
break;
+
case PRE_DEC:
- output_asm_insn ("sub%?\t%m0, %m0, #8", operands);
- output_asm_insn ("stm%?ia\t%m0, %M1", operands);
+ output_asm_insn ("stm%?db\t%m0!, %M1", operands);
break;
+
case POST_INC:
output_asm_insn ("stm%?ia\t%m0!, %M1", operands);
break;
+
case POST_DEC:
- output_asm_insn ("stm%?ia\t%m0, %M1", operands);
- output_asm_insn ("sub%?\t%m0, %m0, #8", operands);
+ abort (); /* Should never happen now */
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:
otherops[0] = adj_offsettable_operand (operands[0], 4);
otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
@@ -2217,7 +3057,8 @@ output_move_double (operands)
output_asm_insn ("str%?\t%1, %0", otherops);
}
}
- else abort(); /* Constraints should prevent this */
+ else
+ abort(); /* Constraints should prevent this */
return "";
}
@@ -2454,7 +3295,7 @@ int_log2 (power)
{
HOST_WIDE_INT shift = 0;
- while (((1 << shift) & power) == 0)
+ while (((((HOST_WIDE_INT) 1) << shift) & power) == 0)
{
if (shift > 31)
abort ();
@@ -2680,6 +3521,7 @@ output_return_instruction (operand, really_return)
/* Otherwise, trap an attempted return by aborting. */
ops[0] = operand;
ops[1] = gen_rtx (SYMBOL_REF, Pmode, "abort");
+ assemble_external_libcall (ops[1]);
output_asm_insn ("bl%d0\t%a1", ops);
return "";
}
@@ -2732,13 +3574,13 @@ output_return_instruction (operand, really_return)
strcat (instr, "%|");
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);
}
else if (really_return)
{
- strcpy (instr,
- TARGET_6 ? "mov%?%d0\t%|pc, lr" : "mov%?%d0s\t%|pc, %|lr");
+ strcpy (instr, (TARGET_APCS_32
+ ? "mov%?%d0\t%|pc, %|lr" : "mov%?%d0s\t%|pc, %|lr"));
output_asm_insn (instr, &operand);
}
@@ -2866,6 +3708,7 @@ output_func_epilogue (f, frame_size)
if (volatile_func)
{
rtx op = gen_rtx (SYMBOL_REF, Pmode, "abort");
+ assemble_external_libcall (op);
output_asm_insn ("bl\t%a0", &op);
code_size = 4;
goto epilogue_done;
@@ -2891,7 +3734,7 @@ output_func_epilogue (f, frame_size)
live_regs_mask |= 0xA800;
print_multi_reg (f, "ldmea\t%sfp", live_regs_mask,
- TARGET_6 ? FALSE : TRUE);
+ TARGET_APCS_32 ? FALSE : TRUE);
code_size += 4;
}
else
@@ -2914,7 +3757,7 @@ output_func_epilogue (f, frame_size)
if (current_function_pretend_args_size == 0 && regs_ever_live[14])
{
print_multi_reg (f, "ldmfd\t%ssp!", live_regs_mask | 0x8000,
- TARGET_6 ? FALSE : TRUE);
+ TARGET_APCS_32 ? FALSE : TRUE);
code_size += 4;
}
else
@@ -2932,8 +3775,8 @@ output_func_epilogue (f, frame_size)
current_function_pretend_args_size);
output_add_immediate (operands);
}
- fprintf (f,
- TARGET_6 ? "\tmov\t%spc, %slr\n" : "\tmovs\t%spc, %slr\n",
+ fprintf (f, (TARGET_APCS_32 ? "\tmov\t%spc, %slr\n"
+ : "\tmovs\t%spc, %slr\n"),
REGISTER_PREFIX, REGISTER_PREFIX, f);
code_size += 4;
}
@@ -3256,8 +4099,7 @@ arm_asm_output_label (stream, name)
struct label_offset *cur;
int hash = 0;
- assemble_name (stream, name);
- fputs (":\n", stream);
+ ARM_OUTPUT_LABEL (stream, name);
if (! in_text_section ())
return;
@@ -3283,82 +4125,6 @@ arm_asm_output_label (stream, name)
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
directive hence this hack, which works by reserving some `.space' in the
bss segment directly.
@@ -3634,11 +4400,12 @@ final_prescan_insn (insn, opvec, noperands)
break;
case CALL_INSN:
- /* The arm 6xx uses full 32 bit addresses so the cc is not
- preserved over calls */
- if (TARGET_6)
+ /* If using 32-bit addresses the cc is not preserved over
+ calls */
+ if (TARGET_APCS_32)
fail = TRUE;
break;
+
case JUMP_INSN:
/* If this is an unconditional branch to the same label, succeed.
If it is to another label, do nothing. If it is conditional,
@@ -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 */