diff options
author | Richard Earnshaw <erich@gnu.org> | 1995-12-06 11:41:39 +0000 |
---|---|---|
committer | Richard Earnshaw <erich@gnu.org> | 1995-12-06 11:41:39 +0000 |
commit | 2b835d68114d222704505707d0315902a4201250 (patch) | |
tree | e77783f05a0d139fd6e72215a8e806a53f4ed558 | |
parent | 9fb7806b3797b39b4931aa570e95ee056381dc0f (diff) | |
download | gcc-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.c | 1361 |
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 */ |