diff options
author | David E. O'Brien <obrien@FreeBSD.org> | 2002-02-01 18:16:02 +0000 |
---|---|---|
committer | David E. O'Brien <obrien@FreeBSD.org> | 2002-02-01 18:16:02 +0000 |
commit | 1952e2e1c1be6f107fa3ce8b10025cfd1cd7943b (patch) | |
tree | 086d9d6c8fbd4fc8fe4495059332f66bc0f8d12b /contrib/gcc/config/arm | |
parent | d337ceafd72ec93f99dfbee5ea0e70ed180a2dd6 (diff) |
Enlist the FreeBSD-CURRENT users as testers of what is to become Gcc 3.1.0.
These bits are taken from the FSF anoncvs repo on 1-Feb-2002 08:20 PST.
Notes
Notes:
svn path=/vendor/gcc/dist/; revision=90075
Diffstat (limited to 'contrib/gcc/config/arm')
49 files changed, 28320 insertions, 0 deletions
diff --git a/contrib/gcc/config/arm/README-interworking b/contrib/gcc/config/arm/README-interworking new file mode 100644 index 000000000000..46b76c992425 --- /dev/null +++ b/contrib/gcc/config/arm/README-interworking @@ -0,0 +1,742 @@ + Arm / Thumb Interworking + ======================== + +The Cygnus GNU Pro Toolkit for the ARM7T processor supports function +calls between code compiled for the ARM instruction set and code +compiled for the Thumb instruction set and vice versa. This document +describes how that interworking support operates and explains the +command line switches that should be used in order to produce working +programs. + +Note: The Cygnus GNU Pro Toolkit does not support switching between +compiling for the ARM instruction set and the Thumb instruction set +on anything other than a per file basis. There are in fact two +completely separate compilers, one that produces ARM assembler +instructions and one that produces Thumb assembler instructions. The +two compilers share the same assembler, linker and so on. + + +1. Explicit interworking support for C and C++ files +==================================================== + +By default if a file is compiled without any special command line +switches then the code produced will not support interworking. +Provided that a program is made up entirely from object files and +libraries produced in this way and which contain either exclusively +ARM instructions or exclusively Thumb instructions then this will not +matter and a working executable will be created. If an attempt is +made to link together mixed ARM and Thumb object files and libraries, +then warning messages will be produced by the linker and a non-working +executable will be created. + +In order to produce code which does support interworking it should be +compiled with the + + -mthumb-interwork + +command line option. Provided that a program is made up entirely from +object files and libraries built with this command line switch a +working executable will be produced, even if both ARM and Thumb +instructions are used by the various components of the program. (No +warning messages will be produced by the linker either). + +Note that specifying -mthumb-interwork does result in slightly larger, +slower code being produced. This is why interworking support must be +specifically enabled by a switch. + + +2. Explicit interworking support for assembler files +==================================================== + +If assembler files are to be included into an interworking program +then the following rules must be obeyed: + + * Any externally visible functions must return by using the BX + instruction. + + * Normal function calls can just use the BL instruction. The + linker will automatically insert code to switch between ARM + and Thumb modes as necessary. + + * Calls via function pointers should use the BX instruction if + the call is made in ARM mode: + + .code 32 + mov lr, pc + bx rX + + This code sequence will not work in Thumb mode however, since + the mov instruction will not set the bottom bit of the lr + register. Instead a branch-and-link to the _call_via_rX + functions should be used instead: + + .code 16 + bl _call_via_rX + + where rX is replaced by the name of the register containing + the function address. + + * All externally visible functions which should be entered in + Thumb mode must have the .thumb_func pseudo op specified just + before their entry point. eg: + + .code 16 + .global function + .thumb_func + function: + ...start of function.... + + * All assembler files must be assembled with the switch + -mthumb-interwork specified on the command line. (If the file + is assembled by calling gcc it will automatically pass on the + -mthumb-interwork switch to the assembler, provided that it + was specified on the gcc command line in the first place.) + + +3. Support for old, non-interworking aware code. +================================================ + +If it is necessary to link together code produced by an older, +non-interworking aware compiler, or code produced by the new compiler +but without the -mthumb-interwork command line switch specified, then +there are two command line switches that can be used to support this. + +The switch + + -mcaller-super-interworking + +will allow calls via function pointers in Thumb mode to work, +regardless of whether the function pointer points to old, +non-interworking aware code or not. Specifying this switch does +produce slightly slower code however. + +Note: There is no switch to allow calls via function pointers in ARM +mode to be handled specially. Calls via function pointers from +interworking aware ARM code to non-interworking aware ARM code work +without any special considerations by the compiler. Calls via +function pointers from interworking aware ARM code to non-interworking +aware Thumb code however will not work. (Actually under some +circumstances they may work, but there are no guarantees). This is +because only the new compiler is able to produce Thumb code, and this +compiler already has a command line switch to produce interworking +aware code. + + +The switch + + -mcallee-super-interworking + +will allow non-interworking aware ARM or Thumb code to call Thumb +functions, either directly or via function pointers. Specifying this +switch does produce slightly larger, slower code however. + +Note: There is no switch to allow non-interworking aware ARM or Thumb +code to call ARM functions. There is no need for any special handling +of calls from non-interworking aware ARM code to interworking aware +ARM functions, they just work normally. Calls from non-interworking +aware Thumb functions to ARM code however, will not work. There is no +option to support this, since it is always possible to recompile the +Thumb code to be interworking aware. + +As an alternative to the command line switch +-mcallee-super-interworking, which affects all externally visible +functions in a file, it is possible to specify an attribute or +declspec for individual functions, indicating that that particular +function should support being called by non-interworking aware code. +The function should be defined like this: + + int __attribute__((interfacearm)) function + { + ... body of function ... + } + +or + + int __declspec(interfacearm) function + { + ... body of function ... + } + + + +4. Interworking support in dlltool +================================== + +It is possible to create DLLs containing mixed ARM and Thumb code. It +is also possible to call Thumb code in a DLL from an ARM program and +vice versa. It is even possible to call ARM DLLs that have been compiled +without interworking support (say by an older version of the compiler), +from Thumb programs and still have things work properly. + + A version of the `dlltool' program which supports the `--interwork' +command line switch is needed, as well as the following special +considerations when building programs and DLLs: + +*Use `-mthumb-interwork'* + When compiling files for a DLL or a program the `-mthumb-interwork' + command line switch should be specified if calling between ARM and + Thumb code can happen. If a program is being compiled and the + mode of the DLLs that it uses is not known, then it should be + assumed that interworking might occur and the switch used. + +*Use `-m thumb'* + If the exported functions from a DLL are all Thumb encoded then the + `-m thumb' command line switch should be given to dlltool when + building the stubs. This will make dlltool create Thumb encoded + stubs, rather than its default of ARM encoded stubs. + + If the DLL consists of both exported Thumb functions and exported + ARM functions then the `-m thumb' switch should not be used. + Instead the Thumb functions in the DLL should be compiled with the + `-mcallee-super-interworking' switch, or with the `interfacearm' + attribute specified on their prototypes. In this way they will be + given ARM encoded prologues, which will work with the ARM encoded + stubs produced by dlltool. + +*Use `-mcaller-super-interworking'* + If it is possible for Thumb functions in a DLL to call + non-interworking aware code via a function pointer, then the Thumb + code must be compiled with the `-mcaller-super-interworking' + command line switch. This will force the function pointer calls + to use the _interwork_call_via_rX stub functions which will + correctly restore Thumb mode upon return from the called function. + +*Link with `libgcc.a'* + When the dll is built it may have to be linked with the GCC + library (`libgcc.a') in order to extract the _call_via_rX functions + or the _interwork_call_via_rX functions. This represents a partial + redundancy since the same functions *may* be present in the + application itself, but since they only take up 372 bytes this + should not be too much of a consideration. + +*Use `--support-old-code'* + When linking a program with an old DLL which does not support + interworking, the `--support-old-code' command line switch to the + linker should be used. This causes the linker to generate special + interworking stubs which can cope with old, non-interworking aware + ARM code, at the cost of generating bulkier code. The linker will + still generate a warning message along the lines of: + "Warning: input file XXX does not support interworking, whereas YYY does." + but this can now be ignored because the --support-old-code switch + has been used. + + + +5. How interworking support works +================================= + +Switching between the ARM and Thumb instruction sets is accomplished +via the BX instruction which takes as an argument a register name. +Control is transfered to the address held in this register (with the +bottom bit masked out), and if the bottom bit is set, then Thumb +instruction processing is enabled, otherwise ARM instruction +processing is enabled. + +When the -mthumb-interwork command line switch is specified, gcc +arranges for all functions to return to their caller by using the BX +instruction. Thus provided that the return address has the bottom bit +correctly initialised to indicate the instruction set of the caller, +correct operation will ensue. + +When a function is called explicitly (rather than via a function +pointer), the compiler generates a BL instruction to do this. The +Thumb version of the BL instruction has the special property of +setting the bottom bit of the LR register after it has stored the +return address into it, so that a future BX instruction will correctly +return the instruction after the BL instruction, in Thumb mode. + +The BL instruction does not change modes itself however, so if an ARM +function is calling a Thumb function, or vice versa, it is necessary +to generate some extra instructions to handle this. This is done in +the linker when it is storing the address of the referenced function +into the BL instruction. If the BL instruction is an ARM style BL +instruction, but the referenced function is a Thumb function, then the +linker automatically generates a calling stub that converts from ARM +mode to Thumb mode, puts the address of this stub into the BL +instruction, and puts the address of the referenced function into the +stub. Similarly if the BL instruction is a Thumb BL instruction, and +the referenced function is an ARM function, the linker generates a +stub which converts from Thumb to ARM mode, puts the address of this +stub into the BL instruction, and the address of the referenced +function into the stub. + +This is why it is necessary to mark Thumb functions with the +.thumb_func pseudo op when creating assembler files. This pseudo op +allows the assembler to distinguish between ARM functions and Thumb +functions. (The Thumb version of GCC automatically generates these +pseudo ops for any Thumb functions that it generates). + +Calls via function pointers work differently. Whenever the address of +a function is taken, the linker examines the type of the function +being referenced. If the function is a Thumb function, then it sets +the bottom bit of the address. Technically this makes the address +incorrect, since it is now one byte into the start of the function, +but this is never a problem because: + + a. with interworking enabled all calls via function pointer + are done using the BX instruction and this ignores the + bottom bit when computing where to go to. + + b. the linker will always set the bottom bit when the address + of the function is taken, so it is never possible to take + the address of the function in two different places and + then compare them and find that they are not equal. + +As already mentioned any call via a function pointer will use the BX +instruction (provided that interworking is enabled). The only problem +with this is computing the return address for the return from the +called function. For ARM code this can easily be done by the code +sequence: + + mov lr, pc + bx rX + +(where rX is the name of the register containing the function +pointer). This code does not work for the Thumb instruction set, +since the MOV instruction will not set the bottom bit of the LR +register, so that when the called function returns, it will return in +ARM mode not Thumb mode. Instead the compiler generates this +sequence: + + bl _call_via_rX + +(again where rX is the name if the register containing the function +pointer). The special call_via_rX functions look like this: + + .thumb_func +_call_via_r0: + bx r0 + nop + +The BL instruction ensures that the correct return address is stored +in the LR register and then the BX instruction jumps to the address +stored in the function pointer, switch modes if necessary. + + +6. How caller-super-interworking support works +============================================== + +When the -mcaller-super-interworking command line switch is specified +it changes the code produced by the Thumb compiler so that all calls +via function pointers (including virtual function calls) now go via a +different stub function. The code to call via a function pointer now +looks like this: + + bl _interwork_call_via_r0 + +Note: The compiler does not insist that r0 be used to hold the +function address. Any register will do, and there are a suite of stub +functions, one for each possible register. The stub functions look +like this: + + .code 16 + .thumb_func +_interwork_call_via_r0 + bx pc + nop + + .code 32 + tst r0, #1 + stmeqdb r13!, {lr} + adreq lr, _arm_return + bx r0 + +The stub first switches to ARM mode, since it is a lot easier to +perform the necessary operations using ARM instructions. It then +tests the bottom bit of the register containing the address of the +function to be called. If this bottom bit is set then the function +being called uses Thumb instructions and the BX instruction to come +will switch back into Thumb mode before calling this function. (Note +that it does not matter how this called function chooses to return to +its caller, since the both the caller and callee are Thumb functions, +and mode switching is necessary). If the function being called is an +ARM mode function however, the stub pushes the return address (with +its bottom bit set) onto the stack, replaces the return address with +the address of the a piece of code called '_arm_return' and then +performs a BX instruction to call the function. + +The '_arm_return' code looks like this: + + .code 32 +_arm_return: + ldmia r13!, {r12} + bx r12 + .code 16 + + +It simply retrieves the return address from the stack, and then +performs a BX operation to return to the caller and switch back into +Thumb mode. + + +7. How callee-super-interworking support works +============================================== + +When -mcallee-super-interworking is specified on the command line the +Thumb compiler behaves as if every externally visible function that it +compiles has had the (interfacearm) attribute specified for it. What +this attribute does is to put a special, ARM mode header onto the +function which forces a switch into Thumb mode: + + without __attribute__((interfacearm)): + + .code 16 + .thumb_func + function: + ... start of function ... + + with __attribute__((interfacearm)): + + .code 32 + function: + orr r12, pc, #1 + bx r12 + + .code 16 + .thumb_func + .real_start_of_function: + + ... start of function ... + +Note that since the function now expects to be entered in ARM mode, it +no longer has the .thumb_func pseudo op specified for its name. +Instead the pseudo op is attached to a new label .real_start_of_<name> +(where <name> is the name of the function) which indicates the start +of the Thumb code. This does have the interesting side effect in that +if this function is now called from a Thumb mode piece of code +outsside of the current file, the linker will generate a calling stub +to switch from Thumb mode into ARM mode, and then this is immediately +overridden by the function's header which switches back into Thumb +mode. + +In addition the (interfacearm) attribute also forces the function to +return by using the BX instruction, even if has not been compiled with +the -mthumb-interwork command line flag, so that the correct mode will +be restored upon exit from the function. + + +8. Some examples +================ + + Given these two test files: + + int arm (void) { return 1 + thumb (); } + + int thumb (void) { return 2 + arm (); } + + The following pieces of assembler are produced by the ARM and Thumb +version of GCC depending upon the command line options used: + + `-O2': + .code 32 .code 16 + .global _arm .global _thumb + .thumb_func + _arm: _thumb: + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} push {lr} + sub fp, ip, #4 + bl _thumb bl _arm + add r0, r0, #1 add r0, r0, #2 + ldmea fp, {fp, sp, pc} pop {pc} + + Note how the functions return without using the BX instruction. If +these files were assembled and linked together they would fail to work +because they do not change mode when returning to their caller. + + `-O2 -mthumb-interwork': + + .code 32 .code 16 + .global _arm .global _thumb + .thumb_func + _arm: _thumb: + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} push {lr} + sub fp, ip, #4 + bl _thumb bl _arm + add r0, r0, #1 add r0, r0, #2 + ldmea fp, {fp, sp, lr} pop {r1} + bx lr bx r1 + + Now the functions use BX to return their caller. They have grown by +4 and 2 bytes respectively, but they can now successfully be linked +together and be expect to work. The linker will replace the +destinations of the two BL instructions with the addresses of calling +stubs which convert to the correct mode before jumping to the called +function. + + `-O2 -mcallee-super-interworking': + + .code 32 .code 32 + .global _arm .global _thumb + _arm: _thumb: + orr r12, pc, #1 + bx r12 + mov ip, sp .code 16 + stmfd sp!, {fp, ip, lr, pc} push {lr} + sub fp, ip, #4 + bl _thumb bl _arm + add r0, r0, #1 add r0, r0, #2 + ldmea fp, {fp, sp, lr} pop {r1} + bx lr bx r1 + + The thumb function now has an ARM encoded prologue, and it no longer +has the `.thumb-func' pseudo op attached to it. The linker will not +generate a calling stub for the call from arm() to thumb(), but it will +still have to generate a stub for the call from thumb() to arm(). Also +note how specifying `--mcallee-super-interworking' automatically +implies `-mthumb-interworking'. + + +9. Some Function Pointer Examples +================================= + + Given this test file: + + int func (void) { return 1; } + + int call (int (* ptr)(void)) { return ptr (); } + + The following varying pieces of assembler are produced by the Thumb +version of GCC depending upon the command line options used: + + `-O2': + .code 16 + .globl _func + .thumb_func + _func: + mov r0, #1 + bx lr + + .globl _call + .thumb_func + _call: + push {lr} + bl __call_via_r0 + pop {pc} + + Note how the two functions have different exit sequences. In +particular call() uses pop {pc} to return, which would not work if the +caller was in ARM mode. func() however, uses the BX instruction, even +though `-mthumb-interwork' has not been specified, as this is the most +efficient way to exit a function when the return address is held in the +link register. + + `-O2 -mthumb-interwork': + + .code 16 + .globl _func + .thumb_func + _func: + mov r0, #1 + bx lr + + .globl _call + .thumb_func + _call: + push {lr} + bl __call_via_r0 + pop {r1} + bx r1 + + This time both functions return by using the BX instruction. This +means that call() is now two bytes longer and several cycles slower +than the previous version. + + `-O2 -mcaller-super-interworking': + .code 16 + .globl _func + .thumb_func + _func: + mov r0, #1 + bx lr + + .globl _call + .thumb_func + _call: + push {lr} + bl __interwork_call_via_r0 + pop {pc} + + Very similar to the first (non-interworking) version, except that a +different stub is used to call via the function pointer. This new stub +will work even if the called function is not interworking aware, and +tries to return to call() in ARM mode. Note that the assembly code for +call() is still not interworking aware itself, and so should not be +called from ARM code. + + `-O2 -mcallee-super-interworking': + + .code 32 + .globl _func + _func: + orr r12, pc, #1 + bx r12 + + .code 16 + .globl .real_start_of_func + .thumb_func + .real_start_of_func: + mov r0, #1 + bx lr + + .code 32 + .globl _call + _call: + orr r12, pc, #1 + bx r12 + + .code 16 + .globl .real_start_of_call + .thumb_func + .real_start_of_call: + push {lr} + bl __call_via_r0 + pop {r1} + bx r1 + + Now both functions have an ARM coded prologue, and both functions +return by using the BX instruction. These functions are interworking +aware therefore and can safely be called from ARM code. The code for +the call() function is now 10 bytes longer than the original, non +interworking aware version, an increase of over 200%. + + If a prototype for call() is added to the source code, and this +prototype includes the `interfacearm' attribute: + + int __attribute__((interfacearm)) call (int (* ptr)(void)); + + then this code is produced (with only -O2 specified on the command +line): + + .code 16 + .globl _func + .thumb_func + _func: + mov r0, #1 + bx lr + + .globl _call + .code 32 + _call: + orr r12, pc, #1 + bx r12 + + .code 16 + .globl .real_start_of_call + .thumb_func + .real_start_of_call: + push {lr} + bl __call_via_r0 + pop {r1} + bx r1 + + So now both call() and func() can be safely called via +non-interworking aware ARM code. If, when such a file is assembled, +the assembler detects the fact that call() is being called by another +function in the same file, it will automatically adjust the target of +the BL instruction to point to .real_start_of_call. In this way there +is no need for the linker to generate a Thumb-to-ARM calling stub so +that call can be entered in ARM mode. + + +10. How to use dlltool to build ARM/Thumb DLLs +============================================== + Given a program (`prog.c') like this: + + extern int func_in_dll (void); + + int main (void) { return func_in_dll(); } + + And a DLL source file (`dll.c') like this: + + int func_in_dll (void) { return 1; } + + Here is how to build the DLL and the program for a purely ARM based +environment: + +*Step One + Build a `.def' file describing the DLL: + + ; example.def + ; This file describes the contents of the DLL + LIBRARY example + HEAPSIZE 0x40000, 0x2000 + EXPORTS + func_in_dll 1 + +*Step Two + Compile the DLL source code: + + arm-pe-gcc -O2 -c dll.c + +*Step Three + Use `dlltool' to create an exports file and a library file: + + dlltool --def example.def --output-exp example.o --output-lib example.a + +*Step Four + Link together the complete DLL: + + arm-pe-ld dll.o example.o -o example.dll + +*Step Five + Compile the program's source code: + + arm-pe-gcc -O2 -c prog.c + +*Step Six + Link together the program and the DLL's library file: + + arm-pe-gcc prog.o example.a -o prog + + If instead this was a Thumb DLL being called from an ARM program, the +steps would look like this. (To save space only those steps that are +different from the previous version are shown): + +*Step Two + Compile the DLL source code (using the Thumb compiler): + + thumb-pe-gcc -O2 -c dll.c -mthumb-interwork + +*Step Three + Build the exports and library files (and support interworking): + + dlltool -d example.def -z example.o -l example.a --interwork -m thumb + +*Step Five + Compile the program's source code (and support interworking): + + arm-pe-gcc -O2 -c prog.c -mthumb-interwork + + If instead, the DLL was an old, ARM DLL which does not support +interworking, and which cannot be rebuilt, then these steps would be +used. + +*Step One + Skip. If you do not have access to the sources of a DLL, there is + no point in building a `.def' file for it. + +*Step Two + Skip. With no DLL sources there is nothing to compile. + +*Step Three + Skip. Without a `.def' file you cannot use dlltool to build an + exports file or a library file. + +*Step Four + Skip. Without a set of DLL object files you cannot build the DLL. + Besides it has already been built for you by somebody else. + +*Step Five + Compile the program's source code, this is the same as before: + + arm-pe-gcc -O2 -c prog.c + +*Step Six + Link together the program and the DLL's library file, passing the + `--support-old-code' option to the linker: + + arm-pe-gcc prog.o example.a -Wl,--support-old-code -o prog + + Ignore the warning message about the input file not supporting + interworking as the --support-old-code switch has taken care if this. diff --git a/contrib/gcc/config/arm/aof.h b/contrib/gcc/config/arm/aof.h new file mode 100644 index 000000000000..750bc08d1236 --- /dev/null +++ b/contrib/gcc/config/arm/aof.h @@ -0,0 +1,351 @@ +/* Definitions of target machine for GNU compiler, for Advanced RISC Machines + ARM compilation, AOF Assembler. + Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rearnsha@armltd.co.uk) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + + +#define AOF_ASSEMBLER + +#define LINK_LIBGCC_SPECIAL 1 + +#define LINK_SPEC "%{aof} %{bin} %{aif} %{ihf} %{shl,*} %{reent*} %{split} \ + %{ov*,*} %{reloc*} -nodebug" + +#define STARTFILE_SPEC "crtbegin.o%s" + +#define ENDFILE_SPEC "crtend.o%s" + +#ifndef ASM_SPEC +#define ASM_SPEC "%{g -g} -arch 4 \ +-apcs 3%{mapcs-32:/32bit}%{mapcs-26:/26bit}%{!mapcs-26:%{!macps-32:/26bit}}" +#endif + +#ifndef LIB_SPEC +#define LIB_SPEC "%{Eb: armlib_h.32b%s}%{!Eb: armlib_h.32l%s}" +#endif + +#define LIBGCC_SPEC "libgcc.a%s" + +/* Dividing the Output into Sections (Text, Data, ...) */ +/* AOF Assembler syntax is a nightmare when it comes to areas, since once + we change from one area to another, we can't go back again. Instead, + we must create a new area with the same attributes and add the new output + to that. Unfortunately, there is nothing we can do here to guarantee that + two areas with the same attributes will be linked adjacently in the + resulting executable, so we have to be careful not to do pc-relative + addressing across such boundaries. */ +#define TEXT_SECTION_ASM_OP aof_text_section () + +#define SELECT_RTX_SECTION(MODE,RTX,ALIGN) text_section (); + +#define DATA_SECTION_ASM_OP aof_data_section () + +#define EXTRA_SECTIONS in_zero_init, in_common + +#define EXTRA_SECTION_FUNCTIONS \ +ZERO_INIT_SECTION \ +COMMON_SECTION + +#define ZERO_INIT_SECTION \ +void \ +zero_init_section () \ +{ \ + static int zero_init_count = 1; \ + if (in_section != in_zero_init) \ + { \ + fprintf (asm_out_file, "\tAREA |C$$zidata%d|,NOINIT\n", \ + zero_init_count++); \ + in_section = in_zero_init; \ + } \ +} + +/* Used by ASM_OUTPUT_COMMON (below) to tell varasm.c that we've + changed areas. */ +#define COMMON_SECTION \ +void \ +common_section () \ +{ \ + if (in_section != in_common) \ + { \ + in_section = in_common; \ + } \ +} +#define CTOR_LIST_BEGIN \ +asm (CTORS_SECTION_ASM_OP); \ +extern func_ptr __CTOR_END__[1]; \ +func_ptr __CTOR_LIST__[1] = {__CTOR_END__}; + +#define CTOR_LIST_END \ +asm (CTORS_SECTION_ASM_OP); \ +func_ptr __CTOR_END__[1] = { (func_ptr) 0 }; + +#define DO_GLOBAL_CTORS_BODY \ +do { \ + func_ptr *ptr = __CTOR_LIST__ + 1; \ + while (*ptr) \ + (*ptr++) (); \ +} while (0) + +#define DTOR_LIST_BEGIN \ +asm (DTORS_SECTION_ASM_OP); \ +extern func_ptr __DTOR_END__[1]; \ +func_ptr __DTOR_LIST__[1] = {__DTOR_END__}; + +#define DTOR_LIST_END \ +asm (DTORS_SECTION_ASM_OP); \ +func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; + +#define DO_GLOBAL_DTORS_BODY \ +do { \ + func_ptr *ptr = __DTOR_LIST__ + 1; \ + while (*ptr) \ + (*ptr++) (); \ +} while (0) + +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +#ifndef ARM_OS_NAME +#define ARM_OS_NAME "(generic)" +#endif + +/* For the AOF linker, we need to reference __main to force the standard + library to get linked in. */ + +#define ASM_FILE_START(STREAM) \ +{ \ + fprintf ((STREAM), "%s Generated by gcc %s for ARM/%s\n", \ + ASM_COMMENT_START, version_string, ARM_OS_NAME); \ + fprintf ((STREAM), "__r0\tRN\t0\n"); \ + fprintf ((STREAM), "__a1\tRN\t0\n"); \ + fprintf ((STREAM), "__a2\tRN\t1\n"); \ + fprintf ((STREAM), "__a3\tRN\t2\n"); \ + fprintf ((STREAM), "__a4\tRN\t3\n"); \ + fprintf ((STREAM), "__v1\tRN\t4\n"); \ + fprintf ((STREAM), "__v2\tRN\t5\n"); \ + fprintf ((STREAM), "__v3\tRN\t6\n"); \ + fprintf ((STREAM), "__v4\tRN\t7\n"); \ + fprintf ((STREAM), "__v5\tRN\t8\n"); \ + fprintf ((STREAM), "__v6\tRN\t9\n"); \ + fprintf ((STREAM), "__sl\tRN\t10\n"); \ + fprintf ((STREAM), "__fp\tRN\t11\n"); \ + fprintf ((STREAM), "__ip\tRN\t12\n"); \ + fprintf ((STREAM), "__sp\tRN\t13\n"); \ + fprintf ((STREAM), "__lr\tRN\t14\n"); \ + fprintf ((STREAM), "__pc\tRN\t15\n"); \ + fprintf ((STREAM), "__f0\tFN\t0\n"); \ + fprintf ((STREAM), "__f1\tFN\t1\n"); \ + fprintf ((STREAM), "__f2\tFN\t2\n"); \ + fprintf ((STREAM), "__f3\tFN\t3\n"); \ + fprintf ((STREAM), "__f4\tFN\t4\n"); \ + fprintf ((STREAM), "__f5\tFN\t5\n"); \ + fprintf ((STREAM), "__f6\tFN\t6\n"); \ + fprintf ((STREAM), "__f7\tFN\t7\n"); \ + text_section (); \ +} + +/* Some systems use __main in a way incompatible with its use in gcc, in these + cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to + give the same symbol without quotes for an alternative entry point. You + must define both, or neither. */ +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain + +#define ASM_FILE_END(STREAM) \ +do \ +{ \ + if (flag_pic) \ + aof_dump_pic_table (STREAM); \ + aof_dump_imports (STREAM); \ + fputs ("\tEND\n", (STREAM)); \ +} while (0); + +#define ASM_COMMENT_START ";" + +#define ASM_APP_ON "" + +#define ASM_APP_OFF "" + +#define ASM_OUTPUT_ASCII(STREAM,PTR,LEN) \ +{ \ + int i; \ + const char *ptr = (PTR); \ + fprintf ((STREAM), "\tDCB"); \ + for (i = 0; i < (long)(LEN); i++) \ + fprintf ((STREAM), " &%02x%s", \ + (unsigned ) *(ptr++), \ + (i + 1 < (long)(LEN) \ + ? ((i & 3) == 3 ? "\n\tDCB" : ",") \ + : "\n")); \ +} + +#define IS_ASM_LOGICAL_LINE_SEPARATOR(C) ((C) == '\n') + +/* Output of Uninitialized Variables */ + +#define ASM_OUTPUT_COMMON(STREAM,NAME,SIZE,ROUNDED) \ + (common_section (), \ + fprintf ((STREAM), "\tAREA "), \ + assemble_name ((STREAM), (NAME)), \ + fprintf ((STREAM), ", DATA, COMMON\n\t%% %d\t%s size=%d\n", \ + (ROUNDED), ASM_COMMENT_START, SIZE)) + +#define ASM_OUTPUT_LOCAL(STREAM,NAME,SIZE,ROUNDED) \ + (zero_init_section (), \ + assemble_name ((STREAM), (NAME)), \ + fprintf ((STREAM), "\n"), \ + fprintf ((STREAM), "\t%% %d\t%s size=%d\n", \ + (ROUNDED), ASM_COMMENT_START, SIZE)) + +/* Output and Generation of Labels */ + +extern int arm_main_function; + +#define ASM_GLOBALIZE_LABEL(STREAM,NAME) \ +do { \ + fprintf ((STREAM), "\tEXPORT\t"); \ + assemble_name ((STREAM), (NAME)); \ + fputc ('\n', (STREAM)); \ + if ((NAME)[0] == 'm' && ! strcmp ((NAME), "main")) \ + arm_main_function = 1; \ +} while (0) + +#define ASM_OUTPUT_LABEL(STREAM,NAME) \ +do { \ + assemble_name (STREAM,NAME); \ + fputs ("\n", STREAM); \ +} while (0) + +#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL) \ +{ \ + if (TARGET_POKE_FUNCTION_NAME) \ + arm_poke_function_name ((STREAM), (NAME)); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + if (! TREE_PUBLIC (DECL)) \ + { \ + fputs ("\tKEEP ", STREAM); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + } \ + aof_delete_import ((NAME)); \ +} + +#define ASM_DECLARE_OBJECT_NAME(STREAM,NAME,DECL) \ +{ \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + if (! TREE_PUBLIC (DECL)) \ + { \ + fputs ("\tKEEP ", STREAM); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + } \ + aof_delete_import ((NAME)); \ +} + +#define ASM_OUTPUT_EXTERNAL(STREAM,DECL,NAME) \ + aof_add_import ((NAME)) + +#define ASM_OUTPUT_EXTERNAL_LIBCALL(STREAM,SYMREF) \ + (fprintf ((STREAM), "\tIMPORT\t"), \ + assemble_name ((STREAM), XSTR ((SYMREF), 0)), \ + fputc ('\n', (STREAM))) + +#define ASM_OUTPUT_LABELREF(STREAM,NAME) \ + fprintf ((STREAM), "|%s|", NAME) + +#define ASM_GENERATE_INTERNAL_LABEL(STRING,PREFIX,NUM) \ + sprintf ((STRING), "*|%s..%ld|", (PREFIX), (long)(NUM)) + +#define ASM_FORMAT_PRIVATE_NAME(OUTVAR,NAME,NUMBER) \ + ((OUTVAR) = (char *) alloca (strlen ((NAME)) + 10), \ + sprintf ((OUTVAR), "%s.%d", (NAME), (NUMBER))) + +/* How initialization functions are handled */ + +#define CTORS_SECTION_ASM_OP "\tAREA\t|C$$gnu_ctorsvec|, DATA, READONLY" +#define DTORS_SECTION_ASM_OP "\tAREA\t|C$$gnu_dtorsvec|, DATA, READONLY" + +/* Output of Assembler Instructions */ + +#define REGISTER_NAMES \ +{ \ + "a1", "a2", "a3", "a4", \ + "v1", "v2", "v3", "v4", \ + "v5", "v6", "sl", "fp", \ + "ip", "sp", "lr", "pc", \ + "f0", "f1", "f2", "f3", \ + "f4", "f5", "f6", "f7", \ + "cc", "sfp", "afp" \ +} + +#define ADDITIONAL_REGISTER_NAMES \ +{ \ + {"r0", 0}, {"a1", 0}, \ + {"r1", 1}, {"a2", 1}, \ + {"r2", 2}, {"a3", 2}, \ + {"r3", 3}, {"a4", 3}, \ + {"r4", 4}, {"v1", 4}, \ + {"r5", 5}, {"v2", 5}, \ + {"r6", 6}, {"v3", 6}, \ + {"r7", 7}, {"wr", 7}, \ + {"r8", 8}, {"v5", 8}, \ + {"r9", 9}, {"v6", 9}, \ + {"r10", 10}, {"sl", 10}, {"v7", 10}, \ + {"r11", 11}, {"fp", 11}, \ + {"r12", 12}, {"ip", 12}, \ + {"r13", 13}, {"sp", 13}, \ + {"r14", 14}, {"lr", 14}, \ + {"r15", 15}, {"pc", 15} \ +} + +#define REGISTER_PREFIX "__" +#define USER_LABEL_PREFIX "" +#define LOCAL_LABEL_PREFIX "" + +/* AOF does not prefix user function names with an underscore. */ +#define ARM_MCOUNT_NAME "_mcount" + +/* Output of Dispatch Tables */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,BODY,VALUE,REL) \ + fprintf ((STREAM), "\tb\t|L..%d|\n", (VALUE)) + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \ + fprintf ((STREAM), "\tDCD\t|L..%d|\n", (VALUE)) + +/* A label marking the start of a jump table is a data label. */ +#define ASM_OUTPUT_CASE_LABEL(STREAM,PREFIX,NUM,TABLE) \ + fprintf ((STREAM), "\tALIGN\n|%s..%d|\n", (PREFIX), (NUM)) + +/* Assembler Commands for Alignment */ + +#define ASM_OUTPUT_SKIP(STREAM,NBYTES) \ + fprintf ((STREAM), "\t%%\t%d\n", (NBYTES)) + +#define ASM_OUTPUT_ALIGN(STREAM,POWER) \ +do { \ + register int amount = 1 << (POWER); \ + if (amount == 2) \ + fprintf ((STREAM), "\tALIGN 2\n"); \ + else if (amount == 4) \ + fprintf ((STREAM), "\tALIGN\n"); \ + else \ + fprintf ((STREAM), "\tALIGN %d\n", amount); \ +} while (0) + +#undef DBX_DEBUGGING_INFO diff --git a/contrib/gcc/config/arm/aout.h b/contrib/gcc/config/arm/aout.h new file mode 100644 index 000000000000..64ca8b7caac1 --- /dev/null +++ b/contrib/gcc/config/arm/aout.h @@ -0,0 +1,260 @@ +/* Definitions of target machine for GNU compiler, for ARM with a.out + Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 + Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rearnsha@armltd.co.uk). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef ARM_OS_NAME +#define ARM_OS_NAME "(generic)" +#endif + +/* The text to go at the start of the assembler file */ +#ifndef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ +{ \ + asm_fprintf (STREAM,"%Rrfp\t.req\t%Rr9\n"); \ + asm_fprintf (STREAM,"%Rsl\t.req\t%Rr10\n"); \ + asm_fprintf (STREAM,"%Rfp\t.req\t%Rr11\n"); \ + asm_fprintf (STREAM,"%Rip\t.req\t%Rr12\n"); \ + asm_fprintf (STREAM,"%Rsp\t.req\t%Rr13\n"); \ + asm_fprintf (STREAM,"%Rlr\t.req\t%Rr14\n"); \ + asm_fprintf (STREAM,"%Rpc\t.req\t%Rr15\n"); \ +} +#endif + +#ifndef ASM_APP_ON +#define ASM_APP_ON "" +#endif +#ifndef ASM_APP_OFF +#define ASM_APP_OFF "" +#endif + +/* Switch to the text or data segment. */ +#define TEXT_SECTION_ASM_OP "\t.text" +#define DATA_SECTION_ASM_OP "\t.data" +#define BSS_SECTION_ASM_OP "\t.bss" + +/* Note: If USER_LABEL_PREFIX or LOCAL_LABEL_PREFIX are changed, + make sure that this change is reflected in the function + coff_arm_is_local_label_name() in bfd/coff-arm.c */ +#ifndef REGISTER_PREFIX +#define REGISTER_PREFIX "" +#endif + +#ifndef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" +#endif + +#ifndef LOCAL_LABEL_PREFIX +#define LOCAL_LABEL_PREFIX "" +#endif + + +/* The assembler's names for the registers. */ +#ifndef REGISTER_NAMES +#define REGISTER_NAMES \ +{ \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "cc", "sfp", "afp" \ +} +#endif + +#ifndef ADDITIONAL_REGISTER_NAMES +#define ADDITIONAL_REGISTER_NAMES \ +{ \ + {"a1", 0}, \ + {"a2", 1}, \ + {"a3", 2}, \ + {"a4", 3}, \ + {"v1", 4}, \ + {"v2", 5}, \ + {"v3", 6}, \ + {"v4", 7}, \ + {"v5", 8}, \ + {"v6", 9}, \ + {"rfp", 9}, /* Gcc used to call it this */ \ + {"sb", 9}, \ + {"v7", 10}, \ + {"r10", 10}, /* sl */ \ + {"r11", 11}, /* fp */ \ + {"r12", 12}, /* ip */ \ + {"r13", 13}, /* sp */ \ + {"r14", 14}, /* lr */ \ + {"r15", 15} /* pc */ \ +} +#endif + +/* Arm Assembler barfs on dollars */ +#define DOLLARS_IN_IDENTIFIERS 0 + +#ifndef NO_DOLLAR_IN_LABEL +#define NO_DOLLAR_IN_LABEL 1 +#endif + +/* Generate DBX debugging information. riscix.h will undefine this because + the native assembler does not support stabs. */ +#ifndef DBX_DEBUGGING_INFO +#define DBX_DEBUGGING_INFO 1 +#endif + +/* Acorn dbx moans about continuation chars, so don't use any. */ +#ifndef DBX_CONTIN_LENGTH +#define DBX_CONTIN_LENGTH 0 +#endif + +/* Output a source filename for the debugger. RISCiX dbx insists that the + ``desc'' field is set to compiler version number >= 315 (sic). */ +#define DBX_OUTPUT_MAIN_SOURCE_FILENAME(STREAM, NAME) \ + do \ + { \ + fprintf (STREAM, ".stabs "); \ + output_quoted_string (STREAM, NAME); \ + fprintf (STREAM, ",%d,0,315,%s\n", N_SO, <ext_label_name[1]); \ + text_section (); \ + ASM_OUTPUT_INTERNAL_LABEL (STREAM, "Ltext", 0); \ + } \ + while (0) + +/* Output a function label definition. */ +#ifndef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do \ + { \ + ARM_DECLARE_FUNCTION_NAME (STREAM, NAME, DECL); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + } \ + while (0) +#endif + +#ifndef ASM_OUTPUT_LABEL +#define ASM_OUTPUT_LABEL(STREAM, NAME) \ + do \ + { \ + assemble_name (STREAM,NAME); \ + fputs (":\n", STREAM); \ + } \ + while (0) +#endif + +/* Output a globalising directive for a label. */ +#ifndef ASM_GLOBALIZE_LABEL +#define ASM_GLOBALIZE_LABEL(STREAM, NAME) \ + do \ + { \ + fprintf (STREAM, "\t.global\t"); \ + assemble_name (STREAM, NAME); \ + fputc ('\n',STREAM); \ + } \ + while (0) +#endif + +/* Make an internal label into a string. */ +#ifndef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(STRING, PREFIX, NUM) \ + sprintf (STRING, "*%s%s%u", LOCAL_LABEL_PREFIX, PREFIX, (unsigned int)(NUM)) +#endif + +/* Construct a private name. */ +#define ASM_FORMAT_PRIVATE_NAME(OUTVAR, NAME, NUMBER) \ + ((OUTVAR) = (char *) alloca (strlen (NAME) + 10), \ + sprintf (OUTVAR, "%s.%d", NAME, NUMBER)) + +/* Output an element of a dispatch table. */ +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + asm_fprintf (STREAM, "\t.word\t%LL%d\n", VALUE) + +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ + asm_fprintf (STREAM, "\tb\t%LL%d\n", VALUE) + +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(STREAM, PTR, LEN) \ + output_ascii_pseudo_op (STREAM, (const unsigned char *)(PTR), LEN) + +/* Output a gap. In fact we fill it with nulls. */ +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \ + fprintf (STREAM, "\t.space\t%d\n", NBYTES) + +/* Align output to a power of two. Horrible /bin/as. */ +#ifndef ASM_OUTPUT_ALIGN +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + do \ + { \ + register int amount = 1 << (POWER); \ + \ + if (amount == 2) \ + fprintf (STREAM, "\t.even\n"); \ + else if (amount != 1) \ + fprintf (STREAM, "\t.align\t%d\n", amount - 4); \ + } \ + while (0) +#endif + +/* Output a common block */ +#ifndef ASM_OUTPUT_COMMON +#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \ + do \ + { \ + fprintf (STREAM, "\t.comm\t"); \ + assemble_name (STREAM, NAME); \ + asm_fprintf (STREAM, ", %d\t%@ %d\n", \ + ROUNDED, SIZE); \ + } \ + while (0) +#endif + +/* Output a local common block. /bin/as can't do this, so hack a + `.space' into the bss segment. Note that this is *bad* practice, + which is guaranteed NOT to work since it doesn't define STATIC + COMMON space but merely STATIC BSS space. */ +#ifndef ASM_OUTPUT_ALIGNED_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \ + do \ + { \ + bss_section (); \ + ASM_OUTPUT_ALIGN (STREAM, floor_log2 (ALIGN / BITS_PER_UNIT)); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + fprintf (STREAM, "\t.space\t%d\n", SIZE); \ + } \ + while (0) +#endif + +/* Output a zero-initialized block. */ +#ifndef ASM_OUTPUT_ALIGNED_BSS +#define ASM_OUTPUT_ALIGNED_BSS(STREAM, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (STREAM, DECL, NAME, SIZE, ALIGN) +#endif + +/* Output a source line for the debugger. */ +/* #define ASM_OUTPUT_SOURCE_LINE(STREAM,LINE) */ + +/* Output a #ident directive. */ +#ifndef ASM_OUTPUT_IDENT +#define ASM_OUTPUT_IDENT(STREAM,STRING) \ + asm_fprintf (STREAM, "%@ - - - ident %s\n", STRING) +#endif + +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START "@" +#endif + +/* This works for GAS and some other assemblers. */ +#define SET_ASM_OP "\t.set\t" diff --git a/contrib/gcc/config/arm/arm-protos.h b/contrib/gcc/config/arm/arm-protos.h new file mode 100644 index 000000000000..b42da823869a --- /dev/null +++ b/contrib/gcc/config/arm/arm-protos.h @@ -0,0 +1,207 @@ +/* Prototypes for exported functions defined in arm.c and pe.c + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rearnsha@arm.com) + Minor hacks by Nick Clifton (nickc@cygnus.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef GCC_ARM_PROTOS_H +#define GCC_ARM_PROTOS_H + +extern void rdata_section PARAMS ((void)); +extern void arm_override_options PARAMS ((void)); +extern int use_return_insn PARAMS ((int)); +extern int arm_regno_class PARAMS ((int)); +extern void arm_finalize_pic PARAMS ((int)); +extern int arm_volatile_func PARAMS ((void)); +extern const char * arm_output_epilogue PARAMS ((int)); +extern void arm_expand_prologue PARAMS ((void)); +/* Used in arm.md, but defined in output.c. */ +extern void assemble_align PARAMS ((int)); +extern const char * arm_strip_name_encoding PARAMS ((const char *)); +extern unsigned long arm_current_func_type PARAMS ((void)); +extern unsigned int arm_compute_initial_elimination_offset PARAMS ((unsigned int, unsigned int)); + +#ifdef TREE_CODE +extern int arm_return_in_memory PARAMS ((tree)); +extern void arm_encode_call_attribute PARAMS ((tree, int)); +extern int arm_function_ok_for_sibcall PARAMS ((tree)); +#endif +#ifdef RTX_CODE +extern int arm_hard_regno_mode_ok PARAMS ((unsigned int, enum machine_mode)); +extern int const_ok_for_arm PARAMS ((HOST_WIDE_INT)); +extern int arm_split_constant PARAMS ((RTX_CODE, enum machine_mode, + HOST_WIDE_INT, rtx, rtx, int)); +extern RTX_CODE arm_canonicalize_comparison PARAMS ((RTX_CODE, rtx *)); +extern int legitimate_pic_operand_p PARAMS ((rtx)); +extern rtx legitimize_pic_address PARAMS ((rtx, enum machine_mode, rtx)); +extern int arm_rtx_costs PARAMS ((rtx, RTX_CODE, RTX_CODE)); +extern int const_double_rtx_ok_for_fpu PARAMS ((rtx)); +extern int neg_const_double_rtx_ok_for_fpu PARAMS ((rtx)); + +/* Predicates. */ +extern int s_register_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_hard_register_operand PARAMS ((rtx, enum machine_mode)); +extern int f_register_operand PARAMS ((rtx, enum machine_mode)); +extern int reg_or_int_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_reload_memory_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_rhs_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_rhsm_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_add_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_not_operand PARAMS ((rtx, enum machine_mode)); +extern int offsettable_memory_operand PARAMS ((rtx, enum machine_mode)); +extern int alignable_memory_operand PARAMS ((rtx, enum machine_mode)); +extern int bad_signed_byte_operand PARAMS ((rtx, enum machine_mode)); +extern int fpu_rhs_operand PARAMS ((rtx, enum machine_mode)); +extern int fpu_add_operand PARAMS ((rtx, enum machine_mode)); +extern int power_of_two_operand PARAMS ((rtx, enum machine_mode)); +extern int nonimmediate_di_operand PARAMS ((rtx, enum machine_mode)); +extern int di_operand PARAMS ((rtx, enum machine_mode)); +extern int nonimmediate_soft_df_operand PARAMS ((rtx, enum machine_mode)); +extern int soft_df_operand PARAMS ((rtx, enum machine_mode)); +extern int index_operand PARAMS ((rtx, enum machine_mode)); +extern int const_shift_operand PARAMS ((rtx, enum machine_mode)); +extern int arm_comparison_operator PARAMS ((rtx, enum machine_mode)); +extern int shiftable_operator PARAMS ((rtx, enum machine_mode)); +extern int shift_operator PARAMS ((rtx, enum machine_mode)); +extern int equality_operator PARAMS ((rtx, enum machine_mode)); +extern int minmax_operator PARAMS ((rtx, enum machine_mode)); +extern int cc_register PARAMS ((rtx, enum machine_mode)); +extern int dominant_cc_register PARAMS ((rtx, enum machine_mode)); +extern int logical_binary_operator PARAMS ((rtx, enum machine_mode)); +extern int multi_register_push PARAMS ((rtx, enum machine_mode)); +extern int load_multiple_operation PARAMS ((rtx, enum machine_mode)); +extern int store_multiple_operation PARAMS ((rtx, enum machine_mode)); + +extern int symbol_mentioned_p PARAMS ((rtx)); +extern int label_mentioned_p PARAMS ((rtx)); +extern RTX_CODE minmax_code PARAMS ((rtx)); +extern int adjacent_mem_locations PARAMS ((rtx, rtx)); +extern int load_multiple_sequence PARAMS ((rtx *, int, int *, int *, + HOST_WIDE_INT *)); +extern const char * emit_ldm_seq PARAMS ((rtx *, int)); +extern int store_multiple_sequence PARAMS ((rtx *, int, int *, int *, + HOST_WIDE_INT *)); +extern const char * emit_stm_seq PARAMS ((rtx *, int)); +extern rtx arm_gen_load_multiple PARAMS ((int, int, rtx, int, int, int, + int, int)); +extern rtx arm_gen_store_multiple PARAMS ((int, int, rtx, int, int, int, + int, int)); +extern int arm_gen_movstrqi PARAMS ((rtx *)); +extern rtx arm_gen_rotated_half_load PARAMS ((rtx)); +extern enum machine_mode arm_select_cc_mode PARAMS ((RTX_CODE, rtx, rtx)); +extern rtx arm_gen_compare_reg PARAMS ((RTX_CODE, rtx, rtx)); +extern void arm_reload_in_hi PARAMS ((rtx *)); +extern void arm_reload_out_hi PARAMS ((rtx *)); +extern void arm_reorg PARAMS ((rtx)); +extern const char * fp_immediate_constant PARAMS ((rtx)); +extern const char * output_call PARAMS ((rtx *)); +extern const char * output_call_mem PARAMS ((rtx *)); +extern const char * output_mov_long_double_fpu_from_arm PARAMS ((rtx *)); +extern const char * output_mov_long_double_arm_from_fpu PARAMS ((rtx *)); +extern const char * output_mov_long_double_arm_from_arm PARAMS ((rtx *)); +extern const char * output_mov_double_fpu_from_arm PARAMS ((rtx *)); +extern const char * output_mov_double_arm_from_fpu PARAMS ((rtx *)); +extern const char * output_move_double PARAMS ((rtx *)); +extern const char * output_mov_immediate PARAMS ((rtx *)); +extern const char * output_add_immediate PARAMS ((rtx *)); +extern const char * arithmetic_instr PARAMS ((rtx, int)); +extern void output_ascii_pseudo_op PARAMS ((FILE *, const unsigned char *, int)); +extern const char * output_return_instruction PARAMS ((rtx, int, int)); +extern void arm_poke_function_name PARAMS ((FILE *, const char *)); +extern void arm_print_operand PARAMS ((FILE *, rtx, int)); +extern void arm_print_operand_address PARAMS ((FILE *, rtx)); +extern void arm_final_prescan_insn PARAMS ((rtx)); +extern int arm_go_if_legitimate_address PARAMS ((enum machine_mode, rtx)); +extern int arm_debugger_arg_offset PARAMS ((int, rtx)); +extern int arm_is_longcall_p PARAMS ((rtx, int, int)); + +#if defined TREE_CODE +extern rtx arm_function_arg PARAMS ((CUMULATIVE_ARGS *, + enum machine_mode, tree, int)); +extern void arm_init_cumulative_args PARAMS ((CUMULATIVE_ARGS *, tree, rtx, + int)); +#endif + +#if defined AOF_ASSEMBLER +extern rtx aof_pic_entry PARAMS ((rtx)); +extern void aof_dump_pic_table PARAMS ((FILE *)); +extern char * aof_text_section PARAMS ((void)); +extern char * aof_data_section PARAMS ((void)); +extern void aof_add_import PARAMS ((const char *)); +extern void aof_delete_import PARAMS ((const char *)); +extern void aof_dump_imports PARAMS ((FILE *)); +extern void zero_init_section PARAMS ((void)); +extern void common_section PARAMS ((void)); +#endif /* AOF_ASSEMBLER */ + +#endif /* RTX_CODE */ + +/* Thumb functions. */ +extern void arm_init_expanders PARAMS ((void)); +extern int thumb_far_jump_used_p PARAMS ((int)); +extern const char * thumb_unexpanded_epilogue PARAMS ((void)); +extern void thumb_expand_prologue PARAMS ((void)); +extern void thumb_expand_epilogue PARAMS ((void)); +#ifdef TREE_CODE +extern int is_called_in_ARM_mode PARAMS ((tree)); +#endif +extern int thumb_shiftable_const PARAMS ((unsigned HOST_WIDE_INT)); +#ifdef RTX_CODE +extern void thumb_final_prescan_insn PARAMS ((rtx)); +extern const char * thumb_load_double_from_address + PARAMS ((rtx *)); +extern const char * thumb_output_move_mem_multiple + PARAMS ((int, rtx *)); +extern void thumb_expand_movstrqi PARAMS ((rtx *)); +extern int thumb_cmp_operand PARAMS ((rtx, enum machine_mode)); +extern rtx * thumb_legitimize_pic_address + PARAMS ((rtx, enum machine_mode, rtx)); +extern int thumb_go_if_legitimate_address + PARAMS ((enum machine_mode, rtx)); +extern rtx arm_return_addr PARAMS ((int, rtx)); +extern void thumb_reload_out_hi PARAMS ((rtx *)); +extern void thumb_reload_in_hi PARAMS ((rtx *)); +#endif + +/* Defined in pe.c. */ +extern int arm_dllexport_name_p PARAMS ((const char *)); +extern int arm_dllimport_name_p PARAMS ((const char *)); + +#ifdef TREE_CODE +extern void arm_pe_unique_section PARAMS ((tree, int)); +extern void arm_pe_encode_section_info PARAMS ((tree)); +extern int arm_dllexport_p PARAMS ((tree)); +extern int arm_dllimport_p PARAMS ((tree)); +extern void arm_mark_dllexport PARAMS ((tree)); +extern void arm_mark_dllimport PARAMS ((tree)); +#endif + +extern void arm_init_builtins PARAMS ((void)); +#if defined (TREE_CODE) && defined (RTX_CODE) +extern rtx arm_expand_builtin PARAMS ((tree, rtx, rtx, + enum machine_mode, int)); +#endif + +#ifdef GCC_C_PRAGMA_H /* included from code that cares about pragmas */ +extern void arm_pr_long_calls PARAMS ((cpp_reader *)); +extern void arm_pr_no_long_calls PARAMS ((cpp_reader *)); +extern void arm_pr_long_calls_off PARAMS ((cpp_reader *)); +#endif + +#endif /* ! GCC_ARM_PROTOS_H */ diff --git a/contrib/gcc/config/arm/arm-wince-pe.h b/contrib/gcc/config/arm/arm-wince-pe.h new file mode 100644 index 000000000000..e43ea9963195 --- /dev/null +++ b/contrib/gcc/config/arm/arm-wince-pe.h @@ -0,0 +1,66 @@ +/* Definitions of target machine for GNU compiler, + for ARM with PE obj format running under the WinCE operating system. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define ARM_WINCE 1 + +#include "pe.h" + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/WinCE/PE)", stderr); + +/* The next three definitions are defined in pe.h, + undefined in arm/arm-pe.h and then redefined back here! */ +#undef LIB_SPEC +#define LIB_SPEC "-lcoredll -lcorelibc" + +#define MATH_LIBRARY "" + +#define LIBSTDCXX "-lc" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" +#define ENDFILE_SPEC "" + +#undef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" + +#undef CC1_SPEC +#define CC1_SPEC "%{!mapcs-32:%{!mapcs-26:-mapcs-32}}" + +#undef ASM_SPEC +#define ASM_SPEC " \ +%{mbig-endian:-EB} \ +%{mcpu=*:-m%*} \ +%{march=*:-m%*} \ +%{mapcs-*:-mapcs-%*} \ +%{mthumb-interwork:-mthumb-interwork} \ +%{!mapcs-32:%{!mapcs-26:-mapcs-32}} \ +" + +/* WinCE headers require -DARM */ +#undef PE_SUBTARGET_CPP_SPEC +#define PE_SUBTARGET_CPP_SPEC "-D__pe__ -DARM -D__unaligned=__attribute__((aligned(1))) " + +#undef SIZE_TYPE +#define SIZE_TYPE "long unsigned int" diff --git a/contrib/gcc/config/arm/arm.c b/contrib/gcc/config/arm/arm.c new file mode 100644 index 000000000000..dd823a2b206e --- /dev/null +++ b/contrib/gcc/config/arm/arm.c @@ -0,0 +1,11028 @@ +/* Output routines for GCC for ARM. + Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) + and Martin Simmons (@harleqn.co.uk). + More major hacks by Richard Earnshaw (rearnsha@arm.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "rtl.h" +#include "tree.h" +#include "obstack.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "reload.h" +#include "function.h" +#include "expr.h" +#include "optabs.h" +#include "toplev.h" +#include "recog.h" +#include "ggc.h" +#include "except.h" +#include "c-pragma.h" +#include "integrate.h" +#include "tm_p.h" +#include "target.h" +#include "target-def.h" + +/* Forward definitions of types. */ +typedef struct minipool_node Mnode; +typedef struct minipool_fixup Mfix; + +/* In order to improve the layout of the prototypes below + some short type abbreviations are defined here. */ +#define Hint HOST_WIDE_INT +#define Mmode enum machine_mode +#define Ulong unsigned long +#define Ccstar const char * + +const struct attribute_spec arm_attribute_table[]; + +/* Forward function declarations. */ +static void arm_add_gc_roots PARAMS ((void)); +static int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int)); +static Ulong bit_count PARAMS ((signed int)); +static int const_ok_for_op PARAMS ((Hint, enum rtx_code)); +static int eliminate_lr2ip PARAMS ((rtx *)); +static rtx emit_multi_reg_push PARAMS ((int)); +static rtx emit_sfm PARAMS ((int, int)); +#ifndef AOF_ASSEMBLER +static bool arm_assemble_integer PARAMS ((rtx, unsigned int, int)); +#endif +static Ccstar fp_const_from_val PARAMS ((REAL_VALUE_TYPE *)); +static arm_cc get_arm_condition_code PARAMS ((rtx)); +static void init_fpa_table PARAMS ((void)); +static Hint int_log2 PARAMS ((Hint)); +static rtx is_jump_table PARAMS ((rtx)); +static Ccstar output_multi_immediate PARAMS ((rtx *, Ccstar, Ccstar, int, Hint)); +static void print_multi_reg PARAMS ((FILE *, Ccstar, int, int)); +static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint)); +static Ccstar shift_op PARAMS ((rtx, Hint *)); +static void arm_init_machine_status PARAMS ((struct function *)); +static void arm_mark_machine_status PARAMS ((struct function *)); +static void arm_free_machine_status PARAMS ((struct function *)); +static int number_of_first_bit_set PARAMS ((int)); +static void replace_symbols_in_block PARAMS ((tree, rtx, rtx)); +static void thumb_exit PARAMS ((FILE *, int, rtx)); +static void thumb_pushpop PARAMS ((FILE *, int, int)); +static Ccstar thumb_condition_code PARAMS ((rtx, int)); +static rtx is_jump_table PARAMS ((rtx)); +static Hint get_jump_table_size PARAMS ((rtx)); +static Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint)); +static Mnode * add_minipool_forward_ref PARAMS ((Mfix *)); +static Mnode * move_minipool_fix_backward_ref PARAMS ((Mnode *, Mnode *, Hint)); +static Mnode * add_minipool_backward_ref PARAMS ((Mfix *)); +static void assign_minipool_offsets PARAMS ((Mfix *)); +static void arm_print_value PARAMS ((FILE *, rtx)); +static void dump_minipool PARAMS ((rtx)); +static int arm_barrier_cost PARAMS ((rtx)); +static Mfix * create_fix_barrier PARAMS ((Mfix *, Hint)); +static void push_minipool_barrier PARAMS ((rtx, Hint)); +static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx)); +static void note_invalid_constants PARAMS ((rtx, Hint)); +static int current_file_function_operand PARAMS ((rtx)); +static Ulong arm_compute_save_reg0_reg12_mask PARAMS ((void)); +static Ulong arm_compute_save_reg_mask PARAMS ((void)); +static Ulong arm_isr_value PARAMS ((tree)); +static Ulong arm_compute_func_type PARAMS ((void)); +static tree arm_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *)); +static tree arm_handle_isr_attribute PARAMS ((tree *, tree, tree, int, bool *)); +static void arm_output_function_epilogue PARAMS ((FILE *, Hint)); +static void arm_output_function_prologue PARAMS ((FILE *, Hint)); +static void thumb_output_function_prologue PARAMS ((FILE *, Hint)); +static int arm_comp_type_attributes PARAMS ((tree, tree)); +static void arm_set_default_type_attributes PARAMS ((tree)); +static int arm_adjust_cost PARAMS ((rtx, rtx, rtx, int)); +#ifdef OBJECT_FORMAT_ELF +static void arm_elf_asm_named_section PARAMS ((const char *, unsigned int)); +#endif + +#undef Hint +#undef Mmode +#undef Ulong +#undef Ccstar + +/* Initialize the GCC target structure. */ +#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES +#undef TARGET_MERGE_DECL_ATTRIBUTES +#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes +#endif + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE arm_attribute_table + +#ifdef AOF_ASSEMBLER +#undef TARGET_ASM_BYTE_OP +#define TARGET_ASM_BYTE_OP "\tDCB\t" +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\tDCW\t" +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP "\tDCD\t" +#else +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP NULL +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER arm_assemble_integer +#endif + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE arm_output_function_prologue + +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue + +#undef TARGET_COMP_TYPE_ATTRIBUTES +#define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes + +#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES +#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS arm_init_builtins + +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN arm_expand_builtin + +#undef TARGET_SCHED_ADJUST_COST +#define TARGET_SCHED_ADJUST_COST arm_adjust_cost + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Obstack for minipool constant handling. */ +static struct obstack minipool_obstack; +static char * minipool_startobj; + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* The maximum number of insns skipped which + will be conditionalised if possible. */ +static int max_insns_skipped = 5; + +extern FILE * asm_out_file; + +/* True if we are currently building a constant table. */ +int making_const_table; + +/* Define the information needed to generate branch insns. This is + stored from the compare operation. */ +rtx arm_compare_op0, arm_compare_op1; + +/* What type of floating point are we tuning for? */ +enum floating_point_type arm_fpu; + +/* What type of floating point instructions are available? */ +enum floating_point_type arm_fpu_arch; + +/* What program mode is the cpu running in? 26-bit mode or 32-bit mode. */ +enum prog_mode_type arm_prgmode; + +/* Set by the -mfp=... option. */ +const char * target_fp_name = NULL; + +/* Used to parse -mstructure_size_boundary command line option. */ +const char * structure_size_string = NULL; +int arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY; + +/* Bit values used to identify processor capabilities. */ +#define FL_CO_PROC (1 << 0) /* Has external co-processor bus */ +#define FL_FAST_MULT (1 << 1) /* Fast multiply */ +#define FL_MODE26 (1 << 2) /* 26-bit mode support */ +#define FL_MODE32 (1 << 3) /* 32-bit mode support */ +#define FL_ARCH4 (1 << 4) /* Architecture rel 4 */ +#define FL_ARCH5 (1 << 5) /* Architecture rel 5 */ +#define FL_THUMB (1 << 6) /* Thumb aware */ +#define FL_LDSCHED (1 << 7) /* Load scheduling necessary */ +#define FL_STRONG (1 << 8) /* StrongARM */ +#define FL_ARCH5E (1 << 9) /* DSP extenstions to v5 */ +#define FL_XSCALE (1 << 10) /* XScale */ + +/* The bits in this mask specify which + instructions we are allowed to generate. */ +static int insn_flags = 0; + +/* The bits in this mask specify which instruction scheduling options should + be used. Note - there is an overlap with the FL_FAST_MULT. For some + hardware we want to be able to generate the multiply instructions, but to + tune as if they were not present in the architecture. */ +static int tune_flags = 0; + +/* The following are used in the arm.md file as equivalents to bits + in the above two flag variables. */ + +/* Nonzero if this is an "M" variant of the processor. */ +int arm_fast_multiply = 0; + +/* Nonzero if this chip supports the ARM Architecture 4 extensions. */ +int arm_arch4 = 0; + +/* Nonzero if this chip supports the ARM Architecture 5 extensions. */ +int arm_arch5 = 0; + +/* Nonzero if this chip supports the ARM Architecture 5E extensions. */ +int arm_arch5e = 0; + +/* Nonzero if this chip can benefit from load scheduling. */ +int arm_ld_sched = 0; + +/* Nonzero if this chip is a StrongARM. */ +int arm_is_strong = 0; + +/* Nonzero if this chip is an XScale. */ +int arm_is_xscale = 0; + +/* Nonzero if this chip is an ARM6 or an ARM7. */ +int arm_is_6_or_7 = 0; + +/* Nonzero if generating Thumb instructions. */ +int thumb_code = 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. */ +enum machine_mode output_memory_reference_mode; + +/* Nonzero if the prologue must setup `fp'. */ +int current_function_anonymous_args; + +/* The register number to be used for the PIC offset register. */ +const char * arm_pic_register_string = NULL; +int arm_pic_register = 9; + +/* Set to 1 when a return insn is output, this means that the epilogue + is not needed. */ +int return_used_this_function; + +/* Set to 1 after arm_reorg has started. Reset to start at the start of + the next function. */ +static int after_arm_reorg = 0; + +/* The maximum number of insns to be used when loading a constant. */ +static int arm_constant_limit = 3; + +/* For an explanation of these variables, see final_prescan_insn below. */ +int arm_ccfsm_state; +enum arm_cond_code arm_current_cc; +rtx arm_target_insn; +int arm_target_label; + +/* The condition codes of the ARM, and the inverse function. */ +static const char * const arm_condition_codes[] = +{ + "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" +}; + +#define streq(string1, string2) (strcmp (string1, string2) == 0) + +/* Initialization code. */ + +struct processors +{ + const char *const name; + const unsigned int flags; +}; + +/* Not all of these give usefully different compilation alternatives, + but there is no simple way of generalizing them. */ +static const struct processors all_cores[] = +{ + /* ARM Cores */ + + {"arm2", FL_CO_PROC | FL_MODE26 }, + {"arm250", FL_CO_PROC | FL_MODE26 }, + {"arm3", FL_CO_PROC | FL_MODE26 }, + {"arm6", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm60", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm600", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm610", FL_MODE26 | FL_MODE32 }, + {"arm620", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm7", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + /* arm7m doesn't exist on its own, but only with D, (and I), but + those don't alter the code, so arm7m is sometimes used. */ + {"arm7m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, + {"arm7d", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm7dm", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, + {"arm7di", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm7dmi", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, + {"arm70", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm710", FL_MODE26 | FL_MODE32 }, + {"arm710t", FL_MODE26 | FL_MODE32 | FL_THUMB }, + {"arm720", FL_MODE26 | FL_MODE32 }, + {"arm720t", FL_MODE26 | FL_MODE32 | FL_THUMB }, + {"arm740t", FL_MODE26 | FL_MODE32 | FL_THUMB }, + {"arm710c", FL_MODE26 | FL_MODE32 }, + {"arm7100", FL_MODE26 | FL_MODE32 }, + {"arm7500", FL_MODE26 | FL_MODE32 }, + /* Doesn't have an external co-proc, but does have embedded fpu. */ + {"arm7500fe", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + {"arm7tdmi", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, + {"arm8", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, + {"arm810", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, + {"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, + {"arm920", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, + {"arm920t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, + {"arm940t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, + {"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, + {"arm9e", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, + {"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, + {"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, + {"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, + {"strongarm1110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, + {"arm10tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, + {"arm1020t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, + {"xscale", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE }, + + {NULL, 0} +}; + +static const struct processors all_architectures[] = +{ + /* ARM Architectures */ + + { "armv2", FL_CO_PROC | FL_MODE26 }, + { "armv2a", FL_CO_PROC | FL_MODE26 }, + { "armv3", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, + { "armv3m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, + { "armv4", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 }, + /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no + implementations that support it, so we will leave it out for now. */ + { "armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, + { "armv5", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, + { "armv5t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, + { "armv5te", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E }, + { NULL, 0 } +}; + +/* This is a magic stucture. The 'string' field is magically filled in + with a pointer to the value specified by the user on the command line + assuming that the user has specified such a value. */ + +struct arm_cpu_select arm_select[] = +{ + /* string name processors */ + { NULL, "-mcpu=", all_cores }, + { NULL, "-march=", all_architectures }, + { NULL, "-mtune=", all_cores } +}; + +/* Return the number of bits set in value' */ +static unsigned long +bit_count (value) + signed int value; +{ + unsigned long count = 0; + + while (value) + { + value &= ~(value & -value); + ++count; + } + + return count; +} + +/* Fix up any incompatible options that the user has specified. + This has now turned into a maze. */ +void +arm_override_options () +{ + unsigned i; + + /* Set up the flags based on the cpu/architecture selected by the user. */ + for (i = ARRAY_SIZE (arm_select); i--;) + { + struct arm_cpu_select * ptr = arm_select + i; + + if (ptr->string != NULL && ptr->string[0] != '\0') + { + const struct processors * sel; + + for (sel = ptr->processors; sel->name != NULL; sel++) + if (streq (ptr->string, sel->name)) + { + if (i == 2) + tune_flags = sel->flags; + else + { + /* If we have been given an architecture and a processor + make sure that they are compatible. We only generate + a warning though, and we prefer the CPU over the + architecture. */ + if (insn_flags != 0 && (insn_flags ^ sel->flags)) + warning ("switch -mcpu=%s conflicts with -march= switch", + ptr->string); + + insn_flags = sel->flags; + } + + break; + } + + if (sel->name == NULL) + error ("bad value (%s) for %s switch", ptr->string, ptr->name); + } + } + + /* If the user did not specify a processor, choose one for them. */ + if (insn_flags == 0) + { + const struct processors * sel; + unsigned int sought; + static const struct cpu_default + { + const int cpu; + const char *const name; + } + cpu_defaults[] = + { + { TARGET_CPU_arm2, "arm2" }, + { TARGET_CPU_arm6, "arm6" }, + { TARGET_CPU_arm610, "arm610" }, + { TARGET_CPU_arm710, "arm710" }, + { TARGET_CPU_arm7m, "arm7m" }, + { TARGET_CPU_arm7500fe, "arm7500fe" }, + { TARGET_CPU_arm7tdmi, "arm7tdmi" }, + { TARGET_CPU_arm8, "arm8" }, + { TARGET_CPU_arm810, "arm810" }, + { TARGET_CPU_arm9, "arm9" }, + { TARGET_CPU_strongarm, "strongarm" }, + { TARGET_CPU_xscale, "xscale" }, + { TARGET_CPU_generic, "arm" }, + { 0, 0 } + }; + const struct cpu_default * def; + + /* Find the default. */ + for (def = cpu_defaults; def->name; def++) + if (def->cpu == TARGET_CPU_DEFAULT) + break; + + /* Make sure we found the default CPU. */ + if (def->name == NULL) + abort (); + + /* Find the default CPU's flags. */ + for (sel = all_cores; sel->name != NULL; sel++) + if (streq (def->name, sel->name)) + break; + + if (sel->name == NULL) + abort (); + + insn_flags = sel->flags; + + /* Now check to see if the user has specified some command line + switch that require certain abilities from the cpu. */ + sought = 0; + + if (TARGET_INTERWORK || TARGET_THUMB) + { + sought |= (FL_THUMB | FL_MODE32); + + /* Force apcs-32 to be used for interworking. */ + target_flags |= ARM_FLAG_APCS_32; + + /* There are no ARM processors that support both APCS-26 and + interworking. Therefore we force FL_MODE26 to be removed + from insn_flags here (if it was set), so that the search + below will always be able to find a compatible processor. */ + insn_flags &= ~FL_MODE26; + } + else if (!TARGET_APCS_32) + sought |= FL_MODE26; + + if (sought != 0 && ((sought & insn_flags) != sought)) + { + /* Try to locate a CPU type that supports all of the abilities + of the default CPU, plus the extra abilities requested by + the user. */ + for (sel = all_cores; sel->name != NULL; sel++) + if ((sel->flags & sought) == (sought | insn_flags)) + break; + + if (sel->name == NULL) + { + unsigned int current_bit_count = 0; + const struct processors * best_fit = NULL; + + /* Ideally we would like to issue an error message here + saying that it was not possible to find a CPU compatible + with the default CPU, but which also supports the command + line options specified by the programmer, and so they + ought to use the -mcpu=<name> command line option to + override the default CPU type. + + Unfortunately this does not work with multilibing. We + need to be able to support multilibs for -mapcs-26 and for + -mthumb-interwork and there is no CPU that can support both + options. Instead if we cannot find a cpu that has both the + characteristics of the default cpu and the given command line + options we scan the array again looking for a best match. */ + for (sel = all_cores; sel->name != NULL; sel++) + if ((sel->flags & sought) == sought) + { + unsigned int count; + + count = bit_count (sel->flags & insn_flags); + + if (count >= current_bit_count) + { + best_fit = sel; + current_bit_count = count; + } + } + + if (best_fit == NULL) + abort (); + else + sel = best_fit; + } + + insn_flags = sel->flags; + } + } + + /* If tuning has not been specified, tune for whichever processor or + architecture has been selected. */ + if (tune_flags == 0) + tune_flags = insn_flags; + + /* Make sure that the processor choice does not conflict with any of the + other command line choices. */ + if (TARGET_APCS_32 && !(insn_flags & FL_MODE32)) + { + /* If APCS-32 was not the default then it must have been set by the + user, so issue a warning message. If the user has specified + "-mapcs-32 -mcpu=arm2" then we loose here. */ + if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0) + warning ("target CPU does not support APCS-32" ); + target_flags &= ~ARM_FLAG_APCS_32; + } + else if (!TARGET_APCS_32 && !(insn_flags & FL_MODE26)) + { + warning ("target CPU does not support APCS-26" ); + target_flags |= ARM_FLAG_APCS_32; + } + + if (TARGET_INTERWORK && !(insn_flags & FL_THUMB)) + { + warning ("target CPU does not support interworking" ); + target_flags &= ~ARM_FLAG_INTERWORK; + } + + if (TARGET_THUMB && !(insn_flags & FL_THUMB)) + { + warning ("target CPU does not support THUMB instructions"); + target_flags &= ~ARM_FLAG_THUMB; + } + + if (TARGET_APCS_FRAME && TARGET_THUMB) + { + /* warning ("ignoring -mapcs-frame because -mthumb was used"); */ + target_flags &= ~ARM_FLAG_APCS_FRAME; + } + + /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done + from here where no function is being compiled currently. */ + if ((target_flags & (THUMB_FLAG_LEAF_BACKTRACE | THUMB_FLAG_BACKTRACE)) + && TARGET_ARM) + warning ("enabling backtrace support is only meaningful when compiling for the Thumb"); + + if (TARGET_ARM && TARGET_CALLEE_INTERWORKING) + warning ("enabling callee interworking support is only meaningful when compiling for the Thumb"); + + if (TARGET_ARM && TARGET_CALLER_INTERWORKING) + warning ("enabling caller interworking support is only meaningful when compiling for the Thumb"); + + /* If interworking is enabled then APCS-32 must be selected as well. */ + if (TARGET_INTERWORK) + { + if (!TARGET_APCS_32) + warning ("interworking forces APCS-32 to be used" ); + target_flags |= ARM_FLAG_APCS_32; + } + + if (TARGET_APCS_STACK && !TARGET_APCS_FRAME) + { + warning ("-mapcs-stack-check incompatible with -mno-apcs-frame"); + target_flags |= ARM_FLAG_APCS_FRAME; + } + + if (TARGET_POKE_FUNCTION_NAME) + target_flags |= ARM_FLAG_APCS_FRAME; + + if (TARGET_APCS_REENT && flag_pic) + error ("-fpic and -mapcs-reent are incompatible"); + + if (TARGET_APCS_REENT) + warning ("APCS reentrant code not supported. Ignored"); + + /* If this target is normally configured to use APCS frames, warn if they + are turned off and debugging is turned on. */ + if (TARGET_ARM + && write_symbols != NO_DEBUG + && !TARGET_APCS_FRAME + && (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME)) + warning ("-g with -mno-apcs-frame may not give sensible debugging"); + + /* If stack checking is disabled, we can use r10 as the PIC register, + which keeps r9 available. */ + if (flag_pic && !TARGET_APCS_STACK) + arm_pic_register = 10; + + if (TARGET_APCS_FLOAT) + warning ("passing floating point arguments in fp regs not yet supported"); + + /* Initialise boolean versions of the flags, for use in the arm.md file. */ + arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0; + arm_arch4 = (insn_flags & FL_ARCH4) != 0; + arm_arch5 = (insn_flags & FL_ARCH5) != 0; + arm_arch5e = (insn_flags & FL_ARCH5E) != 0; + arm_is_xscale = (insn_flags & FL_XSCALE) != 0; + + arm_ld_sched = (tune_flags & FL_LDSCHED) != 0; + arm_is_strong = (tune_flags & FL_STRONG) != 0; + thumb_code = (TARGET_ARM == 0); + arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32)) + && !(tune_flags & FL_ARCH4))) != 0; + + /* Default value for floating point code... if no co-processor + bus, then schedule for emulated floating point. Otherwise, + assume the user has an FPA. + Note: this does not prevent use of floating point instructions, + -msoft-float does that. */ + arm_fpu = (tune_flags & FL_CO_PROC) ? FP_HARD : FP_SOFT3; + + if (target_fp_name) + { + if (streq (target_fp_name, "2")) + arm_fpu_arch = FP_SOFT2; + else if (streq (target_fp_name, "3")) + arm_fpu_arch = FP_SOFT3; + else + error ("invalid floating point emulation option: -mfpe-%s", + target_fp_name); + } + else + arm_fpu_arch = FP_DEFAULT; + + 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) + && (tune_flags & FL_MODE32) == 0) + flag_schedule_insns = flag_schedule_insns_after_reload = 0; + + arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26; + + if (structure_size_string != NULL) + { + int size = strtol (structure_size_string, NULL, 0); + + if (size == 8 || size == 32) + arm_structure_size_boundary = size; + else + warning ("structure size boundary can only be set to 8 or 32"); + } + + if (arm_pic_register_string != NULL) + { + int pic_register; + + if (!flag_pic) + warning ("-mpic-register= is useless without -fpic"); + + pic_register = decode_reg_name (arm_pic_register_string); + + /* Prevent the user from choosing an obviously stupid PIC register. */ + if (pic_register < 0 || call_used_regs[pic_register] + || pic_register == HARD_FRAME_POINTER_REGNUM + || pic_register == STACK_POINTER_REGNUM + || pic_register >= PC_REGNUM) + error ("unable to use '%s' for PIC register", arm_pic_register_string); + else + arm_pic_register = pic_register; + } + + if (TARGET_THUMB && flag_schedule_insns) + { + /* Don't warn since it's on by default in -O2. */ + flag_schedule_insns = 0; + } + + /* If optimizing for space, don't synthesize constants. + For processors with load scheduling, it never costs more than 2 cycles + to load a constant, and the load scheduler may well reduce that to 1. */ + if (optimize_size || (tune_flags & FL_LDSCHED)) + arm_constant_limit = 1; + + if (arm_is_xscale) + arm_constant_limit = 2; + + /* If optimizing for size, bump the number of instructions that we + are prepared to conditionally execute (even on a StrongARM). + Otherwise for the StrongARM, which has early execution of branches, + a sequence that is worth skipping is shorter. */ + if (optimize_size) + max_insns_skipped = 6; + else if (arm_is_strong) + max_insns_skipped = 3; + + /* Register global variables with the garbage collector. */ + arm_add_gc_roots (); +} + +static void +arm_add_gc_roots () +{ + ggc_add_rtx_root (&arm_compare_op0, 1); + ggc_add_rtx_root (&arm_compare_op1, 1); + ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root. */ + + gcc_obstack_init(&minipool_obstack); + minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0); +} + +/* A table of known ARM exception types. + For use with the interrupt function attribute. */ + +typedef struct +{ + const char *const arg; + const unsigned long return_value; +} +isr_attribute_arg; + +static const isr_attribute_arg isr_attribute_args [] = +{ + { "IRQ", ARM_FT_ISR }, + { "irq", ARM_FT_ISR }, + { "FIQ", ARM_FT_FIQ }, + { "fiq", ARM_FT_FIQ }, + { "ABORT", ARM_FT_ISR }, + { "abort", ARM_FT_ISR }, + { "ABORT", ARM_FT_ISR }, + { "abort", ARM_FT_ISR }, + { "UNDEF", ARM_FT_EXCEPTION }, + { "undef", ARM_FT_EXCEPTION }, + { "SWI", ARM_FT_EXCEPTION }, + { "swi", ARM_FT_EXCEPTION }, + { NULL, ARM_FT_NORMAL } +}; + +/* Returns the (interrupt) function type of the current + function, or ARM_FT_UNKNOWN if the type cannot be determined. */ + +static unsigned long +arm_isr_value (argument) + tree argument; +{ + const isr_attribute_arg * ptr; + const char * arg; + + /* No argument - default to IRQ. */ + if (argument == NULL_TREE) + return ARM_FT_ISR; + + /* Get the value of the argument. */ + if (TREE_VALUE (argument) == NULL_TREE + || TREE_CODE (TREE_VALUE (argument)) != STRING_CST) + return ARM_FT_UNKNOWN; + + arg = TREE_STRING_POINTER (TREE_VALUE (argument)); + + /* Check it against the list of known arguments. */ + for (ptr = isr_attribute_args; ptr->arg != NULL; ptr ++) + if (streq (arg, ptr->arg)) + return ptr->return_value; + + /* An unrecognised interrupt type. */ + return ARM_FT_UNKNOWN; +} + +/* Computes the type of the current function. */ + +static unsigned long +arm_compute_func_type () +{ + unsigned long type = ARM_FT_UNKNOWN; + tree a; + tree attr; + + if (TREE_CODE (current_function_decl) != FUNCTION_DECL) + abort (); + + /* Decide if the current function is volatile. Such functions + never return, and many memory cycles can be saved by not storing + register values that will never be needed again. This optimization + was added to speed up context switching in a kernel application. */ + if (optimize > 0 + && current_function_nothrow + && TREE_THIS_VOLATILE (current_function_decl)) + type |= ARM_FT_VOLATILE; + + if (current_function_needs_context) + type |= ARM_FT_NESTED; + + attr = DECL_ATTRIBUTES (current_function_decl); + + a = lookup_attribute ("naked", attr); + if (a != NULL_TREE) + type |= ARM_FT_NAKED; + + if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX) + type |= ARM_FT_EXCEPTION_HANDLER; + else + { + a = lookup_attribute ("isr", attr); + if (a == NULL_TREE) + a = lookup_attribute ("interrupt", attr); + + if (a == NULL_TREE) + type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL; + else + type |= arm_isr_value (TREE_VALUE (a)); + } + + return type; +} + +/* Returns the type of the current function. */ + +unsigned long +arm_current_func_type () +{ + if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN) + cfun->machine->func_type = arm_compute_func_type (); + + return cfun->machine->func_type; +} + +/* Return 1 if it is possible to return using a single instruction. */ + +int +use_return_insn (iscond) + int iscond; +{ + int regno; + unsigned int func_type; + + /* Never use a return instruction before reload has run. */ + if (!reload_completed) + return 0; + + func_type = arm_current_func_type (); + + /* Naked functions, volatile functiond and interrupt + functions all need special consideration. */ + if (func_type & (ARM_FT_INTERRUPT | ARM_FT_VOLATILE | ARM_FT_NAKED)) + return 0; + + /* As do variadic functions. */ + if (current_function_pretend_args_size + || current_function_anonymous_args + /* Of if the function calls __builtin_eh_return () */ + || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER + /* Or if there is no frame pointer and there is a stack adjustment. */ + || ((get_frame_size () + current_function_outgoing_args_size != 0) + && !frame_pointer_needed)) + return 0; + + /* Can't be done if interworking with Thumb, and any registers have been + stacked. Similarly, on StrongARM, conditional returns are expensive + if they aren't taken and registers have been stacked. */ + if (iscond && arm_is_strong && frame_pointer_needed) + return 0; + + if ((iscond && arm_is_strong) + || TARGET_INTERWORK) + { + for (regno = 0; regno <= LAST_ARM_REGNUM; regno++) + if (regs_ever_live[regno] && !call_used_regs[regno]) + return 0; + + if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) + return 0; + } + + /* Can't be done if any of the FPU regs are pushed, + since this also requires an insn. */ + if (TARGET_HARD_FLOAT) + for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++) + if (regs_ever_live[regno] && !call_used_regs[regno]) + return 0; + + return 1; +} + +/* Return TRUE if int I is a valid immediate ARM constant. */ + +int +const_ok_for_arm (i) + HOST_WIDE_INT i; +{ + unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF; + + /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must + be all zero, or all one. */ + if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0 + && ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) + != ((~(unsigned HOST_WIDE_INT) 0) + & ~(unsigned HOST_WIDE_INT) 0xffffffff))) + return FALSE; + + /* Fast return for 0 and powers of 2 */ + if ((i & (i - 1)) == 0) + return TRUE; + + do + { + if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) + return TRUE; + mask = + (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) + >> (32 - 2)) | ~(unsigned HOST_WIDE_INT) 0xffffffff; + } + while (mask != ~(unsigned HOST_WIDE_INT) 0xFF); + + return FALSE; +} + +/* Return true if I is a valid constant for the operation CODE. */ +static int +const_ok_for_op (i, code) + HOST_WIDE_INT i; + enum rtx_code code; +{ + if (const_ok_for_arm (i)) + return 1; + + switch (code) + { + case PLUS: + return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); + + case MINUS: /* Should only occur with (MINUS I reg) => rsb */ + case XOR: + case IOR: + return 0; + + case AND: + return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); + + default: + abort (); + } +} + +/* Emit a sequence of insns to handle a large constant. + CODE is the code of the operation required, it can be any of SET, PLUS, + IOR, AND, XOR, MINUS; + MODE is the mode in which the operation is being performed; + 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. + Return value is the number of insns emitted. */ + +int +arm_split_constant (code, mode, val, target, source, subtargets) + enum rtx_code code; + enum machine_mode mode; + HOST_WIDE_INT val; + rtx target; + rtx source; + int subtargets; +{ + if (subtargets || code == SET + || (GET_CODE (target) == REG && GET_CODE (source) == REG + && REGNO (target) != REGNO (source))) + { + /* After arm_reorg has been called, we can't fix up expensive + constants by pushing them into memory so we must synthesise + them in-line, regardless of the cost. This is only likely to + be more costly on chips that have load delay slots and we are + compiling without running the scheduler (so no splitting + occurred before the final instruction emission). + + Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c + */ + if (!after_arm_reorg + && (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_MINUS (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); +} + +static int +count_insns_for_constant (HOST_WIDE_INT remainder, int i) +{ + HOST_WIDE_INT temp1; + int num_insns = 0; + do + { + int end; + + if (i <= 0) + i += 32; + if (remainder & (3 << (i - 2))) + { + end = i - 8; + if (end < 0) + end += 32; + temp1 = remainder & ((0x0ff << end) + | ((i < end) ? (0xff >> (32 - end)) : 0)); + remainder &= ~temp1; + num_insns++; + i -= 6; + } + i -= 2; + } while (remainder); + return num_insns; +} + +/* As above, but extra parameter GENERATE which, if clear, suppresses + RTL generation. */ + +static 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_invert = 0; + int can_negate = 0; + int can_negate_initial = 0; + int can_shift = 0; + int i; + int num_bits_set = 0; + int set_sign_bit_copies = 0; + int clear_sign_bit_copies = 0; + int clear_zero_bit_copies = 0; + int set_zero_bit_copies = 0; + int insns = 0; + unsigned HOST_WIDE_INT temp1, temp2; + unsigned HOST_WIDE_INT remainder = val & 0xffffffff; + + /* Find out which operations are safe for a given CODE. Also do a quick + check for degenerate cases; these can occur when DImode operations + are split. */ + switch (code) + { + case SET: + can_invert = 1; + can_shift = 1; + can_negate = 1; + break; + + case PLUS: + can_negate = 1; + can_negate_initial = 1; + break; + + case IOR: + if (remainder == 0xffffffff) + { + 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; + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, source)); + return 1; + } + break; + + case AND: + if (remainder == 0) + { + 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; + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, source)); + return 1; + } + can_invert = 1; + break; + + case XOR: + if (remainder == 0) + { + if (reload_completed && rtx_equal_p (target, source)) + return 0; + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, source)); + return 1; + } + if (remainder == 0xffffffff) + { + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, + gen_rtx_NOT (mode, source))); + return 1; + } + + /* We don't know how to handle this yet below. */ + abort (); + + case MINUS: + /* We treat MINUS as (val - source), since (source - val) is always + passed as (source + (-val)). */ + if (remainder == 0) + { + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, + gen_rtx_NEG (mode, source))); + return 1; + } + if (const_ok_for_arm (val)) + { + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, + gen_rtx_MINUS (mode, GEN_INT (val), + source))); + return 1; + } + can_negate = 1; + + break; + + default: + abort (); + } + + /* If we can do it in one insn get out quickly. */ + if (const_ok_for_arm (val) + || (can_negate_initial && const_ok_for_arm (-val)) + || (can_invert && const_ok_for_arm (~val))) + { + if (generate) + emit_insn (gen_rtx_SET (VOIDmode, target, + (source ? gen_rtx (code, mode, source, + GEN_INT (val)) + : GEN_INT (val)))); + return 1; + } + + /* Calculate a few attributes that may be useful for specific + optimizations. */ + for (i = 31; i >= 0; i--) + { + if ((remainder & (1 << i)) == 0) + clear_sign_bit_copies++; + else + break; + } + + for (i = 31; i >= 0; i--) + { + if ((remainder & (1 << i)) != 0) + set_sign_bit_copies++; + else + break; + } + + for (i = 0; i <= 31; i++) + { + if ((remainder & (1 << i)) == 0) + clear_zero_bit_copies++; + else + break; + } + + for (i = 0; i <= 31; i++) + { + if ((remainder & (1 << i)) != 0) + set_zero_bit_copies++; + else + break; + } + + switch (code) + { + case SET: + /* See if we can do this by sign_extending a constant that is known + to be negative. This is a good, way of doing it, since the shift + may well merge into a subsequent insn. */ + if (set_sign_bit_copies > 1) + { + if (const_ok_for_arm + (temp1 = ARM_SIGN_EXTEND (remainder + << (set_sign_bit_copies - 1)))) + { + if (generate) + { + rtx 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, + these will be shifted out of harm's way. */ + temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; + if (const_ok_for_arm (~temp1)) + { + if (generate) + { + rtx 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; + } + } + + /* See if we can generate this by setting the bottom (or the top) + 16 bits, and then shifting these into the other half of the + word. We only look for the simplest cases, to do more would cost + too much. Be careful, however, not to generate this when the + alternative would take fewer insns. */ + if (val & 0xffff0000) + { + temp1 = remainder & 0xffff0000; + temp2 = remainder & 0x0000ffff; + + /* Overlaps outside this range are best done using other methods. */ + for (i = 9; i < 24; i++) + { + if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) + && !const_ok_for_arm (temp2)) + { + rtx new_src = (subtargets + ? (generate ? gen_reg_rtx (mode) : NULL_RTX) + : target); + insns = arm_gen_constant (code, mode, temp2, new_src, + source, subtargets, generate); + source = new_src; + 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; + } + } + + /* Don't duplicate cases already considered. */ + for (i = 17; i < 24; i++) + { + if (((temp1 | (temp1 >> i)) == remainder) + && !const_ok_for_arm (temp1)) + { + rtx new_src = (subtargets + ? (generate ? gen_reg_rtx (mode) : NULL_RTX) + : target); + insns = arm_gen_constant (code, mode, temp1, new_src, + source, subtargets, generate); + source = new_src; + 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; + } + } + } + break; + + case IOR: + case XOR: + /* If we have IOR or XOR, and the constant can be loaded in a + single instruction, and we can find a temporary to put it in, + then this can be done in two instructions instead of 3-4. */ + if (subtargets + /* TARGET can't be NULL if SUBTARGETS is 0 */ + || (reload_completed && !reg_mentioned_p (target, source))) + { + if (const_ok_for_arm (ARM_SIGN_EXTEND (~val))) + { + if (generate) + { + rtx sub = subtargets ? gen_reg_rtx (mode) : target; + + emit_insn (gen_rtx_SET (VOIDmode, sub, GEN_INT (val))); + emit_insn (gen_rtx_SET (VOIDmode, target, + gen_rtx (code, mode, source, sub))); + } + return 2; + } + } + + if (code == XOR) + break; + + if (set_sign_bit_copies > 8 + && (val & (-1 << (32 - set_sign_bit_copies))) == val) + { + 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) + { + 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))) + { + 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; + + case AND: + /* See if two shifts will do 2 or more insn's worth of work. */ + if (clear_sign_bit_copies >= 16 && clear_sign_bit_copies < 24) + { + HOST_WIDE_INT shift_mask = ((0xffffffff + << (32 - clear_sign_bit_copies)) + & 0xffffffff); + + if ((remainder | shift_mask) != 0xffffffff) + { + if (generate) + { + rtx new_src = subtargets ? gen_reg_rtx (mode) : target; + insns = arm_gen_constant (AND, mode, remainder | shift_mask, + new_src, source, subtargets, 1); + source = new_src; + } + else + { + rtx targ = subtargets ? NULL_RTX : target; + insns = arm_gen_constant (AND, mode, remainder | shift_mask, + targ, source, subtargets, 0); + } + } + + if (generate) + { + rtx new_src = subtargets ? gen_reg_rtx (mode) : target; + rtx shift = GEN_INT (clear_sign_bit_copies); + + emit_insn (gen_ashlsi3 (new_src, source, shift)); + emit_insn (gen_lshrsi3 (target, new_src, shift)); + } + + return insns + 2; + } + + if (clear_zero_bit_copies >= 16 && clear_zero_bit_copies < 24) + { + HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1; + + if ((remainder | shift_mask) != 0xffffffff) + { + if (generate) + { + rtx new_src = subtargets ? gen_reg_rtx (mode) : target; + + insns = arm_gen_constant (AND, mode, remainder | shift_mask, + new_src, source, subtargets, 1); + source = new_src; + } + else + { + rtx targ = subtargets ? NULL_RTX : target; + + insns = arm_gen_constant (AND, mode, remainder | shift_mask, + targ, source, subtargets, 0); + } + } + + if (generate) + { + rtx new_src = subtargets ? gen_reg_rtx (mode) : target; + rtx shift = GEN_INT (clear_zero_bit_copies); + + emit_insn (gen_lshrsi3 (new_src, source, shift)); + emit_insn (gen_ashlsi3 (target, new_src, shift)); + } + + return insns + 2; + } + + break; + + default: + break; + } + + for (i = 0; i < 32; i++) + if (remainder & (1 << i)) + num_bits_set++; + + if (code == AND || (can_invert && num_bits_set > 16)) + remainder = (~remainder) & 0xffffffff; + else if (code == PLUS && num_bits_set > 16) + remainder = (-remainder) & 0xffffffff; + else + { + can_invert = 0; + can_negate = 0; + } + + /* Now try and find a way of doing the job in either two or three + instructions. + We start by looking for the largest block of zeros that are aligned on + a 2-bit boundary, we then fill up the temps, wrapping around to the + top of the word when we drop off the bottom. + In the worst case this code should produce no more than four insns. */ + { + int best_start = 0; + int best_consecutive_zeros = 0; + + for (i = 0; i < 32; i += 2) + { + int consecutive_zeros = 0; + + if (!(remainder & (3 << i))) + { + while ((i < 32) && !(remainder & (3 << i))) + { + consecutive_zeros += 2; + i += 2; + } + if (consecutive_zeros > best_consecutive_zeros) + { + best_consecutive_zeros = consecutive_zeros; + best_start = i - consecutive_zeros; + } + i -= 2; + } + } + + /* So long as it won't require any more insns to do so, it's + desirable to emit a small constant (in bits 0...9) in the last + insn. This way there is more chance that it can be combined with + a later addressing insn to form a pre-indexed load or store + operation. Consider: + + *((volatile int *)0xe0000100) = 1; + *((volatile int *)0xe0000110) = 2; + + We want this to wind up as: + + mov rA, #0xe0000000 + mov rB, #1 + str rB, [rA, #0x100] + mov rB, #2 + str rB, [rA, #0x110] + + rather than having to synthesize both large constants from scratch. + + Therefore, we calculate how many insns would be required to emit + the constant starting from `best_start', and also starting from + zero (ie with bit 31 first to be output). If `best_start' doesn't + yield a shorter sequence, we may as well use zero. */ + if (best_start != 0 + && ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder) + && (count_insns_for_constant (remainder, 0) <= + count_insns_for_constant (remainder, best_start))) + best_start = 0; + + /* Now start emitting the insns. */ + i = best_start; + do + { + int end; + + if (i <= 0) + i += 32; + if (remainder & (3 << (i - 2))) + { + end = i - 8; + if (end < 0) + end += 32; + temp1 = remainder & ((0x0ff << end) + | ((i < end) ? (0xff >> (32 - end)) : 0)); + remainder &= ~temp1; + + if (generate) + { + rtx new_src, temp1_rtx; + + if (code == SET || code == MINUS) + { + new_src = (subtargets ? gen_reg_rtx (mode) : target); + if (can_invert && code != MINUS) + temp1 = ~temp1; + } + else + { + if (remainder && subtargets) + new_src = gen_reg_rtx (mode); + else + new_src = target; + if (can_invert) + temp1 = ~temp1; + else if (can_negate) + temp1 = -temp1; + } + + temp1 = trunc_int_for_mode (temp1, mode); + temp1_rtx = GEN_INT (temp1); + + if (code == SET) + ; + else if (code == MINUS) + temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source); + else + temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx); + + emit_insn (gen_rtx_SET (VOIDmode, new_src, temp1_rtx)); + source = new_src; + } + + if (code == SET) + { + can_invert = 0; + code = PLUS; + } + else if (code == MINUS) + code = PLUS; + + insns++; + i -= 6; + } + i -= 2; + } + while (remainder); + } + + return insns; +} + +/* Canonicalize a comparison so that we are more likely to recognize it. + This can be done for a few constant compares, where we can make the + immediate value easier to load. */ + +enum rtx_code +arm_canonicalize_comparison (code, op1) + enum rtx_code code; + rtx * op1; +{ + unsigned HOST_WIDE_INT i = INTVAL (*op1); + + switch (code) + { + case EQ: + case NE: + return code; + + case GT: + case LE: + if (i != ((((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) - 1) + && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) + { + *op1 = GEN_INT (i + 1); + return code == GT ? GE : LT; + } + break; + + case GE: + case LT: + if (i != (((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) + && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) + { + *op1 = GEN_INT (i - 1); + return code == GE ? GT : LE; + } + break; + + case GTU: + case LEU: + if (i != ~((unsigned HOST_WIDE_INT) 0) + && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) + { + *op1 = GEN_INT (i + 1); + return code == GTU ? GEU : LTU; + } + break; + + case GEU: + case LTU: + if (i != 0 + && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) + { + *op1 = GEN_INT (i - 1); + return code == GEU ? GTU : LEU; + } + break; + + default: + abort (); + } + + return code; +} + +/* Decide whether a type should be returned in memory (true) + or in a register (false). This is called by the macro + RETURN_IN_MEMORY. */ + +int +arm_return_in_memory (type) + tree type; +{ + if (!AGGREGATE_TYPE_P (type)) + /* All simple types are returned in registers. */ + return 0; + + /* For the arm-wince targets we choose to be compitable with Microsoft's + ARM and Thumb compilers, which always return aggregates in memory. */ +#ifndef ARM_WINCE + /* All structures/unions bigger than one word are returned in memory. + Also catch the case where int_size_in_bytes returns -1. In this case + the aggregate is either huge or of varaible size, and in either case + we will want to return it via memory and not in a register. */ + if (((unsigned int) int_size_in_bytes (type)) > UNITS_PER_WORD) + return 1; + + if (TREE_CODE (type) == RECORD_TYPE) + { + tree field; + + /* For a struct the APCS says that we only return in a register + if the type is 'integer like' and every addressable element + has an offset of zero. For practical purposes this means + that the structure can have at most one non bit-field element + and that this element must be the first one in the structure. */ + + /* Find the first field, ignoring non FIELD_DECL things which will + have been created by C++. */ + for (field = TYPE_FIELDS (type); + field && TREE_CODE (field) != FIELD_DECL; + field = TREE_CHAIN (field)) + continue; + + if (field == NULL) + return 0; /* An empty structure. Allowed by an extension to ANSI C. */ + + /* Check that the first field is valid for returning in a register. */ + + /* ... Floats are not allowed */ + if (FLOAT_TYPE_P (TREE_TYPE (field))) + return 1; + + /* ... Aggregates that are not themselves valid for returning in + a register are not allowed. */ + if (RETURN_IN_MEMORY (TREE_TYPE (field))) + return 1; + + /* Now check the remaining fields, if any. Only bitfields are allowed, + since they are not addressable. */ + for (field = TREE_CHAIN (field); + field; + field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + if (!DECL_BIT_FIELD_TYPE (field)) + return 1; + } + + return 0; + } + + 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) + continue; + + if (FLOAT_TYPE_P (TREE_TYPE (field))) + return 1; + + if (RETURN_IN_MEMORY (TREE_TYPE (field))) + return 1; + } + + return 0; + } +#endif /* not ARM_WINCE */ + + /* Return all other types in memory. */ + return 1; +} + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is NULL. */ +void +arm_init_cumulative_args (pcum, fntype, libname, indirect) + CUMULATIVE_ARGS * pcum; + tree fntype; + rtx libname ATTRIBUTE_UNUSED; + int indirect ATTRIBUTE_UNUSED; +{ + /* On the ARM, the offset starts at 0. */ + pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype))) ? 1 : 0); + + pcum->call_cookie = CALL_NORMAL; + + if (TARGET_LONG_CALLS) + pcum->call_cookie = CALL_LONG; + + /* Check for long call/short call attributes. The attributes + override any command line option. */ + if (fntype) + { + if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (fntype))) + pcum->call_cookie = CALL_SHORT; + else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (fntype))) + pcum->call_cookie = CALL_LONG; + } +} + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ + +rtx +arm_function_arg (pcum, mode, type, named) + CUMULATIVE_ARGS * pcum; + enum machine_mode mode; + tree type ATTRIBUTE_UNUSED; + int named; +{ + if (mode == VOIDmode) + /* Compute operand 2 of the call insn. */ + return GEN_INT (pcum->call_cookie); + + if (!named || pcum->nregs >= NUM_ARG_REGS) + return NULL_RTX; + + return gen_rtx_REG (mode, pcum->nregs); +} + +/* Encode the current state of the #pragma [no_]long_calls. */ +typedef enum +{ + OFF, /* No #pramgma [no_]long_calls is in effect. */ + LONG, /* #pragma long_calls is in effect. */ + SHORT /* #pragma no_long_calls is in effect. */ +} arm_pragma_enum; + +static arm_pragma_enum arm_pragma_long_calls = OFF; + +void +arm_pr_long_calls (pfile) + cpp_reader * pfile ATTRIBUTE_UNUSED; +{ + arm_pragma_long_calls = LONG; +} + +void +arm_pr_no_long_calls (pfile) + cpp_reader * pfile ATTRIBUTE_UNUSED; +{ + arm_pragma_long_calls = SHORT; +} + +void +arm_pr_long_calls_off (pfile) + cpp_reader * pfile ATTRIBUTE_UNUSED; +{ + arm_pragma_long_calls = OFF; +} + +/* Table of machine attributes. */ +const struct attribute_spec arm_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + /* Function calls made to this symbol must be done indirectly, because + it may lie outside of the 26 bit addressing range of a normal function + call. */ + { "long_call", 0, 0, false, true, true, NULL }, + /* Whereas these functions are always known to reside within the 26 bit + addressing range. */ + { "short_call", 0, 0, false, true, true, NULL }, + /* Interrupt Service Routines have special prologue and epilogue requirements. */ + { "isr", 0, 1, false, false, false, arm_handle_isr_attribute }, + { "interrupt", 0, 1, false, false, false, arm_handle_isr_attribute }, + { "naked", 0, 0, true, false, false, arm_handle_fndecl_attribute }, +#ifdef ARM_PE + /* ARM/PE has three new attributes: + interfacearm - ? + dllexport - for exporting a function/variable that will live in a dll + dllimport - for importing a function/variable from a dll + + Microsoft allows multiple declspecs in one __declspec, separating + them with spaces. We do NOT support this. Instead, use __declspec + multiple times. + */ + { "dllimport", 0, 0, true, false, false, NULL }, + { "dllexport", 0, 0, true, false, false, NULL }, + { "interfacearm", 0, 0, true, false, false, arm_handle_fndecl_attribute }, +#endif + { NULL, 0, 0, false, false, false, NULL } +}; + +/* Handle an attribute requiring a FUNCTION_DECL; + arguments as in struct attribute_spec.handler. */ + +static tree +arm_handle_fndecl_attribute (node, name, args, flags, no_add_attrs) + tree * node; + tree name; + tree args ATTRIBUTE_UNUSED; + int flags ATTRIBUTE_UNUSED; + bool * no_add_attrs; +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning ("`%s' attribute only applies to functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle an "interrupt" or "isr" attribute; + arguments as in struct attribute_spec.handler. */ + +static tree +arm_handle_isr_attribute (node, name, args, flags, no_add_attrs) + tree * node; + tree name; + tree args; + int flags; + bool * no_add_attrs; +{ + if (DECL_P (*node)) + { + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning ("`%s' attribute only applies to functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + /* FIXME: the argument if any is checked for type attributes; + should it be checked for decl ones? */ + } + else + { + if (TREE_CODE (*node) == FUNCTION_TYPE + || TREE_CODE (*node) == METHOD_TYPE) + { + if (arm_isr_value (args) == ARM_FT_UNKNOWN) + { + warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + } + else if (TREE_CODE (*node) == POINTER_TYPE + && (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE) + && arm_isr_value (args) != ARM_FT_UNKNOWN) + { + *node = build_type_copy (*node); + TREE_TYPE (*node) = build_type_attribute_variant + (TREE_TYPE (*node), + tree_cons (name, args, TYPE_ATTRIBUTES (TREE_TYPE (*node)))); + *no_add_attrs = true; + } + else + { + /* Possibly pass this attribute on from the type to a decl. */ + if (flags & ((int) ATTR_FLAG_DECL_NEXT + | (int) ATTR_FLAG_FUNCTION_NEXT + | (int) ATTR_FLAG_ARRAY_NEXT)) + { + *no_add_attrs = true; + return tree_cons (name, args, NULL_TREE); + } + else + { + warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); + } + } + } + + return NULL_TREE; +} + +/* Return 0 if the attributes for two types are incompatible, 1 if they + are compatible, and 2 if they are nearly compatible (which causes a + warning to be generated). */ + +static int +arm_comp_type_attributes (type1, type2) + tree type1; + tree type2; +{ + int l1, l2, s1, s2; + + /* Check for mismatch of non-default calling convention. */ + if (TREE_CODE (type1) != FUNCTION_TYPE) + return 1; + + /* Check for mismatched call attributes. */ + l1 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type1)) != NULL; + l2 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type2)) != NULL; + s1 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type1)) != NULL; + s2 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type2)) != NULL; + + /* Only bother to check if an attribute is defined. */ + if (l1 | l2 | s1 | s2) + { + /* If one type has an attribute, the other must have the same attribute. */ + if ((l1 != l2) || (s1 != s2)) + return 0; + + /* Disallow mixed attributes. */ + if ((l1 & s2) || (l2 & s1)) + return 0; + } + + /* Check for mismatched ISR attribute. */ + l1 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type1)) != NULL; + if (! l1) + l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type1)) != NULL; + l2 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type2)) != NULL; + if (! l2) + l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type2)) != NULL; + if (l1 != l2) + return 0; + + return 1; +} + +/* Encode long_call or short_call attribute by prefixing + symbol name in DECL with a special character FLAG. */ + +void +arm_encode_call_attribute (decl, flag) + tree decl; + int flag; +{ + const char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0); + int len = strlen (str); + char * newstr; + + if (TREE_CODE (decl) != FUNCTION_DECL) + return; + + /* Do not allow weak functions to be treated as short call. */ + if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR) + return; + + newstr = alloca (len + 2); + newstr[0] = flag; + strcpy (newstr + 1, str); + + newstr = (char *) ggc_alloc_string (newstr, len + 1); + XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr; +} + +/* Assigns default attributes to newly defined type. This is used to + set short_call/long_call attributes for function types of + functions defined inside corresponding #pragma scopes. */ + +static void +arm_set_default_type_attributes (type) + tree type; +{ + /* Add __attribute__ ((long_call)) to all functions, when + inside #pragma long_calls or __attribute__ ((short_call)), + when inside #pragma no_long_calls. */ + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) + { + tree type_attr_list, attr_name; + type_attr_list = TYPE_ATTRIBUTES (type); + + if (arm_pragma_long_calls == LONG) + attr_name = get_identifier ("long_call"); + else if (arm_pragma_long_calls == SHORT) + attr_name = get_identifier ("short_call"); + else + return; + + type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list); + TYPE_ATTRIBUTES (type) = type_attr_list; + } +} + +/* Return 1 if the operand is a SYMBOL_REF for a function known to be + defined within the current compilation unit. If this caanot be + determined, then 0 is returned. */ + +static int +current_file_function_operand (sym_ref) + rtx sym_ref; +{ + /* This is a bit of a fib. A function will have a short call flag + applied to its name if it has the short call attribute, or it has + already been defined within the current compilation unit. */ + if (ENCODED_SHORT_CALL_ATTR_P (XSTR (sym_ref, 0))) + return 1; + + /* The current function is always defined within the current compilation + unit. if it s a weak definition however, then this may not be the real + definition of the function, and so we have to say no. */ + if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0) + && !DECL_WEAK (current_function_decl)) + return 1; + + /* We cannot make the determination - default to returning 0. */ + return 0; +} + +/* Return non-zero if a 32 bit "long_call" should be generated for + this call. We generate a long_call if the function: + + a. has an __attribute__((long call)) + or b. is within the scope of a #pragma long_calls + or c. the -mlong-calls command line switch has been specified + + However we do not generate a long call if the function: + + d. has an __attribute__ ((short_call)) + or e. is inside the scope of a #pragma no_long_calls + or f. has an __attribute__ ((section)) + or g. is defined within the current compilation unit. + + This function will be called by C fragments contained in the machine + description file. CALL_REF and CALL_COOKIE correspond to the matched + rtl operands. CALL_SYMBOL is used to distinguish between + two different callers of the function. It is set to 1 in the + "call_symbol" and "call_symbol_value" patterns and to 0 in the "call" + and "call_value" patterns. This is because of the difference in the + SYM_REFs passed by these patterns. */ + +int +arm_is_longcall_p (sym_ref, call_cookie, call_symbol) + rtx sym_ref; + int call_cookie; + int call_symbol; +{ + if (!call_symbol) + { + if (GET_CODE (sym_ref) != MEM) + return 0; + + sym_ref = XEXP (sym_ref, 0); + } + + if (GET_CODE (sym_ref) != SYMBOL_REF) + return 0; + + if (call_cookie & CALL_SHORT) + return 0; + + if (TARGET_LONG_CALLS && flag_function_sections) + return 1; + + if (current_file_function_operand (sym_ref)) + return 0; + + return (call_cookie & CALL_LONG) + || ENCODED_LONG_CALL_ATTR_P (XSTR (sym_ref, 0)) + || TARGET_LONG_CALLS; +} + +/* Return non-zero if it is ok to make a tail-call to DECL. */ + +int +arm_function_ok_for_sibcall (decl) + tree decl; +{ + int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL; + + /* Never tailcall something for which we have no decl, or if we + are in Thumb mode. */ + if (decl == NULL || TARGET_THUMB) + return 0; + + /* Get the calling method. */ + if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) + call_type = CALL_SHORT; + else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) + call_type = CALL_LONG; + + /* Cannot tail-call to long calls, since these are out of range of + a branch instruction. However, if not compiling PIC, we know + we can reach the symbol if it is in this compilation unit. */ + if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl))) + return 0; + + /* If we are interworking and the function is not declared static + then we can't tail-call it unless we know that it exists in this + compilation unit (since it might be a Thumb routine). */ + if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl)) + return 0; + + /* Never tailcall from an ISR routine - it needs a special exit sequence. */ + if (IS_INTERRUPT (arm_current_func_type ())) + return 0; + + /* Everything else is ok. */ + return 1; +} + + +int +legitimate_pic_operand_p (x) + rtx x; +{ + if (CONSTANT_P (x) + && flag_pic + && (GET_CODE (x) == SYMBOL_REF + || (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF))) + return 0; + + return 1; +} + +rtx +legitimize_pic_address (orig, mode, reg) + rtx orig; + enum machine_mode mode; + rtx reg; +{ + if (GET_CODE (orig) == SYMBOL_REF + || GET_CODE (orig) == LABEL_REF) + { +#ifndef AOF_ASSEMBLER + rtx pic_ref, address; +#endif + rtx insn; + int subregs = 0; + + if (reg == 0) + { + if (no_new_pseudos) + abort (); + else + reg = gen_reg_rtx (Pmode); + + subregs = 1; + } + +#ifdef AOF_ASSEMBLER + /* The AOF assembler can generate relocations for these directly, and + understands that the PIC register has to be added into the offset. */ + insn = emit_insn (gen_pic_load_addr_based (reg, orig)); +#else + if (subregs) + address = gen_reg_rtx (Pmode); + else + address = reg; + + if (TARGET_ARM) + emit_insn (gen_pic_load_addr_arm (address, orig)); + else + emit_insn (gen_pic_load_addr_thumb (address, orig)); + + if (GET_CODE (orig) == LABEL_REF && NEED_GOT_RELOC) + pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, address); + else + { + pic_ref = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, pic_offset_table_rtx, + address)); + RTX_UNCHANGING_P (pic_ref) = 1; + } + + insn = emit_move_insn (reg, pic_ref); +#endif + current_function_uses_pic_offset_table = 1; + /* Put a REG_EQUAL note on this insn, so that it can be optimized + by loop. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, + REG_NOTES (insn)); + return reg; + } + else if (GET_CODE (orig) == CONST) + { + rtx base, offset; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + { + if (no_new_pseudos) + abort (); + else + reg = gen_reg_rtx (Pmode); + } + + if (GET_CODE (XEXP (orig, 0)) == PLUS) + { + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); + } + else + abort (); + + if (GET_CODE (offset) == CONST_INT) + { + /* The base register doesn't really matter, we only want to + test the index for the appropriate mode. */ + ARM_GO_IF_LEGITIMATE_INDEX (mode, 0, offset, win); + + if (!no_new_pseudos) + offset = force_reg (Pmode, offset); + else + abort (); + + win: + if (GET_CODE (offset) == CONST_INT) + return plus_constant (base, INTVAL (offset)); + } + + if (GET_MODE_SIZE (mode) > 4 + && (GET_MODE_CLASS (mode) == MODE_INT + || TARGET_SOFT_FLOAT)) + { + emit_insn (gen_addsi3 (reg, base, offset)); + return reg; + } + + return gen_rtx_PLUS (Pmode, base, offset); + } + + return orig; +} + +/* Generate code to load the PIC register. PROLOGUE is true if + called from arm_expand_prologue (in which case we want the + generated insns at the start of the function); false if called + by an exception receiver that needs the PIC register reloaded + (in which case the insns are just dumped at the current location). */ + +void +arm_finalize_pic (prologue) + int prologue ATTRIBUTE_UNUSED; +{ +#ifndef AOF_ASSEMBLER + rtx l1, pic_tmp, pic_tmp2, seq, pic_rtx; + rtx global_offset_table; + + if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE) + return; + + if (!flag_pic) + abort (); + + start_sequence (); + l1 = gen_label_rtx (); + + global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + /* On the ARM the PC register contains 'dot + 8' at the time of the + addition, on the Thumb it is 'dot + 4'. */ + pic_tmp = plus_constant (gen_rtx_LABEL_REF (Pmode, l1), TARGET_ARM ? 8 : 4); + if (GOT_PCREL) + pic_tmp2 = gen_rtx_CONST (VOIDmode, + gen_rtx_PLUS (Pmode, global_offset_table, pc_rtx)); + else + pic_tmp2 = gen_rtx_CONST (VOIDmode, global_offset_table); + + pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp)); + + if (TARGET_ARM) + { + emit_insn (gen_pic_load_addr_arm (pic_offset_table_rtx, pic_rtx)); + emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1)); + } + else + { + emit_insn (gen_pic_load_addr_thumb (pic_offset_table_rtx, pic_rtx)); + emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1)); + } + + seq = gen_sequence (); + end_sequence (); + if (prologue) + emit_insn_after (seq, get_insns ()); + else + emit_insn (seq); + + /* Need to emit this whether or not we obey regdecls, + since setjmp/longjmp can cause life info to screw up. */ + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); +#endif /* AOF_ASSEMBLER */ +} + +#define REG_OR_SUBREG_REG(X) \ + (GET_CODE (X) == REG \ + || (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG)) + +#define REG_OR_SUBREG_RTX(X) \ + (GET_CODE (X) == REG ? (X) : SUBREG_REG (X)) + +#ifndef COSTS_N_INSNS +#define COSTS_N_INSNS(N) ((N) * 4 - 2) +#endif + +int +arm_rtx_costs (x, code, outer) + rtx x; + enum rtx_code code; + enum rtx_code outer; +{ + enum machine_mode mode = GET_MODE (x); + enum rtx_code subcode; + int extra_cost; + + if (TARGET_THUMB) + { + switch (code) + { + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + case ROTATERT: + case PLUS: + case MINUS: + case COMPARE: + case NEG: + case NOT: + return COSTS_N_INSNS (1); + + case MULT: + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + int cycles = 0; + unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1)); + + while (i) + { + i >>= 2; + cycles++; + } + return COSTS_N_INSNS (2) + cycles; + } + return COSTS_N_INSNS (1) + 16; + + case SET: + return (COSTS_N_INSNS (1) + + 4 * ((GET_CODE (SET_SRC (x)) == MEM) + + GET_CODE (SET_DEST (x)) == MEM)); + + case CONST_INT: + if (outer == SET) + { + if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256) + return 0; + if (thumb_shiftable_const (INTVAL (x))) + return COSTS_N_INSNS (2); + return COSTS_N_INSNS (3); + } + else if (outer == PLUS + && INTVAL (x) < 256 && INTVAL (x) > -256) + return 0; + else if (outer == COMPARE + && (unsigned HOST_WIDE_INT) INTVAL (x) < 256) + return 0; + else if (outer == ASHIFT || outer == ASHIFTRT + || outer == LSHIFTRT) + return 0; + return COSTS_N_INSNS (2); + + case CONST: + case CONST_DOUBLE: + case LABEL_REF: + case SYMBOL_REF: + return COSTS_N_INSNS (3); + + case UDIV: + case UMOD: + case DIV: + case MOD: + return 100; + + case TRUNCATE: + return 99; + + case AND: + case XOR: + case IOR: + /* XXX guess. */ + return 8; + + case ADDRESSOF: + case MEM: + /* XXX another guess. */ + /* Memory costs quite a lot for the first word, but subsequent words + load at the equivalent of a single insn each. */ + return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) + + (CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); + + case IF_THEN_ELSE: + /* XXX a guess. */ + if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) + return 14; + return 2; + + case ZERO_EXTEND: + /* XXX still guessing. */ + switch (GET_MODE (XEXP (x, 0))) + { + case QImode: + return (1 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case HImode: + return (4 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case SImode: + return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + default: + return 99; + } + + default: + return 99; +#if 0 + case FFS: + case FLOAT: + case FIX: + case UNSIGNED_FIX: + /* XXX guess */ + fprintf (stderr, "unexpected code for thumb in rtx_costs: %s\n", + rtx_name[code]); + abort (); +#endif + } + } + + switch (code) + { + case MEM: + /* Memory costs quite a lot for the first word, but subsequent words + load at the equivalent of a single insn each. */ + return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) + + (CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); + + case DIV: + case MOD: + return 100; + + case ROTATE: + if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG) + return 4; + /* Fall through */ + case ROTATERT: + if (mode != SImode) + return 8; + /* Fall through */ + case ASHIFT: case LSHIFTRT: case ASHIFTRT: + if (mode == DImode) + return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8) + + ((GET_CODE (XEXP (x, 0)) == REG + || (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) + ? 0 : 8)); + return (1 + ((GET_CODE (XEXP (x, 0)) == REG + || (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) + ? 0 : 4) + + ((GET_CODE (XEXP (x, 1)) == REG + || (GET_CODE (XEXP (x, 1)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG) + || (GET_CODE (XEXP (x, 1)) == CONST_INT)) + ? 0 : 4)); + + case MINUS: + if (mode == DImode) + return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 0)) + || (GET_CODE (XEXP (x, 0)) == CONST_INT + && const_ok_for_arm (INTVAL (XEXP (x, 0))))) + ? 0 : 8)); + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) + ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 0)) + || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 0)))) + ? 0 : 8)); + + if (((GET_CODE (XEXP (x, 0)) == CONST_INT + && const_ok_for_arm (INTVAL (XEXP (x, 0))) + && REG_OR_SUBREG_REG (XEXP (x, 1)))) + || (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT + || subcode == ASHIFTRT || subcode == LSHIFTRT + || subcode == ROTATE || subcode == ROTATERT + || (subcode == MULT + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && ((INTVAL (XEXP (XEXP (x, 1), 1)) & + (INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0))) + && REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0)) + && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1)) + || GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT) + && REG_OR_SUBREG_REG (XEXP (x, 0)))) + return 1; + /* Fall through */ + + case PLUS: + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) + ? 0 : 8)); + + /* Fall through */ + case AND: case XOR: case IOR: + extra_cost = 0; + + /* Normally the frame registers will be spilt into reg+const during + reload, so it is a bad idea to combine them with other instructions, + since then they might not be moved outside of loops. As a compromise + we allow integration with ops that have a constant as their second + operand. */ + if ((REG_OR_SUBREG_REG (XEXP (x, 0)) + && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))) + && GET_CODE (XEXP (x, 1)) != CONST_INT) + || (REG_OR_SUBREG_REG (XEXP (x, 0)) + && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))))) + extra_cost = 4; + + if (mode == DImode) + return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_INT + && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) + ? 0 : 8)); + + if (REG_OR_SUBREG_REG (XEXP (x, 0))) + return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_INT + && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) + ? 0 : 4)); + + else if (REG_OR_SUBREG_REG (XEXP (x, 1))) + return (1 + extra_cost + + ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT + || subcode == LSHIFTRT || subcode == ASHIFTRT + || subcode == ROTATE || subcode == ROTATERT + || (subcode == MULT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ((INTVAL (XEXP (XEXP (x, 0), 1)) & + (INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0))) + && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0))) + && ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1))) + || GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)) + ? 0 : 4)); + + return 8; + + case MULT: + /* There is no point basing this on the tuning, since it is always the + fast variant if it exists at all. */ + 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) + { + 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; + + /* Tune as appropriate. */ + int booth_unit_size = ((tune_flags & FL_FAST_MULT) ? 8 : 2); + + for (j = 0; i && j < 32; j += booth_unit_size) + { + i >>= booth_unit_size; + add_cost += 2; + } + + return add_cost; + } + + return (((tune_flags & FL_FAST_MULT) ? 8 : 30) + + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4) + + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4)); + + case TRUNCATE: + if (arm_fast_multiply && mode == SImode + && GET_CODE (XEXP (x, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT + && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) + == GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1))) + && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND + || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND)) + return 8; + return 99; + + case NEG: + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6); + /* Fall through */ + case NOT: + if (mode == DImode) + return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); + + return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); + + case IF_THEN_ELSE: + if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) + return 14; + return 2; + + case COMPARE: + return 1; + + case ABS: + return 4 + (mode == DImode ? 4 : 0); + + case SIGN_EXTEND: + if (GET_MODE (XEXP (x, 0)) == QImode) + return (4 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + /* Fall through */ + case ZERO_EXTEND: + switch (GET_MODE (XEXP (x, 0))) + { + case QImode: + return (1 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case HImode: + return (4 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case SImode: + return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + default: + break; + } + abort (); + + case CONST_INT: + if (const_ok_for_arm (INTVAL (x))) + return outer == SET ? 2 : -1; + else if (outer == AND + && const_ok_for_arm (~INTVAL (x))) + return -1; + else if ((outer == COMPARE + || outer == PLUS || outer == MINUS) + && const_ok_for_arm (-INTVAL (x))) + return -1; + else + return 5; + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + return 6; + + case CONST_DOUBLE: + if (const_double_rtx_ok_for_fpu (x)) + return outer == SET ? 2 : -1; + else if ((outer == COMPARE || outer == PLUS) + && neg_const_double_rtx_ok_for_fpu (x)) + return -1; + return 7; + + default: + return 99; + } +} + +static int +arm_adjust_cost (insn, link, dep, cost) + rtx insn; + rtx link; + rtx dep; + int cost; +{ + rtx i_pat, d_pat; + + /* Some true dependencies can have a higher cost depending + on precisely how certain input operands are used. */ + if (arm_is_xscale + && REG_NOTE_KIND (link) == 0 + && recog_memoized (insn) < 0 + && recog_memoized (dep) < 0) + { + int shift_opnum = get_attr_shift (insn); + enum attr_type attr_type = get_attr_type (dep); + + /* If nonzero, SHIFT_OPNUM contains the operand number of a shifted + operand for INSN. If we have a shifted input operand and the + instruction we depend on is another ALU instruction, then we may + have to account for an additional stall. */ + if (shift_opnum != 0 && attr_type == TYPE_NORMAL) + { + rtx shifted_operand; + int opno; + + /* Get the shifted operand. */ + extract_insn (insn); + shifted_operand = recog_data.operand[shift_opnum]; + + /* Iterate over all the operands in DEP. If we write an operand + that overlaps with SHIFTED_OPERAND, then we have increase the + cost of this dependency. */ + extract_insn (dep); + preprocess_constraints (); + for (opno = 0; opno < recog_data.n_operands; opno++) + { + /* We can ignore strict inputs. */ + if (recog_data.operand_type[opno] == OP_IN) + continue; + + if (reg_overlap_mentioned_p (recog_data.operand[opno], + shifted_operand)) + return 2; + } + } + } + + /* XXX This is not strictly true for the FPA. */ + if (REG_NOTE_KIND (link) == REG_DEP_ANTI + || REG_NOTE_KIND (link) == REG_DEP_OUTPUT) + return 0; + + /* Call insns don't incur a stall, even if they follow a load. */ + if (REG_NOTE_KIND (link) == 0 + && GET_CODE (insn) == CALL_INSN) + return 1; + + if ((i_pat = single_set (insn)) != NULL + && GET_CODE (SET_SRC (i_pat)) == MEM + && (d_pat = single_set (dep)) != NULL + && GET_CODE (SET_DEST (d_pat)) == MEM) + { + /* This is a load after a store, there is no conflict if the load reads + from a cached area. Assume that loads from the stack, and from the + constant pool are cached, and that others will miss. This is a + hack. */ + + if (CONSTANT_POOL_ADDRESS_P (XEXP (SET_SRC (i_pat), 0)) + || reg_mentioned_p (stack_pointer_rtx, XEXP (SET_SRC (i_pat), 0)) + || reg_mentioned_p (frame_pointer_rtx, XEXP (SET_SRC (i_pat), 0)) + || reg_mentioned_p (hard_frame_pointer_rtx, + XEXP (SET_SRC (i_pat), 0))) + return 1; + } + + return cost; +} + +/* This code has been fixed for cross compilation. */ + +static int fpa_consts_inited = 0; + +static const char * const strings_fpa[8] = +{ + "0", "1", "2", "3", + "4", "5", "0.5", "10" +}; + +static REAL_VALUE_TYPE values_fpa[8]; + +static void +init_fpa_table () +{ + int i; + REAL_VALUE_TYPE r; + + for (i = 0; i < 8; i++) + { + r = REAL_VALUE_ATOF (strings_fpa[i], DFmode); + values_fpa[i] = r; + } + + fpa_consts_inited = 1; +} + +/* Return TRUE if rtx X is a valid immediate FPU constant. */ + +int +const_double_rtx_ok_for_fpu (x) + rtx x; +{ + REAL_VALUE_TYPE r; + int i; + + if (!fpa_consts_inited) + init_fpa_table (); + + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + if (REAL_VALUE_MINUS_ZERO (r)) + return 0; + + for (i = 0; i < 8; i++) + if (REAL_VALUES_EQUAL (r, values_fpa[i])) + return 1; + + return 0; +} + +/* Return TRUE if rtx X is a valid immediate FPU constant. */ + +int +neg_const_double_rtx_ok_for_fpu (x) + rtx x; +{ + REAL_VALUE_TYPE r; + int i; + + if (!fpa_consts_inited) + init_fpa_table (); + + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + r = REAL_VALUE_NEGATE (r); + if (REAL_VALUE_MINUS_ZERO (r)) + return 0; + + for (i = 0; i < 8; i++) + if (REAL_VALUES_EQUAL (r, values_fpa[i])) + return 1; + + return 0; +} + +/* Predicates for `match_operand' and `match_operator'. */ + +/* s_register_operand is the same as register_operand, but it doesn't accept + (SUBREG (MEM)...). + + This function exists because at the time it was put in it led to better + code. SUBREG(MEM) always needs a reload in the places where + s_register_operand is used, and this seemed to lead to excessive + reloading. */ + +int +s_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + /* XXX might have to check for lo regs only for thumb ??? */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); +} + +/* A hard register operand (even before reload. */ + +int +arm_hard_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + return (GET_CODE (op) == REG + && REGNO (op) < FIRST_PSEUDO_REGISTER); +} + +/* Only accept reg, subreg(reg), const_int. */ + +int +reg_or_int_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT) + return 1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); +} + +/* Return 1 if OP is an item in memory, given that we are in reload. */ + +int +arm_reload_memory_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int regno = true_regnum (op); + + return (!CONSTANT_P (op) + && (regno == -1 + || (GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER))); +} + +/* Return 1 if OP is a valid memory address, but not valid for a signed byte + memory access (architecture V4). + MODE is QImode if called when computing constraints, or VOIDmode when + emitting patterns. In this latter case we cannot use memory_operand() + because it will fail on badly formed MEMs, which is precisly what we are + trying to catch. */ + +int +bad_signed_byte_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ +#if 0 + if ((mode == QImode && !memory_operand (op, mode)) || GET_CODE (op) != MEM) + return 0; +#endif + if (GET_CODE (op) != MEM) + return 0; + + op = XEXP (op, 0); + + /* A sum of anything more complex than reg + reg or reg + const is bad. */ + if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS) + && (!s_register_operand (XEXP (op, 0), VOIDmode) + || (!s_register_operand (XEXP (op, 1), VOIDmode) + && GET_CODE (XEXP (op, 1)) != CONST_INT))) + return 1; + + /* Big constants are also bad. */ + if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT + && (INTVAL (XEXP (op, 1)) > 0xff + || -INTVAL (XEXP (op, 1)) > 0xff)) + return 1; + + /* Everything else is good, or can will automatically be made so. */ + return 0; +} + +/* Return TRUE for valid operands for the rhs of an ARM instruction. */ + +int +arm_rhs_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (s_register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); +} + +/* Return TRUE for valid operands for the + rhs of an ARM instruction, or a load. */ + +int +arm_rhsm_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (s_register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))) + || memory_operand (op, mode)); +} + +/* Return TRUE for valid operands for the rhs of an ARM instruction, or if a + constant that is valid when negated. */ + +int +arm_add_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (TARGET_THUMB) + return thumb_cmp_operand (op, mode); + + return (s_register_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && (const_ok_for_arm (INTVAL (op)) + || const_ok_for_arm (-INTVAL (op))))); +} + +int +arm_not_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (s_register_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && (const_ok_for_arm (INTVAL (op)) + || const_ok_for_arm (~INTVAL (op))))); +} + +/* Return TRUE if the operand is a memory reference which contains an + offsettable address. */ + +int +offsettable_memory_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode == VOIDmode) + mode = GET_MODE (op); + + return (mode == GET_MODE (op) + && GET_CODE (op) == MEM + && offsettable_address_p (reload_completed | reload_in_progress, + mode, XEXP (op, 0))); +} + +/* Return TRUE if the operand is a memory reference which is, or can be + made word aligned by adjusting the offset. */ + +int +alignable_memory_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx reg; + + if (mode == VOIDmode) + mode = GET_MODE (op); + + if (mode != GET_MODE (op) || GET_CODE (op) != MEM) + return 0; + + op = XEXP (op, 0); + + return ((GET_CODE (reg = op) == REG + || (GET_CODE (op) == SUBREG + && GET_CODE (reg = SUBREG_REG (op)) == REG) + || (GET_CODE (op) == PLUS + && GET_CODE (XEXP (op, 1)) == CONST_INT + && (GET_CODE (reg = XEXP (op, 0)) == REG + || (GET_CODE (XEXP (op, 0)) == SUBREG + && GET_CODE (reg = SUBREG_REG (XEXP (op, 0))) == REG)))) + && REGNO_POINTER_ALIGN (REGNO (reg)) >= 32); +} + +/* Similar to s_register_operand, but does not allow hard integer + registers. */ + +int +f_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) == FPU_REGS)); +} + +/* Return TRUE for valid operands for the rhs of an FPU instruction. */ + +int +fpu_rhs_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == CONST_DOUBLE) + return const_double_rtx_ok_for_fpu (op); + + return FALSE; +} + +int +fpu_add_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == CONST_DOUBLE) + return (const_double_rtx_ok_for_fpu (op) + || neg_const_double_rtx_ok_for_fpu (op)); + + return FALSE; +} + +/* Return nonzero if OP is a constant power of two. */ + +int +power_of_two_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (op); + + return value != 0 && (value & (value - 1)) == 0; + } + + return FALSE; +} + +/* Return TRUE for a valid operand of a DImode operation. + Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). + Note that this disallows MEM(REG+REG), but allows + MEM(PRE/POST_INC/DEC(REG)). */ + +int +di_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + switch (GET_CODE (op)) + { + case CONST_DOUBLE: + case CONST_INT: + return TRUE; + + case MEM: + return memory_address_p (DImode, XEXP (op, 0)); + + default: + return FALSE; + } +} + +/* Like di_operand, but don't accept constants. */ + +int +nonimmediate_di_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == MEM) + return memory_address_p (DImode, XEXP (op, 0)); + + return FALSE; +} + +/* Return TRUE for a valid operand of a DFmode operation when -msoft-float. + Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). + Note that this disallows MEM(REG+REG), but allows + MEM(PRE/POST_INC/DEC(REG)). */ + +int +soft_df_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (mode != VOIDmode && GET_MODE (op) != mode) + return FALSE; + + if (GET_CODE (op) == SUBREG && CONSTANT_P (SUBREG_REG (op))) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + switch (GET_CODE (op)) + { + case CONST_DOUBLE: + return TRUE; + + case MEM: + return memory_address_p (DFmode, XEXP (op, 0)); + + default: + return FALSE; + } +} + +/* Like soft_df_operand, but don't accept constants. */ + +int +nonimmediate_soft_df_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (s_register_operand (op, mode)) + return TRUE; + + if (mode != VOIDmode && GET_MODE (op) != mode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == MEM) + return memory_address_p (DFmode, XEXP (op, 0)); + return FALSE; +} + +/* Return TRUE for valid index operands. */ + +int +index_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (s_register_operand (op, mode) + || (immediate_operand (op, mode) + && (GET_CODE (op) != CONST_INT + || (INTVAL (op) < 4096 && INTVAL (op) > -4096)))); +} + +/* Return TRUE for valid shifts by a constant. This also accepts any + power of two on the (somewhat overly relaxed) assumption that the + shift operator in this case was a mult. */ + +int +const_shift_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (power_of_two_operand (op, mode) + || (immediate_operand (op, mode) + && (GET_CODE (op) != CONST_INT + || (INTVAL (op) < 32 && INTVAL (op) > 0)))); +} + +/* Return TRUE for arithmetic operators which can be combined with a multiply + (shift). */ + +int +shiftable_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code; + + if (GET_MODE (x) != mode) + return FALSE; + + code = GET_CODE (x); + + return (code == PLUS || code == MINUS + || code == IOR || code == XOR || code == AND); +} + +/* Return TRUE for binary logical operators. */ + +int +logical_binary_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code; + + if (GET_MODE (x) != mode) + return FALSE; + + code = GET_CODE (x); + + return (code == IOR || code == XOR || code == AND); +} + +/* Return TRUE for shift operators. */ + +int +shift_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code; + + if (GET_MODE (x) != mode) + return FALSE; + + code = GET_CODE (x); + + if (code == MULT) + return power_of_two_operand (XEXP (x, 1), mode); + + return (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT + || code == ROTATERT); +} + +/* Return TRUE if x is EQ or NE. */ + +int +equality_operator (x, mode) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return GET_CODE (x) == EQ || GET_CODE (x) == NE; +} + +/* Return TRUE if x is a comparison operator other than LTGT or UNEQ. */ + +int +arm_comparison_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + return (comparison_operator (x, mode) + && GET_CODE (x) != LTGT + && GET_CODE (x) != UNEQ); +} + +/* Return TRUE for SMIN SMAX UMIN UMAX operators. */ + +int +minmax_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (GET_MODE (x) != mode) + return FALSE; + + return code == SMIN || code == SMAX || code == UMIN || code == UMAX; +} + +/* Return TRUE if this is the condition code register, if we aren't given + a mode, accept any class CCmode register. */ + +int +cc_register (x, mode) + rtx x; + enum machine_mode mode; +{ + if (mode == VOIDmode) + { + mode = GET_MODE (x); + + if (GET_MODE_CLASS (mode) != MODE_CC) + return FALSE; + } + + if ( GET_MODE (x) == mode + && GET_CODE (x) == REG + && REGNO (x) == CC_REGNUM) + return TRUE; + + return FALSE; +} + +/* Return TRUE if this is the condition code register, if we aren't given + a mode, accept any class CCmode register which indicates a dominance + expression. */ + +int +dominant_cc_register (x, mode) + rtx x; + enum machine_mode mode; +{ + if (mode == VOIDmode) + { + mode = GET_MODE (x); + + if (GET_MODE_CLASS (mode) != MODE_CC) + return FALSE; + } + + if ( mode != CC_DNEmode && mode != CC_DEQmode + && mode != CC_DLEmode && mode != CC_DLTmode + && mode != CC_DGEmode && mode != CC_DGTmode + && mode != CC_DLEUmode && mode != CC_DLTUmode + && mode != CC_DGEUmode && mode != CC_DGTUmode) + return FALSE; + + return cc_register (x, mode); +} + +/* Return TRUE if X references a SYMBOL_REF. */ + +int +symbol_mentioned_p (x) + rtx x; +{ + const char * fmt; + 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') + { + 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; +{ + const char * fmt; + 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') + { + 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; +{ + enum rtx_code code = GET_CODE (x); + + if (code == SMAX) + return GE; + else if (code == SMIN) + return LE; + else if (code == UMIN) + return LEU; + else if (code == UMAX) + return GEU; + + abort (); +} + +/* Return 1 if memory locations are adjacent. */ + +int +adjacent_mem_locations (a, b) + rtx a, b; +{ + if ((GET_CODE (XEXP (a, 0)) == REG + || (GET_CODE (XEXP (a, 0)) == PLUS + && GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT)) + && (GET_CODE (XEXP (b, 0)) == REG + || (GET_CODE (XEXP (b, 0)) == PLUS + && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT))) + { + int val0 = 0, val1 = 0; + int reg0, reg1; + + if (GET_CODE (XEXP (a, 0)) == PLUS) + { + reg0 = REGNO (XEXP (XEXP (a, 0), 0)); + val0 = INTVAL (XEXP (XEXP (a, 0), 1)); + } + else + reg0 = REGNO (XEXP (a, 0)); + + if (GET_CODE (XEXP (b, 0)) == PLUS) + { + reg1 = REGNO (XEXP (XEXP (b, 0), 0)); + val1 = INTVAL (XEXP (XEXP (b, 0), 1)); + } + else + reg1 = REGNO (XEXP (b, 0)); + + return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4); + } + return 0; +} + +/* Return 1 if OP is a load multiple operation. It is known to be + parallel and the first section will be tested. */ + +int +load_multiple_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT count = XVECLEN (op, 0); + int dest_regno; + rtx src_addr; + HOST_WIDE_INT i = 1, base = 0; + rtx elt; + + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET) + return 0; + + /* Check to see if this might be a write-back. */ + if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) + { + i++; + base = 1; + + /* Now check it more carefully. */ + if (GET_CODE (SET_DEST (elt)) != REG + || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG + || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt)) + || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT + || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) + return 0; + } + + /* Perform a quick check so we don't blow up below. */ + if (count <= i + || GET_CODE (XVECEXP (op, 0, i - 1)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM) + return 0; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1))); + src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0); + + for (; i < count; i++) + { + elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_DEST (elt)) != REG + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != (unsigned int)(dest_regno + i - base) + || GET_CODE (SET_SRC (elt)) != MEM + || GET_MODE (SET_SRC (elt)) != SImode + || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS + || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) + || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4) + return 0; + } + + return 1; +} + +/* Return 1 if OP is a store multiple operation. It is known to be + parallel and the first section will be tested. */ + +int +store_multiple_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT count = XVECLEN (op, 0); + int src_regno; + rtx dest_addr; + HOST_WIDE_INT i = 1, base = 0; + rtx elt; + + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET) + return 0; + + /* Check to see if this might be a write-back. */ + if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) + { + i++; + base = 1; + + /* Now check it more carefully. */ + if (GET_CODE (SET_DEST (elt)) != REG + || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG + || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt)) + || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT + || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) + return 0; + } + + /* Perform a quick check so we don't blow up below. */ + if (count <= i + || GET_CODE (XVECEXP (op, 0, i - 1)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM + || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG) + return 0; + + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1))); + dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0); + + for (; i < count; i++) + { + elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned int)(src_regno + i - base) + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) + || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4) + return 0; + } + + return 1; +} + +int +load_multiple_sequence (operands, nops, regs, base, load_offset) + rtx * operands; + int nops; + int * regs; + int * base; + HOST_WIDE_INT * load_offset; +{ + int unsorted_regs[4]; + HOST_WIDE_INT unsorted_offsets[4]; + int order[4]; + int base_reg = -1; + int i; + + /* Can only handle 2, 3, or 4 insns at present, + though could be easily extended if required. */ + if (nops < 2 || nops > 4) + abort (); + + /* Loop over the operands and check that the memory references are + suitable (ie immediate offsets from the same base register). At + the same time, extract the target register, and the memory + offsets. */ + for (i = 0; i < nops; i++) + { + rtx reg; + rtx offset; + + /* Convert a subreg of a mem into the mem itself. */ + if (GET_CODE (operands[nops + i]) == SUBREG) + operands[nops + i] = alter_subreg (operands + (nops + i)); + + if (GET_CODE (operands[nops + i]) != MEM) + abort (); + + /* Don't reorder volatile memory references; it doesn't seem worth + looking for the case where the order is ok anyway. */ + if (MEM_VOLATILE_P (operands[nops + i])) + return 0; + + offset = const0_rtx; + + if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG + || (GET_CODE (reg) == SUBREG + && GET_CODE (reg = SUBREG_REG (reg)) == REG)) + || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS + && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) + == REG) + || (GET_CODE (reg) == SUBREG + && GET_CODE (reg = SUBREG_REG (reg)) == REG)) + && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) + == CONST_INT))) + { + if (i == 0) + { + base_reg = REGNO (reg); + unsorted_regs[0] = (GET_CODE (operands[i]) == REG + ? REGNO (operands[i]) + : REGNO (SUBREG_REG (operands[i]))); + order[0] = 0; + } + else + { + if (base_reg != (int) REGNO (reg)) + /* Not addressed from the same base register. */ + return 0; + + unsorted_regs[i] = (GET_CODE (operands[i]) == REG + ? REGNO (operands[i]) + : REGNO (SUBREG_REG (operands[i]))); + if (unsorted_regs[i] < unsorted_regs[order[0]]) + order[0] = i; + } + + /* If it isn't an integer register, or if it overwrites the + base register but isn't the last insn in the list, then + we can't do this. */ + if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14 + || (i != nops - 1 && unsorted_regs[i] == base_reg)) + return 0; + + unsorted_offsets[i] = INTVAL (offset); + } + else + /* Not a suitable memory address. */ + return 0; + } + + /* All the useful information has now been extracted from the + operands into unsorted_regs and unsorted_offsets; additionally, + order[0] has been set to the lowest numbered register in the + list. Sort the registers into order, and check that the memory + offsets are ascending and adjacent. */ + + for (i = 1; i < nops; i++) + { + int j; + + order[i] = order[i - 1]; + for (j = 0; j < nops; j++) + if (unsorted_regs[j] > unsorted_regs[order[i - 1]] + && (order[i] == order[i - 1] + || unsorted_regs[j] < unsorted_regs[order[i]])) + order[i] = j; + + /* Have we found a suitable register? if not, one must be used more + than once. */ + if (order[i] == order[i - 1]) + return 0; + + /* Is the memory address adjacent and ascending? */ + if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) + return 0; + } + + if (base) + { + *base = base_reg; + + for (i = 0; i < nops; i++) + regs[i] = unsorted_regs[order[i]]; + + *load_offset = unsorted_offsets[order[0]]; + } + + if (unsorted_offsets[order[0]] == 0) + return 1; /* ldmia */ + + if (unsorted_offsets[order[0]] == 4) + return 2; /* ldmib */ + + if (unsorted_offsets[order[nops - 1]] == 0) + return 3; /* ldmda */ + + if (unsorted_offsets[order[nops - 1]] == -4) + return 4; /* ldmdb */ + + /* For ARM8,9 & StrongARM, 2 ldr instructions are faster than an ldm + if the offset isn't small enough. The reason 2 ldrs are faster + is because these ARMs are able to do more than one cache access + in a single cycle. The ARM9 and StrongARM have Harvard caches, + whilst the ARM8 has a double bandwidth cache. This means that + these cores can do both an instruction fetch and a data fetch in + a single cycle, so the trick of calculating the address into a + scratch register (one of the result regs) and then doing a load + multiple actually becomes slower (and no smaller in code size). + That is the transformation + + ldr rd1, [rbase + offset] + ldr rd2, [rbase + offset + 4] + + to + + add rd1, rbase, offset + ldmia rd1, {rd1, rd2} + + produces worse code -- '3 cycles + any stalls on rd2' instead of + '2 cycles + any stalls on rd2'. On ARMs with only one cache + access per cycle, the first sequence could never complete in less + than 6 cycles, whereas the ldm sequence would only take 5 and + would make better use of sequential accesses if not hitting the + cache. + + We cheat here and test 'arm_ld_sched' which we currently know to + only be true for the ARM8, ARM9 and StrongARM. If this ever + changes, then the test below needs to be reworked. */ + if (nops == 2 && arm_ld_sched) + return 0; + + /* Can't do it without setting up the offset, only do this if it takes + no more than one insn. */ + return (const_ok_for_arm (unsorted_offsets[order[0]]) + || const_ok_for_arm (-unsorted_offsets[order[0]])) ? 5 : 0; +} + +const char * +emit_ldm_seq (operands, nops) + rtx * operands; + int nops; +{ + int regs[4]; + int base_reg; + HOST_WIDE_INT offset; + char buf[100]; + int i; + + switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset)) + { + case 1: + strcpy (buf, "ldm%?ia\t"); + break; + + case 2: + strcpy (buf, "ldm%?ib\t"); + break; + + case 3: + strcpy (buf, "ldm%?da\t"); + break; + + case 4: + strcpy (buf, "ldm%?db\t"); + break; + + case 5: + if (offset >= 0) + sprintf (buf, "add%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, + reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], + (long) offset); + else + sprintf (buf, "sub%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, + reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], + (long) -offset); + output_asm_insn (buf, operands); + base_reg = regs[0]; + strcpy (buf, "ldm%?ia\t"); + break; + + default: + abort (); + } + + sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, + reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); + + for (i = 1; i < nops; i++) + sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, + reg_names[regs[i]]); + + strcat (buf, "}\t%@ phole ldm"); + + output_asm_insn (buf, operands); + return ""; +} + +int +store_multiple_sequence (operands, nops, regs, base, load_offset) + rtx * operands; + int nops; + int * regs; + int * base; + HOST_WIDE_INT * load_offset; +{ + int unsorted_regs[4]; + HOST_WIDE_INT unsorted_offsets[4]; + int order[4]; + int base_reg = -1; + int i; + + /* Can only handle 2, 3, or 4 insns at present, though could be easily + extended if required. */ + if (nops < 2 || nops > 4) + abort (); + + /* Loop over the operands and check that the memory references are + suitable (ie immediate offsets from the same base register). At + the same time, extract the target register, and the memory + offsets. */ + for (i = 0; i < nops; i++) + { + rtx reg; + rtx offset; + + /* Convert a subreg of a mem into the mem itself. */ + if (GET_CODE (operands[nops + i]) == SUBREG) + operands[nops + i] = alter_subreg (operands + (nops + i)); + + if (GET_CODE (operands[nops + i]) != MEM) + abort (); + + /* Don't reorder volatile memory references; it doesn't seem worth + looking for the case where the order is ok anyway. */ + if (MEM_VOLATILE_P (operands[nops + i])) + return 0; + + offset = const0_rtx; + + if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG + || (GET_CODE (reg) == SUBREG + && GET_CODE (reg = SUBREG_REG (reg)) == REG)) + || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS + && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) + == REG) + || (GET_CODE (reg) == SUBREG + && GET_CODE (reg = SUBREG_REG (reg)) == REG)) + && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) + == CONST_INT))) + { + if (i == 0) + { + base_reg = REGNO (reg); + unsorted_regs[0] = (GET_CODE (operands[i]) == REG + ? REGNO (operands[i]) + : REGNO (SUBREG_REG (operands[i]))); + order[0] = 0; + } + else + { + if (base_reg != (int) REGNO (reg)) + /* Not addressed from the same base register. */ + return 0; + + unsorted_regs[i] = (GET_CODE (operands[i]) == REG + ? REGNO (operands[i]) + : REGNO (SUBREG_REG (operands[i]))); + if (unsorted_regs[i] < unsorted_regs[order[0]]) + order[0] = i; + } + + /* If it isn't an integer register, then we can't do this. */ + if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14) + return 0; + + unsorted_offsets[i] = INTVAL (offset); + } + else + /* Not a suitable memory address. */ + return 0; + } + + /* All the useful information has now been extracted from the + operands into unsorted_regs and unsorted_offsets; additionally, + order[0] has been set to the lowest numbered register in the + list. Sort the registers into order, and check that the memory + offsets are ascending and adjacent. */ + + for (i = 1; i < nops; i++) + { + int j; + + order[i] = order[i - 1]; + for (j = 0; j < nops; j++) + if (unsorted_regs[j] > unsorted_regs[order[i - 1]] + && (order[i] == order[i - 1] + || unsorted_regs[j] < unsorted_regs[order[i]])) + order[i] = j; + + /* Have we found a suitable register? if not, one must be used more + than once. */ + if (order[i] == order[i - 1]) + return 0; + + /* Is the memory address adjacent and ascending? */ + if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) + return 0; + } + + if (base) + { + *base = base_reg; + + for (i = 0; i < nops; i++) + regs[i] = unsorted_regs[order[i]]; + + *load_offset = unsorted_offsets[order[0]]; + } + + if (unsorted_offsets[order[0]] == 0) + return 1; /* stmia */ + + if (unsorted_offsets[order[0]] == 4) + return 2; /* stmib */ + + if (unsorted_offsets[order[nops - 1]] == 0) + return 3; /* stmda */ + + if (unsorted_offsets[order[nops - 1]] == -4) + return 4; /* stmdb */ + + return 0; +} + +const char * +emit_stm_seq (operands, nops) + rtx * operands; + int nops; +{ + int regs[4]; + int base_reg; + HOST_WIDE_INT offset; + char buf[100]; + int i; + + switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset)) + { + case 1: + strcpy (buf, "stm%?ia\t"); + break; + + case 2: + strcpy (buf, "stm%?ib\t"); + break; + + case 3: + strcpy (buf, "stm%?da\t"); + break; + + case 4: + strcpy (buf, "stm%?db\t"); + break; + + default: + abort (); + } + + sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, + reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); + + for (i = 1; i < nops; i++) + sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, + reg_names[regs[i]]); + + strcat (buf, "}\t%@ phole stm"); + + output_asm_insn (buf, operands); + return ""; +} + +int +multi_register_push (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (GET_CODE (op) != PARALLEL + || (GET_CODE (XVECEXP (op, 0, 0)) != SET) + || (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) + || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSH_MULT)) + return 0; + + return 1; +} + +/* Routines for use in generating RTL. */ + +rtx +arm_gen_load_multiple (base_regno, count, from, up, write_back, unchanging_p, + in_struct_p, scalar_p) + int base_regno; + int count; + rtx from; + int up; + int write_back; + int unchanging_p; + int in_struct_p; + int scalar_p; +{ + int i = 0, j; + rtx result; + int sign = up ? 1 : -1; + rtx mem; + + /* XScale has load-store double instructions, but they have stricter + alignment requirements than load-store multiple, so we can not + use them. + + For XScale ldm requires 2 + NREGS cycles to complete and blocks + the pipeline until completion. + + NREGS CYCLES + 1 3 + 2 4 + 3 5 + 4 6 + + An ldr instruction takes 1-3 cycles, but does not block the + pipeline. + + NREGS CYCLES + 1 1-3 + 2 2-6 + 3 3-9 + 4 4-12 + + Best case ldr will always win. However, the more ldr instructions + we issue, the less likely we are to be able to schedule them well. + Using ldr instructions also increases code size. + + As a compromise, we use ldr for counts of 1 or 2 regs, and ldm + for counts of 3 or 4 regs. */ + if (arm_is_xscale && count <= 2 && ! optimize_size) + { + rtx seq; + + start_sequence (); + + for (i = 0; i < count; i++) + { + mem = gen_rtx_MEM (SImode, plus_constant (from, i * 4 * sign)); + RTX_UNCHANGING_P (mem) = unchanging_p; + MEM_IN_STRUCT_P (mem) = in_struct_p; + MEM_SCALAR_P (mem) = scalar_p; + emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem); + } + + if (write_back) + emit_move_insn (from, plus_constant (from, count * 4 * sign)); + + seq = gen_sequence (); + end_sequence (); + + return seq; + } + + result = gen_rtx_PARALLEL (VOIDmode, + rtvec_alloc (count + (write_back ? 1 : 0))); + if (write_back) + { + XVECEXP (result, 0, 0) + = gen_rtx_SET (GET_MODE (from), from, + plus_constant (from, count * 4 * sign)); + i = 1; + count++; + } + + for (j = 0; i < count; i++, j++) + { + mem = gen_rtx_MEM (SImode, plus_constant (from, j * 4 * sign)); + RTX_UNCHANGING_P (mem) = unchanging_p; + MEM_IN_STRUCT_P (mem) = in_struct_p; + MEM_SCALAR_P (mem) = scalar_p; + XVECEXP (result, 0, i) + = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, base_regno + j), mem); + } + + return result; +} + +rtx +arm_gen_store_multiple (base_regno, count, to, up, write_back, unchanging_p, + in_struct_p, scalar_p) + int base_regno; + int count; + rtx to; + int up; + int write_back; + int unchanging_p; + int in_struct_p; + int scalar_p; +{ + int i = 0, j; + rtx result; + int sign = up ? 1 : -1; + rtx mem; + + /* See arm_gen_load_multiple for discussion of + the pros/cons of ldm/stm usage for XScale. */ + if (arm_is_xscale && count <= 2 && ! optimize_size) + { + rtx seq; + + start_sequence (); + + for (i = 0; i < count; i++) + { + mem = gen_rtx_MEM (SImode, plus_constant (to, i * 4 * sign)); + RTX_UNCHANGING_P (mem) = unchanging_p; + MEM_IN_STRUCT_P (mem) = in_struct_p; + MEM_SCALAR_P (mem) = scalar_p; + emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i)); + } + + if (write_back) + emit_move_insn (to, plus_constant (to, count * 4 * sign)); + + seq = gen_sequence (); + end_sequence (); + + return seq; + } + + result = gen_rtx_PARALLEL (VOIDmode, + rtvec_alloc (count + (write_back ? 1 : 0))); + if (write_back) + { + XVECEXP (result, 0, 0) + = gen_rtx_SET (GET_MODE (to), to, + plus_constant (to, count * 4 * sign)); + i = 1; + count++; + } + + for (j = 0; i < count; i++, j++) + { + mem = gen_rtx_MEM (SImode, plus_constant (to, j * 4 * sign)); + RTX_UNCHANGING_P (mem) = unchanging_p; + MEM_IN_STRUCT_P (mem) = in_struct_p; + MEM_SCALAR_P (mem) = scalar_p; + + XVECEXP (result, 0, i) + = gen_rtx_SET (VOIDmode, mem, gen_rtx_REG (SImode, base_regno + j)); + } + + return result; +} + +int +arm_gen_movstrqi (operands) + rtx * operands; +{ + HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes; + int i; + rtx src, dst; + rtx st_src, st_dst, fin_src, fin_dst; + rtx part_bytes_reg = NULL; + rtx mem; + int dst_unchanging_p, dst_in_struct_p, src_unchanging_p, src_in_struct_p; + int dst_scalar_p, src_scalar_p; + + if (GET_CODE (operands[2]) != CONST_INT + || GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[2]) > 64 + || INTVAL (operands[3]) & 3) + return 0; + + st_dst = XEXP (operands[0], 0); + st_src = XEXP (operands[1], 0); + + dst_unchanging_p = RTX_UNCHANGING_P (operands[0]); + dst_in_struct_p = MEM_IN_STRUCT_P (operands[0]); + dst_scalar_p = MEM_SCALAR_P (operands[0]); + src_unchanging_p = RTX_UNCHANGING_P (operands[1]); + src_in_struct_p = MEM_IN_STRUCT_P (operands[1]); + src_scalar_p = MEM_SCALAR_P (operands[1]); + + fin_dst = dst = copy_to_mode_reg (SImode, st_dst); + fin_src = src = copy_to_mode_reg (SImode, st_src); + + in_words_to_go = NUM_INTS (INTVAL (operands[2])); + out_words_to_go = INTVAL (operands[2]) / 4; + last_bytes = INTVAL (operands[2]) & 3; + + if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0) + part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3); + + for (i = 0; in_words_to_go >= 2; i+=4) + { + if (in_words_to_go > 4) + emit_insn (arm_gen_load_multiple (0, 4, src, TRUE, TRUE, + src_unchanging_p, + src_in_struct_p, + src_scalar_p)); + else + emit_insn (arm_gen_load_multiple (0, in_words_to_go, src, TRUE, + FALSE, src_unchanging_p, + src_in_struct_p, src_scalar_p)); + + if (out_words_to_go) + { + if (out_words_to_go > 4) + emit_insn (arm_gen_store_multiple (0, 4, dst, TRUE, TRUE, + dst_unchanging_p, + dst_in_struct_p, + dst_scalar_p)); + else if (out_words_to_go != 1) + emit_insn (arm_gen_store_multiple (0, out_words_to_go, + dst, TRUE, + (last_bytes == 0 + ? FALSE : TRUE), + dst_unchanging_p, + dst_in_struct_p, + dst_scalar_p)); + else + { + mem = gen_rtx_MEM (SImode, dst); + RTX_UNCHANGING_P (mem) = dst_unchanging_p; + MEM_IN_STRUCT_P (mem) = dst_in_struct_p; + MEM_SCALAR_P (mem) = dst_scalar_p; + emit_move_insn (mem, gen_rtx_REG (SImode, 0)); + if (last_bytes != 0) + emit_insn (gen_addsi3 (dst, dst, GEN_INT (4))); + } + } + + in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4; + out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4; + } + + /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do. */ + if (out_words_to_go) + { + rtx sreg; + + mem = gen_rtx_MEM (SImode, src); + RTX_UNCHANGING_P (mem) = src_unchanging_p; + MEM_IN_STRUCT_P (mem) = src_in_struct_p; + MEM_SCALAR_P (mem) = src_scalar_p; + emit_move_insn (sreg = gen_reg_rtx (SImode), mem); + emit_move_insn (fin_src = gen_reg_rtx (SImode), plus_constant (src, 4)); + + mem = gen_rtx_MEM (SImode, dst); + RTX_UNCHANGING_P (mem) = dst_unchanging_p; + MEM_IN_STRUCT_P (mem) = dst_in_struct_p; + MEM_SCALAR_P (mem) = dst_scalar_p; + emit_move_insn (mem, sreg); + emit_move_insn (fin_dst = gen_reg_rtx (SImode), plus_constant (dst, 4)); + in_words_to_go--; + + if (in_words_to_go) /* Sanity check */ + abort (); + } + + if (in_words_to_go) + { + if (in_words_to_go < 0) + abort (); + + mem = gen_rtx_MEM (SImode, src); + RTX_UNCHANGING_P (mem) = src_unchanging_p; + MEM_IN_STRUCT_P (mem) = src_in_struct_p; + MEM_SCALAR_P (mem) = src_scalar_p; + part_bytes_reg = copy_to_mode_reg (SImode, mem); + } + + if (last_bytes && part_bytes_reg == NULL) + abort (); + + if (BYTES_BIG_ENDIAN && last_bytes) + { + rtx tmp = gen_reg_rtx (SImode); + + /* The bytes we want are in the top end of the word. */ + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, + GEN_INT (8 * (4 - last_bytes)))); + part_bytes_reg = tmp; + + while (last_bytes) + { + mem = gen_rtx_MEM (QImode, plus_constant (dst, last_bytes - 1)); + RTX_UNCHANGING_P (mem) = dst_unchanging_p; + MEM_IN_STRUCT_P (mem) = dst_in_struct_p; + MEM_SCALAR_P (mem) = dst_scalar_p; + emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0)); + + if (--last_bytes) + { + tmp = gen_reg_rtx (SImode); + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8))); + part_bytes_reg = tmp; + } + } + + } + else + { + if (last_bytes > 1) + { + mem = gen_rtx_MEM (HImode, dst); + RTX_UNCHANGING_P (mem) = dst_unchanging_p; + MEM_IN_STRUCT_P (mem) = dst_in_struct_p; + MEM_SCALAR_P (mem) = dst_scalar_p; + emit_move_insn (mem, gen_rtx_SUBREG (HImode, part_bytes_reg, 0)); + last_bytes -= 2; + if (last_bytes) + { + rtx tmp = gen_reg_rtx (SImode); + + emit_insn (gen_addsi3 (dst, dst, GEN_INT (2))); + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (16))); + part_bytes_reg = tmp; + } + } + + if (last_bytes) + { + mem = gen_rtx_MEM (QImode, dst); + RTX_UNCHANGING_P (mem) = dst_unchanging_p; + MEM_IN_STRUCT_P (mem) = dst_in_struct_p; + MEM_SCALAR_P (mem) = dst_scalar_p; + emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0)); + } + } + + return 1; +} + +/* Generate a memory reference for a half word, such that it will be loaded + into the top 16 bits of the word. We can assume that the address is + known to be alignable and of the form reg, or plus (reg, const). */ + +rtx +arm_gen_rotated_half_load (memref) + rtx memref; +{ + HOST_WIDE_INT offset = 0; + rtx base = XEXP (memref, 0); + + if (GET_CODE (base) == PLUS) + { + offset = INTVAL (XEXP (base, 1)); + base = XEXP (base, 0); + } + + /* If we aren't allowed to generate unaligned addresses, then fail. */ + if (TARGET_MMU_TRAPS + && ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 0))) + return NULL; + + base = gen_rtx_MEM (SImode, plus_constant (base, offset & ~2)); + + if ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 2)) + return base; + + return gen_rtx_ROTATE (SImode, base, GEN_INT (16)); +} + +/* Select a dominance comparison mode if possible. We support three forms. + COND_OR == 0 => (X && Y) + COND_OR == 1 => ((! X( || Y) + COND_OR == 2 => (X || Y) + If we are unable to support a dominance comparsison we return CC mode. + This will then fail to match for the RTL expressions that generate this + call. */ + +static enum machine_mode +select_dominance_cc_mode (x, y, cond_or) + rtx x; + rtx y; + HOST_WIDE_INT cond_or; +{ + enum rtx_code cond1, cond2; + int swapped = 0; + + /* Currently we will probably get the wrong result if the individual + comparisons are not simple. This also ensures that it is safe to + reverse a comparison if necessary. */ + if ((arm_select_cc_mode (cond1 = GET_CODE (x), XEXP (x, 0), XEXP (x, 1)) + != CCmode) + || (arm_select_cc_mode (cond2 = GET_CODE (y), XEXP (y, 0), XEXP (y, 1)) + != CCmode)) + return CCmode; + + /* The if_then_else variant of this tests the second condition if the + first passes, but is true if the first fails. Reverse the first + condition to get a true "inclusive-or" expression. */ + if (cond_or == 1) + cond1 = reverse_condition (cond1); + + /* If the comparisons are not equal, and one doesn't dominate the other, + then we can't do this. */ + if (cond1 != cond2 + && !comparison_dominates_p (cond1, cond2) + && (swapped = 1, !comparison_dominates_p (cond2, cond1))) + return CCmode; + + if (swapped) + { + enum rtx_code temp = cond1; + cond1 = cond2; + cond2 = temp; + } + + switch (cond1) + { + case EQ: + if (cond2 == EQ || !cond_or) + return CC_DEQmode; + + switch (cond2) + { + case LE: return CC_DLEmode; + case LEU: return CC_DLEUmode; + case GE: return CC_DGEmode; + case GEU: return CC_DGEUmode; + default: break; + } + + break; + + case LT: + if (cond2 == LT || !cond_or) + return CC_DLTmode; + if (cond2 == LE) + return CC_DLEmode; + if (cond2 == NE) + return CC_DNEmode; + break; + + case GT: + if (cond2 == GT || !cond_or) + return CC_DGTmode; + if (cond2 == GE) + return CC_DGEmode; + if (cond2 == NE) + return CC_DNEmode; + break; + + case LTU: + if (cond2 == LTU || !cond_or) + return CC_DLTUmode; + if (cond2 == LEU) + return CC_DLEUmode; + if (cond2 == NE) + return CC_DNEmode; + break; + + case GTU: + if (cond2 == GTU || !cond_or) + return CC_DGTUmode; + if (cond2 == GEU) + return CC_DGEUmode; + if (cond2 == NE) + return CC_DNEmode; + break; + + /* The remaining cases only occur when both comparisons are the + same. */ + case NE: + return CC_DNEmode; + + case LE: + return CC_DLEmode; + + case GE: + return CC_DGEmode; + + case LEU: + return CC_DLEUmode; + + case GEU: + return CC_DGEUmode; + + default: + break; + } + + abort (); +} + +enum machine_mode +arm_select_cc_mode (op, x, y) + enum rtx_code op; + rtx x; + rtx y; +{ + /* All floating point compares return CCFP if it is an equality + comparison, and CCFPE otherwise. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + switch (op) + { + case EQ: + case NE: + case UNORDERED: + case ORDERED: + case UNLT: + case UNLE: + case UNGT: + case UNGE: + case UNEQ: + case LTGT: + return CCFPmode; + + case LT: + case LE: + case GT: + case GE: + return CCFPEmode; + + default: + abort (); + } + } + + /* A compare with a shifted operand. Because of canonicalization, the + comparison will have to be swapped when we emit the assembler. */ + if (GET_MODE (y) == SImode && GET_CODE (y) == REG + && (GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT + || GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ROTATE + || GET_CODE (x) == ROTATERT)) + return CC_SWPmode; + + /* This is a special case that is used by combine to allow a + comparison of a shifted byte load to be split into a zero-extend + followed by a comparison of the shifted integer (only valid for + equalities and unsigned inequalities). */ + if (GET_MODE (x) == SImode + && GET_CODE (x) == ASHIFT + && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 24 + && GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == MEM + && GET_MODE (SUBREG_REG (XEXP (x, 0))) == QImode + && (op == EQ || op == NE + || op == GEU || op == GTU || op == LTU || op == LEU) + && GET_CODE (y) == CONST_INT) + return CC_Zmode; + + /* A construct for a conditional compare, if the false arm contains + 0, then both conditions must be true, otherwise either condition + must be true. Not all conditions are possible, so CCmode is + returned if it can't be done. */ + if (GET_CODE (x) == IF_THEN_ELSE + && (XEXP (x, 2) == const0_rtx + || XEXP (x, 2) == const1_rtx) + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') + return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), + INTVAL (XEXP (x, 2))); + + /* Alternate canonicalizations of the above. These are somewhat cleaner. */ + if (GET_CODE (x) == AND + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') + return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 0); + + if (GET_CODE (x) == IOR + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') + return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 2); + + /* An operation that sets the condition codes as a side-effect, the + V flag is not set correctly, so we can only use comparisons where + this doesn't matter. (For LT and GE we can use "mi" and "pl" + instead. */ + if (GET_MODE (x) == SImode + && y == const0_rtx + && (op == EQ || op == NE || op == LT || op == GE) + && (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS + || GET_CODE (x) == AND || GET_CODE (x) == IOR + || GET_CODE (x) == XOR || GET_CODE (x) == MULT + || GET_CODE (x) == NOT || GET_CODE (x) == NEG + || GET_CODE (x) == LSHIFTRT + || GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT + || GET_CODE (x) == ROTATERT || GET_CODE (x) == ZERO_EXTRACT)) + return CC_NOOVmode; + + if (GET_MODE (x) == QImode && (op == EQ || op == NE)) + return CC_Zmode; + + if (GET_MODE (x) == SImode && (op == LTU || op == GEU) + && GET_CODE (x) == PLUS + && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y))) + return CC_Cmode; + + return CCmode; +} + +/* X and Y are two things to compare using CODE. Emit the compare insn and + return the rtx for register 0 in the proper mode. FP means this is a + floating point compare: I don't think that it is needed on the arm. */ + +rtx +arm_gen_compare_reg (code, x, y) + enum rtx_code code; + rtx x, y; +{ + enum machine_mode mode = SELECT_CC_MODE (code, x, y); + rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM); + + emit_insn (gen_rtx_SET (VOIDmode, cc_reg, + gen_rtx_COMPARE (mode, x, y))); + + return cc_reg; +} + +void +arm_reload_in_hi (operands) + rtx * operands; +{ + rtx ref = operands[1]; + rtx base, scratch; + HOST_WIDE_INT offset = 0; + + if (GET_CODE (ref) == SUBREG) + { + offset = SUBREG_BYTE (ref); + ref = SUBREG_REG (ref); + } + + if (GET_CODE (ref) == REG) + { + /* We have a pseudo which has been spilt onto the stack; there + are two cases here: the first where there is a simple + stack-slot replacement and a second where the stack-slot is + out of range, or is used as a subreg. */ + if (reg_equiv_mem[REGNO (ref)]) + { + ref = reg_equiv_mem[REGNO (ref)]; + base = find_replacement (&XEXP (ref, 0)); + } + else + /* The slot is out of range, or was dressed up in a SUBREG. */ + base = reg_equiv_address[REGNO (ref)]; + } + else + base = find_replacement (&XEXP (ref, 0)); + + /* Handle the case where the address is too complex to be offset by 1. */ + if (GET_CODE (base) == MINUS + || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) + { + rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); + + emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); + base = base_plus; + } + else if (GET_CODE (base) == PLUS) + { + /* The addend must be CONST_INT, or we would have dealt with it above. */ + HOST_WIDE_INT hi, lo; + + offset += INTVAL (XEXP (base, 1)); + base = XEXP (base, 0); + + /* Rework the address into a legal sequence of insns. */ + /* Valid range for lo is -4095 -> 4095 */ + lo = (offset >= 0 + ? (offset & 0xfff) + : -((-offset) & 0xfff)); + + /* Corner case, if lo is the max offset then we would be out of range + once we have added the additional 1 below, so bump the msb into the + pre-loading insn(s). */ + if (lo == 4095) + lo &= 0x7ff; + + hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) + ^ (HOST_WIDE_INT) 0x80000000) + - (HOST_WIDE_INT) 0x80000000); + + if (hi + lo != offset) + abort (); + + if (hi != 0) + { + rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); + + /* Get the base address; addsi3 knows how to handle constants + that require more than one insn. */ + emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); + base = base_plus; + offset = lo; + } + } + + scratch = gen_rtx_REG (SImode, REGNO (operands[2])); + emit_insn (gen_zero_extendqisi2 (scratch, + gen_rtx_MEM (QImode, + plus_constant (base, + offset)))); + emit_insn (gen_zero_extendqisi2 (gen_rtx_SUBREG (SImode, operands[0], 0), + gen_rtx_MEM (QImode, + plus_constant (base, + offset + 1)))); + if (!BYTES_BIG_ENDIAN) + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), + gen_rtx_IOR (SImode, + gen_rtx_ASHIFT + (SImode, + gen_rtx_SUBREG (SImode, operands[0], 0), + GEN_INT (8)), + scratch))); + else + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), + gen_rtx_IOR (SImode, + gen_rtx_ASHIFT (SImode, scratch, + GEN_INT (8)), + gen_rtx_SUBREG (SImode, operands[0], + 0)))); +} + +/* Handle storing a half-word to memory during reload by synthesising as two + byte stores. Take care not to clobber the input values until after we + have moved them somewhere safe. This code assumes that if the DImode + scratch in operands[2] overlaps either the input value or output address + in some way, then that value must die in this insn (we absolutely need + two scratch registers for some corner cases). */ + +void +arm_reload_out_hi (operands) + rtx * operands; +{ + rtx ref = operands[0]; + rtx outval = operands[1]; + rtx base, scratch; + HOST_WIDE_INT offset = 0; + + if (GET_CODE (ref) == SUBREG) + { + offset = SUBREG_BYTE (ref); + ref = SUBREG_REG (ref); + } + + if (GET_CODE (ref) == REG) + { + /* We have a pseudo which has been spilt onto the stack; there + are two cases here: the first where there is a simple + stack-slot replacement and a second where the stack-slot is + out of range, or is used as a subreg. */ + if (reg_equiv_mem[REGNO (ref)]) + { + ref = reg_equiv_mem[REGNO (ref)]; + base = find_replacement (&XEXP (ref, 0)); + } + else + /* The slot is out of range, or was dressed up in a SUBREG. */ + base = reg_equiv_address[REGNO (ref)]; + } + else + base = find_replacement (&XEXP (ref, 0)); + + scratch = gen_rtx_REG (SImode, REGNO (operands[2])); + + /* Handle the case where the address is too complex to be offset by 1. */ + if (GET_CODE (base) == MINUS + || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) + { + rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); + + /* Be careful not to destroy OUTVAL. */ + if (reg_overlap_mentioned_p (base_plus, outval)) + { + /* Updating base_plus might destroy outval, see if we can + swap the scratch and base_plus. */ + if (!reg_overlap_mentioned_p (scratch, outval)) + { + rtx tmp = scratch; + scratch = base_plus; + base_plus = tmp; + } + else + { + rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); + + /* Be conservative and copy OUTVAL into the scratch now, + this should only be necessary if outval is a subreg + of something larger than a word. */ + /* XXX Might this clobber base? I can't see how it can, + since scratch is known to overlap with OUTVAL, and + must be wider than a word. */ + emit_insn (gen_movhi (scratch_hi, outval)); + outval = scratch_hi; + } + } + + emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); + base = base_plus; + } + else if (GET_CODE (base) == PLUS) + { + /* The addend must be CONST_INT, or we would have dealt with it above. */ + HOST_WIDE_INT hi, lo; + + offset += INTVAL (XEXP (base, 1)); + base = XEXP (base, 0); + + /* Rework the address into a legal sequence of insns. */ + /* Valid range for lo is -4095 -> 4095 */ + lo = (offset >= 0 + ? (offset & 0xfff) + : -((-offset) & 0xfff)); + + /* Corner case, if lo is the max offset then we would be out of range + once we have added the additional 1 below, so bump the msb into the + pre-loading insn(s). */ + if (lo == 4095) + lo &= 0x7ff; + + hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) + ^ (HOST_WIDE_INT) 0x80000000) + - (HOST_WIDE_INT) 0x80000000); + + if (hi + lo != offset) + abort (); + + if (hi != 0) + { + rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); + + /* Be careful not to destroy OUTVAL. */ + if (reg_overlap_mentioned_p (base_plus, outval)) + { + /* Updating base_plus might destroy outval, see if we + can swap the scratch and base_plus. */ + if (!reg_overlap_mentioned_p (scratch, outval)) + { + rtx tmp = scratch; + scratch = base_plus; + base_plus = tmp; + } + else + { + rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); + + /* Be conservative and copy outval into scratch now, + this should only be necessary if outval is a + subreg of something larger than a word. */ + /* XXX Might this clobber base? I can't see how it + can, since scratch is known to overlap with + outval. */ + emit_insn (gen_movhi (scratch_hi, outval)); + outval = scratch_hi; + } + } + + /* Get the base address; addsi3 knows how to handle constants + that require more than one insn. */ + emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); + base = base_plus; + offset = lo; + } + } + + if (BYTES_BIG_ENDIAN) + { + emit_insn (gen_movqi (gen_rtx_MEM (QImode, + plus_constant (base, offset + 1)), + gen_rtx_SUBREG (QImode, outval, 0))); + emit_insn (gen_lshrsi3 (scratch, + gen_rtx_SUBREG (SImode, outval, 0), + GEN_INT (8))); + emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), + gen_rtx_SUBREG (QImode, scratch, 0))); + } + else + { + emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), + gen_rtx_SUBREG (QImode, outval, 0))); + emit_insn (gen_lshrsi3 (scratch, + gen_rtx_SUBREG (SImode, outval, 0), + GEN_INT (8))); + emit_insn (gen_movqi (gen_rtx_MEM (QImode, + plus_constant (base, offset + 1)), + gen_rtx_SUBREG (QImode, scratch, 0))); + } +} + +/* Print a symbolic form of X to the debug file, F. */ + +static void +arm_print_value (f, x) + FILE * f; + rtx x; +{ + switch (GET_CODE (x)) + { + case CONST_INT: + fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x)); + return; + + case CONST_DOUBLE: + fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3)); + return; + + case CONST_STRING: + fprintf (f, "\"%s\"", XSTR (x, 0)); + return; + + case SYMBOL_REF: + fprintf (f, "`%s'", XSTR (x, 0)); + return; + + case LABEL_REF: + fprintf (f, "L%d", INSN_UID (XEXP (x, 0))); + return; + + case CONST: + arm_print_value (f, XEXP (x, 0)); + return; + + case PLUS: + arm_print_value (f, XEXP (x, 0)); + fprintf (f, "+"); + arm_print_value (f, XEXP (x, 1)); + return; + + case PC: + fprintf (f, "pc"); + return; + + default: + fprintf (f, "????"); + return; + } +} + +/* Routines for manipulation of the constant pool. */ + +/* Arm instructions cannot load a large constant directly into a + register; they have to come from a pc relative load. The constant + must therefore be placed in the addressable range of the pc + relative load. Depending on the precise pc relative load + instruction the range is somewhere between 256 bytes and 4k. 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 the code larger. + + Normally we can hide the table after an existing unconditional + branch so that there is no interruption of the flow, but in the + worst case the code looks like this: + + 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 after scheduling, which notices + which instructions need to have their operands fetched from the + constant table and builds the table. + + The algorithm starts by building a table of all the constants that + need fixing up and all the natural barriers in the function (places + where a constant table can be dropped without breaking the flow). + For each fixup we note how far the pc-relative replacement will be + able to reach and the offset of the instruction into the function. + + Having built the table we then group the fixes together to form + tables that are as large as possible (subject to addressing + constraints) and emit each table of constants after the last + barrier that is within range of all the instructions in the group. + If a group does not contain a barrier, then we forcibly create one + by inserting a jump instruction into the flow. Once the table has + been inserted, the insns are then modified to reference the + relevant entry in the pool. + + Possible enhancements to the algorithm (not implemented) are: + + 1) For some processors and object formats, there may be benefit in + aligning the pools to the start of cache lines; this alignment + would need to be taken into account when calculating addressability + of a pool. */ + +/* These typedefs are located at the start of this file, so that + they can be used in the prototypes there. This comment is to + remind readers of that fact so that the following structures + can be understood more easily. + + typedef struct minipool_node Mnode; + typedef struct minipool_fixup Mfix; */ + +struct minipool_node +{ + /* Doubly linked chain of entries. */ + Mnode * next; + Mnode * prev; + /* The maximum offset into the code that this entry can be placed. While + pushing fixes for forward references, all entries are sorted in order + of increasing max_address. */ + HOST_WIDE_INT max_address; + /* Similarly for an entry inserted for a backwards ref. */ + HOST_WIDE_INT min_address; + /* The number of fixes referencing this entry. This can become zero + if we "unpush" an entry. In this case we ignore the entry when we + come to emit the code. */ + int refcount; + /* The offset from the start of the minipool. */ + HOST_WIDE_INT offset; + /* The value in table. */ + rtx value; + /* The mode of value. */ + enum machine_mode mode; + int fix_size; +}; + +struct minipool_fixup +{ + Mfix * next; + rtx insn; + HOST_WIDE_INT address; + rtx * loc; + enum machine_mode mode; + int fix_size; + rtx value; + Mnode * minipool; + HOST_WIDE_INT forwards; + HOST_WIDE_INT backwards; +}; + +/* Fixes less than a word need padding out to a word boundary. */ +#define MINIPOOL_FIX_SIZE(mode) \ + (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4) + +static Mnode * minipool_vector_head; +static Mnode * minipool_vector_tail; +static rtx minipool_vector_label; + +/* The linked list of all minipool fixes required for this function. */ +Mfix * minipool_fix_head; +Mfix * minipool_fix_tail; +/* The fix entry for the current minipool, once it has been placed. */ +Mfix * minipool_barrier; + +/* Determines if INSN is the start of a jump table. Returns the end + of the TABLE or NULL_RTX. */ + +static rtx +is_jump_table (insn) + rtx insn; +{ + rtx table; + + if (GET_CODE (insn) == JUMP_INSN + && JUMP_LABEL (insn) != NULL + && ((table = next_real_insn (JUMP_LABEL (insn))) + == next_real_insn (insn)) + && table != NULL + && GET_CODE (table) == JUMP_INSN + && (GET_CODE (PATTERN (table)) == ADDR_VEC + || GET_CODE (PATTERN (table)) == ADDR_DIFF_VEC)) + return table; + + return NULL_RTX; +} + +static HOST_WIDE_INT +get_jump_table_size (insn) + rtx insn; +{ + rtx body = PATTERN (insn); + int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0; + + return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, elt); +} + +/* Move a minipool fix MP from its current location to before MAX_MP. + If MAX_MP is NULL, then MP doesn't need moving, but the addressing + contrains may need updating. */ + +static Mnode * +move_minipool_fix_forward_ref (mp, max_mp, max_address) + Mnode * mp; + Mnode * max_mp; + HOST_WIDE_INT max_address; +{ + /* This should never be true and the code below assumes these are + different. */ + if (mp == max_mp) + abort (); + + if (max_mp == NULL) + { + if (max_address < mp->max_address) + mp->max_address = max_address; + } + else + { + if (max_address > max_mp->max_address - mp->fix_size) + mp->max_address = max_mp->max_address - mp->fix_size; + else + mp->max_address = max_address; + + /* Unlink MP from its current position. Since max_mp is non-null, + mp->prev must be non-null. */ + mp->prev->next = mp->next; + if (mp->next != NULL) + mp->next->prev = mp->prev; + else + minipool_vector_tail = mp->prev; + + /* Re-insert it before MAX_MP. */ + mp->next = max_mp; + mp->prev = max_mp->prev; + max_mp->prev = mp; + + if (mp->prev != NULL) + mp->prev->next = mp; + else + minipool_vector_head = mp; + } + + /* Save the new entry. */ + max_mp = mp; + + /* Scan over the preceding entries and adjust their addresses as + required. */ + while (mp->prev != NULL + && mp->prev->max_address > mp->max_address - mp->prev->fix_size) + { + mp->prev->max_address = mp->max_address - mp->prev->fix_size; + mp = mp->prev; + } + + return max_mp; +} + +/* Add a constant to the minipool for a forward reference. Returns the + node added or NULL if the constant will not fit in this pool. */ + +static Mnode * +add_minipool_forward_ref (fix) + Mfix * fix; +{ + /* If set, max_mp is the first pool_entry that has a lower + constraint than the one we are trying to add. */ + Mnode * max_mp = NULL; + HOST_WIDE_INT max_address = fix->address + fix->forwards; + Mnode * mp; + + /* If this fix's address is greater than the address of the first + entry, then we can't put the fix in this pool. We subtract the + size of the current fix to ensure that if the table is fully + packed we still have enough room to insert this value by suffling + the other fixes forwards. */ + if (minipool_vector_head && + fix->address >= minipool_vector_head->max_address - fix->fix_size) + return NULL; + + /* Scan the pool to see if a constant with the same value has + already been added. While we are doing this, also note the + location where we must insert the constant if it doesn't already + exist. */ + for (mp = minipool_vector_head; mp != NULL; mp = mp->next) + { + if (GET_CODE (fix->value) == GET_CODE (mp->value) + && fix->mode == mp->mode + && (GET_CODE (fix->value) != CODE_LABEL + || (CODE_LABEL_NUMBER (fix->value) + == CODE_LABEL_NUMBER (mp->value))) + && rtx_equal_p (fix->value, mp->value)) + { + /* More than one fix references this entry. */ + mp->refcount++; + return move_minipool_fix_forward_ref (mp, max_mp, max_address); + } + + /* Note the insertion point if necessary. */ + if (max_mp == NULL + && mp->max_address > max_address) + max_mp = mp; + } + + /* The value is not currently in the minipool, so we need to create + a new entry for it. If MAX_MP is NULL, the entry will be put on + the end of the list since the placement is less constrained than + any existing entry. Otherwise, we insert the new fix before + MAX_MP and, if neceesary, adjust the constraints on the other + entries. */ + mp = xmalloc (sizeof (* mp)); + mp->fix_size = fix->fix_size; + mp->mode = fix->mode; + mp->value = fix->value; + mp->refcount = 1; + /* Not yet required for a backwards ref. */ + mp->min_address = -65536; + + if (max_mp == NULL) + { + mp->max_address = max_address; + mp->next = NULL; + mp->prev = minipool_vector_tail; + + if (mp->prev == NULL) + { + minipool_vector_head = mp; + minipool_vector_label = gen_label_rtx (); + } + else + mp->prev->next = mp; + + minipool_vector_tail = mp; + } + else + { + if (max_address > max_mp->max_address - mp->fix_size) + mp->max_address = max_mp->max_address - mp->fix_size; + else + mp->max_address = max_address; + + mp->next = max_mp; + mp->prev = max_mp->prev; + max_mp->prev = mp; + if (mp->prev != NULL) + mp->prev->next = mp; + else + minipool_vector_head = mp; + } + + /* Save the new entry. */ + max_mp = mp; + + /* Scan over the preceding entries and adjust their addresses as + required. */ + while (mp->prev != NULL + && mp->prev->max_address > mp->max_address - mp->prev->fix_size) + { + mp->prev->max_address = mp->max_address - mp->prev->fix_size; + mp = mp->prev; + } + + return max_mp; +} + +static Mnode * +move_minipool_fix_backward_ref (mp, min_mp, min_address) + Mnode * mp; + Mnode * min_mp; + HOST_WIDE_INT min_address; +{ + HOST_WIDE_INT offset; + + /* This should never be true, and the code below assumes these are + different. */ + if (mp == min_mp) + abort (); + + if (min_mp == NULL) + { + if (min_address > mp->min_address) + mp->min_address = min_address; + } + else + { + /* We will adjust this below if it is too loose. */ + mp->min_address = min_address; + + /* Unlink MP from its current position. Since min_mp is non-null, + mp->next must be non-null. */ + mp->next->prev = mp->prev; + if (mp->prev != NULL) + mp->prev->next = mp->next; + else + minipool_vector_head = mp->next; + + /* Reinsert it after MIN_MP. */ + mp->prev = min_mp; + mp->next = min_mp->next; + min_mp->next = mp; + if (mp->next != NULL) + mp->next->prev = mp; + else + minipool_vector_tail = mp; + } + + min_mp = mp; + + offset = 0; + for (mp = minipool_vector_head; mp != NULL; mp = mp->next) + { + mp->offset = offset; + if (mp->refcount > 0) + offset += mp->fix_size; + + if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size) + mp->next->min_address = mp->min_address + mp->fix_size; + } + + return min_mp; +} + +/* Add a constant to the minipool for a backward reference. Returns the + node added or NULL if the constant will not fit in this pool. + + Note that the code for insertion for a backwards reference can be + somewhat confusing because the calculated offsets for each fix do + not take into account the size of the pool (which is still under + construction. */ + +static Mnode * +add_minipool_backward_ref (fix) + Mfix * fix; +{ + /* If set, min_mp is the last pool_entry that has a lower constraint + than the one we are trying to add. */ + Mnode * min_mp = NULL; + /* This can be negative, since it is only a constraint. */ + HOST_WIDE_INT min_address = fix->address - fix->backwards; + Mnode * mp; + + /* If we can't reach the current pool from this insn, or if we can't + insert this entry at the end of the pool without pushing other + fixes out of range, then we don't try. This ensures that we + can't fail later on. */ + if (min_address >= minipool_barrier->address + || (minipool_vector_tail->min_address + fix->fix_size + >= minipool_barrier->address)) + return NULL; + + /* Scan the pool to see if a constant with the same value has + already been added. While we are doing this, also note the + location where we must insert the constant if it doesn't already + exist. */ + for (mp = minipool_vector_tail; mp != NULL; mp = mp->prev) + { + if (GET_CODE (fix->value) == GET_CODE (mp->value) + && fix->mode == mp->mode + && (GET_CODE (fix->value) != CODE_LABEL + || (CODE_LABEL_NUMBER (fix->value) + == CODE_LABEL_NUMBER (mp->value))) + && rtx_equal_p (fix->value, mp->value) + /* Check that there is enough slack to move this entry to the + end of the table (this is conservative). */ + && (mp->max_address + > (minipool_barrier->address + + minipool_vector_tail->offset + + minipool_vector_tail->fix_size))) + { + mp->refcount++; + return move_minipool_fix_backward_ref (mp, min_mp, min_address); + } + + if (min_mp != NULL) + mp->min_address += fix->fix_size; + else + { + /* Note the insertion point if necessary. */ + if (mp->min_address < min_address) + min_mp = mp; + else if (mp->max_address + < minipool_barrier->address + mp->offset + fix->fix_size) + { + /* Inserting before this entry would push the fix beyond + its maximum address (which can happen if we have + re-located a forwards fix); force the new fix to come + after it. */ + min_mp = mp; + min_address = mp->min_address + fix->fix_size; + } + } + } + + /* We need to create a new entry. */ + mp = xmalloc (sizeof (* mp)); + mp->fix_size = fix->fix_size; + mp->mode = fix->mode; + mp->value = fix->value; + mp->refcount = 1; + mp->max_address = minipool_barrier->address + 65536; + + mp->min_address = min_address; + + if (min_mp == NULL) + { + mp->prev = NULL; + mp->next = minipool_vector_head; + + if (mp->next == NULL) + { + minipool_vector_tail = mp; + minipool_vector_label = gen_label_rtx (); + } + else + mp->next->prev = mp; + + minipool_vector_head = mp; + } + else + { + mp->next = min_mp->next; + mp->prev = min_mp; + min_mp->next = mp; + + if (mp->next != NULL) + mp->next->prev = mp; + else + minipool_vector_tail = mp; + } + + /* Save the new entry. */ + min_mp = mp; + + if (mp->prev) + mp = mp->prev; + else + mp->offset = 0; + + /* Scan over the following entries and adjust their offsets. */ + while (mp->next != NULL) + { + if (mp->next->min_address < mp->min_address + mp->fix_size) + mp->next->min_address = mp->min_address + mp->fix_size; + + if (mp->refcount) + mp->next->offset = mp->offset + mp->fix_size; + else + mp->next->offset = mp->offset; + + mp = mp->next; + } + + return min_mp; +} + +static void +assign_minipool_offsets (barrier) + Mfix * barrier; +{ + HOST_WIDE_INT offset = 0; + Mnode * mp; + + minipool_barrier = barrier; + + for (mp = minipool_vector_head; mp != NULL; mp = mp->next) + { + mp->offset = offset; + + if (mp->refcount > 0) + offset += mp->fix_size; + } +} + +/* Output the literal table */ +static void +dump_minipool (scan) + rtx scan; +{ + Mnode * mp; + Mnode * nmp; + + if (rtl_dump_file) + fprintf (rtl_dump_file, + ";; Emitting minipool after insn %u; address %ld\n", + INSN_UID (scan), (unsigned long) minipool_barrier->address); + + scan = emit_label_after (gen_label_rtx (), scan); + scan = emit_insn_after (gen_align_4 (), scan); + scan = emit_label_after (minipool_vector_label, scan); + + for (mp = minipool_vector_head; mp != NULL; mp = nmp) + { + if (mp->refcount > 0) + { + if (rtl_dump_file) + { + fprintf (rtl_dump_file, + ";; Offset %u, min %ld, max %ld ", + (unsigned) mp->offset, (unsigned long) mp->min_address, + (unsigned long) mp->max_address); + arm_print_value (rtl_dump_file, mp->value); + fputc ('\n', rtl_dump_file); + } + + switch (mp->fix_size) + { +#ifdef HAVE_consttable_1 + case 1: + scan = emit_insn_after (gen_consttable_1 (mp->value), scan); + break; + +#endif +#ifdef HAVE_consttable_2 + case 2: + scan = emit_insn_after (gen_consttable_2 (mp->value), scan); + break; + +#endif +#ifdef HAVE_consttable_4 + case 4: + scan = emit_insn_after (gen_consttable_4 (mp->value), scan); + break; + +#endif +#ifdef HAVE_consttable_8 + case 8: + scan = emit_insn_after (gen_consttable_8 (mp->value), scan); + break; + +#endif + default: + abort (); + break; + } + } + + nmp = mp->next; + free (mp); + } + + minipool_vector_head = minipool_vector_tail = NULL; + scan = emit_insn_after (gen_consttable_end (), scan); + scan = emit_barrier_after (scan); +} + +/* Return the cost of forcibly inserting a barrier after INSN. */ + +static int +arm_barrier_cost (insn) + rtx insn; +{ + /* Basing the location of the pool on the loop depth is preferable, + but at the moment, the basic block information seems to be + corrupt by this stage of the compilation. */ + int base_cost = 50; + rtx next = next_nonnote_insn (insn); + + if (next != NULL && GET_CODE (next) == CODE_LABEL) + base_cost -= 20; + + switch (GET_CODE (insn)) + { + case CODE_LABEL: + /* It will always be better to place the table before the label, rather + than after it. */ + return 50; + + case INSN: + case CALL_INSN: + return base_cost; + + case JUMP_INSN: + return base_cost - 10; + + default: + return base_cost + 10; + } +} + +/* Find the best place in the insn stream in the range + (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier. + Create the barrier by inserting a jump and add a new fix entry for + it. */ + +static Mfix * +create_fix_barrier (fix, max_address) + Mfix * fix; + HOST_WIDE_INT max_address; +{ + HOST_WIDE_INT count = 0; + rtx barrier; + rtx from = fix->insn; + rtx selected = from; + int selected_cost; + HOST_WIDE_INT selected_address; + Mfix * new_fix; + HOST_WIDE_INT max_count = max_address - fix->address; + rtx label = gen_label_rtx (); + + selected_cost = arm_barrier_cost (from); + selected_address = fix->address; + + while (from && count < max_count) + { + rtx tmp; + int new_cost; + + /* This code shouldn't have been called if there was a natural barrier + within range. */ + if (GET_CODE (from) == BARRIER) + abort (); + + /* Count the length of this insn. */ + count += get_attr_length (from); + + /* If there is a jump table, add its length. */ + tmp = is_jump_table (from); + if (tmp != NULL) + { + count += get_jump_table_size (tmp); + + /* Jump tables aren't in a basic block, so base the cost on + the dispatch insn. If we select this location, we will + still put the pool after the table. */ + new_cost = arm_barrier_cost (from); + + if (count < max_count && new_cost <= selected_cost) + { + selected = tmp; + selected_cost = new_cost; + selected_address = fix->address + count; + } + + /* Continue after the dispatch table. */ + from = NEXT_INSN (tmp); + continue; + } + + new_cost = arm_barrier_cost (from); + + if (count < max_count && new_cost <= selected_cost) + { + selected = from; + selected_cost = new_cost; + selected_address = fix->address + count; + } + + from = NEXT_INSN (from); + } + + /* Create a new JUMP_INSN that branches around a barrier. */ + from = emit_jump_insn_after (gen_jump (label), selected); + JUMP_LABEL (from) = label; + barrier = emit_barrier_after (from); + emit_label_after (label, barrier); + + /* Create a minipool barrier entry for the new barrier. */ + new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix)); + new_fix->insn = barrier; + new_fix->address = selected_address; + new_fix->next = fix->next; + fix->next = new_fix; + + return new_fix; +} + +/* Record that there is a natural barrier in the insn stream at + ADDRESS. */ +static void +push_minipool_barrier (insn, address) + rtx insn; + HOST_WIDE_INT address; +{ + Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); + + fix->insn = insn; + fix->address = address; + + fix->next = NULL; + if (minipool_fix_head != NULL) + minipool_fix_tail->next = fix; + else + minipool_fix_head = fix; + + minipool_fix_tail = fix; +} + +/* Record INSN, which will need fixing up to load a value from the + minipool. ADDRESS is the offset of the insn since the start of the + function; LOC is a pointer to the part of the insn which requires + fixing; VALUE is the constant that must be loaded, which is of type + MODE. */ +static void +push_minipool_fix (insn, address, loc, mode, value) + rtx insn; + HOST_WIDE_INT address; + rtx * loc; + enum machine_mode mode; + rtx value; +{ + Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); + +#ifdef AOF_ASSEMBLER + /* PIC symbol refereneces need to be converted into offsets into the + based area. */ + /* XXX This shouldn't be done here. */ + if (flag_pic && GET_CODE (value) == SYMBOL_REF) + value = aof_pic_entry (value); +#endif /* AOF_ASSEMBLER */ + + fix->insn = insn; + fix->address = address; + fix->loc = loc; + fix->mode = mode; + fix->fix_size = MINIPOOL_FIX_SIZE (mode); + fix->value = value; + fix->forwards = get_attr_pool_range (insn); + fix->backwards = get_attr_neg_pool_range (insn); + fix->minipool = NULL; + + /* If an insn doesn't have a range defined for it, then it isn't + expecting to be reworked by this code. Better to abort now than + to generate duff assembly code. */ + if (fix->forwards == 0 && fix->backwards == 0) + abort (); + + if (rtl_dump_file) + { + fprintf (rtl_dump_file, + ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ", + GET_MODE_NAME (mode), + INSN_UID (insn), (unsigned long) address, + -1 * (long)fix->backwards, (long)fix->forwards); + arm_print_value (rtl_dump_file, fix->value); + fprintf (rtl_dump_file, "\n"); + } + + /* Add it to the chain of fixes. */ + fix->next = NULL; + + if (minipool_fix_head != NULL) + minipool_fix_tail->next = fix; + else + minipool_fix_head = fix; + + minipool_fix_tail = fix; +} + +/* Scan INSN and note any of its operands that need fixing. */ + +static void +note_invalid_constants (insn, address) + rtx insn; + HOST_WIDE_INT address; +{ + int opno; + + extract_insn (insn); + + if (!constrain_operands (1)) + fatal_insn_not_found (insn); + + /* Fill in recog_op_alt with information about the constraints of this + insn. */ + preprocess_constraints (); + + for (opno = 0; opno < recog_data.n_operands; opno++) + { + /* Things we need to fix can only occur in inputs. */ + if (recog_data.operand_type[opno] != OP_IN) + continue; + + /* If this alternative is a memory reference, then any mention + of constants in this alternative is really to fool reload + into allowing us to accept one there. We need to fix them up + now so that we output the right code. */ + if (recog_op_alt[opno][which_alternative].memory_ok) + { + rtx op = recog_data.operand[opno]; + + if (CONSTANT_P (op)) + push_minipool_fix (insn, address, recog_data.operand_loc[opno], + recog_data.operand_mode[opno], op); +#if 0 + /* RWE: Now we look correctly at the operands for the insn, + this shouldn't be needed any more. */ +#ifndef AOF_ASSEMBLER + /* XXX Is this still needed? */ + else if (GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_PIC_SYM) + push_minipool_fix (insn, address, recog_data.operand_loc[opno], + recog_data.operand_mode[opno], + XVECEXP (op, 0, 0)); +#endif +#endif + else if (GET_CODE (op) == MEM + && GET_CODE (XEXP (op, 0)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0))) + push_minipool_fix (insn, address, recog_data.operand_loc[opno], + recog_data.operand_mode[opno], + get_pool_constant (XEXP (op, 0))); + } + } +} + +void +arm_reorg (first) + rtx first; +{ + rtx insn; + HOST_WIDE_INT address = 0; + Mfix * fix; + + minipool_fix_head = minipool_fix_tail = NULL; + + /* The first insn must always be a note, or the code below won't + scan it properly. */ + if (GET_CODE (first) != NOTE) + abort (); + + /* Scan all the insns and record the operands that will need fixing. */ + for (insn = next_nonnote_insn (first); insn; insn = next_nonnote_insn (insn)) + { + if (GET_CODE (insn) == BARRIER) + push_minipool_barrier (insn, address); + else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + { + rtx table; + + note_invalid_constants (insn, address); + address += get_attr_length (insn); + + /* If the insn is a vector jump, add the size of the table + and skip the table. */ + if ((table = is_jump_table (insn)) != NULL) + { + address += get_jump_table_size (table); + insn = table; + } + } + } + + fix = minipool_fix_head; + + /* Now scan the fixups and perform the required changes. */ + while (fix) + { + Mfix * ftmp; + Mfix * fdel; + Mfix * last_added_fix; + Mfix * last_barrier = NULL; + Mfix * this_fix; + + /* Skip any further barriers before the next fix. */ + while (fix && GET_CODE (fix->insn) == BARRIER) + fix = fix->next; + + /* No more fixes. */ + if (fix == NULL) + break; + + last_added_fix = NULL; + + for (ftmp = fix; ftmp; ftmp = ftmp->next) + { + if (GET_CODE (ftmp->insn) == BARRIER) + { + if (ftmp->address >= minipool_vector_head->max_address) + break; + + last_barrier = ftmp; + } + else if ((ftmp->minipool = add_minipool_forward_ref (ftmp)) == NULL) + break; + + last_added_fix = ftmp; /* Keep track of the last fix added. */ + } + + /* If we found a barrier, drop back to that; any fixes that we + could have reached but come after the barrier will now go in + the next mini-pool. */ + if (last_barrier != NULL) + { + /* Reduce the refcount for those fixes that won't go into this + pool after all. */ + for (fdel = last_barrier->next; + fdel && fdel != ftmp; + fdel = fdel->next) + { + fdel->minipool->refcount--; + fdel->minipool = NULL; + } + + ftmp = last_barrier; + } + else + { + /* ftmp is first fix that we can't fit into this pool and + there no natural barriers that we could use. Insert a + new barrier in the code somewhere between the previous + fix and this one, and arrange to jump around it. */ + HOST_WIDE_INT max_address; + + /* The last item on the list of fixes must be a barrier, so + we can never run off the end of the list of fixes without + last_barrier being set. */ + if (ftmp == NULL) + abort (); + + max_address = minipool_vector_head->max_address; + /* Check that there isn't another fix that is in range that + we couldn't fit into this pool because the pool was + already too large: we need to put the pool before such an + instruction. */ + if (ftmp->address < max_address) + max_address = ftmp->address; + + last_barrier = create_fix_barrier (last_added_fix, max_address); + } + + assign_minipool_offsets (last_barrier); + + while (ftmp) + { + if (GET_CODE (ftmp->insn) != BARRIER + && ((ftmp->minipool = add_minipool_backward_ref (ftmp)) + == NULL)) + break; + + ftmp = ftmp->next; + } + + /* Scan over the fixes we have identified for this pool, fixing them + up and adding the constants to the pool itself. */ + for (this_fix = fix; this_fix && ftmp != this_fix; + this_fix = this_fix->next) + if (GET_CODE (this_fix->insn) != BARRIER) + { + rtx addr + = plus_constant (gen_rtx_LABEL_REF (VOIDmode, + minipool_vector_label), + this_fix->minipool->offset); + *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr); + } + + dump_minipool (last_barrier->insn); + fix = ftmp; + } + + /* From now on we must synthesize any constants that we can't handle + directly. This can happen if the RTL gets split during final + instruction generation. */ + after_arm_reorg = 1; + + /* Free the minipool memory. */ + obstack_free (&minipool_obstack, minipool_startobj); +} + +/* Routines to output assembly language. */ + +/* If the rtx is the correct value then return the string of the number. + In this way we can ensure that valid double constants are generated even + when cross compiling. */ + +const char * +fp_immediate_constant (x) + rtx x; +{ + REAL_VALUE_TYPE r; + int i; + + if (!fpa_consts_inited) + init_fpa_table (); + + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + for (i = 0; i < 8; i++) + if (REAL_VALUES_EQUAL (r, values_fpa[i])) + return strings_fpa[i]; + + abort (); +} + +/* As for fp_immediate_constant, but value is passed directly, not in rtx. */ + +static const char * +fp_const_from_val (r) + REAL_VALUE_TYPE * r; +{ + int i; + + if (!fpa_consts_inited) + init_fpa_table (); + + for (i = 0; i < 8; i++) + if (REAL_VALUES_EQUAL (*r, values_fpa[i])) + return strings_fpa[i]; + + abort (); +} + +/* Output the operands of a LDM/STM instruction to STREAM. + MASK is the ARM register set mask of which only bits 0-15 are important. + REG is the base register, either the frame pointer or the stack pointer, + INSTR is the possibly suffixed load or store instruction. */ + +static void +print_multi_reg (stream, instr, reg, mask) + FILE * stream; + const char * instr; + int reg; + int mask; +{ + int i; + int not_first = FALSE; + + fputc ('\t', stream); + asm_fprintf (stream, instr, reg); + fputs (", {", stream); + + for (i = 0; i <= LAST_ARM_REGNUM; i++) + if (mask & (1 << i)) + { + if (not_first) + fprintf (stream, ", "); + + asm_fprintf (stream, "%r", i); + not_first = TRUE; + } + + fprintf (stream, "}%s\n", TARGET_APCS_32 ? "" : "^"); +} + +/* Output a 'call' insn. */ + +const char * +output_call (operands) + rtx * operands; +{ + /* Handle calls to lr using ip (which may be clobbered in subr anyway). */ + + if (REGNO (operands[0]) == LR_REGNUM) + { + operands[0] = gen_rtx_REG (SImode, IP_REGNUM); + output_asm_insn ("mov%?\t%0, %|lr", operands); + } + + output_asm_insn ("mov%?\t%|lr, %|pc", operands); + + if (TARGET_INTERWORK) + output_asm_insn ("bx%?\t%0", operands); + else + output_asm_insn ("mov%?\t%|pc, %0", operands); + + return ""; +} + +static int +eliminate_lr2ip (x) + rtx * x; +{ + int something_changed = 0; + rtx x0 = * x; + int code = GET_CODE (x0); + int i, j; + const char * fmt; + + switch (code) + { + case REG: + if (REGNO (x0) == LR_REGNUM) + { + *x = gen_rtx_REG (SImode, IP_REGNUM); + return 1; + } + return 0; + default: + /* Scan through the sub-elements and change any references there. */ + fmt = GET_RTX_FORMAT (code); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + something_changed |= eliminate_lr2ip (&XEXP (x0, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x0, i); j++) + something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j)); + + return something_changed; + } +} + +/* Output a 'call' insn that is a reference in memory. */ + +const char * +output_call_mem (operands) + rtx * operands; +{ + operands[0] = copy_rtx (operands[0]); /* Be ultra careful. */ + /* Handle calls using lr by using ip (which may be clobbered in subr anyway). */ + if (eliminate_lr2ip (&operands[0])) + output_asm_insn ("mov%?\t%|ip, %|lr", operands); + + if (TARGET_INTERWORK) + { + output_asm_insn ("ldr%?\t%|ip, %0", operands); + output_asm_insn ("mov%?\t%|lr, %|pc", operands); + output_asm_insn ("bx%?\t%|ip", operands); + } + else + { + output_asm_insn ("mov%?\t%|lr, %|pc", operands); + output_asm_insn ("ldr%?\t%|pc, %0", operands); + } + + return ""; +} + + +/* Output a move from arm registers to an fpu registers. + OPERANDS[0] is an fpu register. + OPERANDS[1] is the first registers of an arm register pair. */ + +const char * +output_mov_long_double_fpu_from_arm (operands) + rtx * operands; +{ + int arm_reg0 = REGNO (operands[1]); + rtx ops[3]; + + if (arm_reg0 == IP_REGNUM) + abort (); + + ops[0] = gen_rtx_REG (SImode, arm_reg0); + ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); + ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); + + output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops); + output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands); + + return ""; +} + +/* Output a move from an fpu register to arm registers. + OPERANDS[0] is the first registers of an arm register pair. + OPERANDS[1] is an fpu register. */ + +const char * +output_mov_long_double_arm_from_fpu (operands) + rtx * operands; +{ + int arm_reg0 = REGNO (operands[0]); + rtx ops[3]; + + if (arm_reg0 == IP_REGNUM) + abort (); + + ops[0] = gen_rtx_REG (SImode, arm_reg0); + ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); + ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); + + output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands); + output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops); + return ""; +} + +/* Output a move from arm registers to arm registers of a long double + OPERANDS[0] is the destination. + OPERANDS[1] is the source. */ + +const char * +output_mov_long_double_arm_from_arm (operands) + rtx * operands; +{ + /* We have to be careful here because the two might overlap. */ + int dest_start = REGNO (operands[0]); + int src_start = REGNO (operands[1]); + rtx ops[2]; + int i; + + if (dest_start < src_start) + { + for (i = 0; i < 3; i++) + { + ops[0] = gen_rtx_REG (SImode, dest_start + i); + ops[1] = gen_rtx_REG (SImode, src_start + i); + output_asm_insn ("mov%?\t%0, %1", ops); + } + } + else + { + for (i = 2; i >= 0; i--) + { + ops[0] = gen_rtx_REG (SImode, dest_start + i); + ops[1] = gen_rtx_REG (SImode, src_start + i); + output_asm_insn ("mov%?\t%0, %1", ops); + } + } + + return ""; +} + + +/* Output a move from arm registers to an fpu registers. + OPERANDS[0] is an fpu register. + OPERANDS[1] is the first registers of an arm register pair. */ + +const char * +output_mov_double_fpu_from_arm (operands) + rtx * operands; +{ + int arm_reg0 = REGNO (operands[1]); + rtx ops[2]; + + if (arm_reg0 == IP_REGNUM) + abort (); + + ops[0] = gen_rtx_REG (SImode, arm_reg0); + ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); + output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops); + output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands); + return ""; +} + +/* Output a move from an fpu register to arm registers. + OPERANDS[0] is the first registers of an arm register pair. + OPERANDS[1] is an fpu register. */ + +const char * +output_mov_double_arm_from_fpu (operands) + rtx * operands; +{ + int arm_reg0 = REGNO (operands[0]); + rtx ops[2]; + + if (arm_reg0 == IP_REGNUM) + abort (); + + ops[0] = gen_rtx_REG (SImode, arm_reg0); + ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); + output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands); + output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops); + return ""; +} + +/* Output a move between double words. + It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM + or MEM<-REG and all MEMs must be offsettable addresses. */ + +const char * +output_move_double (operands) + rtx * operands; +{ + enum rtx_code code0 = GET_CODE (operands[0]); + enum rtx_code code1 = GET_CODE (operands[1]); + rtx otherops[3]; + + if (code0 == REG) + { + int reg0 = REGNO (operands[0]); + + otherops[0] = gen_rtx_REG (SImode, 1 + reg0); + + if (code1 == REG) + { + int reg1 = REGNO (operands[1]); + if (reg1 == IP_REGNUM) + abort (); + + /* Ensure the second source is not overwritten. */ + if (reg1 == reg0 + (WORDS_BIG_ENDIAN ? -1 : 1)) + output_asm_insn ("mov%?\t%Q0, %Q1\n\tmov%?\t%R0, %R1", operands); + else + output_asm_insn ("mov%?\t%R0, %R1\n\tmov%?\t%Q0, %Q1", operands); + } + else if (code1 == CONST_DOUBLE) + { + if (GET_MODE (operands[1]) == DFmode) + { + long l[2]; + union real_extract u; + + memcpy (&u, &CONST_DOUBLE_LOW (operands[1]), sizeof (u)); + REAL_VALUE_TO_TARGET_DOUBLE (u.d, l); + otherops[1] = GEN_INT (l[1]); + operands[1] = GEN_INT (l[0]); + } + else if (GET_MODE (operands[1]) != VOIDmode) + abort (); + else if (WORDS_BIG_ENDIAN) + { + otherops[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); + } + else + { + otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); + operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + } + + output_mov_immediate (operands); + output_mov_immediate (otherops); + } + else if (code1 == CONST_INT) + { +#if HOST_BITS_PER_WIDE_INT > 32 + /* If HOST_WIDE_INT is more than 32 bits, the intval tells us + what the upper word is. */ + if (WORDS_BIG_ENDIAN) + { + otherops[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); + operands[1] = GEN_INT (INTVAL (operands[1]) >> 32); + } + else + { + otherops[1] = GEN_INT (INTVAL (operands[1]) >> 32); + operands[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); + } +#else + /* Sign extend the intval into the high-order word. */ + if (WORDS_BIG_ENDIAN) + { + otherops[1] = operands[1]; + operands[1] = (INTVAL (operands[1]) < 0 + ? constm1_rtx : const0_rtx); + } + else + otherops[1] = INTVAL (operands[1]) < 0 ? constm1_rtx : const0_rtx; +#endif + output_mov_immediate (otherops); + output_mov_immediate (operands); + } + else if (code1 == MEM) + { + switch (GET_CODE (XEXP (operands[1], 0))) + { + case REG: + output_asm_insn ("ldm%?ia\t%m1, %M0", operands); + break; + + case PRE_INC: + abort (); /* Should never happen now. */ + break; + + case PRE_DEC: + 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: + 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: + if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1), + GET_MODE (XEXP (XEXP (operands[1], 0), 1)))) + { + 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] = adjust_address (operands[1], VOIDmode, 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 if (code0 == MEM && code1 == REG) + { + if (REGNO (operands[1]) == IP_REGNUM) + abort (); + + switch (GET_CODE (XEXP (operands[0], 0))) + { + case REG: + output_asm_insn ("stm%?ia\t%m0, %M1", operands); + break; + + case PRE_INC: + abort (); /* Should never happen now. */ + break; + + case PRE_DEC: + 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: + 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] = adjust_address (operands[0], VOIDmode, 4); + otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1])); + output_asm_insn ("str%?\t%1, %0", operands); + output_asm_insn ("str%?\t%1, %0", otherops); + } + } + else + /* Constraints should prevent this. */ + abort (); + + return ""; +} + + +/* Output an arbitrary MOV reg, #n. + OPERANDS[0] is a register. OPERANDS[1] is a const_int. */ + +const char * +output_mov_immediate (operands) + rtx * operands; +{ + HOST_WIDE_INT n = INTVAL (operands[1]); + + /* Try to use one MOV. */ + if (const_ok_for_arm (n)) + output_asm_insn ("mov%?\t%0, %1", operands); + + /* Try to use one MVN. */ + else if (const_ok_for_arm (~n)) + { + operands[1] = GEN_INT (~n); + output_asm_insn ("mvn%?\t%0, %1", operands); + } + else + { + int n_ones = 0; + int i; + + /* If all else fails, make it out of ORRs or BICs as appropriate. */ + for (i = 0; i < 32; i ++) + if (n & 1 << i) + n_ones ++; + + if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */ + output_multi_immediate (operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, ~ n); + else + output_multi_immediate (operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, n); + } + + return ""; +} + +/* Output an ADD r, s, #n where n may be too big for one instruction. + If adding zero to one register, output nothing. */ + +const char * +output_add_immediate (operands) + rtx * operands; +{ + HOST_WIDE_INT n = INTVAL (operands[2]); + + if (n != 0 || REGNO (operands[0]) != REGNO (operands[1])) + { + if (n < 0) + output_multi_immediate (operands, + "sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2, + -n); + else + output_multi_immediate (operands, + "add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2, + n); + } + + return ""; +} + +/* Output a multiple immediate operation. + OPERANDS is the vector of operands referred to in the output patterns. + INSTR1 is the output pattern to use for the first constant. + INSTR2 is the output pattern to use for subsequent constants. + IMMED_OP is the index of the constant slot in OPERANDS. + N is the constant value. */ + +static const char * +output_multi_immediate (operands, instr1, instr2, immed_op, n) + rtx * operands; + const char * instr1; + const char * instr2; + int immed_op; + HOST_WIDE_INT n; +{ +#if HOST_BITS_PER_WIDE_INT > 32 + n &= 0xffffffff; +#endif + + if (n == 0) + { + /* Quick and easy output. */ + operands[immed_op] = const0_rtx; + output_asm_insn (instr1, operands); + } + else + { + int i; + const char * instr = instr1; + + /* Note that n is never zero here (which would give no output). */ + for (i = 0; i < 32; i += 2) + { + if (n & (3 << i)) + { + operands[immed_op] = GEN_INT (n & (255 << i)); + output_asm_insn (instr, operands); + instr = instr2; + i += 6; + } + } + } + + return ""; +} + +/* Return the appropriate ARM instruction for the operation code. + The returned result should not be overwritten. OP is the rtx of the + operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator + was shifted. */ + +const char * +arithmetic_instr (op, shift_first_arg) + rtx op; + int shift_first_arg; +{ + switch (GET_CODE (op)) + { + case PLUS: + return "add"; + + case MINUS: + return shift_first_arg ? "rsb" : "sub"; + + case IOR: + return "orr"; + + case XOR: + return "eor"; + + case AND: + return "and"; + + default: + abort (); + } +} + +/* Ensure valid constant shifts and return the appropriate shift mnemonic + for the operation code. The returned result should not be overwritten. + OP is the rtx code of the shift. + On exit, *AMOUNTP will be -1 if the shift is by a register, or a constant + shift. */ + +static const char * +shift_op (op, amountp) + rtx op; + HOST_WIDE_INT *amountp; +{ + const char * mnem; + enum rtx_code code = GET_CODE (op); + + if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG) + *amountp = -1; + else if (GET_CODE (XEXP (op, 1)) == CONST_INT) + *amountp = INTVAL (XEXP (op, 1)); + else + abort (); + + switch (code) + { + case ASHIFT: + mnem = "asl"; + break; + + case ASHIFTRT: + mnem = "asr"; + break; + + case LSHIFTRT: + mnem = "lsr"; + break; + + case ROTATERT: + mnem = "ror"; + break; + + case MULT: + /* We never have to worry about the amount being other than a + power of 2, since this case can never be reloaded from a reg. */ + if (*amountp != -1) + *amountp = int_log2 (*amountp); + else + abort (); + return "asl"; + + default: + abort (); + } + + if (*amountp != -1) + { + /* This is not 100% correct, but follows from the desire to merge + multiplication by a power of 2 with the recognizer for a + shift. >=32 is not a valid shift for "asl", so we must try and + output a shift that produces the correct arithmetical result. + Using lsr #32 is identical except for the fact that the carry bit + is not set correctly if we set the flags; but we never use the + carry bit from such an operation, so we can ignore that. */ + if (code == ROTATERT) + /* Rotate is just modulo 32. */ + *amountp &= 31; + else if (*amountp != (*amountp & 31)) + { + if (code == ASHIFT) + mnem = "lsr"; + *amountp = 32; + } + + /* Shifts of 0 are no-ops. */ + if (*amountp == 0) + return NULL; + } + + return mnem; +} + +/* Obtain the shift from the POWER of two. */ + +static HOST_WIDE_INT +int_log2 (power) + HOST_WIDE_INT power; +{ + HOST_WIDE_INT shift = 0; + + while ((((HOST_WIDE_INT) 1 << shift) & power) == 0) + { + if (shift > 31) + abort (); + shift ++; + } + + return shift; +} + +/* Output a .ascii pseudo-op, keeping track of lengths. This is because + /bin/as is horribly restrictive. */ +#define MAX_ASCII_LEN 51 + +void +output_ascii_pseudo_op (stream, p, len) + FILE * stream; + const unsigned char * p; + int len; +{ + int i; + int len_so_far = 0; + + fputs ("\t.ascii\t\"", stream); + + for (i = 0; i < len; i++) + { + int c = p[i]; + + if (len_so_far >= MAX_ASCII_LEN) + { + fputs ("\"\n\t.ascii\t\"", stream); + len_so_far = 0; + } + + switch (c) + { + case TARGET_TAB: + fputs ("\\t", stream); + len_so_far += 2; + break; + + case TARGET_FF: + fputs ("\\f", stream); + len_so_far += 2; + break; + + case TARGET_BS: + fputs ("\\b", stream); + len_so_far += 2; + break; + + case TARGET_CR: + fputs ("\\r", stream); + len_so_far += 2; + break; + + case TARGET_NEWLINE: + fputs ("\\n", stream); + c = p [i + 1]; + if ((c >= ' ' && c <= '~') + || c == TARGET_TAB) + /* This is a good place for a line break. */ + len_so_far = MAX_ASCII_LEN; + else + len_so_far += 2; + break; + + case '\"': + case '\\': + putc ('\\', stream); + len_so_far++; + /* drop through. */ + + default: + if (c >= ' ' && c <= '~') + { + putc (c, stream); + len_so_far++; + } + else + { + fprintf (stream, "\\%03o", c); + len_so_far += 4; + } + break; + } + } + + fputs ("\"\n", stream); +} + +/* Compute the register sabe mask for registers 0 through 12 + inclusive. This code is used by both arm_compute_save_reg_mask + and arm_compute_initial_elimination_offset. */ + +static unsigned long +arm_compute_save_reg0_reg12_mask () +{ + unsigned long func_type = arm_current_func_type (); + unsigned int save_reg_mask = 0; + unsigned int reg; + + if (IS_INTERRUPT (func_type)) + { + unsigned int max_reg; + /* Interrupt functions must not corrupt any registers, + even call clobbered ones. If this is a leaf function + we can just examine the registers used by the RTL, but + otherwise we have to assume that whatever function is + called might clobber anything, and so we have to save + all the call-clobbered registers as well. */ + if (ARM_FUNC_TYPE (func_type) == ARM_FT_FIQ) + /* FIQ handlers have registers r8 - r12 banked, so + we only need to check r0 - r7, Normal ISRs only + bank r14 and r15, so we must check up to r12. + r13 is the stack pointer which is always preserved, + so we do not need to consider it here. */ + max_reg = 7; + else + max_reg = 12; + + for (reg = 0; reg <= max_reg; reg++) + if (regs_ever_live[reg] + || (! current_function_is_leaf && call_used_regs [reg])) + save_reg_mask |= (1 << reg); + } + else + { + /* In the normal case we only need to save those registers + which are call saved and which are used by this function. */ + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs [reg]) + save_reg_mask |= (1 << reg); + + /* Handle the frame pointer as a special case. */ + if (! TARGET_APCS_FRAME + && ! frame_pointer_needed + && regs_ever_live[HARD_FRAME_POINTER_REGNUM] + && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) + save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM; + + /* If we aren't loading the PIC register, + don't stack it even though it may be live. */ + if (flag_pic + && ! TARGET_SINGLE_PIC_BASE + && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) + save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM; + } + + return save_reg_mask; +} + +/* Compute a bit mask of which registers need to be + saved on the stack for the current function. */ + +static unsigned long +arm_compute_save_reg_mask () +{ + unsigned int save_reg_mask = 0; + unsigned long func_type = arm_current_func_type (); + + if (IS_NAKED (func_type)) + /* This should never really happen. */ + return 0; + + /* If we are creating a stack frame, then we must save the frame pointer, + IP (which will hold the old stack pointer), LR and the PC. */ + if (frame_pointer_needed) + save_reg_mask |= + (1 << ARM_HARD_FRAME_POINTER_REGNUM) + | (1 << IP_REGNUM) + | (1 << LR_REGNUM) + | (1 << PC_REGNUM); + + /* Volatile functions do not return, so there + is no need to save any other registers. */ + if (IS_VOLATILE (func_type)) + return save_reg_mask; + + save_reg_mask |= arm_compute_save_reg0_reg12_mask (); + + /* Decide if we need to save the link register. + Interrupt routines have their own banked link register, + so they never need to save it. + Otheriwse if we do not use the link register we do not need to save + it. If we are pushing other registers onto the stack however, we + can save an instruction in the epilogue by pushing the link register + now and then popping it back into the PC. This incurs extra memory + accesses though, so we only do it when optimising for size, and only + if we know that we will not need a fancy return sequence. */ + if (! IS_INTERRUPT (func_type) + && (regs_ever_live [LR_REGNUM] + || (save_reg_mask + && optimize_size + && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL))) + save_reg_mask |= 1 << LR_REGNUM; + + if (cfun->machine->lr_save_eliminated) + save_reg_mask &= ~ (1 << LR_REGNUM); + + return save_reg_mask; +} + +/* Generate a function exit sequence. If REALLY_RETURN is true, then do + everything bar the final return instruction. */ + +const char * +output_return_instruction (operand, really_return, reverse) + rtx operand; + int really_return; + int reverse; +{ + char conditional[10]; + char instr[100]; + int reg; + unsigned long live_regs_mask; + unsigned long func_type; + + func_type = arm_current_func_type (); + + if (IS_NAKED (func_type)) + return ""; + + if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) + { + /* If this function was declared non-returning, and we have found a tail + call, then we have to trust that the called function won't return. */ + if (really_return) + { + rtx ops[2]; + + /* Otherwise, trap an attempted return by aborting. */ + ops[0] = operand; + ops[1] = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" + : "abort"); + assemble_external_libcall (ops[1]); + output_asm_insn (reverse ? "bl%D0\t%a1" : "bl%d0\t%a1", ops); + } + + return ""; + } + + if (current_function_calls_alloca && !really_return) + abort (); + + /* Construct the conditional part of the instruction(s) to be emitted. */ + sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd'); + + return_used_this_function = 1; + + live_regs_mask = arm_compute_save_reg_mask (); + + /* On some ARM architectures it is faster to use LDR rather than LDM to + load a single register. On other architectures, the cost is the same. + In 26 bit mode we have to use LDM in order to be able to restore the CPSR. */ + if ((live_regs_mask == (1 << LR_REGNUM)) + && ! TARGET_INTERWORK + && ! IS_INTERRUPT (func_type) + && (! really_return || TARGET_APCS_32)) + { + if (! really_return) + sprintf (instr, "ldr%s\t%%|lr, [%%|sp], #4", conditional); + else + sprintf (instr, "ldr%s\t%%|pc, [%%|sp], #4", conditional); + } + else if (live_regs_mask) + { + if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM)) + /* There are two possible reasons for the IP register being saved. + Either a stack frame was created, in which case IP contains the + old stack pointer, or an ISR routine corrupted it. If this in an + ISR routine then just restore IP, otherwise restore IP into SP. */ + if (! IS_INTERRUPT (func_type)) + { + live_regs_mask &= ~ (1 << IP_REGNUM); + live_regs_mask |= (1 << SP_REGNUM); + } + + /* Generate the load multiple instruction to restore the registers. */ + if (frame_pointer_needed) + sprintf (instr, "ldm%sea\t%%|fp, {", conditional); + else + sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional); + + for (reg = 0; reg <= SP_REGNUM; reg++) + if (live_regs_mask & (1 << reg)) + { + strcat (instr, "%|"); + strcat (instr, reg_names[reg]); + strcat (instr, ", "); + } + + if ((live_regs_mask & (1 << LR_REGNUM)) == 0) + { + /* If we are not restoring the LR register then we will + have added one too many commas to the list above. + Replace it with a closing brace. */ + instr [strlen (instr) - 2] = '}'; + } + else + { + strcat (instr, "%|"); + + /* At this point there should only be one or two registers left in + live_regs_mask: always LR, and possibly PC if we created a stack + frame. LR contains the return address. If we do not have any + special requirements for function exit (eg interworking, or ISR) + then we can load this value directly into the PC and save an + instruction. */ + if (! TARGET_INTERWORK + && ! IS_INTERRUPT (func_type) + && really_return) + strcat (instr, reg_names [PC_REGNUM]); + else + strcat (instr, reg_names [LR_REGNUM]); + + strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^"); + } + + if (really_return) + { + /* See if we need to generate an extra instruction to + perform the actual function return. */ + switch ((int) ARM_FUNC_TYPE (func_type)) + { + case ARM_FT_ISR: + case ARM_FT_FIQ: + output_asm_insn (instr, & operand); + + strcpy (instr, "sub"); + strcat (instr, conditional); + strcat (instr, "s\t%|pc, %|lr, #4"); + break; + + case ARM_FT_EXCEPTION: + output_asm_insn (instr, & operand); + + strcpy (instr, "mov"); + strcat (instr, conditional); + strcat (instr, "s\t%|pc, %|lr"); + break; + + case ARM_FT_INTERWORKED: + output_asm_insn (instr, & operand); + + strcpy (instr, "bx"); + strcat (instr, conditional); + strcat (instr, "\t%|lr"); + break; + + default: + /* The return has already been handled + by loading the LR into the PC. */ + if ((live_regs_mask & (1 << LR_REGNUM)) == 0) + { + output_asm_insn (instr, & operand); + + strcpy (instr, "mov"); + strcat (instr, conditional); + if (! TARGET_APCS_32) + strcat (instr, "s"); + strcat (instr, "\t%|pc, %|lr"); + } + break; + } + } + } + else if (really_return) + { + switch ((int) ARM_FUNC_TYPE (func_type)) + { + case ARM_FT_ISR: + case ARM_FT_FIQ: + sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional); + break; + + case ARM_FT_INTERWORKED: + sprintf (instr, "bx%s\t%%|lr", conditional); + break; + + case ARM_FT_EXCEPTION: + sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional); + break; + + default: + sprintf (instr, "mov%s%s\t%%|pc, %%|lr", + conditional, TARGET_APCS_32 ? "" : "s"); + break; + } + } + else + /* Nothing to load off the stack, and + no return instruction to generate. */ + return ""; + + output_asm_insn (instr, & operand); + + return ""; +} + +/* Write the function name into the code section, directly preceding + the function prologue. + + Code will be output similar to this: + t0 + .ascii "arm_poke_function_name", 0 + .align + t1 + .word 0xff000000 + (t1 - t0) + arm_poke_function_name + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + + When performing a stack backtrace, code can inspect the value + of 'pc' stored at 'fp' + 0. If the trace function then looks + at location pc - 12 and the top 8 bits are set, then we know + that there is a function name embedded immediately preceding this + location and has length ((pc[-3]) & 0xff000000). + + We assume that pc is declared as a pointer to an unsigned long. + + It is of no benefit to output the function name if we are assembling + a leaf function. These function types will not contain a stack + backtrace structure, therefore it is not possible to determine the + function name. */ + +void +arm_poke_function_name (stream, name) + FILE * stream; + const char * name; +{ + unsigned long alignlength; + unsigned long length; + rtx x; + + length = strlen (name) + 1; + alignlength = ROUND_UP (length); + + ASM_OUTPUT_ASCII (stream, name, length); + ASM_OUTPUT_ALIGN (stream, 2); + x = GEN_INT ((unsigned HOST_WIDE_INT) 0xff000000 + alignlength); + assemble_aligned_integer (UNITS_PER_WORD, x); +} + +/* Place some comments into the assembler stream + describing the current function. */ + +static void +arm_output_function_prologue (f, frame_size) + FILE * f; + HOST_WIDE_INT frame_size; +{ + unsigned long func_type; + + if (!TARGET_ARM) + { + thumb_output_function_prologue (f, frame_size); + return; + } + + /* Sanity check. */ + if (arm_ccfsm_state || arm_target_insn) + abort (); + + func_type = arm_current_func_type (); + + switch ((int) ARM_FUNC_TYPE (func_type)) + { + default: + case ARM_FT_NORMAL: + break; + case ARM_FT_INTERWORKED: + asm_fprintf (f, "\t%@ Function supports interworking.\n"); + break; + case ARM_FT_EXCEPTION_HANDLER: + asm_fprintf (f, "\t%@ C++ Exception Handler.\n"); + break; + case ARM_FT_ISR: + asm_fprintf (f, "\t%@ Interrupt Service Routine.\n"); + break; + case ARM_FT_FIQ: + asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n"); + break; + case ARM_FT_EXCEPTION: + asm_fprintf (f, "\t%@ ARM Exception Handler.\n"); + break; + } + + if (IS_NAKED (func_type)) + asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n"); + + if (IS_VOLATILE (func_type)) + asm_fprintf (f, "\t%@ Volatile: function does not return.\n"); + + if (IS_NESTED (func_type)) + asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n"); + + asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n", + current_function_args_size, + current_function_pretend_args_size, frame_size); + + asm_fprintf (f, "\t%@ frame_needed = %d, current_function_anonymous_args = %d\n", + frame_pointer_needed, + current_function_anonymous_args); + + if (cfun->machine->lr_save_eliminated) + asm_fprintf (f, "\t%@ link register save eliminated.\n"); + +#ifdef AOF_ASSEMBLER + if (flag_pic) + asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM); +#endif + + return_used_this_function = 0; +} + +const char * +arm_output_epilogue (really_return) + int really_return; +{ + int reg; + unsigned long saved_regs_mask; + unsigned long func_type; + /* If we need this, then it will always be at least this much. */ + int floats_offset = 12; + rtx operands[3]; + int frame_size = get_frame_size (); + FILE * f = asm_out_file; + rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; + + /* If we have already generated the return instruction + then it is futile to generate anything else. */ + if (use_return_insn (FALSE) && return_used_this_function) + return ""; + + func_type = arm_current_func_type (); + + if (IS_NAKED (func_type)) + /* Naked functions don't have epilogues. */ + return ""; + + if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) + { + rtx op; + + /* A volatile function should never return. Call abort. */ + op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort"); + assemble_external_libcall (op); + output_asm_insn ("bl\t%a0", &op); + + return ""; + } + + if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER + && ! really_return) + /* If we are throwing an exception, then we really must + be doing a return, so we can't tail-call. */ + abort (); + + saved_regs_mask = arm_compute_save_reg_mask (); + + /* Compute how far away the floats will be. */ + for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++) + if (saved_regs_mask & (1 << reg)) + floats_offset += 4; + + if (frame_pointer_needed) + { + if (arm_fpu_arch == FP_SOFT2) + { + for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + floats_offset += 12; + asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n", + reg, FP_REGNUM, floats_offset); + } + } + else + { + int start_reg = LAST_ARM_FP_REGNUM; + + for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) + { + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + floats_offset += 12; + + /* We can't unstack more than four registers at once. */ + if (start_reg - reg == 3) + { + asm_fprintf (f, "\tlfm\t%r, 4, [%r, #-%d]\n", + reg, FP_REGNUM, floats_offset); + start_reg = reg - 1; + } + } + else + { + if (reg != start_reg) + asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", + reg + 1, start_reg - reg, + FP_REGNUM, floats_offset); + start_reg = reg - 1; + } + } + + /* Just in case the last register checked also needs unstacking. */ + if (reg != start_reg) + asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", + reg + 1, start_reg - reg, + FP_REGNUM, floats_offset); + } + + /* saved_regs_mask should contain the IP, which at the time of stack + frame generation actually contains the old stack pointer. So a + quick way to unwind the stack is just pop the IP register directly + into the stack pointer. */ + if ((saved_regs_mask & (1 << IP_REGNUM)) == 0) + abort (); + saved_regs_mask &= ~ (1 << IP_REGNUM); + saved_regs_mask |= (1 << SP_REGNUM); + + /* There are two registers left in saved_regs_mask - LR and PC. We + only need to restore the LR register (the return address), but to + save time we can load it directly into the PC, unless we need a + special function exit sequence, or we are not really returning. */ + if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL) + /* Delete the LR from the register mask, so that the LR on + the stack is loaded into the PC in the register mask. */ + saved_regs_mask &= ~ (1 << LR_REGNUM); + else + saved_regs_mask &= ~ (1 << PC_REGNUM); + + print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask); + + if (IS_INTERRUPT (func_type)) + /* Interrupt handlers will have pushed the + IP onto the stack, so restore it now. */ + print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, 1 << IP_REGNUM); + } + else + { + /* Restore stack pointer if necessary. */ + if (frame_size + current_function_outgoing_args_size != 0) + { + operands[0] = operands[1] = stack_pointer_rtx; + operands[2] = GEN_INT (frame_size + + current_function_outgoing_args_size); + output_add_immediate (operands); + } + + if (arm_fpu_arch == FP_SOFT2) + { + for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) + if (regs_ever_live[reg] && !call_used_regs[reg]) + asm_fprintf (f, "\tldfe\t%r, [%r], #12\n", + reg, SP_REGNUM); + } + else + { + int start_reg = FIRST_ARM_FP_REGNUM; + + for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) + { + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + if (reg - start_reg == 3) + { + asm_fprintf (f, "\tlfmfd\t%r, 4, [%r]!\n", + start_reg, SP_REGNUM); + start_reg = reg + 1; + } + } + else + { + if (reg != start_reg) + asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", + start_reg, reg - start_reg, + SP_REGNUM); + + start_reg = reg + 1; + } + } + + /* Just in case the last register checked also needs unstacking. */ + if (reg != start_reg) + asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", + start_reg, reg - start_reg, SP_REGNUM); + } + + /* If we can, restore the LR into the PC. */ + if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL + && really_return + && current_function_pretend_args_size == 0 + && saved_regs_mask & (1 << LR_REGNUM)) + { + saved_regs_mask &= ~ (1 << LR_REGNUM); + saved_regs_mask |= (1 << PC_REGNUM); + } + + /* Load the registers off the stack. If we only have one register + to load use the LDR instruction - it is faster. */ + if (saved_regs_mask == (1 << LR_REGNUM)) + { + /* The excpetion handler ignores the LR, so we do + not really need to load it off the stack. */ + if (eh_ofs) + asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM); + else + asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM); + } + else if (saved_regs_mask) + print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask); + + if (current_function_pretend_args_size) + { + /* Unwind the pre-pushed regs. */ + operands[0] = operands[1] = stack_pointer_rtx; + operands[2] = GEN_INT (current_function_pretend_args_size); + output_add_immediate (operands); + } + } + +#if 0 + if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER) + /* Adjust the stack to remove the exception handler stuff. */ + asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM, + REGNO (eh_ofs)); +#endif + + if (! really_return) + return ""; + + /* Generate the return instruction. */ + switch ((int) ARM_FUNC_TYPE (func_type)) + { + case ARM_FT_EXCEPTION_HANDLER: + /* Even in 26-bit mode we do a mov (rather than a movs) + because we don't have the PSR bits set in the address. */ + asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM); + break; + + case ARM_FT_ISR: + case ARM_FT_FIQ: + asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM); + break; + + case ARM_FT_EXCEPTION: + asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); + break; + + case ARM_FT_INTERWORKED: + asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM); + break; + + default: + if (frame_pointer_needed) + /* If we used the frame pointer then the return adddress + will have been loaded off the stack directly into the + PC, so there is no need to issue a MOV instruction + here. */ + ; + else if (current_function_pretend_args_size == 0 + && (saved_regs_mask & (1 << LR_REGNUM))) + /* Similarly we may have been able to load LR into the PC + even if we did not create a stack frame. */ + ; + else if (TARGET_APCS_32) + asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM); + else + asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); + break; + } + + return ""; +} + +static void +arm_output_function_epilogue (file, frame_size) + FILE *file ATTRIBUTE_UNUSED; + HOST_WIDE_INT frame_size; +{ + if (TARGET_THUMB) + { + /* ??? Probably not safe to set this here, since it assumes that a + function will be emitted as assembly immediately after we generate + RTL for it. This does not happen for inline functions. */ + return_used_this_function = 0; + } + else + { + if (use_return_insn (FALSE) + && return_used_this_function + && (frame_size + current_function_outgoing_args_size) != 0 + && !frame_pointer_needed) + abort (); + + /* Reset the ARM-specific per-function variables. */ + current_function_anonymous_args = 0; + after_arm_reorg = 0; + } +} + +/* Generate and emit an insn that we will recognize as a push_multi. + Unfortunately, since this insn does not reflect very well the actual + semantics of the operation, we need to annotate the insn for the benefit + of DWARF2 frame unwind information. */ + +static rtx +emit_multi_reg_push (mask) + int mask; +{ + int num_regs = 0; + int num_dwarf_regs; + int i, j; + rtx par; + rtx dwarf; + int dwarf_par_index; + rtx tmp, reg; + + for (i = 0; i <= LAST_ARM_REGNUM; i++) + if (mask & (1 << i)) + num_regs++; + + if (num_regs == 0 || num_regs > 16) + abort (); + + /* We don't record the PC in the dwarf frame information. */ + num_dwarf_regs = num_regs; + if (mask & (1 << PC_REGNUM)) + num_dwarf_regs--; + + /* For the body of the insn we are going to generate an UNSPEC in + parallel with several USEs. This allows the insn to be recognised + by the push_multi pattern in the arm.md file. The insn looks + something like this: + + (parallel [ + (set (mem:BLK (pre_dec:BLK (reg:SI sp))) + (unspec:BLK [(reg:SI r4)] UNSPEC_PUSH_MULT)) + (use (reg:SI 11 fp)) + (use (reg:SI 12 ip)) + (use (reg:SI 14 lr)) + (use (reg:SI 15 pc)) + ]) + + For the frame note however, we try to be more explicit and actually + show each register being stored into the stack frame, plus a (single) + decrement of the stack pointer. We do it this way in order to be + friendly to the stack unwinding code, which only wants to see a single + stack decrement per instruction. The RTL we generate for the note looks + something like this: + + (sequence [ + (set (reg:SI sp) (plus:SI (reg:SI sp) (const_int -20))) + (set (mem:SI (reg:SI sp)) (reg:SI r4)) + (set (mem:SI (plus:SI (reg:SI sp) (const_int 4))) (reg:SI fp)) + (set (mem:SI (plus:SI (reg:SI sp) (const_int 8))) (reg:SI ip)) + (set (mem:SI (plus:SI (reg:SI sp) (const_int 12))) (reg:SI lr)) + ]) + + This sequence is used both by the code to support stack unwinding for + exceptions handlers and the code to generate dwarf2 frame debugging. */ + + par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs)); + dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_dwarf_regs + 1)); + RTX_FRAME_RELATED_P (dwarf) = 1; + dwarf_par_index = 1; + + for (i = 0; i <= LAST_ARM_REGNUM; i++) + { + if (mask & (1 << i)) + { + reg = gen_rtx_REG (SImode, i); + + XVECEXP (par, 0, 0) + = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_PRE_DEC (BLKmode, + stack_pointer_rtx)), + gen_rtx_UNSPEC (BLKmode, + gen_rtvec (1, reg), + UNSPEC_PUSH_MULT)); + + if (i != PC_REGNUM) + { + tmp = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (SImode, stack_pointer_rtx), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, dwarf_par_index) = tmp; + dwarf_par_index++; + } + + break; + } + } + + for (j = 1, i++; j < num_regs; i++) + { + if (mask & (1 << i)) + { + reg = gen_rtx_REG (SImode, i); + + XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg); + + if (i != PC_REGNUM) + { + tmp = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (SImode, + plus_constant (stack_pointer_rtx, + 4 * j)), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, dwarf_par_index++) = tmp; + } + + j++; + } + } + + par = emit_insn (par); + + tmp = gen_rtx_SET (SImode, + stack_pointer_rtx, + gen_rtx_PLUS (SImode, + stack_pointer_rtx, + GEN_INT (-4 * num_regs))); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, 0) = tmp; + + REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, + REG_NOTES (par)); + return par; +} + +static rtx +emit_sfm (base_reg, count) + int base_reg; + int count; +{ + rtx par; + rtx dwarf; + rtx tmp, reg; + int i; + + par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + dwarf = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + RTX_FRAME_RELATED_P (dwarf) = 1; + + reg = gen_rtx_REG (XFmode, base_reg++); + + XVECEXP (par, 0, 0) + = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), + gen_rtx_UNSPEC (BLKmode, + gen_rtvec (1, reg), + UNSPEC_PUSH_MULT)); + tmp + = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (XFmode, + gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, count - 1) = tmp; + + for (i = 1; i < count; i++) + { + reg = gen_rtx_REG (XFmode, base_reg++); + XVECEXP (par, 0, i) = gen_rtx_USE (VOIDmode, reg); + + tmp = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (XFmode, + gen_rtx_PRE_DEC (BLKmode, + stack_pointer_rtx)), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, count - i - 1) = tmp; + } + + par = emit_insn (par); + REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, + REG_NOTES (par)); + return par; +} + +/* Compute the distance from register FROM to register TO. + These can be the arg pointer (26), the soft frame pointer (25), + the stack pointer (13) or the hard frame pointer (11). + Typical stack layout looks like this: + + old stack pointer -> | | + ---- + | | \ + | | saved arguments for + | | vararg functions + | | / + -- + hard FP & arg pointer -> | | \ + | | stack + | | frame + | | / + -- + | | \ + | | call saved + | | registers + soft frame pointer -> | | / + -- + | | \ + | | local + | | variables + | | / + -- + | | \ + | | outgoing + | | arguments + current stack pointer -> | | / + -- + + For a given funciton some or all of these stack compomnents + may not be needed, giving rise to the possibility of + eliminating some of the registers. + + The values returned by this function must reflect the behaviour + of arm_expand_prologue() and arm_compute_save_reg_mask(). + + The sign of the number returned reflects the direction of stack + growth, so the values are positive for all eliminations except + from the soft frame pointer to the hard frame pointer. */ + +unsigned int +arm_compute_initial_elimination_offset (from, to) + unsigned int from; + unsigned int to; +{ + unsigned int local_vars = (get_frame_size () + 3) & ~3; + unsigned int outgoing_args = current_function_outgoing_args_size; + unsigned int stack_frame; + unsigned int call_saved_registers; + unsigned long func_type; + + func_type = arm_current_func_type (); + + /* Volatile functions never return, so there is + no need to save call saved registers. */ + call_saved_registers = 0; + if (! IS_VOLATILE (func_type)) + { + unsigned int reg_mask; + unsigned int reg; + + /* Make sure that we compute which registers will be saved + on the stack using the same algorithm that is used by + arm_compute_save_reg_mask(). */ + reg_mask = arm_compute_save_reg0_reg12_mask (); + + /* Now count the number of bits set in save_reg_mask. + For each set bit we need 4 bytes of stack space. */ + while (reg_mask) + { + call_saved_registers += 4; + reg_mask = reg_mask & ~ (reg_mask & - reg_mask); + } + + if (regs_ever_live[LR_REGNUM] + /* If a stack frame is going to be created, the LR will + be saved as part of that, so we do not need to allow + for it here. */ + && ! frame_pointer_needed) + call_saved_registers += 4; + + /* If the hard floating point registers are going to be + used then they must be saved on the stack as well. + Each register occupies 12 bytes of stack space. */ + for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg ++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) + call_saved_registers += 12; + } + + /* The stack frame contains 4 registers - the old frame pointer, + the old stack pointer, the return address and PC of the start + of the function. */ + stack_frame = frame_pointer_needed ? 16 : 0; + + /* OK, now we have enough information to compute the distances. + There must be an entry in these switch tables for each pair + of registers in ELIMINABLE_REGS, even if some of the entries + seem to be redundant or useless. */ + switch (from) + { + case ARG_POINTER_REGNUM: + switch (to) + { + case THUMB_HARD_FRAME_POINTER_REGNUM: + return 0; + + case FRAME_POINTER_REGNUM: + /* This is the reverse of the soft frame pointer + to hard frame pointer elimination below. */ + if (call_saved_registers == 0 && stack_frame == 0) + return 0; + return (call_saved_registers + stack_frame - 4); + + case ARM_HARD_FRAME_POINTER_REGNUM: + /* If there is no stack frame then the hard + frame pointer and the arg pointer coincide. */ + if (stack_frame == 0 && call_saved_registers != 0) + return 0; + /* FIXME: Not sure about this. Maybe we should always return 0 ? */ + return (frame_pointer_needed + && current_function_needs_context + && ! current_function_anonymous_args) ? 4 : 0; + + case STACK_POINTER_REGNUM: + /* If nothing has been pushed on the stack at all + then this will return -4. This *is* correct! */ + return call_saved_registers + stack_frame + local_vars + outgoing_args - 4; + + default: + abort (); + } + break; + + case FRAME_POINTER_REGNUM: + switch (to) + { + case THUMB_HARD_FRAME_POINTER_REGNUM: + return 0; + + case ARM_HARD_FRAME_POINTER_REGNUM: + /* The hard frame pointer points to the top entry in the + stack frame. The soft frame pointer to the bottom entry + in the stack frame. If there is no stack frame at all, + then they are identical. */ + if (call_saved_registers == 0 && stack_frame == 0) + return 0; + return - (call_saved_registers + stack_frame - 4); + + case STACK_POINTER_REGNUM: + return local_vars + outgoing_args; + + default: + abort (); + } + break; + + default: + /* You cannot eliminate from the stack pointer. + In theory you could eliminate from the hard frame + pointer to the stack pointer, but this will never + happen, since if a stack frame is not needed the + hard frame pointer will never be used. */ + abort (); + } +} + +/* Generate the prologue instructions for entry into an ARM function. */ + +void +arm_expand_prologue () +{ + int reg; + rtx amount; + rtx insn; + rtx ip_rtx; + unsigned long live_regs_mask; + unsigned long func_type; + int fp_offset = 0; + int saved_pretend_args = 0; + unsigned int args_to_push; + + func_type = arm_current_func_type (); + + /* Naked functions don't have prologues. */ + if (IS_NAKED (func_type)) + return; + + /* Make a copy of c_f_p_a_s as we may need to modify it locally. */ + args_to_push = current_function_pretend_args_size; + + /* Compute which register we will have to save onto the stack. */ + live_regs_mask = arm_compute_save_reg_mask (); + + ip_rtx = gen_rtx_REG (SImode, IP_REGNUM); + + if (frame_pointer_needed) + { + if (IS_INTERRUPT (func_type)) + { + /* Interrupt functions must not corrupt any registers. + Creating a frame pointer however, corrupts the IP + register, so we must push it first. */ + insn = emit_multi_reg_push (1 << IP_REGNUM); + + /* Do not set RTX_FRAME_RELATED_P on this insn. + The dwarf stack unwinding code only wants to see one + stack decrement per function, and this is not it. If + this instruction is labeled as being part of the frame + creation sequence then dwarf2out_frame_debug_expr will + abort when it encounters the assignment of IP to FP + later on, since the use of SP here establishes SP as + the CFA register and not IP. + + Anyway this instruction is not really part of the stack + frame creation although it is part of the prologue. */ + } + else if (IS_NESTED (func_type)) + { + /* The Static chain register is the same as the IP register + used as a scratch register during stack frame creation. + To get around this need to find somewhere to store IP + whilst the frame is being created. We try the following + places in order: + + 1. The last argument register. + 2. A slot on the stack above the frame. (This only + works if the function is not a varargs function). + 3. Register r3, after pushing the argument registers + onto the stack. + + Note - we only need to tell the dwarf2 backend about the SP + adjustment in the second variant; the static chain register + doesn't need to be unwound, as it doesn't contain a value + inherited from the caller. */ + + if (regs_ever_live[3] == 0) + { + insn = gen_rtx_REG (SImode, 3); + insn = gen_rtx_SET (SImode, insn, ip_rtx); + insn = emit_insn (insn); + } + else if (args_to_push == 0) + { + rtx dwarf; + insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx); + insn = gen_rtx_MEM (SImode, insn); + insn = gen_rtx_SET (VOIDmode, insn, ip_rtx); + insn = emit_insn (insn); + + fp_offset = 4; + + /* Just tell the dwarf backend that we adjusted SP. */ + dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + gen_rtx_PLUS (SImode, stack_pointer_rtx, + GEN_INT (-fp_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + dwarf, REG_NOTES (insn)); + } + else + { + /* Store the args on the stack. */ + if (current_function_anonymous_args) + insn = emit_multi_reg_push + ((0xf0 >> (args_to_push / 4)) & 0xf); + else + insn = emit_insn + (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (- args_to_push))); + + RTX_FRAME_RELATED_P (insn) = 1; + + saved_pretend_args = 1; + fp_offset = args_to_push; + args_to_push = 0; + + /* Now reuse r3 to preserve IP. */ + insn = gen_rtx_REG (SImode, 3); + insn = gen_rtx_SET (SImode, insn, ip_rtx); + (void) emit_insn (insn); + } + } + + if (fp_offset) + { + insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset)); + insn = gen_rtx_SET (SImode, ip_rtx, insn); + } + else + insn = gen_movsi (ip_rtx, stack_pointer_rtx); + + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (args_to_push) + { + /* Push the argument registers, or reserve space for them. */ + if (current_function_anonymous_args) + insn = emit_multi_reg_push + ((0xf0 >> (args_to_push / 4)) & 0xf); + else + insn = emit_insn + (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (- args_to_push))); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (live_regs_mask) + { + insn = emit_multi_reg_push (live_regs_mask); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (! IS_VOLATILE (func_type)) + { + /* Save any floating point call-saved registers used by this function. */ + if (arm_fpu_arch == FP_SOFT2) + { + for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --) + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx); + insn = gen_rtx_MEM (XFmode, insn); + insn = emit_insn (gen_rtx_SET (VOIDmode, insn, + gen_rtx_REG (XFmode, reg))); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + else + { + int start_reg = LAST_ARM_FP_REGNUM; + + for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --) + { + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + if (start_reg - reg == 3) + { + insn = emit_sfm (reg, 4); + RTX_FRAME_RELATED_P (insn) = 1; + start_reg = reg - 1; + } + } + else + { + if (start_reg != reg) + { + insn = emit_sfm (reg + 1, start_reg - reg); + RTX_FRAME_RELATED_P (insn) = 1; + } + start_reg = reg - 1; + } + } + + if (start_reg != reg) + { + insn = emit_sfm (reg + 1, start_reg - reg); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + } + + if (frame_pointer_needed) + { + /* Create the new frame pointer. */ + insn = GEN_INT (-(4 + args_to_push + fp_offset)); + insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn)); + RTX_FRAME_RELATED_P (insn) = 1; + + if (IS_NESTED (func_type)) + { + /* Recover the static chain register. */ + if (regs_ever_live [3] == 0 + || saved_pretend_args) + insn = gen_rtx_REG (SImode, 3); + else /* if (current_function_pretend_args_size == 0) */ + { + insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, GEN_INT (4)); + insn = gen_rtx_MEM (SImode, insn); + } + + emit_insn (gen_rtx_SET (SImode, ip_rtx, insn)); + /* Add a USE to stop propagate_one_insn() from barfing. */ + emit_insn (gen_prologue_use (ip_rtx)); + } + } + + amount = GEN_INT (-(get_frame_size () + + current_function_outgoing_args_size)); + + if (amount != const0_rtx) + { + /* This add can produce multiple insns for a large constant, so we + need to get tricky. */ + rtx last = get_last_insn (); + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + amount)); + do + { + last = last ? NEXT_INSN (last) : get_insns (); + RTX_FRAME_RELATED_P (last) = 1; + } + while (last != insn); + + /* If the frame pointer is needed, emit a special barrier that + will prevent the scheduler from moving stores to the frame + before the stack adjustment. */ + if (frame_pointer_needed) + { + rtx unspec = gen_rtx_UNSPEC (SImode, + gen_rtvec (2, stack_pointer_rtx, + hard_frame_pointer_rtx), + UNSPEC_PRLG_STK); + + insn = emit_insn (gen_rtx_CLOBBER (VOIDmode, + gen_rtx_MEM (BLKmode, unspec))); + } + } + + /* If we are profiling, make sure no instructions are scheduled before + the call to mcount. Similarly if the user has requested no + scheduling in the prolog. */ + if (current_function_profile || TARGET_NO_SCHED_PRO) + emit_insn (gen_blockage ()); + + /* If the link register is being kept alive, with the return address in it, + then make sure that it does not get reused by the ce2 pass. */ + if ((live_regs_mask & (1 << LR_REGNUM)) == 0) + { + emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM))); + cfun->machine->lr_save_eliminated = 1; + } +} + +/* If CODE is 'd', then the X is a condition operand and the instruction + should only be executed if the condition is true. + if CODE is 'D', then the X is a condition operand and the instruction + should only be executed if the condition is false: however, if the mode + of the comparison is CCFPEmode, then always execute the instruction -- we + do this because in these circumstances !GE does not necessarily imply LT; + in these cases the instruction pattern will take care to make sure that + an instruction containing %d will follow, thereby undoing the effects of + doing this instruction unconditionally. + If CODE is 'N' then X is a floating point operand that must be negated + before output. + If CODE is 'B' then output a bitwise inverted value of X (a const int). + If X is a REG and CODE is `M', output a ldm/stm style multi-reg. */ + +void +arm_print_operand (stream, x, code) + FILE * stream; + rtx x; + int code; +{ + switch (code) + { + case '@': + fputs (ASM_COMMENT_START, stream); + return; + + case '_': + fputs (user_label_prefix, stream); + return; + + case '|': + fputs (REGISTER_PREFIX, stream); + return; + + case '?': + if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) + { + if (TARGET_THUMB || current_insn_predicate != NULL) + abort (); + + fputs (arm_condition_codes[arm_current_cc], stream); + } + else if (current_insn_predicate) + { + enum arm_cond_code code; + + if (TARGET_THUMB) + abort (); + + code = get_arm_condition_code (current_insn_predicate); + fputs (arm_condition_codes[code], stream); + } + return; + + case 'N': + { + REAL_VALUE_TYPE r; + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + r = REAL_VALUE_NEGATE (r); + fprintf (stream, "%s", fp_const_from_val (&r)); + } + return; + + case 'B': + if (GET_CODE (x) == CONST_INT) + { + HOST_WIDE_INT val; + val = ARM_SIGN_EXTEND (~INTVAL (x)); + fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val); + } + else + { + putc ('~', stream); + output_addr_const (stream, x); + } + return; + + case 'i': + fprintf (stream, "%s", arithmetic_instr (x, 1)); + return; + + case 'I': + fprintf (stream, "%s", arithmetic_instr (x, 0)); + return; + + case 'S': + { + HOST_WIDE_INT val; + const char * shift = shift_op (x, &val); + + if (shift) + { + fprintf (stream, ", %s ", shift_op (x, &val)); + if (val == -1) + arm_print_operand (stream, XEXP (x, 1), 0); + else + { + fputc ('#', stream); + fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val); + } + } + } + return; + + /* An explanation of the 'Q', 'R' and 'H' register operands: + + In a pair of registers containing a DI or DF value the 'Q' + operand returns the register number of the register containing + the least signficant part of the value. The 'R' operand returns + the register number of the register containing the most + significant part of the value. + + The 'H' operand returns the higher of the two register numbers. + On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the + same as the 'Q' operand, since the most signficant part of the + value is held in the lower number register. The reverse is true + on systems where WORDS_BIG_ENDIAN is false. + + The purpose of these operands is to distinguish between cases + where the endian-ness of the values is important (for example + when they are added together), and cases where the endian-ness + is irrelevant, but the order of register operations is important. + For example when loading a value from memory into a register + pair, the endian-ness does not matter. Provided that the value + from the lower memory address is put into the lower numbered + register, and the value from the higher address is put into the + higher numbered register, the load will work regardless of whether + the value being loaded is big-wordian or little-wordian. The + order of the two register loads can matter however, if the address + of the memory location is actually held in one of the registers + being overwritten by the load. */ + case 'Q': + if (REGNO (x) > LAST_ARM_REGNUM) + abort (); + asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)); + return; + + case 'R': + if (REGNO (x) > LAST_ARM_REGNUM) + abort (); + asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)); + return; + + case 'H': + if (REGNO (x) > LAST_ARM_REGNUM) + abort (); + asm_fprintf (stream, "%r", REGNO (x) + 1); + return; + + case 'm': + asm_fprintf (stream, "%r", + GET_CODE (XEXP (x, 0)) == REG + ? REGNO (XEXP (x, 0)) : REGNO (XEXP (XEXP (x, 0), 0))); + return; + + case 'M': + asm_fprintf (stream, "{%r-%r}", + REGNO (x), + REGNO (x) + NUM_REGS (GET_MODE (x)) - 1); + return; + + case 'd': + if (!x) + return; + + if (TARGET_ARM) + fputs (arm_condition_codes[get_arm_condition_code (x)], + stream); + else + fputs (thumb_condition_code (x, 0), stream); + return; + + case 'D': + if (!x) + return; + + if (TARGET_ARM) + fputs (arm_condition_codes[ARM_INVERSE_CONDITION_CODE + (get_arm_condition_code (x))], + stream); + else + fputs (thumb_condition_code (x, 1), stream); + return; + + default: + if (x == 0) + abort (); + + if (GET_CODE (x) == REG) + asm_fprintf (stream, "%r", REGNO (x)); + else if (GET_CODE (x) == MEM) + { + output_memory_reference_mode = GET_MODE (x); + output_address (XEXP (x, 0)); + } + else if (GET_CODE (x) == CONST_DOUBLE) + fprintf (stream, "#%s", fp_immediate_constant (x)); + else if (GET_CODE (x) == NEG) + abort (); /* This should never happen now. */ + else + { + fputc ('#', stream); + output_addr_const (stream, x); + } + } +} + +#ifndef AOF_ASSEMBLER +/* Target hook for assembling integer objects. The ARM version needs to + handle word-sized values specially. */ + +static bool +arm_assemble_integer (x, size, aligned_p) + rtx x; + unsigned int size; + int aligned_p; +{ + if (size == UNITS_PER_WORD && aligned_p) + { + fputs ("\t.word\t", asm_out_file); + output_addr_const (asm_out_file, x); + + /* Mark symbols as position independent. We only do this in the + .text segment, not in the .data segment. */ + if (NEED_GOT_RELOC && flag_pic && making_const_table && + (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)) + { + if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) + fputs ("(GOTOFF)", asm_out_file); + else if (GET_CODE (x) == LABEL_REF) + fputs ("(GOTOFF)", asm_out_file); + else + fputs ("(GOT)", asm_out_file); + } + fputc ('\n', asm_out_file); + return true; + } + + return default_assemble_integer (x, size, aligned_p); +} +#endif + +/* A finite state machine takes care of noticing whether or not instructions + can be conditionally executed, and thus decrease execution time and code + size by deleting branch instructions. The fsm is controlled by + final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. */ + +/* The state of the fsm controlling condition codes are: + 0: normal, do nothing special + 1: make ASM_OUTPUT_OPCODE not output this instruction + 2: make ASM_OUTPUT_OPCODE not output this instruction + 3: make instructions conditional + 4: make instructions conditional + + State transitions (state->state by whom under condition): + 0 -> 1 final_prescan_insn if the `target' is a label + 0 -> 2 final_prescan_insn if the `target' is an unconditional branch + 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch + 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch + 3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached + (the target label has CODE_LABEL_NUMBER equal to arm_target_label). + 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached + (the target insn is arm_target_insn). + + If the jump clobbers the conditions then we use states 2 and 4. + + A similar thing can be done with conditional return insns. + + XXX In case the `target' is an unconditional branch, this conditionalising + of the instructions always reduces code size, but not always execution + time. But then, I want to reduce the code size to somewhere near what + /bin/cc produces. */ + +/* Returns the index of the ARM condition code string in + `arm_condition_codes'. COMPARISON should be an rtx like + `(eq (...) (...))'. */ + +static enum arm_cond_code +get_arm_condition_code (comparison) + rtx comparison; +{ + enum machine_mode mode = GET_MODE (XEXP (comparison, 0)); + int code; + enum rtx_code comp_code = GET_CODE (comparison); + + if (GET_MODE_CLASS (mode) != MODE_CC) + mode = SELECT_CC_MODE (comp_code, XEXP (comparison, 0), + XEXP (comparison, 1)); + + switch (mode) + { + case CC_DNEmode: code = ARM_NE; goto dominance; + case CC_DEQmode: code = ARM_EQ; goto dominance; + case CC_DGEmode: code = ARM_GE; goto dominance; + case CC_DGTmode: code = ARM_GT; goto dominance; + case CC_DLEmode: code = ARM_LE; goto dominance; + case CC_DLTmode: code = ARM_LT; goto dominance; + case CC_DGEUmode: code = ARM_CS; goto dominance; + case CC_DGTUmode: code = ARM_HI; goto dominance; + case CC_DLEUmode: code = ARM_LS; goto dominance; + case CC_DLTUmode: code = ARM_CC; + + dominance: + if (comp_code != EQ && comp_code != NE) + abort (); + + if (comp_code == EQ) + return ARM_INVERSE_CONDITION_CODE (code); + return code; + + case CC_NOOVmode: + switch (comp_code) + { + case NE: return ARM_NE; + case EQ: return ARM_EQ; + case GE: return ARM_PL; + case LT: return ARM_MI; + default: abort (); + } + + case CC_Zmode: + switch (comp_code) + { + case NE: return ARM_NE; + case EQ: return ARM_EQ; + default: abort (); + } + + case CCFPEmode: + case CCFPmode: + /* These encodings assume that AC=1 in the FPA system control + byte. This allows us to handle all cases except UNEQ and + LTGT. */ + switch (comp_code) + { + case GE: return ARM_GE; + case GT: return ARM_GT; + case LE: return ARM_LS; + case LT: return ARM_MI; + case NE: return ARM_NE; + case EQ: return ARM_EQ; + case ORDERED: return ARM_VC; + case UNORDERED: return ARM_VS; + case UNLT: return ARM_LT; + case UNLE: return ARM_LE; + case UNGT: return ARM_HI; + case UNGE: return ARM_PL; + /* UNEQ and LTGT do not have a representation. */ + case UNEQ: /* Fall through. */ + case LTGT: /* Fall through. */ + default: abort (); + } + + case CC_SWPmode: + switch (comp_code) + { + case NE: return ARM_NE; + case EQ: return ARM_EQ; + case GE: return ARM_LE; + case GT: return ARM_LT; + case LE: return ARM_GE; + case LT: return ARM_GT; + case GEU: return ARM_LS; + case GTU: return ARM_CC; + case LEU: return ARM_CS; + case LTU: return ARM_HI; + default: abort (); + } + + case CC_Cmode: + switch (comp_code) + { + case LTU: return ARM_CS; + case GEU: return ARM_CC; + default: abort (); + } + + case CCmode: + switch (comp_code) + { + case NE: return ARM_NE; + case EQ: return ARM_EQ; + case GE: return ARM_GE; + case GT: return ARM_GT; + case LE: return ARM_LE; + case LT: return ARM_LT; + case GEU: return ARM_CS; + case GTU: return ARM_HI; + case LEU: return ARM_LS; + case LTU: return ARM_CC; + default: abort (); + } + + default: abort (); + } + + abort (); +} + + +void +arm_final_prescan_insn (insn) + rtx insn; +{ + /* BODY will hold the body of INSN. */ + rtx body = PATTERN (insn); + + /* This will be 1 if trying to repeat the trick, and things need to be + reversed if it appears to fail. */ + int reverse = 0; + + /* JUMP_CLOBBERS will be one implies that the conditions if a branch is + taken are clobbered, even if the rtl suggests otherwise. It also + means that we have to grub around within the jump expression to find + out what the conditions are when the jump isn't taken. */ + int jump_clobbers = 0; + + /* If we start with a return insn, we only succeed if we find another one. */ + int seeking_return = 0; + + /* START_INSN will hold the insn from where we start looking. This is the + first insn after the following code_label if REVERSE is true. */ + rtx start_insn = insn; + + /* If in state 4, check if the target branch is reached, in order to + change back to state 0. */ + if (arm_ccfsm_state == 4) + { + if (insn == arm_target_insn) + { + arm_target_insn = NULL; + arm_ccfsm_state = 0; + } + return; + } + + /* If in state 3, it is possible to repeat the trick, if this insn is an + unconditional branch to a label, and immediately following this branch + is the previous target label which is only used once, and the label this + branch jumps to is not too far off. */ + if (arm_ccfsm_state == 3) + { + if (simplejump_p (insn)) + { + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == BARRIER) + { + /* XXX Isn't this always a barrier? */ + start_insn = next_nonnote_insn (start_insn); + } + if (GET_CODE (start_insn) == CODE_LABEL + && CODE_LABEL_NUMBER (start_insn) == arm_target_label + && LABEL_NUSES (start_insn) == 1) + reverse = TRUE; + else + return; + } + else if (GET_CODE (body) == RETURN) + { + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == BARRIER) + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == CODE_LABEL + && CODE_LABEL_NUMBER (start_insn) == arm_target_label + && LABEL_NUSES (start_insn) == 1) + { + reverse = TRUE; + seeking_return = 1; + } + else + return; + } + else + return; + } + + if (arm_ccfsm_state != 0 && !reverse) + abort (); + if (GET_CODE (insn) != JUMP_INSN) + return; + + /* This jump might be paralleled with a clobber of the condition codes + the jump should always come first */ + if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0) + body = XVECEXP (body, 0, 0); + +#if 0 + /* If this is a conditional return then we don't want to know */ + if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC + && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE + && (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN + || GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)) + return; +#endif + + if (reverse + || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC + && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) + { + int insns_skipped; + int fail = FALSE, succeed = FALSE; + /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ + int then_not_else = TRUE; + rtx this_insn = start_insn, label = 0; + + /* If the jump cannot be done with one instruction, we cannot + conditionally execute the instruction in the inverse case. */ + if (get_attr_conds (insn) == CONDS_JUMP_CLOB) + { + jump_clobbers = 1; + return; + } + + /* Register the insn jumped to. */ + if (reverse) + { + if (!seeking_return) + label = XEXP (SET_SRC (body), 0); + } + else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) + label = XEXP (XEXP (SET_SRC (body), 1), 0); + else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) + { + label = XEXP (XEXP (SET_SRC (body), 2), 0); + then_not_else = FALSE; + } + else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN) + seeking_return = 1; + else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN) + { + seeking_return = 1; + then_not_else = FALSE; + } + else + abort (); + + /* See how many insns this branch skips, and what kind of insns. If all + insns are okay, and the label or unconditional branch to the same + label is not too far away, succeed. */ + for (insns_skipped = 0; + !fail && !succeed && insns_skipped++ < max_insns_skipped;) + { + rtx scanbody; + + this_insn = next_nonnote_insn (this_insn); + if (!this_insn) + break; + + switch (GET_CODE (this_insn)) + { + case CODE_LABEL: + /* Succeed if it is the target label, otherwise fail since + control falls in from somewhere else. */ + if (this_insn == label) + { + if (jump_clobbers) + { + arm_ccfsm_state = 2; + this_insn = next_nonnote_insn (this_insn); + } + else + arm_ccfsm_state = 1; + succeed = TRUE; + } + else + fail = TRUE; + break; + + case BARRIER: + /* Succeed if the following insn is the target label. + Otherwise fail. + If return insns are used then the last insn in a function + will be a barrier. */ + this_insn = next_nonnote_insn (this_insn); + if (this_insn && this_insn == label) + { + if (jump_clobbers) + { + arm_ccfsm_state = 2; + this_insn = next_nonnote_insn (this_insn); + } + else + arm_ccfsm_state = 1; + succeed = TRUE; + } + else + fail = TRUE; + break; + + case CALL_INSN: + /* If using 32-bit addresses the cc is not preserved over + calls. */ + if (TARGET_APCS_32) + { + /* Succeed if the following insn is the target label, + or if the following two insns are a barrier and + the target label. */ + this_insn = next_nonnote_insn (this_insn); + if (this_insn && GET_CODE (this_insn) == BARRIER) + this_insn = next_nonnote_insn (this_insn); + + if (this_insn && this_insn == label + && insns_skipped < max_insns_skipped) + { + if (jump_clobbers) + { + arm_ccfsm_state = 2; + this_insn = next_nonnote_insn (this_insn); + } + else + arm_ccfsm_state = 1; + succeed = TRUE; + } + else + 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, + fail. */ + /* XXX Probably, the tests for SET and the PC are unnecessary. */ + + scanbody = PATTERN (this_insn); + if (GET_CODE (scanbody) == SET + && GET_CODE (SET_DEST (scanbody)) == PC) + { + if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF + && XEXP (SET_SRC (scanbody), 0) == label && !reverse) + { + arm_ccfsm_state = 2; + succeed = TRUE; + } + else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) + fail = TRUE; + } + /* Fail if a conditional return is undesirable (eg on a + StrongARM), but still allow this if optimizing for size. */ + else if (GET_CODE (scanbody) == RETURN + && !use_return_insn (TRUE) + && !optimize_size) + fail = TRUE; + else if (GET_CODE (scanbody) == RETURN + && seeking_return) + { + arm_ccfsm_state = 2; + succeed = TRUE; + } + else if (GET_CODE (scanbody) == PARALLEL) + { + switch (get_attr_conds (this_insn)) + { + case CONDS_NOCOND: + break; + default: + fail = TRUE; + break; + } + } + else + fail = TRUE; /* Unrecognized jump (eg epilogue). */ + + break; + + case INSN: + /* Instructions using or affecting the condition codes make it + fail. */ + scanbody = PATTERN (this_insn); + if (!(GET_CODE (scanbody) == SET + || GET_CODE (scanbody) == PARALLEL) + || get_attr_conds (this_insn) != CONDS_NOCOND) + fail = TRUE; + break; + + default: + break; + } + } + if (succeed) + { + if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse)) + arm_target_label = CODE_LABEL_NUMBER (label); + else if (seeking_return || arm_ccfsm_state == 2) + { + while (this_insn && GET_CODE (PATTERN (this_insn)) == USE) + { + this_insn = next_nonnote_insn (this_insn); + if (this_insn && (GET_CODE (this_insn) == BARRIER + || GET_CODE (this_insn) == CODE_LABEL)) + abort (); + } + if (!this_insn) + { + /* Oh, dear! we ran off the end.. give up */ + recog (PATTERN (insn), insn, NULL); + arm_ccfsm_state = 0; + arm_target_insn = NULL; + return; + } + arm_target_insn = this_insn; + } + else + abort (); + if (jump_clobbers) + { + if (reverse) + abort (); + arm_current_cc = + get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body), + 0), 0), 1)); + if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND) + arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); + if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE) + arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); + } + else + { + /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from + what it was. */ + if (!reverse) + arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), + 0)); + } + + if (reverse || then_not_else) + arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); + } + + /* Restore recog_data (getting the attributes of other insns can + destroy this array, but final.c assumes that it remains intact + across this call; since the insn has been recognized already we + call recog direct). */ + recog (PATTERN (insn), insn, NULL); + } +} + +/* Returns true if REGNO is a valid register + for holding a quantity of tyoe MODE. */ + +int +arm_hard_regno_mode_ok (regno, mode) + unsigned int regno; + enum machine_mode mode; +{ + if (GET_MODE_CLASS (mode) == MODE_CC) + return regno == CC_REGNUM; + + if (TARGET_THUMB) + /* For the Thumb we only allow values bigger than SImode in + registers 0 - 6, so that there is always a second low + register available to hold the upper part of the value. + We probably we ought to ensure that the register is the + start of an even numbered register pair. */ + return (NUM_REGS (mode) < 2) || (regno < LAST_LO_REGNUM); + + if (regno <= LAST_ARM_REGNUM) + /* We allow an SImode or smaller value to be stored in any + general purpose register. This does not mean, for example + that GCC will choose to store a variable in the stack pointer + since it is a fixed register. But it is important to allow + access to these special registers, so that they can be + referenced from C code via the asm assembler alias, eg: + + register char * stack_ptr asm ("sp"); + + For any mode requiring more than one register to hold the + value we restrict the choice so that r13, r14, and r15 + cannot be part of the register set. */ + return (NUM_REGS (mode) <= 1) + || (regno < (SP_REGNUM - (unsigned) NUM_REGS (mode))); + + if ( regno == FRAME_POINTER_REGNUM + || regno == ARG_POINTER_REGNUM) + /* We only allow integers in the fake hard registers. */ + return GET_MODE_CLASS (mode) == MODE_INT; + + /* The only registers left are the FPU registers + which we only allow to hold FP values. */ + return GET_MODE_CLASS (mode) == MODE_FLOAT + && regno >= FIRST_ARM_FP_REGNUM + && regno <= LAST_ARM_FP_REGNUM; +} + +int +arm_regno_class (regno) + int regno; +{ + if (TARGET_THUMB) + { + if (regno == STACK_POINTER_REGNUM) + return STACK_REG; + if (regno == CC_REGNUM) + return CC_REG; + if (regno < 8) + return LO_REGS; + return HI_REGS; + } + + if ( regno <= LAST_ARM_REGNUM + || regno == FRAME_POINTER_REGNUM + || regno == ARG_POINTER_REGNUM) + return GENERAL_REGS; + + if (regno == CC_REGNUM) + return NO_REGS; + + return FPU_REGS; +} + +/* Handle a special case when computing the offset + of an argument from the frame pointer. */ + +int +arm_debugger_arg_offset (value, addr) + int value; + rtx addr; +{ + rtx insn; + + /* We are only interested if dbxout_parms() failed to compute the offset. */ + if (value != 0) + return 0; + + /* We can only cope with the case where the address is held in a register. */ + if (GET_CODE (addr) != REG) + return 0; + + /* If we are using the frame pointer to point at the argument, then + an offset of 0 is correct. */ + if (REGNO (addr) == (unsigned) HARD_FRAME_POINTER_REGNUM) + return 0; + + /* If we are using the stack pointer to point at the + argument, then an offset of 0 is correct. */ + if ((TARGET_THUMB || !frame_pointer_needed) + && REGNO (addr) == SP_REGNUM) + return 0; + + /* Oh dear. The argument is pointed to by a register rather + than being held in a register, or being stored at a known + offset from the frame pointer. Since GDB only understands + those two kinds of argument we must translate the address + held in the register into an offset from the frame pointer. + We do this by searching through the insns for the function + looking to see where this register gets its value. If the + register is initialised from the frame pointer plus an offset + then we are in luck and we can continue, otherwise we give up. + + This code is exercised by producing debugging information + for a function with arguments like this: + + double func (double a, double b, int c, double d) {return d;} + + Without this code the stab for parameter 'd' will be set to + an offset of 0 from the frame pointer, rather than 8. */ + + /* The if() statement says: + + If the insn is a normal instruction + and if the insn is setting the value in a register + and if the register being set is the register holding the address of the argument + and if the address is computing by an addition + that involves adding to a register + which is the frame pointer + a constant integer + + then... */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if ( GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && REGNO (XEXP (PATTERN (insn), 0)) == REGNO (addr) + && GET_CODE (XEXP (PATTERN (insn), 1)) == PLUS + && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 0)) == REG + && REGNO (XEXP (XEXP (PATTERN (insn), 1), 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM + && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 1)) == CONST_INT + ) + { + value = INTVAL (XEXP (XEXP (PATTERN (insn), 1), 1)); + + break; + } + } + + if (value == 0) + { + debug_rtx (addr); + warning ("unable to compute real location of stacked parameter"); + value = 8; /* XXX magic hack */ + } + + return value; +} + +#define def_builtin(NAME, TYPE, CODE) \ + builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL) + +void +arm_init_builtins () +{ + tree endlink = void_list_node; + tree int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink); + tree pchar_type_node = build_pointer_type (char_type_node); + + tree int_ftype_int, void_ftype_pchar; + + /* void func (void *) */ + void_ftype_pchar + = build_function_type (void_type_node, + tree_cons (NULL_TREE, pchar_type_node, endlink)); + + /* int func (int) */ + int_ftype_int + = build_function_type (integer_type_node, int_endlink); + + /* Initialize arm V5 builtins. */ + if (arm_arch5) + def_builtin ("__builtin_clz", int_ftype_int, ARM_BUILTIN_CLZ); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +rtx +arm_expand_builtin (exp, target, subtarget, mode, ignore) + tree exp; + rtx target; + rtx subtarget ATTRIBUTE_UNUSED; + enum machine_mode mode ATTRIBUTE_UNUSED; + int ignore ATTRIBUTE_UNUSED; +{ + enum insn_code icode; + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree arglist = TREE_OPERAND (exp, 1); + tree arg0; + rtx op0, pat; + enum machine_mode tmode, mode0; + int fcode = DECL_FUNCTION_CODE (fndecl); + + switch (fcode) + { + default: + break; + + case ARM_BUILTIN_CLZ: + icode = CODE_FOR_clz; + arg0 = TREE_VALUE (arglist); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); + tmode = insn_data[icode].operand[0].mode; + mode0 = insn_data[icode].operand[1].mode; + + if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) + op0 = copy_to_mode_reg (mode0, op0); + if (target == 0 + || GET_MODE (target) != tmode + || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + pat = GEN_FCN (icode) (target, op0); + if (! pat) + return 0; + emit_insn (pat); + return target; + } + + /* @@@ Should really do something sensible here. */ + return NULL_RTX; +} + +/* Recursively search through all of the blocks in a function + checking to see if any of the variables created in that + function match the RTX called 'orig'. If they do then + replace them with the RTX called 'new'. */ + +static void +replace_symbols_in_block (block, orig, new) + tree block; + rtx orig; + rtx new; +{ + for (; block; block = BLOCK_CHAIN (block)) + { + tree sym; + + if (!TREE_USED (block)) + continue; + + for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym)) + { + if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL) + || DECL_IGNORED_P (sym) + || TREE_CODE (sym) != VAR_DECL + || DECL_EXTERNAL (sym) + || !rtx_equal_p (DECL_RTL (sym), orig) + ) + continue; + + SET_DECL_RTL (sym, new); + } + + replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new); + } +} + +/* Return the number (counting from 0) of + the least significant set bit in MASK. */ + +#ifdef __GNUC__ +inline +#endif +static int +number_of_first_bit_set (mask) + int mask; +{ + int bit; + + for (bit = 0; + (mask & (1 << bit)) == 0; + ++bit) + continue; + + return bit; +} + +/* Generate code to return from a thumb function. + If 'reg_containing_return_addr' is -1, then the return address is + actually on the stack, at the stack pointer. */ +static void +thumb_exit (f, reg_containing_return_addr, eh_ofs) + FILE * f; + int reg_containing_return_addr; + rtx eh_ofs; +{ + unsigned regs_available_for_popping; + unsigned regs_to_pop; + int pops_needed; + unsigned available; + unsigned required; + int mode; + int size; + int restore_a4 = FALSE; + + /* Compute the registers we need to pop. */ + regs_to_pop = 0; + pops_needed = 0; + + /* There is an assumption here, that if eh_ofs is not NULL, the + normal return address will have been pushed. */ + if (reg_containing_return_addr == -1 || eh_ofs) + { + /* When we are generating a return for __builtin_eh_return, + reg_containing_return_addr must specify the return regno. */ + if (eh_ofs && reg_containing_return_addr == -1) + abort (); + + regs_to_pop |= 1 << LR_REGNUM; + ++pops_needed; + } + + if (TARGET_BACKTRACE) + { + /* Restore the (ARM) frame pointer and stack pointer. */ + regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM); + pops_needed += 2; + } + + /* If there is nothing to pop then just emit the BX instruction and + return. */ + if (pops_needed == 0) + { + if (eh_ofs) + asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); + + asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); + return; + } + /* Otherwise if we are not supporting interworking and we have not created + a backtrace structure and the function was not entered in ARM mode then + just pop the return address straight into the PC. */ + else if (!TARGET_INTERWORK + && !TARGET_BACKTRACE + && !is_called_in_ARM_mode (current_function_decl)) + { + if (eh_ofs) + { + asm_fprintf (f, "\tadd\t%r, #4\n", SP_REGNUM); + asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); + asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); + } + else + asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM); + + return; + } + + /* Find out how many of the (return) argument registers we can corrupt. */ + regs_available_for_popping = 0; + + /* If returning via __builtin_eh_return, the bottom three registers + all contain information needed for the return. */ + if (eh_ofs) + size = 12; + else + { +#ifdef RTX_CODE + /* If we can deduce the registers used from the function's + return value. This is more reliable that examining + regs_ever_live[] because that will be set if the register is + ever used in the function, not just if the register is used + to hold a return value. */ + + if (current_function_return_rtx != 0) + mode = GET_MODE (current_function_return_rtx); + else +#endif + mode = DECL_MODE (DECL_RESULT (current_function_decl)); + + size = GET_MODE_SIZE (mode); + + if (size == 0) + { + /* In a void function we can use any argument register. + In a function that returns a structure on the stack + we can use the second and third argument registers. */ + if (mode == VOIDmode) + regs_available_for_popping = + (1 << ARG_REGISTER (1)) + | (1 << ARG_REGISTER (2)) + | (1 << ARG_REGISTER (3)); + else + regs_available_for_popping = + (1 << ARG_REGISTER (2)) + | (1 << ARG_REGISTER (3)); + } + else if (size <= 4) + regs_available_for_popping = + (1 << ARG_REGISTER (2)) + | (1 << ARG_REGISTER (3)); + else if (size <= 8) + regs_available_for_popping = + (1 << ARG_REGISTER (3)); + } + + /* Match registers to be popped with registers into which we pop them. */ + for (available = regs_available_for_popping, + required = regs_to_pop; + required != 0 && available != 0; + available &= ~(available & - available), + required &= ~(required & - required)) + -- pops_needed; + + /* If we have any popping registers left over, remove them. */ + if (available > 0) + regs_available_for_popping &= ~available; + + /* Otherwise if we need another popping register we can use + the fourth argument register. */ + else if (pops_needed) + { + /* If we have not found any free argument registers and + reg a4 contains the return address, we must move it. */ + if (regs_available_for_popping == 0 + && reg_containing_return_addr == LAST_ARG_REGNUM) + { + asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); + reg_containing_return_addr = LR_REGNUM; + } + else if (size > 12) + { + /* Register a4 is being used to hold part of the return value, + but we have dire need of a free, low register. */ + restore_a4 = TRUE; + + asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM); + } + + if (reg_containing_return_addr != LAST_ARG_REGNUM) + { + /* The fourth argument register is available. */ + regs_available_for_popping |= 1 << LAST_ARG_REGNUM; + + --pops_needed; + } + } + + /* Pop as many registers as we can. */ + thumb_pushpop (f, regs_available_for_popping, FALSE); + + /* Process the registers we popped. */ + if (reg_containing_return_addr == -1) + { + /* The return address was popped into the lowest numbered register. */ + regs_to_pop &= ~(1 << LR_REGNUM); + + reg_containing_return_addr = + number_of_first_bit_set (regs_available_for_popping); + + /* Remove this register for the mask of available registers, so that + the return address will not be corrupted by futher pops. */ + regs_available_for_popping &= ~(1 << reg_containing_return_addr); + } + + /* If we popped other registers then handle them here. */ + if (regs_available_for_popping) + { + int frame_pointer; + + /* Work out which register currently contains the frame pointer. */ + frame_pointer = number_of_first_bit_set (regs_available_for_popping); + + /* Move it into the correct place. */ + asm_fprintf (f, "\tmov\t%r, %r\n", + ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer); + + /* (Temporarily) remove it from the mask of popped registers. */ + regs_available_for_popping &= ~(1 << frame_pointer); + regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM); + + if (regs_available_for_popping) + { + int stack_pointer; + + /* We popped the stack pointer as well, + find the register that contains it. */ + stack_pointer = number_of_first_bit_set (regs_available_for_popping); + + /* Move it into the stack register. */ + asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer); + + /* At this point we have popped all necessary registers, so + do not worry about restoring regs_available_for_popping + to its correct value: + + assert (pops_needed == 0) + assert (regs_available_for_popping == (1 << frame_pointer)) + assert (regs_to_pop == (1 << STACK_POINTER)) */ + } + else + { + /* Since we have just move the popped value into the frame + pointer, the popping register is available for reuse, and + we know that we still have the stack pointer left to pop. */ + regs_available_for_popping |= (1 << frame_pointer); + } + } + + /* If we still have registers left on the stack, but we no longer have + any registers into which we can pop them, then we must move the return + address into the link register and make available the register that + contained it. */ + if (regs_available_for_popping == 0 && pops_needed > 0) + { + regs_available_for_popping |= 1 << reg_containing_return_addr; + + asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, + reg_containing_return_addr); + + reg_containing_return_addr = LR_REGNUM; + } + + /* If we have registers left on the stack then pop some more. + We know that at most we will want to pop FP and SP. */ + if (pops_needed > 0) + { + int popped_into; + int move_to; + + thumb_pushpop (f, regs_available_for_popping, FALSE); + + /* We have popped either FP or SP. + Move whichever one it is into the correct register. */ + popped_into = number_of_first_bit_set (regs_available_for_popping); + move_to = number_of_first_bit_set (regs_to_pop); + + asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into); + + regs_to_pop &= ~(1 << move_to); + + --pops_needed; + } + + /* If we still have not popped everything then we must have only + had one register available to us and we are now popping the SP. */ + if (pops_needed > 0) + { + int popped_into; + + thumb_pushpop (f, regs_available_for_popping, FALSE); + + popped_into = number_of_first_bit_set (regs_available_for_popping); + + asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into); + /* + assert (regs_to_pop == (1 << STACK_POINTER)) + assert (pops_needed == 1) + */ + } + + /* If necessary restore the a4 register. */ + if (restore_a4) + { + if (reg_containing_return_addr != LR_REGNUM) + { + asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); + reg_containing_return_addr = LR_REGNUM; + } + + asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); + } + + if (eh_ofs) + asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); + + /* Return to caller. */ + asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); +} + +/* Emit code to push or pop registers to or from the stack. */ + +static void +thumb_pushpop (f, mask, push) + FILE * f; + int mask; + int push; +{ + int regno; + int lo_mask = mask & 0xFF; + + if (lo_mask == 0 && !push && (mask & (1 << 15))) + { + /* Special case. Do not generate a POP PC statement here, do it in + thumb_exit() */ + thumb_exit (f, -1, NULL_RTX); + return; + } + + fprintf (f, "\t%s\t{", push ? "push" : "pop"); + + /* Look at the low registers first. */ + for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1) + { + if (lo_mask & 1) + { + asm_fprintf (f, "%r", regno); + + if ((lo_mask & ~1) != 0) + fprintf (f, ", "); + } + } + + if (push && (mask & (1 << LR_REGNUM))) + { + /* Catch pushing the LR. */ + if (mask & 0xFF) + fprintf (f, ", "); + + asm_fprintf (f, "%r", LR_REGNUM); + } + else if (!push && (mask & (1 << PC_REGNUM))) + { + /* Catch popping the PC. */ + if (TARGET_INTERWORK || TARGET_BACKTRACE) + { + /* The PC is never poped directly, instead + it is popped into r3 and then BX is used. */ + fprintf (f, "}\n"); + + thumb_exit (f, -1, NULL_RTX); + + return; + } + else + { + if (mask & 0xFF) + fprintf (f, ", "); + + asm_fprintf (f, "%r", PC_REGNUM); + } + } + + fprintf (f, "}\n"); +} + +void +thumb_final_prescan_insn (insn) + rtx insn; +{ + if (flag_print_asm_name) + asm_fprintf (asm_out_file, "%@ 0x%04x\n", + INSN_ADDRESSES (INSN_UID (insn))); +} + +int +thumb_shiftable_const (val) + unsigned HOST_WIDE_INT val; +{ + unsigned HOST_WIDE_INT mask = 0xff; + int i; + + if (val == 0) /* XXX */ + return 0; + + for (i = 0; i < 25; i++) + if ((val & (mask << i)) == val) + return 1; + + return 0; +} + +/* Returns non-zero if the current function contains, + or might contain a far jump. */ + +int +thumb_far_jump_used_p (int in_prologue) +{ + rtx insn; + + /* This test is only important for leaf functions. */ + /* assert (!leaf_function_p ()); */ + + /* If we have already decided that far jumps may be used, + do not bother checking again, and always return true even if + it turns out that they are not being used. Once we have made + the decision that far jumps are present (and that hence the link + register will be pushed onto the stack) we cannot go back on it. */ + if (cfun->machine->far_jump_used) + return 1; + + /* If this function is not being called from the prologue/epilogue + generation code then it must be being called from the + INITIAL_ELIMINATION_OFFSET macro. */ + if (!in_prologue) + { + /* In this case we know that we are being asked about the elimination + of the arg pointer register. If that register is not being used, + then there are no arguments on the stack, and we do not have to + worry that a far jump might force the prologue to push the link + register, changing the stack offsets. In this case we can just + return false, since the presence of far jumps in the function will + not affect stack offsets. + + If the arg pointer is live (or if it was live, but has now been + eliminated and so set to dead) then we do have to test to see if + the function might contain a far jump. This test can lead to some + false negatives, since before reload is completed, then length of + branch instructions is not known, so gcc defaults to returning their + longest length, which in turn sets the far jump attribute to true. + + A false negative will not result in bad code being generated, but it + will result in a needless push and pop of the link register. We + hope that this does not occur too often. */ + if (regs_ever_live [ARG_POINTER_REGNUM]) + cfun->machine->arg_pointer_live = 1; + else if (!cfun->machine->arg_pointer_live) + return 0; + } + + /* Check to see if the function contains a branch + insn with the far jump attribute set. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == JUMP_INSN + /* Ignore tablejump patterns. */ + && GET_CODE (PATTERN (insn)) != ADDR_VEC + && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC + && get_attr_far_jump (insn) == FAR_JUMP_YES + ) + { + /* Record the fact that we have decied that + the function does use far jumps. */ + cfun->machine->far_jump_used = 1; + return 1; + } + } + + return 0; +} + +/* Return non-zero if FUNC must be entered in ARM mode. */ + +int +is_called_in_ARM_mode (func) + tree func; +{ + if (TREE_CODE (func) != FUNCTION_DECL) + abort (); + + /* Ignore the problem about functions whoes address is taken. */ + if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func)) + return TRUE; + +#ifdef ARM_PE + return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE; +#else + return FALSE; +#endif +} + +/* The bits which aren't usefully expanded as rtl. */ + +const char * +thumb_unexpanded_epilogue () +{ + int regno; + int live_regs_mask = 0; + int high_regs_pushed = 0; + int leaf_function = leaf_function_p (); + int had_to_push_lr; + rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; + + if (return_used_this_function) + return ""; + + for (regno = 0; regno <= LAST_LO_REGNUM; regno++) + if (regs_ever_live[regno] && !call_used_regs[regno] + && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) + live_regs_mask |= 1 << regno; + + for (regno = 8; regno < 13; regno++) + { + if (regs_ever_live[regno] && !call_used_regs[regno] + && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) + high_regs_pushed++; + } + + /* The prolog may have pushed some high registers to use as + work registers. eg the testuite file: + gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c + compiles to produce: + push {r4, r5, r6, r7, lr} + mov r7, r9 + mov r6, r8 + push {r6, r7} + as part of the prolog. We have to undo that pushing here. */ + + if (high_regs_pushed) + { + int mask = live_regs_mask; + int next_hi_reg; + int size; + int mode; + +#ifdef RTX_CODE + /* If we can deduce the registers used from the function's return value. + This is more reliable that examining regs_ever_live[] because that + will be set if the register is ever used in the function, not just if + the register is used to hold a return value. */ + + if (current_function_return_rtx != 0) + mode = GET_MODE (current_function_return_rtx); + else +#endif + mode = DECL_MODE (DECL_RESULT (current_function_decl)); + + size = GET_MODE_SIZE (mode); + + /* Unless we are returning a type of size > 12 register r3 is + available. */ + if (size < 13) + mask |= 1 << 3; + + if (mask == 0) + /* Oh dear! We have no low registers into which we can pop + high registers! */ + internal_error + ("no low registers available for popping high registers"); + + for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++) + if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg] + && !(TARGET_SINGLE_PIC_BASE && (next_hi_reg == arm_pic_register))) + break; + + while (high_regs_pushed) + { + /* Find lo register(s) into which the high register(s) can + be popped. */ + for (regno = 0; regno <= LAST_LO_REGNUM; regno++) + { + if (mask & (1 << regno)) + high_regs_pushed--; + if (high_regs_pushed == 0) + break; + } + + mask &= (2 << regno) - 1; /* A noop if regno == 8 */ + + /* Pop the values into the low register(s). */ + thumb_pushpop (asm_out_file, mask, 0); + + /* Move the value(s) into the high registers. */ + for (regno = 0; regno <= LAST_LO_REGNUM; regno++) + { + if (mask & (1 << regno)) + { + asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg, + regno); + + for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++) + if (regs_ever_live[next_hi_reg] + && !call_used_regs[next_hi_reg] + && !(TARGET_SINGLE_PIC_BASE + && (next_hi_reg == arm_pic_register))) + break; + } + } + } + } + + had_to_push_lr = (live_regs_mask || !leaf_function + || thumb_far_jump_used_p (1)); + + if (TARGET_BACKTRACE + && ((live_regs_mask & 0xFF) == 0) + && regs_ever_live [LAST_ARG_REGNUM] != 0) + { + /* The stack backtrace structure creation code had to + push R7 in order to get a work register, so we pop + it now. */ + live_regs_mask |= (1 << LAST_LO_REGNUM); + } + + if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE) + { + if (had_to_push_lr + && !is_called_in_ARM_mode (current_function_decl) + && !eh_ofs) + live_regs_mask |= 1 << PC_REGNUM; + + /* Either no argument registers were pushed or a backtrace + structure was created which includes an adjusted stack + pointer, so just pop everything. */ + if (live_regs_mask) + thumb_pushpop (asm_out_file, live_regs_mask, FALSE); + + if (eh_ofs) + thumb_exit (asm_out_file, 2, eh_ofs); + /* We have either just popped the return address into the + PC or it is was kept in LR for the entire function or + it is still on the stack because we do not want to + return by doing a pop {pc}. */ + else if ((live_regs_mask & (1 << PC_REGNUM)) == 0) + thumb_exit (asm_out_file, + (had_to_push_lr + && is_called_in_ARM_mode (current_function_decl)) ? + -1 : LR_REGNUM, NULL_RTX); + } + else + { + /* Pop everything but the return address. */ + live_regs_mask &= ~(1 << PC_REGNUM); + + if (live_regs_mask) + thumb_pushpop (asm_out_file, live_regs_mask, FALSE); + + if (had_to_push_lr) + /* Get the return address into a temporary register. */ + thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0); + + /* Remove the argument registers that were pushed onto the stack. */ + asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n", + SP_REGNUM, SP_REGNUM, + current_function_pretend_args_size); + + if (eh_ofs) + thumb_exit (asm_out_file, 2, eh_ofs); + else + thumb_exit (asm_out_file, + had_to_push_lr ? LAST_ARG_REGNUM : LR_REGNUM, NULL_RTX); + } + + return ""; +} + +/* Functions to save and restore machine-specific function data. */ + +static void +arm_mark_machine_status (p) + struct function * p; +{ + machine_function *machine = p->machine; + + if (machine) + ggc_mark_rtx (machine->eh_epilogue_sp_ofs); +} + +static void +arm_init_machine_status (p) + struct function * p; +{ + p->machine = + (machine_function *) xcalloc (1, sizeof (machine_function)); + +#if ARM_FT_UNKNOWWN != 0 + ((machine_function *) p->machine)->func_type = ARM_FT_UNKNOWN; +#endif +} + +static void +arm_free_machine_status (p) + struct function * p; +{ + if (p->machine) + { + free (p->machine); + p->machine = NULL; + } +} + +/* Return an RTX indicating where the return address to the + calling function can be found. */ + +rtx +arm_return_addr (count, frame) + int count; + rtx frame ATTRIBUTE_UNUSED; +{ + if (count != 0) + return NULL_RTX; + + if (TARGET_APCS_32) + return get_hard_reg_initial_val (Pmode, LR_REGNUM); + else + { + rtx lr = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM), + GEN_INT (RETURN_ADDR_MASK26)); + return get_func_hard_reg_initial_val (cfun, lr); + } +} + +/* Do anything needed before RTL is emitted for each function. */ + +void +arm_init_expanders () +{ + /* Arrange to initialize and mark the machine per-function status. */ + init_machine_status = arm_init_machine_status; + mark_machine_status = arm_mark_machine_status; + free_machine_status = arm_free_machine_status; +} + +/* Generate the rest of a function's prologue. */ + +void +thumb_expand_prologue () +{ + HOST_WIDE_INT amount = (get_frame_size () + + current_function_outgoing_args_size); + unsigned long func_type; + + func_type = arm_current_func_type (); + + /* Naked functions don't have prologues. */ + if (IS_NAKED (func_type)) + return; + + if (IS_INTERRUPT (func_type)) + { + error ("interrupt Service Routines cannot be coded in Thumb mode"); + return; + } + + if (frame_pointer_needed) + emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); + + if (amount) + { + amount = ROUND_UP (amount); + + if (amount < 512) + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (- amount))); + else + { + int regno; + rtx reg; + + /* The stack decrement is too big for an immediate value in a single + insn. In theory we could issue multiple subtracts, but after + three of them it becomes more space efficient to place the full + value in the constant pool and load into a register. (Also the + ARM debugger really likes to see only one stack decrement per + function). So instead we look for a scratch register into which + we can load the decrement, and then we subtract this from the + stack pointer. Unfortunately on the thumb the only available + scratch registers are the argument registers, and we cannot use + these as they may hold arguments to the function. Instead we + attempt to locate a call preserved register which is used by this + function. If we can find one, then we know that it will have + been pushed at the start of the prologue and so we can corrupt + it now. */ + for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++) + if (regs_ever_live[regno] + && !call_used_regs[regno] /* Paranoia */ + && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)) + && !(frame_pointer_needed + && (regno == THUMB_HARD_FRAME_POINTER_REGNUM))) + break; + + if (regno > LAST_LO_REGNUM) /* Very unlikely */ + { + rtx spare = gen_rtx (REG, SImode, IP_REGNUM); + rtx insn; + + /* Choose an arbitary, non-argument low register. */ + reg = gen_rtx (REG, SImode, LAST_LO_REGNUM); + + /* Save it by copying it into a high, scratch register. */ + emit_insn (gen_movsi (spare, reg)); + /* Add a USE to stop propagate_one_insn() from barfing. */ + emit_insn (gen_prologue_use (spare)); + + /* Decrement the stack. */ + emit_insn (gen_movsi (reg, GEN_INT (- amount))); + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + reg)); + + /* Restore the low register's original value. */ + emit_insn (gen_movsi (reg, spare)); + + /* Emit a USE of the restored scratch register, so that flow + analysis will not consider the restore redundant. The + register won't be used again in this function and isn't + restored by the epilogue. */ + emit_insn (gen_prologue_use (reg)); + } + else + { + reg = gen_rtx (REG, SImode, regno); + + emit_insn (gen_movsi (reg, GEN_INT (- amount))); + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + reg)); + } + } + } + + if (current_function_profile || TARGET_NO_SCHED_PRO) + emit_insn (gen_blockage ()); +} + +void +thumb_expand_epilogue () +{ + HOST_WIDE_INT amount = (get_frame_size () + + current_function_outgoing_args_size); + + /* Naked functions don't have prologues. */ + if (IS_NAKED (arm_current_func_type ())) + return; + + if (frame_pointer_needed) + emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); + else if (amount) + { + amount = ROUND_UP (amount); + + if (amount < 512) + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (amount))); + else + { + /* r3 is always free in the epilogue. */ + rtx reg = gen_rtx (REG, SImode, LAST_ARG_REGNUM); + + emit_insn (gen_movsi (reg, GEN_INT (amount))); + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); + } + } + + /* Emit a USE (stack_pointer_rtx), so that + the stack adjustment will not be deleted. */ + emit_insn (gen_prologue_use (stack_pointer_rtx)); + + if (current_function_profile || TARGET_NO_SCHED_PRO) + emit_insn (gen_blockage ()); +} + +static void +thumb_output_function_prologue (f, size) + FILE * f; + HOST_WIDE_INT size ATTRIBUTE_UNUSED; +{ + int live_regs_mask = 0; + int high_regs_pushed = 0; + int regno; + + if (IS_NAKED (arm_current_func_type ())) + return; + + if (is_called_in_ARM_mode (current_function_decl)) + { + const char * name; + + if (GET_CODE (DECL_RTL (current_function_decl)) != MEM) + abort (); + if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF) + abort (); + name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + + /* Generate code sequence to switch us into Thumb mode. */ + /* The .code 32 directive has already been emitted by + ASM_DECLARE_FUNCTION_NAME. */ + asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM); + asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM); + + /* Generate a label, so that the debugger will notice the + change in instruction sets. This label is also used by + the assembler to bypass the ARM code when this function + is called from a Thumb encoded function elsewhere in the + same file. Hence the definition of STUB_NAME here must + agree with the definition in gas/config/tc-arm.c */ + +#define STUB_NAME ".real_start_of" + + asm_fprintf (f, "\t.code\t16\n"); +#ifdef ARM_PE + if (arm_dllexport_name_p (name)) + name = arm_strip_name_encoding (name); +#endif + asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name); + asm_fprintf (f, "\t.thumb_func\n"); + asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name); + } + + if (current_function_pretend_args_size) + { + if (current_function_anonymous_args) + { + int num_pushes; + + asm_fprintf (f, "\tpush\t{"); + + num_pushes = NUM_INTS (current_function_pretend_args_size); + + for (regno = LAST_ARG_REGNUM + 1 - num_pushes; + regno <= LAST_ARG_REGNUM; + regno++) + asm_fprintf (f, "%r%s", regno, + regno == LAST_ARG_REGNUM ? "" : ", "); + + asm_fprintf (f, "}\n"); + } + else + asm_fprintf (f, "\tsub\t%r, %r, #%d\n", + SP_REGNUM, SP_REGNUM, + current_function_pretend_args_size); + } + + for (regno = 0; regno <= LAST_LO_REGNUM; regno++) + if (regs_ever_live[regno] && !call_used_regs[regno] + && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) + live_regs_mask |= 1 << regno; + + if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1)) + live_regs_mask |= 1 << LR_REGNUM; + + if (TARGET_BACKTRACE) + { + int offset; + int work_register = 0; + int wr; + + /* We have been asked to create a stack backtrace structure. + The code looks like this: + + 0 .align 2 + 0 func: + 0 sub SP, #16 Reserve space for 4 registers. + 2 push {R7} Get a work register. + 4 add R7, SP, #20 Get the stack pointer before the push. + 6 str R7, [SP, #8] Store the stack pointer (before reserving the space). + 8 mov R7, PC Get hold of the start of this code plus 12. + 10 str R7, [SP, #16] Store it. + 12 mov R7, FP Get hold of the current frame pointer. + 14 str R7, [SP, #4] Store it. + 16 mov R7, LR Get hold of the current return address. + 18 str R7, [SP, #12] Store it. + 20 add R7, SP, #16 Point at the start of the backtrace structure. + 22 mov FP, R7 Put this value into the frame pointer. */ + + if ((live_regs_mask & 0xFF) == 0) + { + /* See if the a4 register is free. */ + + if (regs_ever_live [LAST_ARG_REGNUM] == 0) + work_register = LAST_ARG_REGNUM; + else /* We must push a register of our own */ + live_regs_mask |= (1 << LAST_LO_REGNUM); + } + + if (work_register == 0) + { + /* Select a register from the list that will be pushed to + use as our work register. */ + for (work_register = (LAST_LO_REGNUM + 1); work_register--;) + if ((1 << work_register) & live_regs_mask) + break; + } + + asm_fprintf + (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n", + SP_REGNUM, SP_REGNUM); + + if (live_regs_mask) + thumb_pushpop (f, live_regs_mask, 1); + + for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1) + if (wr & live_regs_mask) + offset += 4; + + asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, + offset + 16 + current_function_pretend_args_size); + + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset + 4); + + /* Make sure that the instruction fetching the PC is in the right place + to calculate "start of backtrace creation code + 12". */ + if (live_regs_mask) + { + asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset + 12); + asm_fprintf (f, "\tmov\t%r, %r\n", work_register, + ARM_HARD_FRAME_POINTER_REGNUM); + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset); + } + else + { + asm_fprintf (f, "\tmov\t%r, %r\n", work_register, + ARM_HARD_FRAME_POINTER_REGNUM); + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset); + asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset + 12); + } + + asm_fprintf (f, "\tmov\t%r, %r\n", work_register, LR_REGNUM); + asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, + offset + 8); + asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, + offset + 12); + asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n", + ARM_HARD_FRAME_POINTER_REGNUM, work_register); + } + else if (live_regs_mask) + thumb_pushpop (f, live_regs_mask, 1); + + for (regno = 8; regno < 13; regno++) + { + if (regs_ever_live[regno] && !call_used_regs[regno] + && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) + high_regs_pushed++; + } + + if (high_regs_pushed) + { + int pushable_regs = 0; + int mask = live_regs_mask & 0xff; + int next_hi_reg; + + for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--) + { + if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg] + && !(TARGET_SINGLE_PIC_BASE + && (next_hi_reg == arm_pic_register))) + break; + } + + pushable_regs = mask; + + if (pushable_regs == 0) + { + /* Desperation time -- this probably will never happen. */ + if (regs_ever_live[LAST_ARG_REGNUM] + || !call_used_regs[LAST_ARG_REGNUM]) + asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM); + mask = 1 << LAST_ARG_REGNUM; + } + + while (high_regs_pushed > 0) + { + for (regno = LAST_LO_REGNUM; regno >= 0; regno--) + { + if (mask & (1 << regno)) + { + asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg); + + high_regs_pushed--; + + if (high_regs_pushed) + for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM; + next_hi_reg--) + { + if (regs_ever_live[next_hi_reg] + && !call_used_regs[next_hi_reg] + && !(TARGET_SINGLE_PIC_BASE + && (next_hi_reg == arm_pic_register))) + break; + } + else + { + mask &= ~((1 << regno) - 1); + break; + } + } + } + + thumb_pushpop (f, mask, 1); + } + + if (pushable_regs == 0 + && (regs_ever_live[LAST_ARG_REGNUM] + || !call_used_regs[LAST_ARG_REGNUM])) + asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); + } +} + +/* Handle the case of a double word load into a low register from + a computed memory address. The computed address may involve a + register which is overwritten by the load. */ + +const char * +thumb_load_double_from_address (operands) + rtx *operands; +{ + rtx addr; + rtx base; + rtx offset; + rtx arg1; + rtx arg2; + + if (GET_CODE (operands[0]) != REG) + abort (); + + if (GET_CODE (operands[1]) != MEM) + abort (); + + /* Get the memory address. */ + addr = XEXP (operands[1], 0); + + /* Work out how the memory address is computed. */ + switch (GET_CODE (addr)) + { + case REG: + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[1], 0), 4)); + + if (REGNO (operands[0]) == REGNO (addr)) + { + output_asm_insn ("ldr\t%H0, %2", operands); + output_asm_insn ("ldr\t%0, %1", operands); + } + else + { + output_asm_insn ("ldr\t%0, %1", operands); + output_asm_insn ("ldr\t%H0, %2", operands); + } + break; + + case CONST: + /* Compute <address> + 4 for the high order load. */ + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[1], 0), 4)); + + output_asm_insn ("ldr\t%0, %1", operands); + output_asm_insn ("ldr\t%H0, %2", operands); + break; + + case PLUS: + arg1 = XEXP (addr, 0); + arg2 = XEXP (addr, 1); + + if (CONSTANT_P (arg1)) + base = arg2, offset = arg1; + else + base = arg1, offset = arg2; + + if (GET_CODE (base) != REG) + abort (); + + /* Catch the case of <address> = <reg> + <reg> */ + if (GET_CODE (offset) == REG) + { + int reg_offset = REGNO (offset); + int reg_base = REGNO (base); + int reg_dest = REGNO (operands[0]); + + /* Add the base and offset registers together into the + higher destination register. */ + asm_fprintf (asm_out_file, "\tadd\t%r, %r, %r", + reg_dest + 1, reg_base, reg_offset); + + /* Load the lower destination register from the address in + the higher destination register. */ + asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #0]", + reg_dest, reg_dest + 1); + + /* Load the higher destination register from its own address + plus 4. */ + asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #4]", + reg_dest + 1, reg_dest + 1); + } + else + { + /* Compute <address> + 4 for the high order load. */ + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[1], 0), 4)); + + /* If the computed address is held in the low order register + then load the high order register first, otherwise always + load the low order register first. */ + if (REGNO (operands[0]) == REGNO (base)) + { + output_asm_insn ("ldr\t%H0, %2", operands); + output_asm_insn ("ldr\t%0, %1", operands); + } + else + { + output_asm_insn ("ldr\t%0, %1", operands); + output_asm_insn ("ldr\t%H0, %2", operands); + } + } + break; + + case LABEL_REF: + /* With no registers to worry about we can just load the value + directly. */ + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[1], 0), 4)); + + output_asm_insn ("ldr\t%H0, %2", operands); + output_asm_insn ("ldr\t%0, %1", operands); + break; + + default: + abort (); + break; + } + + return ""; +} + + +const char * +thumb_output_move_mem_multiple (n, operands) + int n; + rtx * operands; +{ + rtx tmp; + + switch (n) + { + case 2: + if (REGNO (operands[4]) > REGNO (operands[5])) + { + tmp = operands[4]; + operands[4] = operands[5]; + operands[5] = tmp; + } + output_asm_insn ("ldmia\t%1!, {%4, %5}", operands); + output_asm_insn ("stmia\t%0!, {%4, %5}", operands); + break; + + case 3: + if (REGNO (operands[4]) > REGNO (operands[5])) + { + tmp = operands[4]; + operands[4] = operands[5]; + operands[5] = tmp; + } + if (REGNO (operands[5]) > REGNO (operands[6])) + { + tmp = operands[5]; + operands[5] = operands[6]; + operands[6] = tmp; + } + if (REGNO (operands[4]) > REGNO (operands[5])) + { + tmp = operands[4]; + operands[4] = operands[5]; + operands[5] = tmp; + } + + output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands); + output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands); + break; + + default: + abort (); + } + + return ""; +} + +/* Routines for generating rtl. */ + +void +thumb_expand_movstrqi (operands) + rtx * operands; +{ + rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); + rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); + HOST_WIDE_INT len = INTVAL (operands[2]); + HOST_WIDE_INT offset = 0; + + while (len >= 12) + { + emit_insn (gen_movmem12b (out, in, out, in)); + len -= 12; + } + + if (len >= 8) + { + emit_insn (gen_movmem8b (out, in, out, in)); + len -= 8; + } + + if (len >= 4) + { + rtx reg = gen_reg_rtx (SImode); + emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in))); + emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg)); + len -= 4; + offset += 4; + } + + if (len >= 2) + { + rtx reg = gen_reg_rtx (HImode); + emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, + plus_constant (in, offset)))); + emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)), + reg)); + len -= 2; + offset += 2; + } + + if (len) + { + rtx reg = gen_reg_rtx (QImode); + emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode, + plus_constant (in, offset)))); + emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)), + reg)); + } +} + +int +thumb_cmp_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return ((GET_CODE (op) == CONST_INT + && (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256) + || register_operand (op, mode)); +} + +static const char * +thumb_condition_code (x, invert) + rtx x; + int invert; +{ + static const char * const conds[] = + { + "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le" + }; + int val; + + switch (GET_CODE (x)) + { + case EQ: val = 0; break; + case NE: val = 1; break; + case GEU: val = 2; break; + case LTU: val = 3; break; + case GTU: val = 8; break; + case LEU: val = 9; break; + case GE: val = 10; break; + case LT: val = 11; break; + case GT: val = 12; break; + case LE: val = 13; break; + default: + abort (); + } + + return conds[val ^ invert]; +} + +/* Handle storing a half-word to memory during reload. */ + +void +thumb_reload_out_hi (operands) + rtx * operands; +{ + emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2])); +} + +/* Handle storing a half-word to memory during reload. */ + +void +thumb_reload_in_hi (operands) + rtx * operands ATTRIBUTE_UNUSED; +{ + abort (); +} + +/* Return the length of a function name prefix + that starts with the character 'c'. */ + +static int +arm_get_strip_length (char c) +{ + switch (c) + { + ARM_NAME_ENCODING_LENGTHS + default: return 0; + } +} + +/* Return a pointer to a function's name with any + and all prefix encodings stripped from it. */ + +const char * +arm_strip_name_encoding (const char * name) +{ + int skip; + + while ((skip = arm_get_strip_length (* name))) + name += skip; + + return name; +} + +#ifdef AOF_ASSEMBLER +/* Special functions only needed when producing AOF syntax assembler. */ + +rtx aof_pic_label = NULL_RTX; +struct pic_chain +{ + struct pic_chain * next; + const char * symname; +}; + +static struct pic_chain * aof_pic_chain = NULL; + +rtx +aof_pic_entry (x) + rtx x; +{ + struct pic_chain ** chainp; + int offset; + + if (aof_pic_label == NULL_RTX) + { + /* We mark this here and not in arm_add_gc_roots() to avoid + polluting even more code with ifdefs, and because it never + contains anything useful until we assign to it here. */ + ggc_add_rtx_root (&aof_pic_label, 1); + aof_pic_label = gen_rtx_SYMBOL_REF (Pmode, "x$adcons"); + } + + for (offset = 0, chainp = &aof_pic_chain; *chainp; + offset += 4, chainp = &(*chainp)->next) + if ((*chainp)->symname == XSTR (x, 0)) + return plus_constant (aof_pic_label, offset); + + *chainp = (struct pic_chain *) xmalloc (sizeof (struct pic_chain)); + (*chainp)->next = NULL; + (*chainp)->symname = XSTR (x, 0); + return plus_constant (aof_pic_label, offset); +} + +void +aof_dump_pic_table (f) + FILE * f; +{ + struct pic_chain * chain; + + if (aof_pic_chain == NULL) + return; + + asm_fprintf (f, "\tAREA |%r$$adcons|, BASED %r\n", + PIC_OFFSET_TABLE_REGNUM, + PIC_OFFSET_TABLE_REGNUM); + fputs ("|x$adcons|\n", f); + + for (chain = aof_pic_chain; chain; chain = chain->next) + { + fputs ("\tDCD\t", f); + assemble_name (f, chain->symname); + fputs ("\n", f); + } +} + +int arm_text_section_count = 1; + +char * +aof_text_section () +{ + static char buf[100]; + 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 beginning 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; + const char * name; +}; + +static struct import * imports_list = NULL; + +void +aof_add_import (name) + const 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) + const 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 */ + +#ifdef OBJECT_FORMAT_ELF +/* Switch to an arbitrary section NAME with attributes as specified + by FLAGS. ALIGN specifies any known alignment requirements for + the section; 0 if the default should be used. + + Differs from the default elf version only in the prefix character + used before the section type. */ + +static void +arm_elf_asm_named_section (name, flags) + const char *name; + unsigned int flags; +{ + char flagchars[8], *f = flagchars; + const char *type; + + if (!(flags & SECTION_DEBUG)) + *f++ = 'a'; + if (flags & SECTION_WRITE) + *f++ = 'w'; + if (flags & SECTION_CODE) + *f++ = 'x'; + if (flags & SECTION_SMALL) + *f++ = 's'; + if (flags & SECTION_MERGE) + *f++ = 'M'; + if (flags & SECTION_STRINGS) + *f++ = 'S'; + *f = '\0'; + + if (flags & SECTION_BSS) + type = "nobits"; + else + type = "progbits"; + + if (flags & SECTION_ENTSIZE) + fprintf (asm_out_file, "\t.section\t%s,\"%s\",%%%s,%d\n", + name, flagchars, type, flags & SECTION_ENTSIZE); + else + fprintf (asm_out_file, "\t.section\t%s,\"%s\",%%%s\n", + name, flagchars, type); +} +#endif diff --git a/contrib/gcc/config/arm/arm.h b/contrib/gcc/config/arm/arm.h new file mode 100644 index 000000000000..46f938ee3e82 --- /dev/null +++ b/contrib/gcc/config/arm/arm.h @@ -0,0 +1,2881 @@ +/* Definitions of target machine for GNU compiler, for ARM. + Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002 Free Software Foundation, Inc. + Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) + and Martin Simmons (@harleqn.co.uk). + More major hacks by Richard Earnshaw (rearnsha@arm.com) + Minor hacks by Nick Clifton (nickc@cygnus.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef GCC_ARM_H +#define GCC_ARM_H + +#define TARGET_CPU_arm2 0x0000 +#define TARGET_CPU_arm250 0x0000 +#define TARGET_CPU_arm3 0x0000 +#define TARGET_CPU_arm6 0x0001 +#define TARGET_CPU_arm600 0x0001 +#define TARGET_CPU_arm610 0x0002 +#define TARGET_CPU_arm7 0x0001 +#define TARGET_CPU_arm7m 0x0004 +#define TARGET_CPU_arm7dm 0x0004 +#define TARGET_CPU_arm7dmi 0x0004 +#define TARGET_CPU_arm700 0x0001 +#define TARGET_CPU_arm710 0x0002 +#define TARGET_CPU_arm7100 0x0002 +#define TARGET_CPU_arm7500 0x0002 +#define TARGET_CPU_arm7500fe 0x1001 +#define TARGET_CPU_arm7tdmi 0x0008 +#define TARGET_CPU_arm8 0x0010 +#define TARGET_CPU_arm810 0x0020 +#define TARGET_CPU_strongarm 0x0040 +#define TARGET_CPU_strongarm110 0x0040 +#define TARGET_CPU_strongarm1100 0x0040 +#define TARGET_CPU_arm9 0x0080 +#define TARGET_CPU_arm9tdmi 0x0080 +#define TARGET_CPU_xscale 0x0100 +/* Configure didn't specify. */ +#define TARGET_CPU_generic 0x8000 + +typedef enum arm_cond_code +{ + ARM_EQ = 0, ARM_NE, ARM_CS, ARM_CC, ARM_MI, ARM_PL, ARM_VS, ARM_VC, + ARM_HI, ARM_LS, ARM_GE, ARM_LT, ARM_GT, ARM_LE, ARM_AL, ARM_NV +} +arm_cc; + +extern arm_cc arm_current_cc; + +#define ARM_INVERSE_CONDITION_CODE(X) ((arm_cc) (((int)X) ^ 1)) + +extern int arm_target_label; +extern int arm_ccfsm_state; +extern struct rtx_def * arm_target_insn; +/* Run-time compilation parameters selecting different hardware subsets. */ +extern int target_flags; +/* The floating point instruction architecture, can be 2 or 3 */ +extern const char * target_fp_name; +/* Define the information needed to generate branch insns. This is + stored from the compare operation. Note that we can't use "rtx" here + since it hasn't been defined! */ +extern struct rtx_def * arm_compare_op0; +extern struct rtx_def * arm_compare_op1; +/* The label of the current constant pool. */ +extern struct rtx_def * pool_vector_label; +/* Set to 1 when a return insn is output, this means that the epilogue + is not needed. */ +extern int return_used_this_function; +/* Nonzero if the prologue must setup `fp'. */ +extern int current_function_anonymous_args; + +/* Just in case configure has failed to define anything. */ +#ifndef TARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT TARGET_CPU_generic +#endif + +/* If the configuration file doesn't specify the cpu, the subtarget may + override it. If it doesn't, then default to an ARM6. */ +#if TARGET_CPU_DEFAULT == TARGET_CPU_generic +#undef TARGET_CPU_DEFAULT + +#ifdef SUBTARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT SUBTARGET_CPU_DEFAULT +#else +#define TARGET_CPU_DEFAULT TARGET_CPU_arm6 +#endif +#endif + +#if TARGET_CPU_DEFAULT == TARGET_CPU_arm2 +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_2__" +#else +#if TARGET_CPU_DEFAULT == TARGET_CPU_arm6 || TARGET_CPU_DEFAULT == TARGET_CPU_arm610 || TARGET_CPU_DEFAULT == TARGET_CPU_arm7500fe +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_3__" +#else +#if TARGET_CPU_DEFAULT == TARGET_CPU_arm7m +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_3M__" +#else +#if TARGET_CPU_DEFAULT == TARGET_CPU_arm7tdmi || TARGET_CPU_DEFAULT == TARGET_CPU_arm9 || TARGET_CPU_DEFAULT == TARGET_CPU_arm9tdmi +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_4T__" +#else +#if TARGET_CPU_DEFAULT == TARGET_CPU_arm8 || TARGET_CPU_DEFAULT == TARGET_CPU_arm810 || TARGET_CPU_DEFAULT == TARGET_CPU_strongarm || TARGET_CPU_DEFAULT == TARGET_CPU_strongarm110 || TARGET_CPU_DEFAULT == TARGET_CPU_strongarm1100 +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_4__" +#else +#if TARGET_CPU_DEFAULT == TARGET_CPU_xscale +#define CPP_ARCH_DEFAULT_SPEC "-D__ARM_ARCH_5TE__ -D__XSCALE__" +#else +Unrecognized value in TARGET_CPU_DEFAULT. +#endif +#endif +#endif +#endif +#endif +#endif + +#undef CPP_SPEC +#define CPP_SPEC "\ +%(cpp_cpu_arch) %(cpp_apcs_pc) %(cpp_float) \ +%(cpp_endian) %(subtarget_cpp_spec) %(cpp_isa) %(cpp_interwork)" + +#define CPP_ISA_SPEC "%{mthumb:-D__thumb__} %{!mthumb:-D__arm__}" + +/* Set the architecture define -- if -march= is set, then it overrides + the -mcpu= setting. */ +#define CPP_CPU_ARCH_SPEC "\ +-Acpu=arm -Amachine=arm \ +%{march=arm2:-D__ARM_ARCH_2__} \ +%{march=arm250:-D__ARM_ARCH_2__} \ +%{march=arm3:-D__ARM_ARCH_2__} \ +%{march=arm6:-D__ARM_ARCH_3__} \ +%{march=arm600:-D__ARM_ARCH_3__} \ +%{march=arm610:-D__ARM_ARCH_3__} \ +%{march=arm7:-D__ARM_ARCH_3__} \ +%{march=arm700:-D__ARM_ARCH_3__} \ +%{march=arm710:-D__ARM_ARCH_3__} \ +%{march=arm720:-D__ARM_ARCH_3__} \ +%{march=arm7100:-D__ARM_ARCH_3__} \ +%{march=arm7500:-D__ARM_ARCH_3__} \ +%{march=arm7500fe:-D__ARM_ARCH_3__} \ +%{march=arm7m:-D__ARM_ARCH_3M__} \ +%{march=arm7dm:-D__ARM_ARCH_3M__} \ +%{march=arm7dmi:-D__ARM_ARCH_3M__} \ +%{march=arm7tdmi:-D__ARM_ARCH_4T__} \ +%{march=arm8:-D__ARM_ARCH_4__} \ +%{march=arm810:-D__ARM_ARCH_4__} \ +%{march=arm9:-D__ARM_ARCH_4T__} \ +%{march=arm920:-D__ARM_ARCH_4__} \ +%{march=arm920t:-D__ARM_ARCH_4T__} \ +%{march=arm9tdmi:-D__ARM_ARCH_4T__} \ +%{march=strongarm:-D__ARM_ARCH_4__} \ +%{march=strongarm110:-D__ARM_ARCH_4__} \ +%{march=strongarm1100:-D__ARM_ARCH_4__} \ +%{march=xscale:-D__ARM_ARCH_5TE__} \ +%{march=xscale:-D__XSCALE__} \ +%{march=armv2:-D__ARM_ARCH_2__} \ +%{march=armv2a:-D__ARM_ARCH_2__} \ +%{march=armv3:-D__ARM_ARCH_3__} \ +%{march=armv3m:-D__ARM_ARCH_3M__} \ +%{march=armv4:-D__ARM_ARCH_4__} \ +%{march=armv4t:-D__ARM_ARCH_4T__} \ +%{march=armv5:-D__ARM_ARCH_5__} \ +%{march=armv5t:-D__ARM_ARCH_5T__} \ +%{march=armv5e:-D__ARM_ARCH_5E__} \ +%{march=armv5te:-D__ARM_ARCH_5TE__} \ +%{!march=*: \ + %{mcpu=arm2:-D__ARM_ARCH_2__} \ + %{mcpu=arm250:-D__ARM_ARCH_2__} \ + %{mcpu=arm3:-D__ARM_ARCH_2__} \ + %{mcpu=arm6:-D__ARM_ARCH_3__} \ + %{mcpu=arm600:-D__ARM_ARCH_3__} \ + %{mcpu=arm610:-D__ARM_ARCH_3__} \ + %{mcpu=arm7:-D__ARM_ARCH_3__} \ + %{mcpu=arm700:-D__ARM_ARCH_3__} \ + %{mcpu=arm710:-D__ARM_ARCH_3__} \ + %{mcpu=arm720:-D__ARM_ARCH_3__} \ + %{mcpu=arm7100:-D__ARM_ARCH_3__} \ + %{mcpu=arm7500:-D__ARM_ARCH_3__} \ + %{mcpu=arm7500fe:-D__ARM_ARCH_3__} \ + %{mcpu=arm7m:-D__ARM_ARCH_3M__} \ + %{mcpu=arm7dm:-D__ARM_ARCH_3M__} \ + %{mcpu=arm7dmi:-D__ARM_ARCH_3M__} \ + %{mcpu=arm7tdmi:-D__ARM_ARCH_4T__} \ + %{mcpu=arm8:-D__ARM_ARCH_4__} \ + %{mcpu=arm810:-D__ARM_ARCH_4__} \ + %{mcpu=arm9:-D__ARM_ARCH_4T__} \ + %{mcpu=arm920:-D__ARM_ARCH_4__} \ + %{mcpu=arm920t:-D__ARM_ARCH_4T__} \ + %{mcpu=arm9tdmi:-D__ARM_ARCH_4T__} \ + %{mcpu=strongarm:-D__ARM_ARCH_4__} \ + %{mcpu=strongarm110:-D__ARM_ARCH_4__} \ + %{mcpu=strongarm1100:-D__ARM_ARCH_4__} \ + %{mcpu=xscale:-D__ARM_ARCH_5TE__} \ + %{mcpu=xscale:-D__XSCALE__} \ + %{!mcpu*:%(cpp_cpu_arch_default)}} \ +" + +/* Define __APCS_26__ if the PC also contains the PSR */ +#define CPP_APCS_PC_SPEC "\ +%{mapcs-32:%{mapcs-26:%e-mapcs-26 and -mapcs-32 may not be used together} \ + -D__APCS_32__} \ +%{mapcs-26:-D__APCS_26__} \ +%{!mapcs-32: %{!mapcs-26:%(cpp_apcs_pc_default)}} \ +" + +#ifndef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_26__" +#endif + +#define CPP_FLOAT_SPEC "\ +%{msoft-float:\ + %{mhard-float:%e-msoft-float and -mhard_float may not be used together} \ + -D__SOFTFP__} \ +%{!mhard-float:%{!msoft-float:%(cpp_float_default)}} \ +" + +/* Default is hard float, which doesn't define anything */ +#define CPP_FLOAT_DEFAULT_SPEC "" + +#define CPP_ENDIAN_SPEC "\ +%{mbig-endian: \ + %{mlittle-endian: \ + %e-mbig-endian and -mlittle-endian may not be used together} \ + -D__ARMEB__ %{mwords-little-endian:-D__ARMWEL__} %{mthumb:-D__THUMBEB__}}\ +%{mlittle-endian:-D__ARMEL__ %{mthumb:-D__THUMBEL__}} \ +%{!mlittle-endian:%{!mbig-endian:%(cpp_endian_default)}} \ +" + +/* Default is little endian. */ +#define CPP_ENDIAN_DEFAULT_SPEC "-D__ARMEL__ %{mthumb:-D__THUMBEL__}" + +/* Add a define for interworking. Needed when building libgcc.a. + This must define __THUMB_INTERWORK__ to the pre-processor if + interworking is enabled by default. */ +#ifndef CPP_INTERWORK_DEFAULT_SPEC +#define CPP_INTERWORK_DEFAULT_SPEC "" +#endif + +#define CPP_INTERWORK_SPEC " \ +%{mthumb-interwork: \ + %{mno-thumb-interwork: %eincompatible interworking options} \ + -D__THUMB_INTERWORK__} \ +%{!mthumb-interwork:%{!mno-thumb-interwork:%(cpp_interwork_default)}} \ +" + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "" +#endif + +#ifndef CC1_SPEC +#define CC1_SPEC "" +#endif + +/* This macro defines names of additional specifications to put in the specs + that can be used in various specifications like CC1_SPEC. Its definition + is an initializer with a subgrouping for each command option. + + Each subgrouping contains a string constant, that defines the + specification name, and a string constant that used by the GNU CC driver + program. + + Do not define this macro if it does not need to do anything. */ +#define EXTRA_SPECS \ + { "cpp_cpu_arch", CPP_CPU_ARCH_SPEC }, \ + { "cpp_cpu_arch_default", CPP_ARCH_DEFAULT_SPEC }, \ + { "cpp_apcs_pc", CPP_APCS_PC_SPEC }, \ + { "cpp_apcs_pc_default", CPP_APCS_PC_DEFAULT_SPEC }, \ + { "cpp_float", CPP_FLOAT_SPEC }, \ + { "cpp_float_default", CPP_FLOAT_DEFAULT_SPEC }, \ + { "cpp_endian", CPP_ENDIAN_SPEC }, \ + { "cpp_endian_default", CPP_ENDIAN_DEFAULT_SPEC }, \ + { "cpp_isa", CPP_ISA_SPEC }, \ + { "cpp_interwork", CPP_INTERWORK_SPEC }, \ + { "cpp_interwork_default", CPP_INTERWORK_DEFAULT_SPEC }, \ + { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \ + SUBTARGET_EXTRA_SPECS + +#ifndef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS +#endif + +#ifndef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "" +#endif + +/* Run-time Target Specification. */ +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/generic)", stderr); +#endif + +/* Nonzero if the function prologue (and epilogue) should obey + the ARM Procedure Call Standard. */ +#define ARM_FLAG_APCS_FRAME (1 << 0) + +/* Nonzero if the function prologue should output the function name to enable + the post mortem debugger to print a backtrace (very useful on RISCOS, + unused on RISCiX). Specifying this flag also enables + -fno-omit-frame-pointer. + XXX Must still be implemented in the prologue. */ +#define ARM_FLAG_POKE (1 << 1) + +/* Nonzero if floating point instructions are emulated by the FPE, in which + case instruction scheduling becomes very uninteresting. */ +#define ARM_FLAG_FPE (1 << 2) + +/* Nonzero if destined for a processor in 32-bit program mode. Takes out bit + that assume restoration of the condition flags when returning from a + branch and link (ie a function). */ +#define ARM_FLAG_APCS_32 (1 << 3) + +/* FLAGS 0x0008 and 0x0010 are now spare (used to be arm3/6 selection). */ + +/* Nonzero if stack checking should be performed on entry to each function + which allocates temporary variables on the stack. */ +#define ARM_FLAG_APCS_STACK (1 << 4) + +/* Nonzero if floating point parameters should be passed to functions in + floating point registers. */ +#define ARM_FLAG_APCS_FLOAT (1 << 5) + +/* Nonzero if re-entrant, position independent code should be generated. + This is equivalent to -fpic. */ +#define ARM_FLAG_APCS_REENT (1 << 6) + +/* Nonzero if the MMU will trap unaligned word accesses, so shorts must + be loaded using either LDRH or LDRB instructions. */ +#define ARM_FLAG_MMU_TRAPS (1 << 7) + +/* Nonzero if all floating point instructions are missing (and there is no + emulator either). Generate function calls for all ops in this case. */ +#define ARM_FLAG_SOFT_FLOAT (1 << 8) + +/* Nonzero if we should compile with BYTES_BIG_ENDIAN set to 1. */ +#define ARM_FLAG_BIG_END (1 << 9) + +/* Nonzero if we should compile for Thumb interworking. */ +#define ARM_FLAG_INTERWORK (1 << 10) + +/* Nonzero if we should have little-endian words even when compiling for + big-endian (for backwards compatibility with older versions of GCC). */ +#define ARM_FLAG_LITTLE_WORDS (1 << 11) + +/* Nonzero if we need to protect the prolog from scheduling */ +#define ARM_FLAG_NO_SCHED_PRO (1 << 12) + +/* Nonzero if a call to abort should be generated if a noreturn + function tries to return. */ +#define ARM_FLAG_ABORT_NORETURN (1 << 13) + +/* Nonzero if function prologues should not load the PIC register. */ +#define ARM_FLAG_SINGLE_PIC_BASE (1 << 14) + +/* Nonzero if all call instructions should be indirect. */ +#define ARM_FLAG_LONG_CALLS (1 << 15) + +/* Nonzero means that the target ISA is the THUMB, not the ARM. */ +#define ARM_FLAG_THUMB (1 << 16) + +/* Set if a TPCS style stack frame should be generated, for non-leaf + functions, even if they do not need one. */ +#define THUMB_FLAG_BACKTRACE (1 << 17) + +/* Set if a TPCS style stack frame should be generated, for leaf + functions, even if they do not need one. */ +#define THUMB_FLAG_LEAF_BACKTRACE (1 << 18) + +/* Set if externally visible functions should assume that they + might be called in ARM mode, from a non-thumb aware code. */ +#define THUMB_FLAG_CALLEE_SUPER_INTERWORKING (1 << 19) + +/* Set if calls via function pointers should assume that their + destination is non-Thumb aware. */ +#define THUMB_FLAG_CALLER_SUPER_INTERWORKING (1 << 20) + +#define TARGET_APCS_FRAME (target_flags & ARM_FLAG_APCS_FRAME) +#define TARGET_POKE_FUNCTION_NAME (target_flags & ARM_FLAG_POKE) +#define TARGET_FPE (target_flags & ARM_FLAG_FPE) +#define TARGET_APCS_32 (target_flags & ARM_FLAG_APCS_32) +#define TARGET_APCS_STACK (target_flags & ARM_FLAG_APCS_STACK) +#define TARGET_APCS_FLOAT (target_flags & ARM_FLAG_APCS_FLOAT) +#define TARGET_APCS_REENT (target_flags & ARM_FLAG_APCS_REENT) +#define TARGET_MMU_TRAPS (target_flags & ARM_FLAG_MMU_TRAPS) +#define TARGET_SOFT_FLOAT (target_flags & ARM_FLAG_SOFT_FLOAT) +#define TARGET_HARD_FLOAT (! TARGET_SOFT_FLOAT) +#define TARGET_BIG_END (target_flags & ARM_FLAG_BIG_END) +#define TARGET_INTERWORK (target_flags & ARM_FLAG_INTERWORK) +#define TARGET_LITTLE_WORDS (target_flags & ARM_FLAG_LITTLE_WORDS) +#define TARGET_NO_SCHED_PRO (target_flags & ARM_FLAG_NO_SCHED_PRO) +#define TARGET_ABORT_NORETURN (target_flags & ARM_FLAG_ABORT_NORETURN) +#define TARGET_SINGLE_PIC_BASE (target_flags & ARM_FLAG_SINGLE_PIC_BASE) +#define TARGET_LONG_CALLS (target_flags & ARM_FLAG_LONG_CALLS) +#define TARGET_THUMB (target_flags & ARM_FLAG_THUMB) +#define TARGET_ARM (! TARGET_THUMB) +#define TARGET_EITHER 1 /* (TARGET_ARM | TARGET_THUMB) */ +#define TARGET_CALLEE_INTERWORKING (target_flags & THUMB_FLAG_CALLEE_SUPER_INTERWORKING) +#define TARGET_CALLER_INTERWORKING (target_flags & THUMB_FLAG_CALLER_SUPER_INTERWORKING) +#define TARGET_BACKTRACE (leaf_function_p () \ + ? (target_flags & THUMB_FLAG_LEAF_BACKTRACE) \ + : (target_flags & THUMB_FLAG_BACKTRACE)) + +/* SUBTARGET_SWITCHES is used to add flags on a per-config basis. + Bit 31 is reserved. See riscix.h. */ +#ifndef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES +#endif + +#define TARGET_SWITCHES \ +{ \ + {"apcs", ARM_FLAG_APCS_FRAME, "" }, \ + {"apcs-frame", ARM_FLAG_APCS_FRAME, \ + N_("Generate APCS conformant stack frames") }, \ + {"no-apcs-frame", -ARM_FLAG_APCS_FRAME, "" }, \ + {"poke-function-name", ARM_FLAG_POKE, \ + N_("Store function names in object code") }, \ + {"no-poke-function-name", -ARM_FLAG_POKE, "" }, \ + {"fpe", ARM_FLAG_FPE, "" }, \ + {"apcs-32", ARM_FLAG_APCS_32, \ + N_("Use the 32-bit version of the APCS") }, \ + {"apcs-26", -ARM_FLAG_APCS_32, \ + N_("Use the 26-bit version of the APCS") }, \ + {"apcs-stack-check", ARM_FLAG_APCS_STACK, "" }, \ + {"no-apcs-stack-check", -ARM_FLAG_APCS_STACK, "" }, \ + {"apcs-float", ARM_FLAG_APCS_FLOAT, \ + N_("Pass FP arguments in FP registers") }, \ + {"no-apcs-float", -ARM_FLAG_APCS_FLOAT, "" }, \ + {"apcs-reentrant", ARM_FLAG_APCS_REENT, \ + N_("Generate re-entrant, PIC code") }, \ + {"no-apcs-reentrant", -ARM_FLAG_APCS_REENT, "" }, \ + {"alignment-traps", ARM_FLAG_MMU_TRAPS, \ + N_("The MMU will trap on unaligned accesses") }, \ + {"no-alignment-traps", -ARM_FLAG_MMU_TRAPS, "" }, \ + {"short-load-bytes", ARM_FLAG_MMU_TRAPS, "" }, \ + {"no-short-load-bytes", -ARM_FLAG_MMU_TRAPS, "" }, \ + {"short-load-words", -ARM_FLAG_MMU_TRAPS, "" }, \ + {"no-short-load-words", ARM_FLAG_MMU_TRAPS, "" }, \ + {"soft-float", ARM_FLAG_SOFT_FLOAT, \ + N_("Use library calls to perform FP operations") }, \ + {"hard-float", -ARM_FLAG_SOFT_FLOAT, \ + N_("Use hardware floating point instructions") }, \ + {"big-endian", ARM_FLAG_BIG_END, \ + N_("Assume target CPU is configured as big endian") }, \ + {"little-endian", -ARM_FLAG_BIG_END, \ + N_("Assume target CPU is configured as little endian") }, \ + {"words-little-endian", ARM_FLAG_LITTLE_WORDS, \ + N_("Assume big endian bytes, little endian words") }, \ + {"thumb-interwork", ARM_FLAG_INTERWORK, \ + N_("Support calls between Thumb and ARM instruction sets") }, \ + {"no-thumb-interwork", -ARM_FLAG_INTERWORK, "" }, \ + {"abort-on-noreturn", ARM_FLAG_ABORT_NORETURN, \ + N_("Generate a call to abort if a noreturn function returns")}, \ + {"no-abort-on-noreturn", -ARM_FLAG_ABORT_NORETURN, "" }, \ + {"no-sched-prolog", ARM_FLAG_NO_SCHED_PRO, \ + N_("Do not move instructions into a function's prologue") }, \ + {"sched-prolog", -ARM_FLAG_NO_SCHED_PRO, "" }, \ + {"single-pic-base", ARM_FLAG_SINGLE_PIC_BASE, \ + N_("Do not load the PIC register in function prologues") }, \ + {"no-single-pic-base", -ARM_FLAG_SINGLE_PIC_BASE, "" }, \ + {"long-calls", ARM_FLAG_LONG_CALLS, \ + N_("Generate call insns as indirect calls, if necessary") }, \ + {"no-long-calls", -ARM_FLAG_LONG_CALLS, "" }, \ + {"thumb", ARM_FLAG_THUMB, \ + N_("Compile for the Thumb not the ARM") }, \ + {"no-thumb", -ARM_FLAG_THUMB, "" }, \ + {"arm", -ARM_FLAG_THUMB, "" }, \ + {"tpcs-frame", THUMB_FLAG_BACKTRACE, \ + N_("Thumb: Generate (non-leaf) stack frames even if not needed") }, \ + {"no-tpcs-frame", -THUMB_FLAG_BACKTRACE, "" }, \ + {"tpcs-leaf-frame", THUMB_FLAG_LEAF_BACKTRACE, \ + N_("Thumb: Generate (leaf) stack frames even if not needed") }, \ + {"no-tpcs-leaf-frame", -THUMB_FLAG_LEAF_BACKTRACE, "" }, \ + {"callee-super-interworking", THUMB_FLAG_CALLEE_SUPER_INTERWORKING, \ + N_("Thumb: Assume non-static functions may be called from ARM code") }, \ + {"no-callee-super-interworking", -THUMB_FLAG_CALLEE_SUPER_INTERWORKING, \ + "" }, \ + {"caller-super-interworking", THUMB_FLAG_CALLER_SUPER_INTERWORKING, \ + N_("Thumb: Assume function pointers may go to non-Thumb aware code") }, \ + {"no-caller-super-interworking", -THUMB_FLAG_CALLER_SUPER_INTERWORKING, \ + "" }, \ + SUBTARGET_SWITCHES \ + {"", TARGET_DEFAULT, "" } \ +} + +#define TARGET_OPTIONS \ +{ \ + {"cpu=", & arm_select[0].string, \ + N_("Specify the name of the target CPU") }, \ + {"arch=", & arm_select[1].string, \ + N_("Specify the name of the target architecture") }, \ + {"tune=", & arm_select[2].string, "" }, \ + {"fpe=", & target_fp_name, "" }, \ + {"fp=", & target_fp_name, \ + N_("Specify the version of the floating point emulator") }, \ + {"structure-size-boundary=", & structure_size_string, \ + N_("Specify the minimum bit alignment of structures") }, \ + {"pic-register=", & arm_pic_register_string, \ + N_("Specify the register to be used for PIC addressing") } \ +} + +struct arm_cpu_select +{ + const char * string; + const char * name; + const struct processors * processors; +}; + +/* This is a magic array. If the user specifies a command line switch + which matches one of the entries in TARGET_OPTIONS then the corresponding + string pointer will be set to the value specified by the user. */ +extern struct arm_cpu_select arm_select[]; + +enum prog_mode_type +{ + prog_mode26, + prog_mode32 +}; + +/* Recast the program mode class to be the prog_mode attribute */ +#define arm_prog_mode ((enum attr_prog_mode) arm_prgmode) + +extern enum prog_mode_type arm_prgmode; + +/* What sort of floating point unit do we have? Hardware or software. + If software, is it issue 2 or issue 3? */ +enum floating_point_type +{ + FP_HARD, + FP_SOFT2, + FP_SOFT3 +}; + +/* Recast the floating point class to be the floating point attribute. */ +#define arm_fpu_attr ((enum attr_fpu) arm_fpu) + +/* What type of floating point to tune for */ +extern enum floating_point_type arm_fpu; + +/* What type of floating point instructions are available */ +extern enum floating_point_type arm_fpu_arch; + +/* Default floating point architecture. Override in sub-target if + necessary. */ +#ifndef FP_DEFAULT +#define FP_DEFAULT FP_SOFT2 +#endif + +/* Nonzero if the processor has a fast multiply insn, and one that does + a 64-bit multiply of two 32-bit values. */ +extern int arm_fast_multiply; + +/* Nonzero if this chip supports the ARM Architecture 4 extensions */ +extern int arm_arch4; + +/* Nonzero if this chip supports the ARM Architecture 5 extensions */ +extern int arm_arch5; + +/* Nonzero if this chip supports the ARM Architecture 5E extensions */ +extern int arm_arch5e; + +/* Nonzero if this chip can benefit from load scheduling. */ +extern int arm_ld_sched; + +/* Nonzero if generating thumb code. */ +extern int thumb_code; + +/* Nonzero if this chip is a StrongARM. */ +extern int arm_is_strong; + +/* Nonzero if this chip is an XScale. */ +extern int arm_is_xscale; + +/* Nonzero if this chip is an ARM6 or an ARM7. */ +extern int arm_is_6_or_7; + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_APCS_FRAME) +#endif + +/* The frame pointer register used in gcc has nothing to do with debugging; + that is controlled by the APCS-FRAME option. */ +#define CAN_DEBUG_WITHOUT_FP + +#undef TARGET_MEM_FUNCTIONS +#define TARGET_MEM_FUNCTIONS 1 + +#define OVERRIDE_OPTIONS arm_override_options () + +/* Nonzero if PIC code requires explicit qualifiers to generate + PLT and GOT relocs rather than the assembler doing so implicitly. + Subtargets can override these if required. */ +#ifndef NEED_GOT_RELOC +#define NEED_GOT_RELOC 0 +#endif +#ifndef NEED_PLT_RELOC +#define NEED_PLT_RELOC 0 +#endif + +/* Nonzero if we need to refer to the GOT with a PC-relative + offset. In other words, generate + + .word _GLOBAL_OFFSET_TABLE_ - [. - (.Lxx + 8)] + + rather than + + .word _GLOBAL_OFFSET_TABLE_ - (.Lxx + 8) + + The default is true, which matches NetBSD. Subtargets can + override this if required. */ +#ifndef GOT_PCREL +#define GOT_PCREL 1 +#endif + +/* Target machine storage Layout. */ + + +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. */ + +/* It is far faster to zero extend chars than to sign extend them */ + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 4) \ + { \ + if (MODE == QImode) \ + UNSIGNEDP = 1; \ + else if (MODE == HImode) \ + UNSIGNEDP = TARGET_MMU_TRAPS != 0; \ + (MODE) = SImode; \ + } + +/* Define this macro if the promotion described by `PROMOTE_MODE' + should also be done for outgoing function arguments. */ +/* This is required to ensure that push insns always push a word. */ +#define PROMOTE_FUNCTION_ARGS + +/* Define for XFmode extended real floating point support. + This will automatically cause REAL_ARITHMETIC to be defined. */ +/* For the ARM: + I think I have added all the code to make this work. Unfortunately, + early releases of the floating point emulation code on RISCiX used a + different format for extended precision numbers. On my RISCiX box there + is a bug somewhere which causes the machine to lock up when running enquire + with long doubles. There is the additional aspect that Norcroft C + treats long doubles as doubles and we ought to remain compatible. + Perhaps someone with an FPA coprocessor and not running RISCiX would like + to try this someday. */ +/* #define LONG_DOUBLE_TYPE_SIZE 96 */ + +/* Disable XFmode patterns in md file */ +#define ENABLE_XF_PATTERNS 0 + +/* Define if you don't want extended real, but do want to use the + software floating point emulator for REAL_ARITHMETIC and + decimal <-> binary conversion. */ +/* See comment above */ +#define REAL_ARITHMETIC + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. */ +#define BITS_BIG_ENDIAN 0 + +/* Define this if most significant byte of a word is the lowest numbered. + Most ARM processors are run in little endian mode, so that is the default. + If you want to have it run-time selectable, change the definition in a + cover file to be TARGET_BIG_ENDIAN. */ +#define BYTES_BIG_ENDIAN (TARGET_BIG_END != 0) + +/* Define this if most significant word of a multiword number is the lowest + numbered. + This is always false, even when in big-endian mode. */ +#define WORDS_BIG_ENDIAN (BYTES_BIG_ENDIAN && ! TARGET_LITTLE_WORDS) + +/* LIBGCC2_WORDS_BIG_ENDIAN has to be a constant, so we define this based + on processor pre-defineds when compiling libgcc2.c. */ +#if defined(__ARMEB__) && !defined(__ARMWEL__) +#define LIBGCC2_WORDS_BIG_ENDIAN 1 +#else +#define LIBGCC2_WORDS_BIG_ENDIAN 0 +#endif + +/* Define this if most significant word of doubles is the lowest numbered. + This is always true, even when in little-endian mode. */ +#define FLOAT_WORDS_BIG_ENDIAN 1 + +/* Number of bits in an addressable storage unit */ +#define BITS_PER_UNIT 8 + +#define BITS_PER_WORD 32 + +#define UNITS_PER_WORD 4 + +#define POINTER_SIZE 32 + +#define PARM_BOUNDARY 32 + +#define STACK_BOUNDARY 32 + +#define FUNCTION_BOUNDARY 32 + +/* The lowest bit is used to indicate Thumb-mode functions, so the + vbit must go into the delta field of pointers to member + functions. */ +#define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_delta + +#define EMPTY_FIELD_BOUNDARY 32 + +#define BIGGEST_ALIGNMENT 32 + +/* Make strings word-aligned so strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT_FACTOR (TARGET_THUMB || ! arm_is_xscale ? 1 : 2) + +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR) \ + ? BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR : (ALIGN)) + +/* Setting STRUCTURE_SIZE_BOUNDARY to 32 produces more efficient code, but the + value set in previous versions of this toolchain was 8, which produces more + compact structures. The command line option -mstructure_size_boundary=<n> + can be used to change this value. For compatibility with the ARM SDK + however the value should be left at 32. ARM SDT Reference Manual (ARM DUI + 0020D) page 2-20 says "Structures are aligned on word boundaries". */ +#define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary +extern int arm_structure_size_boundary; + +/* This is the value used to initialise arm_structure_size_boundary. If a + particular arm target wants to change the default value it should change + the definition of this macro, not STRUCTRUE_SIZE_BOUNDARY. See netbsd.h + for an example of this. */ +#ifndef DEFAULT_STRUCTURE_SIZE_BOUNDARY +#define DEFAULT_STRUCTURE_SIZE_BOUNDARY 32 +#endif + +/* Used when parsing command line option -mstructure_size_boundary. */ +extern const char * structure_size_string; + +/* Non-zero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT + + +/* Standard register usage. */ + +/* Register allocation in ARM Procedure Call Standard (as used on RISCiX): + (S - saved over call). + + r0 * argument word/integer result + r1-r3 argument word + + r4-r8 S register variable + r9 S (rfp) register variable (real frame pointer) + + r10 F S (sl) stack limit (used by -mapcs-stack-check) + r11 F S (fp) argument pointer + r12 (ip) temp workspace + r13 F S (sp) lower end of current stack frame + r14 (lr) link address/workspace + r15 F (pc) program counter + + f0 floating point result + f1-f3 floating point scratch + + f4-f7 S floating point variable + + cc This is NOT a real register, but is used internally + to represent things that use or set the condition + codes. + sfp This isn't either. It is used during rtl generation + since the offset between the frame pointer and the + auto's isn't known until after register allocation. + afp Nor this, we only need this because of non-local + goto. Without it fp appears to be used and the + elimination code won't get rid of sfp. It tracks + fp exactly at all times. + + *: See CONDITIONAL_REGISTER_USAGE */ + +/* The stack backtrace structure is as follows: + fp points to here: | save code pointer | [fp] + | return link value | [fp, #-4] + | return sp value | [fp, #-8] + | return fp value | [fp, #-12] + [| saved r10 value |] + [| saved r9 value |] + [| saved r8 value |] + [| saved r7 value |] + [| saved r6 value |] + [| saved r5 value |] + [| saved r4 value |] + [| saved r3 value |] + [| saved r2 value |] + [| saved r1 value |] + [| saved r0 value |] + [| saved f7 value |] three words + [| saved f6 value |] three words + [| saved f5 value |] three words + [| saved f4 value |] three words + r0-r3 are not normally saved in a C function. */ + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. */ +#define FIXED_REGISTERS \ +{ \ + 0,0,0,0,0,0,0,0, \ + 0,0,0,0,0,1,0,1, \ + 0,0,0,0,0,0,0,0, \ + 1,1,1 \ +} + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. + The CC is not preserved over function calls on the ARM 6, so it is + easier to assume this for all. SFP is preserved, since FP is. */ +#define CALL_USED_REGISTERS \ +{ \ + 1,1,1,1,0,0,0,0, \ + 0,0,0,0,1,1,1,1, \ + 1,1,1,1,0,0,0,0, \ + 1,1,1 \ +} + +#ifndef SUBTARGET_CONDITIONAL_REGISTER_USAGE +#define SUBTARGET_CONDITIONAL_REGISTER_USAGE +#endif + +#define CONDITIONAL_REGISTER_USAGE \ +{ \ + int regno; \ + \ + if (TARGET_SOFT_FLOAT || TARGET_THUMB) \ + { \ + for (regno = FIRST_ARM_FP_REGNUM; \ + regno <= LAST_ARM_FP_REGNUM; ++regno) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + } \ + if (flag_pic) \ + { \ + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \ + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \ + } \ + else if (TARGET_APCS_STACK) \ + { \ + fixed_regs[10] = 1; \ + call_used_regs[10] = 1; \ + } \ + if (TARGET_APCS_FRAME) \ + { \ + fixed_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \ + call_used_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \ + } \ + SUBTARGET_CONDITIONAL_REGISTER_USAGE \ +} + +/* These are a couple of extensions to the formats accecpted + by asm_fprintf: + %@ prints out ASM_COMMENT_START + %r prints out REGISTER_PREFIX reg_names[arg] */ +#define ASM_FPRINTF_EXTENSIONS(FILE, ARGS, P) \ + case '@': \ + fputs (ASM_COMMENT_START, FILE); \ + break; \ + \ + case 'r': \ + fputs (REGISTER_PREFIX, FILE); \ + fputs (reg_names [va_arg (ARGS, int)], FILE); \ + break; + +/* Round X up to the nearest word. */ +#define ROUND_UP(X) (((X) + 3) & ~3) + +/* Convert fron bytes to ints. */ +#define NUM_INTS(X) (((X) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* The number of (integer) registers required to hold a quantity of type MODE. */ +#define NUM_REGS(MODE) \ + NUM_INTS (GET_MODE_SIZE (MODE)) + +/* The number of (integer) registers required to hold a quantity of TYPE MODE. */ +#define NUM_REGS2(MODE, TYPE) \ + NUM_INTS ((MODE) == BLKmode ? \ + int_size_in_bytes (TYPE) : GET_MODE_SIZE (MODE)) + +/* The number of (integer) argument register available. */ +#define NUM_ARG_REGS 4 + +/* Return the regiser number of the N'th (integer) argument. */ +#define ARG_REGISTER(N) (N - 1) + +#if 0 /* FIXME: The ARM backend has special code to handle structure + returns, and will reserve its own hidden first argument. So + if this macro is enabled a *second* hidden argument will be + reserved, which will break binary compatibility with old + toolchains and also thunk handling. One day this should be + fixed. */ +/* RTX for structure returns. NULL means use a hidden first argument. */ +#define STRUCT_VALUE 0 +#else +/* Register in which address to store a structure value + is passed to a function. */ +#define STRUCT_VALUE_REGNUM ARG_REGISTER (1) +#endif + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* The number of the last argument register. */ +#define LAST_ARG_REGNUM ARG_REGISTER (NUM_ARG_REGS) + +/* The number of the last "lo" register (thumb). */ +#define LAST_LO_REGNUM 7 + +/* The register that holds the return address in exception handlers. */ +#define EXCEPTION_LR_REGNUM 2 + +/* The native (Norcroft) Pascal compiler for the ARM passes the static chain + as an invisible last argument (possible since varargs don't exist in + Pascal), so the following is not true. */ +#define STATIC_CHAIN_REGNUM (TARGET_ARM ? 12 : 9) + +/* Define this to be where the real frame pointer is if it is not possible to + work out the offset between the frame pointer and the automatic variables + until after register allocation has taken place. FRAME_POINTER_REGNUM + should point to a special register that we will make sure is eliminated. + + For the Thumb we have another problem. The TPCS defines the frame pointer + as r11, and GCC belives that it is always possible to use the frame pointer + as base register for addressing purposes. (See comments in + find_reloads_address()). But - the Thumb does not allow high registers, + including r11, to be used as base address registers. Hence our problem. + + The solution used here, and in the old thumb port is to use r7 instead of + r11 as the hard frame pointer and to have special code to generate + backtrace structures on the stack (if required to do so via a command line + option) using r11. This is the only 'user visable' use of r11 as a frame + pointer. */ +#define ARM_HARD_FRAME_POINTER_REGNUM 11 +#define THUMB_HARD_FRAME_POINTER_REGNUM 7 + +#define HARD_FRAME_POINTER_REGNUM \ + (TARGET_ARM \ + ? ARM_HARD_FRAME_POINTER_REGNUM \ + : THUMB_HARD_FRAME_POINTER_REGNUM) + +#define FP_REGNUM HARD_FRAME_POINTER_REGNUM + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM SP_REGNUM + +/* ARM floating pointer registers. */ +#define FIRST_ARM_FP_REGNUM 16 +#define LAST_ARM_FP_REGNUM 23 + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 25 + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM 26 + +/* The number of hard registers is 16 ARM + 8 FPU + 1 CC + 1 SFP. */ +#define FIRST_PSEUDO_REGISTER 27 + +/* Value should be nonzero if functions must have frame pointers. + Zero means the frame pointer need not be set up (and parms may be accessed + via the stack pointer) in functions that seem suitable. + If we have to have a frame pointer we might as well make use of it. + APCS says that the frame pointer does not need to be pushed in leaf + functions, or simple tail call functions. */ +#define FRAME_POINTER_REQUIRED \ + (current_function_has_nonlocal_label \ + || (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ())) + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. + + On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP + mode. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((TARGET_ARM \ + && REGNO >= FIRST_ARM_FP_REGNUM \ + && REGNO != FRAME_POINTER_REGNUM \ + && REGNO != ARG_POINTER_REGNUM) \ + ? 1 : NUM_REGS (MODE)) + +/* Return true if REGNO is suitable for holding a quantity of type MODE. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + arm_hard_regno_mode_ok ((REGNO), (MODE)) + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2)) + +/* The order in which register should be allocated. It is good to use ip + since no saving is required (though calls clobber it) and it never contains + function parameters. It is quite good to use lr since other calls may + clobber it anyway. Allocate r0 through r3 in reverse order since r3 is + least likely to contain a function parameter; in addition results are + returned in r0. */ +#define REG_ALLOC_ORDER \ +{ \ + 3, 2, 1, 0, 12, 14, 4, 5, \ + 6, 7, 8, 10, 9, 11, 13, 15, \ + 16, 17, 18, 19, 20, 21, 22, 23, \ + 24, 25, 26 \ +} + +/* Register and constant classes. */ + +/* Register classes: used to be simple, just all ARM regs or all FPU regs + Now that the Thumb is involved it has become more complicated. */ +enum reg_class +{ + NO_REGS, + FPU_REGS, + LO_REGS, + STACK_REG, + BASE_REGS, + HI_REGS, + CC_REG, + GENERAL_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "FPU_REGS", \ + "LO_REGS", \ + "STACK_REG", \ + "BASE_REGS", \ + "HI_REGS", \ + "CC_REG", \ + "GENERAL_REGS", \ + "ALL_REGS", \ +} + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. */ +#define REG_CLASS_CONTENTS \ +{ \ + { 0x0000000 }, /* NO_REGS */ \ + { 0x0FF0000 }, /* FPU_REGS */ \ + { 0x00000FF }, /* LO_REGS */ \ + { 0x0002000 }, /* STACK_REG */ \ + { 0x00020FF }, /* BASE_REGS */ \ + { 0x000FF00 }, /* HI_REGS */ \ + { 0x1000000 }, /* CC_REG */ \ + { 0x200FFFF }, /* GENERAL_REGS */ \ + { 0x2FFFFFF } /* ALL_REGS */ \ +} + +/* The same information, inverted: + Return the class number of the smallest class containing + reg number REGNO. This could be a conditional expression + or could index an array. */ +#define REGNO_REG_CLASS(REGNO) arm_regno_class (REGNO) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS (TARGET_THUMB ? LO_REGS : GENERAL_REGS) +#define BASE_REG_CLASS (TARGET_THUMB ? BASE_REGS : GENERAL_REGS) + +/* For the Thumb the high registers cannot be used as base + registers when addressing quanitities in QI or HI mode. */ +#define MODE_BASE_REG_CLASS(MODE) \ + (TARGET_ARM ? BASE_REGS : \ + (((MODE) == QImode || (MODE) == HImode || (MODE) == VOIDmode) \ + ? LO_REGS : BASE_REGS)) + +/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows + registers explicitly used in the rtl to be used as spill registers + but prevents the compiler from extending the lifetime of these + registers. */ +#define SMALL_REGISTER_CLASSES TARGET_THUMB + +/* Get reg_class from a letter such as appears in the machine description. + We only need constraint `f' for FPU_REGS (`r' == GENERAL_REGS) for the + ARM, but several more letters for the Thumb. */ +#define REG_CLASS_FROM_LETTER(C) \ + ( (C) == 'f' ? FPU_REGS \ + : (C) == 'l' ? (TARGET_ARM ? GENERAL_REGS : LO_REGS) \ + : TARGET_ARM ? NO_REGS \ + : (C) == 'h' ? HI_REGS \ + : (C) == 'b' ? BASE_REGS \ + : (C) == 'k' ? STACK_REG \ + : (C) == 'c' ? CC_REG \ + : NO_REGS) + +/* The letters I, J, K, L and M in a register constraint string + can be used to stand for particular ranges of immediate operands. + This macro defines what the ranges are. + C is the letter, and VALUE is a constant value. + Return 1 if VALUE is in the range specified by C. + I: immediate arithmetic operand (i.e. 8 bits shifted as required). + J: valid indexing constants. + K: ~value ok in rhs argument of data operand. + L: -value ok in rhs argument of data operand. + M: 0..32, or a power of 2 (for shifts, or mult done by shift). */ +#define CONST_OK_FOR_ARM_LETTER(VALUE, C) \ + ((C) == 'I' ? const_ok_for_arm (VALUE) : \ + (C) == 'J' ? ((VALUE) < 4096 && (VALUE) > -4096) : \ + (C) == 'K' ? (const_ok_for_arm (~(VALUE))) : \ + (C) == 'L' ? (const_ok_for_arm (-(VALUE))) : \ + (C) == 'M' ? (((VALUE >= 0 && VALUE <= 32)) \ + || (((VALUE) & ((VALUE) - 1)) == 0)) \ + : 0) + +#define CONST_OK_FOR_THUMB_LETTER(VAL, C) \ + ((C) == 'I' ? (unsigned HOST_WIDE_INT) (VAL) < 256 : \ + (C) == 'J' ? (VAL) > -256 && (VAL) < 0 : \ + (C) == 'K' ? thumb_shiftable_const (VAL) : \ + (C) == 'L' ? (VAL) > -8 && (VAL) < 8 : \ + (C) == 'M' ? ((unsigned HOST_WIDE_INT) (VAL) < 1024 \ + && ((VAL) & 3) == 0) : \ + (C) == 'N' ? ((unsigned HOST_WIDE_INT) (VAL) < 32) : \ + (C) == 'O' ? ((VAL) >= -508 && (VAL) <= 508) \ + : 0) + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + (TARGET_ARM ? \ + CONST_OK_FOR_ARM_LETTER (VALUE, C) : CONST_OK_FOR_THUMB_LETTER (VALUE, C)) + +/* Constant letter 'G' for the FPU immediate constants. + 'H' means the same constant negated. */ +#define CONST_DOUBLE_OK_FOR_ARM_LETTER(X, C) \ + ((C) == 'G' ? const_double_rtx_ok_for_fpu (X) : \ + (C) == 'H' ? neg_const_double_rtx_ok_for_fpu (X) : 0) + +#define CONST_DOUBLE_OK_FOR_LETTER_P(X, C) \ + (TARGET_ARM ? \ + CONST_DOUBLE_OK_FOR_ARM_LETTER (X, C) : 0) + +/* For the ARM, `Q' means that this is a memory operand that is just + an offset from a register. + `S' means any symbol that has the SYMBOL_REF_FLAG set or a CONSTANT_POOL + address. This means that the symbol is in the text segment and can be + accessed without using a load. */ + +#define EXTRA_CONSTRAINT_ARM(OP, C) \ + ((C) == 'Q' ? GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG : \ + (C) == 'R' ? (GET_CODE (OP) == MEM \ + && GET_CODE (XEXP (OP, 0)) == SYMBOL_REF \ + && CONSTANT_POOL_ADDRESS_P (XEXP (OP, 0))) : \ + (C) == 'S' ? (optimize > 0 && CONSTANT_ADDRESS_P (OP)) \ + : 0) + +#define EXTRA_CONSTRAINT_THUMB(X, C) \ + ((C) == 'Q' ? (GET_CODE (X) == MEM \ + && GET_CODE (XEXP (X, 0)) == LABEL_REF) : 0) + +#define EXTRA_CONSTRAINT(X, C) \ + (TARGET_ARM ? \ + EXTRA_CONSTRAINT_ARM (X, C) : EXTRA_CONSTRAINT_THUMB (X, C)) + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS, but for the Thumb we prefer + a LO_REGS class or a subset. */ +#define PREFERRED_RELOAD_CLASS(X, CLASS) \ + (TARGET_ARM ? (CLASS) : \ + ((CLASS) == BASE_REGS ? (CLASS) : LO_REGS)) + +/* Must leave BASE_REGS reloads alone */ +#define THUMB_SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ + ((CLASS) != LO_REGS && (CLASS) != BASE_REGS \ + ? ((true_regnum (X) == -1 ? LO_REGS \ + : (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \ + : NO_REGS)) \ + : NO_REGS) + +#define THUMB_SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ + ((CLASS) != LO_REGS \ + ? ((true_regnum (X) == -1 ? LO_REGS \ + : (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \ + : NO_REGS)) \ + : NO_REGS) + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ + (TARGET_ARM ? \ + (((MODE) == HImode && ! arm_arch4 && true_regnum (X) == -1) \ + ? GENERAL_REGS : NO_REGS) \ + : THUMB_SECONDARY_OUTPUT_RELOAD_CLASS (CLASS, MODE, X)) + +/* If we need to load shorts byte-at-a-time, then we need a scratch. */ +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ + (TARGET_ARM ? \ + (((MODE) == HImode && ! arm_arch4 && TARGET_MMU_TRAPS \ + && (GET_CODE (X) == MEM \ + || ((GET_CODE (X) == REG || GET_CODE (X) == SUBREG) \ + && true_regnum (X) == -1))) \ + ? GENERAL_REGS : NO_REGS) \ + : THUMB_SECONDARY_INPUT_RELOAD_CLASS (CLASS, MODE, X)) + +/* Try a machine-dependent way of reloading an illegitimate address + operand. If we find one, push the reload and jump to WIN. This + macro is used in only one place: `find_reloads_address' in reload.c. + + For the ARM, we wish to handle large displacements off a base + register by splitting the addend across a MOV and the mem insn. + This can cut the number of reloads needed. */ +#define ARM_LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND, WIN) \ + do \ + { \ + if (GET_CODE (X) == PLUS \ + && GET_CODE (XEXP (X, 0)) == REG \ + && REGNO (XEXP (X, 0)) < FIRST_PSEUDO_REGISTER \ + && REG_MODE_OK_FOR_BASE_P (XEXP (X, 0), MODE) \ + && GET_CODE (XEXP (X, 1)) == CONST_INT) \ + { \ + HOST_WIDE_INT val = INTVAL (XEXP (X, 1)); \ + HOST_WIDE_INT low, high; \ + \ + if (MODE == DImode || (TARGET_SOFT_FLOAT && MODE == DFmode)) \ + low = ((val & 0xf) ^ 0x8) - 0x8; \ + else if (MODE == SImode \ + || (MODE == SFmode && TARGET_SOFT_FLOAT) \ + || ((MODE == HImode || MODE == QImode) && ! arm_arch4)) \ + /* Need to be careful, -4096 is not a valid offset. */ \ + low = val >= 0 ? (val & 0xfff) : -((-val) & 0xfff); \ + else if ((MODE == HImode || MODE == QImode) && arm_arch4) \ + /* Need to be careful, -256 is not a valid offset. */ \ + low = val >= 0 ? (val & 0xff) : -((-val) & 0xff); \ + else if (GET_MODE_CLASS (MODE) == MODE_FLOAT \ + && TARGET_HARD_FLOAT) \ + /* Need to be careful, -1024 is not a valid offset. */ \ + low = val >= 0 ? (val & 0x3ff) : -((-val) & 0x3ff); \ + else \ + break; \ + \ + high = ((((val - low) & (unsigned HOST_WIDE_INT) 0xffffffff) \ + ^ (unsigned HOST_WIDE_INT) 0x80000000) \ + - (unsigned HOST_WIDE_INT) 0x80000000); \ + /* Check for overflow or zero */ \ + if (low == 0 || high == 0 || (high + low != val)) \ + break; \ + \ + /* Reload the high part into a base reg; leave the low part \ + in the mem. */ \ + X = gen_rtx_PLUS (GET_MODE (X), \ + gen_rtx_PLUS (GET_MODE (X), XEXP (X, 0), \ + GEN_INT (high)), \ + GEN_INT (low)); \ + push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL, \ + MODE_BASE_REG_CLASS (MODE), GET_MODE (X), \ + VOIDmode, 0, 0, OPNUM, TYPE); \ + goto WIN; \ + } \ + } \ + while (0) + +/* ??? If an HImode FP+large_offset address is converted to an HImode + SP+large_offset address, then reload won't know how to fix it. It sees + only that SP isn't valid for HImode, and so reloads the SP into an index + register, but the resulting address is still invalid because the offset + is too big. We fix it here instead by reloading the entire address. */ +/* We could probably achieve better results by defining PROMOTE_MODE to help + cope with the variances between the Thumb's signed and unsigned byte and + halfword load instructions. */ +#define THUMB_LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \ +{ \ + if (GET_CODE (X) == PLUS \ + && GET_MODE_SIZE (MODE) < 4 \ + && GET_CODE (XEXP (X, 0)) == REG \ + && XEXP (X, 0) == stack_pointer_rtx \ + && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && ! THUMB_LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \ + { \ + rtx orig_X = X; \ + X = copy_rtx (X); \ + push_reload (orig_X, NULL_RTX, &X, NULL, \ + MODE_BASE_REG_CLASS (MODE), \ + Pmode, VOIDmode, 0, 0, OPNUM, TYPE); \ + goto WIN; \ + } \ +} + +#define LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \ + if (TARGET_ARM) \ + ARM_LEGITIMIZE_RELOAD_ADDRESS (X, MODE, OPNUM, TYPE, IND_LEVELS, WIN); \ + else \ + THUMB_LEGITIMIZE_RELOAD_ADDRESS (X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. + ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((CLASS) == FPU_REGS ? 1 : NUM_REGS (MODE)) + +/* Moves between FPU_REGS and GENERAL_REGS are two memory insns. */ +#define REGISTER_MOVE_COST(MODE, FROM, TO) \ + (TARGET_ARM ? \ + ((FROM) == FPU_REGS && (TO) != FPU_REGS ? 20 : \ + (FROM) != FPU_REGS && (TO) == FPU_REGS ? 20 : 2) \ + : \ + ((FROM) == HI_REGS || (TO) == HI_REGS) ? 4 : 2) + +/* Stack layout; function entry, exit and calling. */ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_DOWNWARD 1 + +/* Define this if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +#define FRAME_GROWS_DOWNWARD 1 + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET 0 + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. */ +/* The push insns do not do this rounding implicitly. + So don't define this. */ +/* #define PUSH_ROUNDING(NPUSHED) ROUND_UP (NPUSHED) */ + +/* Define this if the maximum size of all the outgoing args is to be + accumulated and pushed during the prologue. The amount can be + found in the variable current_function_outgoing_args_size. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* Offset of first parameter from the argument pointer register value. */ +#define FIRST_PARM_OFFSET(FNDECL) (TARGET_ARM ? 4 : 0) + +/* Value is the number of byte of arguments automatically + popped when returning from a subroutine call. + FUNDECL is the declaration node of the function (as a tree), + FUNTYPE is the data type of the function (as a tree), + or for a library call it is an identifier node for the subroutine name. + SIZE is the number of bytes of arguments passed on the stack. + + On the ARM, the caller does not pop any of its arguments that were passed + on the stack. */ +#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, SIZE) 0 + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ +#define LIBCALL_VALUE(MODE) \ + (TARGET_ARM && TARGET_HARD_FLOAT && GET_MODE_CLASS (MODE) == MODE_FLOAT \ + ? gen_rtx_REG (MODE, FIRST_ARM_FP_REGNUM) \ + : gen_rtx_REG (MODE, ARG_REGISTER (1))) + +/* Define how to find the value returned by a function. + VALTYPE is the data type of the value (as a tree). + If the precise function being called is known, FUNC is its FUNCTION_DECL; + otherwise, FUNC is 0. */ +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + LIBCALL_VALUE (TYPE_MODE (VALTYPE)) + +/* 1 if N is a possible register number for a function value. + On the ARM, only r0 and f0 can return results. */ +#define FUNCTION_VALUE_REGNO_P(REGNO) \ + ((REGNO) == ARG_REGISTER (1) \ + || (TARGET_ARM && ((REGNO) == FIRST_ARM_FP_REGNUM) && TARGET_HARD_FLOAT)) + +/* How large values are returned */ +/* A C expression which can inhibit the returning of certain function values + in registers, based on the type of value. */ +#define RETURN_IN_MEMORY(TYPE) arm_return_in_memory (TYPE) + +/* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return + values must be in memory. On the ARM, they need only do so if larger + than a word, or if they contain elements offset from zero in the struct. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* Flags for the call/call_value rtl operations set up by function_arg. */ +#define CALL_NORMAL 0x00000000 /* No special processing. */ +#define CALL_LONG 0x00000001 /* Always call indirect. */ +#define CALL_SHORT 0x00000002 /* Never call indirect. */ + +/* These bits describe the different types of function supported + by the ARM backend. They are exclusive. ie a function cannot be both a + normal function and an interworked function, for example. Knowing the + type of a function is important for determining its prologue and + epilogue sequences. + Note value 7 is currently unassigned. Also note that the interrupt + function types all have bit 2 set, so that they can be tested for easily. + Note that 0 is deliberately chosen for ARM_FT_UNKNOWN so that when the + machine_function structure is initialised (to zero) func_type will + default to unknown. This will force the first use of arm_current_func_type + to call arm_compute_func_type. */ +#define ARM_FT_UNKNOWN 0 /* Type has not yet been determined. */ +#define ARM_FT_NORMAL 1 /* Your normal, straightforward function. */ +#define ARM_FT_INTERWORKED 2 /* A function that supports interworking. */ +#define ARM_FT_EXCEPTION_HANDLER 3 /* A C++ exception handler. */ +#define ARM_FT_ISR 4 /* An interrupt service routine. */ +#define ARM_FT_FIQ 5 /* A fast interrupt service routine. */ +#define ARM_FT_EXCEPTION 6 /* An ARM exception handler (subcase of ISR). */ + +#define ARM_FT_TYPE_MASK ((1 << 3) - 1) + +/* In addition functions can have several type modifiers, + outlined by these bit masks: */ +#define ARM_FT_INTERRUPT (1 << 2) /* Note overlap with FT_ISR and above. */ +#define ARM_FT_NAKED (1 << 3) /* No prologue or epilogue. */ +#define ARM_FT_VOLATILE (1 << 4) /* Does not return. */ +#define ARM_FT_NESTED (1 << 5) /* Embedded inside another func. */ + +/* Some macros to test these flags. */ +#define ARM_FUNC_TYPE(t) (t & ARM_FT_TYPE_MASK) +#define IS_INTERRUPT(t) (t & ARM_FT_INTERRUPT) +#define IS_VOLATILE(t) (t & ARM_FT_VOLATILE) +#define IS_NAKED(t) (t & ARM_FT_NAKED) +#define IS_NESTED(t) (t & ARM_FT_NESTED) + +/* A C structure for machine-specific, per-function data. + This is added to the cfun structure. */ +typedef struct machine_function +{ + /* Additionsl stack adjustment in __builtin_eh_throw. */ + struct rtx_def *eh_epilogue_sp_ofs; + /* Records if LR has to be saved for far jumps. */ + int far_jump_used; + /* Records if ARG_POINTER was ever live. */ + int arg_pointer_live; + /* Records if the save of LR has been eliminated. */ + int lr_save_eliminated; + /* Records the type of the current function. */ + unsigned long func_type; +} +machine_function; + +/* A C type for declaring a variable that is used as the first argument of + `FUNCTION_ARG' and other related values. For some target machines, the + type `int' suffices and can hold the number of bytes of argument so far. */ +typedef struct +{ + /* This is the number of registers of arguments scanned so far. */ + int nregs; + /* One of CALL_NORMAL, CALL_LONG or CALL_SHORT . */ + int call_cookie; +} CUMULATIVE_ARGS; + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + + On the ARM, normally the first 16 bytes are passed in registers r0-r3; all + other arguments are passed on the stack. If (NAMED == 0) (which happens + only in assign_parms, since SETUP_INCOMING_VARARGS is defined), say it is + passed in the stack (function_prologue will indeed make it pass in the + stack if necessary). */ +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + arm_function_arg (&(CUM), (MODE), (TYPE), (NAMED)) + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. */ +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ + ( NUM_ARG_REGS > (CUM).nregs \ + && (NUM_ARG_REGS < ((CUM).nregs + NUM_REGS2 (MODE, TYPE))) \ + ? NUM_ARG_REGS - (CUM).nregs : 0) + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + On the ARM, the offset starts at 0. */ +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \ + arm_init_cumulative_args (&(CUM), (FNTYPE), (LIBNAME), (INDIRECT)) + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + (CUM).nregs += NUM_REGS2 (MODE, TYPE) + +/* 1 if N is a possible register number for function argument passing. + On the ARM, r0-r3 are used to pass args. */ +#define FUNCTION_ARG_REGNO_P(REGNO) (IN_RANGE ((REGNO), 0, 3)) + + +/* Tail calling. */ + +/* A C expression that evaluates to true if it is ok to perform a sibling + call to DECL. */ +#define FUNCTION_OK_FOR_SIBCALL(DECL) arm_function_ok_for_sibcall ((DECL)) + +/* Perform any actions needed for a function that is receiving a variable + number of arguments. CUM is as above. MODE and TYPE are the mode and type + of the current parameter. PRETEND_SIZE is a variable that should be set to + the amount of stack that must be pushed by the prolog to pretend that our + caller pushed it. + + Normally, this macro will push all remaining incoming registers on the + stack and set PRETEND_SIZE to the length of the registers pushed. + + On the ARM, PRETEND_SIZE is set in order to have the prologue push the last + named arg and all anonymous args onto the stack. + XXX I know the prologue shouldn't be pushing registers, but it is faster + that way. */ +#define SETUP_INCOMING_VARARGS(CUM, MODE, TYPE, PRETEND_SIZE, NO_RTL) \ +{ \ + extern int current_function_anonymous_args; \ + current_function_anonymous_args = 1; \ + if ((CUM).nregs < NUM_ARG_REGS) \ + (PRETEND_SIZE) = (NUM_ARG_REGS - (CUM).nregs) * UNITS_PER_WORD; \ +} + +/* If your target environment doesn't prefix user functions with an + underscore, you may wish to re-define this to prevent any conflicts. + e.g. AOF may prefix mcount with an underscore. */ +#ifndef ARM_MCOUNT_NAME +#define ARM_MCOUNT_NAME "*mcount" +#endif + +/* Call the function profiler with a given profile label. The Acorn + compiler puts this BEFORE the prolog but gcc puts it afterwards. + On the ARM the full profile code will look like: + .data + LP1 + .word 0 + .text + mov ip, lr + bl mcount + .word LP1 + + profile_function() in final.c outputs the .data section, FUNCTION_PROFILER + will output the .text section. + + The ``mov ip,lr'' seems like a good idea to stick with cc convention. + ``prof'' doesn't seem to mind about this! */ +#ifndef ARM_FUNCTION_PROFILER +#define ARM_FUNCTION_PROFILER(STREAM, LABELNO) \ +{ \ + char temp[20]; \ + rtx sym; \ + \ + asm_fprintf (STREAM, "\tmov\t%r, %r\n\tbl\t", \ + IP_REGNUM, LR_REGNUM); \ + assemble_name (STREAM, ARM_MCOUNT_NAME); \ + fputc ('\n', STREAM); \ + ASM_GENERATE_INTERNAL_LABEL (temp, "LP", LABELNO); \ + sym = gen_rtx (SYMBOL_REF, Pmode, temp); \ + assemble_aligned_integer (UNITS_PER_WORD, sym); \ +} +#endif + +#ifndef THUMB_FUNCTION_PROFILER +#define THUMB_FUNCTION_PROFILER(STREAM, LABELNO) \ +{ \ + fprintf (STREAM, "\tmov\tip, lr\n"); \ + fprintf (STREAM, "\tbl\tmcount\n"); \ + fprintf (STREAM, "\t.word\tLP%d\n", LABELNO); \ +} +#endif + +#define FUNCTION_PROFILER(STREAM, LABELNO) \ + if (TARGET_ARM) \ + ARM_FUNCTION_PROFILER (STREAM, LABELNO) \ + else \ + THUMB_FUNCTION_PROFILER (STREAM, LABELNO) + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. + + On the ARM, the function epilogue recovers the stack pointer from the + frame. */ +#define EXIT_IGNORE_STACK 1 + +#define EPILOGUE_USES(REGNO) (reload_completed && (REGNO) == LR_REGNUM) + +/* Determine if the epilogue should be output as RTL. + You should override this if you define FUNCTION_EXTRA_EPILOGUE. */ +#define USE_RETURN_INSN(ISCOND) \ + (TARGET_ARM ? use_return_insn (ISCOND) : 0) + +/* Definitions for register eliminations. + + This is an array of structures. Each structure initializes one pair + of eliminable registers. The "from" register number is given first, + followed by "to". Eliminations of the same "from" register are listed + in order of preference. + + We have two registers that can be eliminated on the ARM. First, the + arg pointer register can often be eliminated in favor of the stack + pointer register. Secondly, the pseudo frame pointer register can always + be eliminated; it is replaced with either the stack or the real frame + pointer. Note we have to use {ARM|THUMB}_HARD_FRAME_POINTER_REGNUM + because the definition of HARD_FRAME_POINTER_REGNUM is not a constant. */ + +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM },\ + { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM },\ + { ARG_POINTER_REGNUM, ARM_HARD_FRAME_POINTER_REGNUM },\ + { ARG_POINTER_REGNUM, THUMB_HARD_FRAME_POINTER_REGNUM },\ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM },\ + { FRAME_POINTER_REGNUM, ARM_HARD_FRAME_POINTER_REGNUM },\ + { FRAME_POINTER_REGNUM, THUMB_HARD_FRAME_POINTER_REGNUM }} + +/* Given FROM and TO register numbers, say whether this elimination is + allowed. Frame pointer elimination is automatically handled. + + All eliminations are permissible. Note that ARG_POINTER_REGNUM and + HARD_FRAME_POINTER_REGNUM are in fact the same thing. If we need a frame + pointer, we must eliminate FRAME_POINTER_REGNUM into + HARD_FRAME_POINTER_REGNUM and not into STACK_POINTER_REGNUM or + ARG_POINTER_REGNUM. */ +#define CAN_ELIMINATE(FROM, TO) \ + (((TO) == FRAME_POINTER_REGNUM && (FROM) == ARG_POINTER_REGNUM) ? 0 : \ + ((TO) == STACK_POINTER_REGNUM && frame_pointer_needed) ? 0 : \ + ((TO) == ARM_HARD_FRAME_POINTER_REGNUM && TARGET_THUMB) ? 0 : \ + ((TO) == THUMB_HARD_FRAME_POINTER_REGNUM && TARGET_ARM) ? 0 : \ + 1) + +/* Define the offset between two registers, one to be eliminated, and the + other its replacement, at the start of a routine. */ +#define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + do \ + { \ + (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO); \ + } \ + while (0) + +/* Note: This macro must match the code in thumb_function_prologue(). */ +#define THUMB_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ +{ \ + (OFFSET) = 0; \ + if ((FROM) == ARG_POINTER_REGNUM) \ + { \ + int count_regs = 0; \ + int regno; \ + for (regno = 8; regno < 13; regno ++) \ + if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + count_regs ++; \ + if (count_regs) \ + (OFFSET) += 4 * count_regs; \ + count_regs = 0; \ + for (regno = 0; regno <= LAST_LO_REGNUM; regno ++) \ + if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + count_regs ++; \ + if (count_regs || ! leaf_function_p () || thumb_far_jump_used_p (0))\ + (OFFSET) += 4 * (count_regs + 1); \ + if (TARGET_BACKTRACE) \ + { \ + if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) \ + (OFFSET) += 20; \ + else \ + (OFFSET) += 16; \ + } \ + } \ + if ((TO) == STACK_POINTER_REGNUM) \ + { \ + (OFFSET) += current_function_outgoing_args_size; \ + (OFFSET) += ROUND_UP (get_frame_size ()); \ + } \ +} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + if (TARGET_ARM) \ + ARM_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET); \ + else \ + THUMB_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET) + +/* Special case handling of the location of arguments passed on the stack. */ +#define DEBUGGER_ARG_OFFSET(value, addr) value ? value : arm_debugger_arg_offset (value, addr) + +/* Initialize data used by insn expanders. This is called from insn_emit, + once for every function before code is generated. */ +#define INIT_EXPANDERS arm_init_expanders () + +/* Output assembler code for a block containing the constant parts + of a trampoline, leaving space for the variable parts. + + On the ARM, (if r8 is the static chain regnum, and remembering that + referencing pc adds an offset of 8) the trampoline looks like: + ldr r8, [pc, #0] + ldr pc, [pc] + .word static chain value + .word function's address + ??? FIXME: When the trampoline returns, r8 will be clobbered. */ +#define ARM_TRAMPOLINE_TEMPLATE(FILE) \ +{ \ + asm_fprintf (FILE, "\tldr\t%r, [%r, #0]\n", \ + STATIC_CHAIN_REGNUM, PC_REGNUM); \ + asm_fprintf (FILE, "\tldr\t%r, [%r, #0]\n", \ + PC_REGNUM, PC_REGNUM); \ + assemble_aligned_integer (UNITS_PER_WORD, const0_rtx); \ + assemble_aligned_integer (UNITS_PER_WORD, const0_rtx); \ +} + +/* On the Thumb we always switch into ARM mode to execute the trampoline. + Why - because it is easier. This code will always be branched to via + a BX instruction and since the compiler magically generates the address + of the function the linker has no opportunity to ensure that the + bottom bit is set. Thus the processor will be in ARM mode when it + reaches this code. So we duplicate the ARM trampoline code and add + a switch into Thumb mode as well. */ +#define THUMB_TRAMPOLINE_TEMPLATE(FILE) \ +{ \ + fprintf (FILE, "\t.code 32\n"); \ + fprintf (FILE, ".Ltrampoline_start:\n"); \ + asm_fprintf (FILE, "\tldr\t%r, [%r, #8]\n", \ + STATIC_CHAIN_REGNUM, PC_REGNUM); \ + asm_fprintf (FILE, "\tldr\t%r, [%r, #8]\n", \ + IP_REGNUM, PC_REGNUM); \ + asm_fprintf (FILE, "\torr\t%r, %r, #1\n", \ + IP_REGNUM, IP_REGNUM); \ + asm_fprintf (FILE, "\tbx\t%r\n", IP_REGNUM); \ + fprintf (FILE, "\t.word\t0\n"); \ + fprintf (FILE, "\t.word\t0\n"); \ + fprintf (FILE, "\t.code 16\n"); \ +} + +#define TRAMPOLINE_TEMPLATE(FILE) \ + if (TARGET_ARM) \ + ARM_TRAMPOLINE_TEMPLATE (FILE) \ + else \ + THUMB_TRAMPOLINE_TEMPLATE (FILE) + +/* Length in units of the trampoline for entering a nested function. */ +#define TRAMPOLINE_SIZE (TARGET_ARM ? 16 : 24) + +/* Alignment required for a trampoline in bits. */ +#define TRAMPOLINE_ALIGNMENT 32 + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. */ +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + emit_move_insn \ + (gen_rtx_MEM (SImode, plus_constant (TRAMP, TARGET_ARM ? 8 : 16)), CXT); \ + emit_move_insn \ + (gen_rtx_MEM (SImode, plus_constant (TRAMP, TARGET_ARM ? 12 : 20)), FNADDR); \ +} + + +/* Addressing modes, and classification of registers for them. */ +#define HAVE_POST_INCREMENT 1 +#define HAVE_PRE_INCREMENT TARGET_ARM +#define HAVE_POST_DECREMENT TARGET_ARM +#define HAVE_PRE_DECREMENT TARGET_ARM + +/* Macros to check register numbers against specific register classes. */ + +/* These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ +#define TEST_REGNO(R, TEST, VALUE) \ + ((R TEST VALUE) || ((unsigned) reg_renumber[R] TEST VALUE)) + +/* On the ARM, don't allow the pc to be used. */ +#define ARM_REGNO_OK_FOR_BASE_P(REGNO) \ + (TEST_REGNO (REGNO, <, PC_REGNUM) \ + || TEST_REGNO (REGNO, ==, FRAME_POINTER_REGNUM) \ + || TEST_REGNO (REGNO, ==, ARG_POINTER_REGNUM)) + +#define THUMB_REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \ + (TEST_REGNO (REGNO, <=, LAST_LO_REGNUM) \ + || (GET_MODE_SIZE (MODE) >= 4 \ + && TEST_REGNO (REGNO, ==, STACK_POINTER_REGNUM))) + +#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \ + (TARGET_THUMB \ + ? THUMB_REGNO_MODE_OK_FOR_BASE_P (REGNO, MODE) \ + : ARM_REGNO_OK_FOR_BASE_P (REGNO)) + +/* For ARM code, we don't care about the mode, but for Thumb, the index + must be suitable for use in a QImode load. */ +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + REGNO_MODE_OK_FOR_BASE_P (REGNO, QImode) + +/* Maximum number of registers that can appear in a valid memory address. + Shifts in addresses can't be by a register. */ +#define MAX_REGS_PER_ADDRESS 2 + +/* Recognize any constant value that is a valid address. */ +/* XXX We can address any constant, eventually... */ + +#ifdef AOF_ASSEMBLER + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X)) + +#else + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == SYMBOL_REF \ + && (CONSTANT_POOL_ADDRESS_P (X) \ + || (TARGET_ARM && optimize > 0 && SYMBOL_REF_FLAG (X)))) + +#endif /* AOF_ASSEMBLER */ + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. + + On the ARM, allow any integer (invalid ones are removed later by insn + patterns), nice doubles and symbol_refs which refer to the function's + constant pool XXX. + + When generating pic allow anything. */ +#define ARM_LEGITIMATE_CONSTANT_P(X) (flag_pic || ! label_mentioned_p (X)) + +#define THUMB_LEGITIMATE_CONSTANT_P(X) \ + ( GET_CODE (X) == CONST_INT \ + || GET_CODE (X) == CONST_DOUBLE \ + || CONSTANT_ADDRESS_P (X)) + +#define LEGITIMATE_CONSTANT_P(X) \ + (TARGET_ARM ? ARM_LEGITIMATE_CONSTANT_P (X) : THUMB_LEGITIMATE_CONSTANT_P (X)) + +/* Special characters prefixed to function names + in order to encode attribute like information. + Note, '@' and '*' have already been taken. */ +#define SHORT_CALL_FLAG_CHAR '^' +#define LONG_CALL_FLAG_CHAR '#' + +#define ENCODED_SHORT_CALL_ATTR_P(SYMBOL_NAME) \ + (*(SYMBOL_NAME) == SHORT_CALL_FLAG_CHAR) + +#define ENCODED_LONG_CALL_ATTR_P(SYMBOL_NAME) \ + (*(SYMBOL_NAME) == LONG_CALL_FLAG_CHAR) + +#ifndef SUBTARGET_NAME_ENCODING_LENGTHS +#define SUBTARGET_NAME_ENCODING_LENGTHS +#endif + +/* This is a C fragement for the inside of a switch statement. + Each case label should return the number of characters to + be stripped from the start of a function's name, if that + name starts with the indicated character. */ +#define ARM_NAME_ENCODING_LENGTHS \ + case SHORT_CALL_FLAG_CHAR: return 1; \ + case LONG_CALL_FLAG_CHAR: return 1; \ + case '*': return 1; \ + SUBTARGET_NAME_ENCODING_LENGTHS + +/* This has to be handled by a function because more than part of the + ARM backend uses function name prefixes to encode attributes. */ +#undef STRIP_NAME_ENCODING +#define STRIP_NAME_ENCODING(VAR, SYMBOL_NAME) \ + (VAR) = arm_strip_name_encoding (SYMBOL_NAME) + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. */ +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(FILE, NAME) \ + asm_fprintf (FILE, "%U%s", arm_strip_name_encoding (NAME)) + +/* If we are referencing a function that is weak then encode a long call + flag in the function name, otherwise if the function is static or + or known to be defined in this file then encode a short call flag. + This macro is used inside the ENCODE_SECTION macro. */ +#define ARM_ENCODE_CALL_TYPE(decl) \ + if (TREE_CODE (decl) == FUNCTION_DECL) \ + { \ + if (DECL_WEAK (decl)) \ + arm_encode_call_attribute (decl, LONG_CALL_FLAG_CHAR); \ + else if (! TREE_PUBLIC (decl)) \ + arm_encode_call_attribute (decl, SHORT_CALL_FLAG_CHAR); \ + } + +/* Symbols in the text segment can be accessed without indirecting via the + constant pool; it may take an extra binary operation, but this is still + faster than indirecting via memory. Don't do this when not optimizing, + since we won't be calculating al of the offsets necessary to do this + simplification. */ +/* This doesn't work with AOF syntax, since the string table may be in + a different AREA. */ +#ifndef AOF_ASSEMBLER +#define ENCODE_SECTION_INFO(decl) \ +{ \ + if (optimize > 0 && TREE_CONSTANT (decl) \ + && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST)) \ + { \ + rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd' \ + ? TREE_CST_RTL (decl) : DECL_RTL (decl)); \ + SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1; \ + } \ + ARM_ENCODE_CALL_TYPE (decl) \ +} +#else +#define ENCODE_SECTION_INFO(decl) \ +{ \ + ARM_ENCODE_CALL_TYPE (decl) \ +} +#endif + +#define ARM_DECLARE_FUNCTION_SIZE(STREAM, NAME, DECL) \ + arm_encode_call_attribute (DECL, SHORT_CALL_FLAG_CHAR) + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. */ +#ifndef REG_OK_STRICT + +#define ARM_REG_OK_FOR_BASE_P(X) \ + (REGNO (X) <= LAST_ARM_REGNUM \ + || REGNO (X) >= FIRST_PSEUDO_REGISTER \ + || REGNO (X) == FRAME_POINTER_REGNUM \ + || REGNO (X) == ARG_POINTER_REGNUM) + +#define THUMB_REG_MODE_OK_FOR_BASE_P(X, MODE) \ + (REGNO (X) <= LAST_LO_REGNUM \ + || REGNO (X) >= FIRST_PSEUDO_REGISTER \ + || (GET_MODE_SIZE (MODE) >= 4 \ + && (REGNO (X) == STACK_POINTER_REGNUM \ + || (X) == hard_frame_pointer_rtx \ + || (X) == arg_pointer_rtx))) + +#else /* REG_OK_STRICT */ + +#define ARM_REG_OK_FOR_BASE_P(X) \ + ARM_REGNO_OK_FOR_BASE_P (REGNO (X)) + +#define THUMB_REG_MODE_OK_FOR_BASE_P(X, MODE) \ + THUMB_REGNO_MODE_OK_FOR_BASE_P (REGNO (X), MODE) + +#endif /* REG_OK_STRICT */ + +/* Now define some helpers in terms of the above. */ + +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + (TARGET_THUMB \ + ? THUMB_REG_MODE_OK_FOR_BASE_P (X, MODE) \ + : ARM_REG_OK_FOR_BASE_P (X)) + +#define ARM_REG_OK_FOR_INDEX_P(X) ARM_REG_OK_FOR_BASE_P (X) + +/* For Thumb, a valid index register is anything that can be used in + a byte load instruction. */ +#define THUMB_REG_OK_FOR_INDEX_P(X) THUMB_REG_MODE_OK_FOR_BASE_P (X, QImode) + +/* Nonzero if X is a hard reg that can be used as an index + or if it is a pseudo reg. On the Thumb, the stack pointer + is not suitable. */ +#define REG_OK_FOR_INDEX_P(X) \ + (TARGET_THUMB \ + ? THUMB_REG_OK_FOR_INDEX_P (X) \ + : ARM_REG_OK_FOR_INDEX_P (X)) + + +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. + + The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS. */ + +/* --------------------------------arm version----------------------------- */ +#define ARM_BASE_REGISTER_RTX_P(X) \ + (GET_CODE (X) == REG && ARM_REG_OK_FOR_BASE_P (X)) + +#define ARM_INDEX_REGISTER_RTX_P(X) \ + (GET_CODE (X) == REG && ARM_REG_OK_FOR_INDEX_P (X)) + +/* A C statement (sans semicolon) to jump to LABEL for legitimate index RTXs + used by the macro GO_IF_LEGITIMATE_ADDRESS. Floating point indices can + only be small constants. */ +#define ARM_GO_IF_LEGITIMATE_INDEX(MODE, BASE_REGNO, INDEX, LABEL) \ + do \ + { \ + HOST_WIDE_INT range; \ + enum rtx_code code = GET_CODE (INDEX); \ + \ + if (TARGET_HARD_FLOAT && GET_MODE_CLASS (MODE) == MODE_FLOAT) \ + { \ + if (code == CONST_INT && INTVAL (INDEX) < 1024 \ + && INTVAL (INDEX) > -1024 \ + && (INTVAL (INDEX) & 3) == 0) \ + goto LABEL; \ + } \ + else \ + { \ + if (ARM_INDEX_REGISTER_RTX_P (INDEX) \ + && GET_MODE_SIZE (MODE) <= 4) \ + goto LABEL; \ + if (GET_MODE_SIZE (MODE) <= 4 && code == MULT \ + && (! arm_arch4 || (MODE) != HImode)) \ + { \ + rtx xiop0 = XEXP (INDEX, 0); \ + rtx xiop1 = XEXP (INDEX, 1); \ + if (ARM_INDEX_REGISTER_RTX_P (xiop0) \ + && power_of_two_operand (xiop1, SImode)) \ + goto LABEL; \ + if (ARM_INDEX_REGISTER_RTX_P (xiop1) \ + && power_of_two_operand (xiop0, SImode)) \ + goto LABEL; \ + } \ + if (GET_MODE_SIZE (MODE) <= 4 \ + && (code == LSHIFTRT || code == ASHIFTRT \ + || code == ASHIFT || code == ROTATERT) \ + && (! arm_arch4 || (MODE) != HImode)) \ + { \ + rtx op = XEXP (INDEX, 1); \ + if (ARM_INDEX_REGISTER_RTX_P (XEXP (INDEX, 0)) \ + && GET_CODE (op) == CONST_INT && INTVAL (op) > 0 \ + && INTVAL (op) <= 31) \ + goto LABEL; \ + } \ + /* NASTY: Since this limits the addressing of unsigned \ + byte loads. */ \ + range = ((MODE) == HImode || (MODE) == QImode) \ + ? (arm_arch4 ? 256 : 4095) : 4096; \ + if (code == CONST_INT && INTVAL (INDEX) < range \ + && INTVAL (INDEX) > -range) \ + goto LABEL; \ + } \ + } \ + while (0) + +/* Jump to LABEL if X is a valid address RTX. This must take + REG_OK_STRICT into account when deciding about valid registers. + + Allow REG, REG+REG, REG+INDEX, INDEX+REG, REG-INDEX, and non + floating SYMBOL_REF to the constant pool. Allow REG-only and + AUTINC-REG if handling TImode or HImode. Other symbol refs must be + forced though a static cell to ensure addressability. */ +#define ARM_GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ +{ \ + if (ARM_BASE_REGISTER_RTX_P (X)) \ + goto LABEL; \ + else if ((GET_CODE (X) == POST_INC || GET_CODE (X) == PRE_DEC) \ + && GET_CODE (XEXP (X, 0)) == REG \ + && ARM_REG_OK_FOR_BASE_P (XEXP (X, 0))) \ + goto LABEL; \ + else if (GET_MODE_SIZE (MODE) >= 4 && reload_completed \ + && (GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST \ + && GET_CODE (XEXP ((X), 0)) == PLUS \ + && GET_CODE (XEXP (XEXP ((X), 0), 0)) == LABEL_REF \ + && GET_CODE (XEXP (XEXP ((X), 0), 1)) == CONST_INT)))\ + goto LABEL; \ + else if ((MODE) == TImode) \ + ; \ + else if ((MODE) == DImode || (TARGET_SOFT_FLOAT && (MODE) == DFmode)) \ + { \ + if (GET_CODE (X) == PLUS && ARM_BASE_REGISTER_RTX_P (XEXP (X, 0)) \ + && GET_CODE (XEXP (X, 1)) == CONST_INT) \ + { \ + HOST_WIDE_INT val = INTVAL (XEXP (X, 1)); \ + if (val == 4 || val == -4 || val == -8) \ + goto LABEL; \ + } \ + } \ + else if (GET_CODE (X) == PLUS) \ + { \ + rtx xop0 = XEXP (X, 0); \ + rtx xop1 = XEXP (X, 1); \ + \ + if (ARM_BASE_REGISTER_RTX_P (xop0)) \ + ARM_GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop0), xop1, LABEL); \ + else if (ARM_BASE_REGISTER_RTX_P (xop1)) \ + ARM_GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop1), xop0, LABEL); \ + } \ + /* Reload currently can't handle MINUS, so disable this for now */ \ + /* else if (GET_CODE (X) == MINUS) \ + { \ + rtx xop0 = XEXP (X,0); \ + rtx xop1 = XEXP (X,1); \ + \ + if (ARM_BASE_REGISTER_RTX_P (xop0)) \ + ARM_GO_IF_LEGITIMATE_INDEX (MODE, -1, xop1, LABEL); \ + } */ \ + else if (GET_MODE_CLASS (MODE) != MODE_FLOAT \ + && GET_CODE (X) == SYMBOL_REF \ + && CONSTANT_POOL_ADDRESS_P (X) \ + && ! (flag_pic \ + && symbol_mentioned_p (get_pool_constant (X)))) \ + goto LABEL; \ + else if ((GET_CODE (X) == PRE_INC || GET_CODE (X) == POST_DEC) \ + && (GET_MODE_SIZE (MODE) <= 4) \ + && GET_CODE (XEXP (X, 0)) == REG \ + && ARM_REG_OK_FOR_BASE_P (XEXP (X, 0))) \ + goto LABEL; \ +} + +/* ---------------------thumb version----------------------------------*/ +#define THUMB_LEGITIMATE_OFFSET(MODE, VAL) \ + (GET_MODE_SIZE (MODE) == 1 ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \ + : GET_MODE_SIZE (MODE) == 2 ? ((unsigned HOST_WIDE_INT) (VAL) < 64 \ + && ((VAL) & 1) == 0) \ + : ((VAL) >= 0 && ((VAL) + GET_MODE_SIZE (MODE)) <= 128 \ + && ((VAL) & 3) == 0)) + +/* The AP may be eliminated to either the SP or the FP, so we use the + least common denominator, e.g. SImode, and offsets from 0 to 64. */ + +/* ??? Verify whether the above is the right approach. */ + +/* ??? Also, the FP may be eliminated to the SP, so perhaps that + needs special handling also. */ + +/* ??? Look at how the mips16 port solves this problem. It probably uses + better ways to solve some of these problems. */ + +/* Although it is not incorrect, we don't accept QImode and HImode + addresses based on the frame pointer or arg pointer until the + reload pass starts. This is so that eliminating such addresses + into stack based ones won't produce impossible code. */ +#define THUMB_GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \ +{ \ +/* ??? Not clear if this is right. Experiment. */ \ + if (GET_MODE_SIZE (MODE) < 4 \ + && ! (reload_in_progress || reload_completed) \ + && ( reg_mentioned_p (frame_pointer_rtx, X) \ + || reg_mentioned_p (arg_pointer_rtx, X) \ + || reg_mentioned_p (virtual_incoming_args_rtx, X) \ + || reg_mentioned_p (virtual_outgoing_args_rtx, X) \ + || reg_mentioned_p (virtual_stack_dynamic_rtx, X) \ + || reg_mentioned_p (virtual_stack_vars_rtx, X))) \ + ; \ + /* Accept any base register. SP only in SImode or larger. */ \ + else if (GET_CODE (X) == REG \ + && THUMB_REG_MODE_OK_FOR_BASE_P (X, MODE)) \ + goto WIN; \ + /* This is PC relative data before MACHINE_DEPENDENT_REORG runs. */ \ + else if (GET_MODE_SIZE (MODE) >= 4 && CONSTANT_P (X) \ + && CONSTANT_POOL_ADDRESS_P (X) && ! flag_pic) \ + goto WIN; \ + /* This is PC relative data after MACHINE_DEPENDENT_REORG runs. */ \ + else if (GET_MODE_SIZE (MODE) >= 4 && reload_completed \ + && (GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST \ + && GET_CODE (XEXP (X, 0)) == PLUS \ + && GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF \ + && GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT))) \ + goto WIN; \ + /* Post-inc indexing only supported for SImode and larger. */ \ + else if (GET_CODE (X) == POST_INC && GET_MODE_SIZE (MODE) >= 4 \ + && GET_CODE (XEXP (X, 0)) == REG \ + && THUMB_REG_OK_FOR_INDEX_P (XEXP (X, 0))) \ + goto WIN; \ + else if (GET_CODE (X) == PLUS) \ + { \ + /* REG+REG address can be any two index registers. */ \ + /* We disallow FRAME+REG addressing since we know that FRAME \ + will be replaced with STACK, and SP relative addressing only \ + permits SP+OFFSET. */ \ + if (GET_MODE_SIZE (MODE) <= 4 \ + && GET_CODE (XEXP (X, 0)) == REG \ + && GET_CODE (XEXP (X, 1)) == REG \ + && XEXP (X, 0) != frame_pointer_rtx \ + && XEXP (X, 1) != frame_pointer_rtx \ + && XEXP (X, 0) != virtual_stack_vars_rtx \ + && XEXP (X, 1) != virtual_stack_vars_rtx \ + && THUMB_REG_OK_FOR_INDEX_P (XEXP (X, 0)) \ + && THUMB_REG_OK_FOR_INDEX_P (XEXP (X, 1))) \ + goto WIN; \ + /* REG+const has 5-7 bit offset for non-SP registers. */ \ + else if (GET_CODE (XEXP (X, 0)) == REG \ + && (THUMB_REG_OK_FOR_INDEX_P (XEXP (X, 0)) \ + || XEXP (X, 0) == arg_pointer_rtx) \ + && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && THUMB_LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \ + goto WIN; \ + /* REG+const has 10 bit offset for SP, but only SImode and \ + larger is supported. */ \ + /* ??? Should probably check for DI/DFmode overflow here \ + just like GO_IF_LEGITIMATE_OFFSET does. */ \ + else if (GET_CODE (XEXP (X, 0)) == REG \ + && REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \ + && GET_MODE_SIZE (MODE) >= 4 \ + && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && ((unsigned HOST_WIDE_INT) INTVAL (XEXP (X, 1)) \ + + GET_MODE_SIZE (MODE)) <= 1024 \ + && (INTVAL (XEXP (X, 1)) & 3) == 0) \ + goto WIN; \ + else if (GET_CODE (XEXP (X, 0)) == REG \ + && REGNO (XEXP (X, 0)) == FRAME_POINTER_REGNUM \ + && GET_MODE_SIZE (MODE) >= 4 \ + && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (INTVAL (XEXP (X, 1)) & 3) == 0) \ + goto WIN; \ + } \ + else if (GET_MODE_CLASS (MODE) != MODE_FLOAT \ + && GET_CODE (X) == SYMBOL_REF \ + && CONSTANT_POOL_ADDRESS_P (X) \ + && ! (flag_pic \ + && symbol_mentioned_p (get_pool_constant (X)))) \ + goto WIN; \ +} + +/* ------------------------------------------------------------------- */ +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \ + if (TARGET_ARM) \ + ARM_GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN) \ + else /* if (TARGET_THUMB) */ \ + THUMB_GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN) +/* ------------------------------------------------------------------- */ + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. + + On the ARM, try to convert [REG, #BIGCONST] + into ADD BASE, REG, #UPPERCONST and [BASE, #VALIDCONST], + where VALIDCONST == 0 in case of TImode. */ +#define ARM_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ +{ \ + if (GET_CODE (X) == PLUS) \ + { \ + rtx xop0 = XEXP (X, 0); \ + rtx xop1 = XEXP (X, 1); \ + \ + if (CONSTANT_P (xop0) && ! symbol_mentioned_p (xop0)) \ + xop0 = force_reg (SImode, xop0); \ + if (CONSTANT_P (xop1) && ! symbol_mentioned_p (xop1)) \ + xop1 = force_reg (SImode, xop1); \ + if (ARM_BASE_REGISTER_RTX_P (xop0) \ + && GET_CODE (xop1) == CONST_INT) \ + { \ + HOST_WIDE_INT n, low_n; \ + rtx base_reg, val; \ + n = INTVAL (xop1); \ + \ + if (MODE == DImode || (TARGET_SOFT_FLOAT && MODE == DFmode)) \ + { \ + low_n = n & 0x0f; \ + n &= ~0x0f; \ + if (low_n > 4) \ + { \ + n += 16; \ + low_n -= 16; \ + } \ + } \ + else \ + { \ + low_n = ((MODE) == TImode ? 0 \ + : n >= 0 ? (n & 0xfff) : -((-n) & 0xfff)); \ + n -= low_n; \ + } \ + base_reg = gen_reg_rtx (SImode); \ + val = force_operand (gen_rtx_PLUS (SImode, xop0, \ + GEN_INT (n)), NULL_RTX); \ + emit_move_insn (base_reg, val); \ + (X) = (low_n == 0 ? base_reg \ + : gen_rtx_PLUS (SImode, base_reg, GEN_INT (low_n))); \ + } \ + else if (xop0 != XEXP (X, 0) || xop1 != XEXP (x, 1)) \ + (X) = gen_rtx_PLUS (SImode, xop0, xop1); \ + } \ + else if (GET_CODE (X) == MINUS) \ + { \ + rtx xop0 = XEXP (X, 0); \ + rtx xop1 = XEXP (X, 1); \ + \ + if (CONSTANT_P (xop0)) \ + xop0 = force_reg (SImode, xop0); \ + if (CONSTANT_P (xop1) && ! symbol_mentioned_p (xop1)) \ + xop1 = force_reg (SImode, xop1); \ + if (xop0 != XEXP (X, 0) || xop1 != XEXP (X, 1)) \ + (X) = gen_rtx_MINUS (SImode, xop0, xop1); \ + } \ + if (flag_pic) \ + (X) = legitimize_pic_address (OLDX, MODE, NULL_RTX); \ + if (memory_address_p (MODE, X)) \ + goto WIN; \ +} + +#define THUMB_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ + if (flag_pic) \ + (X) = legitimize_pic_address (OLDX, MODE, NULL_RTX); + +#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ + if (TARGET_ARM) \ + ARM_LEGITIMIZE_ADDRESS (X, OLDX, MODE, WIN) \ + else \ + THUMB_LEGITIMIZE_ADDRESS (X, OLDX, MODE, WIN) + +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. */ +#define ARM_GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \ +{ \ + if ( GET_CODE (ADDR) == PRE_DEC || GET_CODE (ADDR) == POST_DEC \ + || GET_CODE (ADDR) == PRE_INC || GET_CODE (ADDR) == POST_INC) \ + goto LABEL; \ +} + +/* Nothing helpful to do for the Thumb */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \ + if (TARGET_ARM) \ + ARM_GO_IF_MODE_DEPENDENT_ADDRESS (ADDR, LABEL) + + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE Pmode + +/* Define as C expression which evaluates to nonzero if the tablejump + instruction expects the table to contain offsets from the address of the + table. + Do not define this if the table should contain absolute addresses. */ +/* #define CASE_VECTOR_PC_RELATIVE 1 */ + +/* signed 'char' is most compatible, but RISC OS wants it unsigned. + unsigned is probably best, but may break some code. */ +#ifndef DEFAULT_SIGNED_CHAR +#define DEFAULT_SIGNED_CHAR 0 +#endif + +/* Don't cse the address of the function being compiled. */ +#define NO_RECURSIVE_FUNCTION_CSE 1 + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 4 + +#undef MOVE_RATIO +#define MOVE_RATIO (arm_is_xscale ? 4 : 2) + +/* Define if operations between registers always perform the operation + on the full register even if a narrower mode is specified. */ +#define WORD_REGISTER_OPERATIONS + +/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD + will either zero-extend or sign-extend. The value of this macro should + be the code that says which one of the two operations is implicitly + done, NIL if none. */ +#define LOAD_EXTEND_OP(MODE) \ + (TARGET_THUMB ? ZERO_EXTEND : \ + ((arm_arch4 || (MODE) == QImode) ? ZERO_EXTEND \ + : ((BYTES_BIG_ENDIAN && (MODE) == HImode) ? SIGN_EXTEND : NIL))) + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 0 + +#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) 1 + +/* Immediate shift counts are truncated by the output routines (or was it + the assembler?). Shift counts in a register are truncated by ARM. Note + that the native compiler puts too large (> 32) immediate shift counts + into a register and shifts by the register, letting the ARM decide what + to do instead of doing that itself. */ +/* This is all wrong. Defining SHIFT_COUNT_TRUNCATED tells combine that + code like (X << (Y % 32)) for register X, Y is equivalent to (X << Y). + On the arm, Y in a register is used modulo 256 for the shift. Only for + rotates is modulo 32 used. */ +/* #define SHIFT_COUNT_TRUNCATED 1 */ + +/* All integers have the same format so truncation is easy. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Calling from registers is a massive pain. */ +#define NO_FUNCTION_CSE 1 + +/* Chars and shorts should be passed as ints. */ +#define PROMOTE_PROTOTYPES 1 + +/* The machine modes of pointers and functions */ +#define Pmode SImode +#define FUNCTION_MODE Pmode + +#define ARM_FRAME_RTX(X) \ + ( (X) == frame_pointer_rtx || (X) == stack_pointer_rtx \ + || (X) == arg_pointer_rtx) + +#define DEFAULT_RTX_COSTS(X, CODE, OUTER_CODE) \ + return arm_rtx_costs (X, CODE, OUTER_CODE); + +/* Moves to and from memory are quite expensive */ +#define MEMORY_MOVE_COST(M, CLASS, IN) \ + (TARGET_ARM ? 10 : \ + ((GET_MODE_SIZE (M) < 4 ? 8 : 2 * GET_MODE_SIZE (M)) \ + * (CLASS == LO_REGS ? 1 : 2))) + +/* All address computations that can be done are free, but rtx cost returns + the same for practically all of them. So we weight the different types + of address here in the order (most pref first): + PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL. */ +#define ARM_ADDRESS_COST(X) \ + (10 - ((GET_CODE (X) == MEM || GET_CODE (X) == LABEL_REF \ + || GET_CODE (X) == SYMBOL_REF) \ + ? 0 \ + : ((GET_CODE (X) == PRE_INC || GET_CODE (X) == PRE_DEC \ + || GET_CODE (X) == POST_INC || GET_CODE (X) == POST_DEC) \ + ? 10 \ + : (((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS) \ + ? 6 + (GET_CODE (XEXP (X, 1)) == CONST_INT ? 2 \ + : ((GET_RTX_CLASS (GET_CODE (XEXP (X, 0))) == '2' \ + || GET_RTX_CLASS (GET_CODE (XEXP (X, 0))) == 'c' \ + || GET_RTX_CLASS (GET_CODE (XEXP (X, 1))) == '2' \ + || GET_RTX_CLASS (GET_CODE (XEXP (X, 1))) == 'c') \ + ? 1 : 0)) \ + : 4))))) + +#define THUMB_ADDRESS_COST(X) \ + ((GET_CODE (X) == REG \ + || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \ + && GET_CODE (XEXP (X, 1)) == CONST_INT)) \ + ? 1 : 2) + +#define ADDRESS_COST(X) \ + (TARGET_ARM ? ARM_ADDRESS_COST (X) : THUMB_ADDRESS_COST (X)) + +/* Try to generate sequences that don't involve branches, we can then use + conditional instructions */ +#define BRANCH_COST \ + (TARGET_ARM ? 4 : (optimize > 1 ? 1 : 0)) + +/* Position Independent Code. */ +/* We decide which register to use based on the compilation options and + the assembler in use; this is more general than the APCS restriction of + using sb (r9) all the time. */ +extern int arm_pic_register; + +/* Used when parsing command line option -mpic-register=. */ +extern const char * arm_pic_register_string; + +/* The register number of the register used to address a table of static + data addresses in memory. */ +#define PIC_OFFSET_TABLE_REGNUM arm_pic_register + +#define FINALIZE_PIC arm_finalize_pic (1) + +/* We can't directly access anything that contains a symbol, + nor can we indirect via the constant pool. */ +#define LEGITIMATE_PIC_OPERAND_P(X) \ + ( ! symbol_mentioned_p (X) \ + && ! label_mentioned_p (X) \ + && (! CONSTANT_POOL_ADDRESS_P (X) \ + || ( ! symbol_mentioned_p (get_pool_constant (X)) \ + && ! label_mentioned_p (get_pool_constant (X))))) + +/* We need to know when we are making a constant pool; this determines + whether data needs to be in the GOT or can be referenced via a GOT + offset. */ +extern int making_const_table; + +/* Handle pragmas for compatibility with Intel's compilers. */ +#define REGISTER_TARGET_PRAGMAS(PFILE) do { \ + cpp_register_pragma (PFILE, 0, "long_calls", arm_pr_long_calls); \ + cpp_register_pragma (PFILE, 0, "no_long_calls", arm_pr_no_long_calls); \ + cpp_register_pragma (PFILE, 0, "long_calls_off", arm_pr_long_calls_off); \ +} while (0) + +/* Condition code information. */ +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. + CCFPEmode should be used with floating inequalities, + CCFPmode should be used with floating equalities. + CC_NOOVmode should be used with SImode integer equalities. + CC_Zmode should be used if only the Z flag is set correctly + CCmode should be used otherwise. */ + +#define EXTRA_CC_MODES \ + CC(CC_NOOVmode, "CC_NOOV") \ + CC(CC_Zmode, "CC_Z") \ + CC(CC_SWPmode, "CC_SWP") \ + CC(CCFPmode, "CCFP") \ + CC(CCFPEmode, "CCFPE") \ + CC(CC_DNEmode, "CC_DNE") \ + CC(CC_DEQmode, "CC_DEQ") \ + CC(CC_DLEmode, "CC_DLE") \ + CC(CC_DLTmode, "CC_DLT") \ + CC(CC_DGEmode, "CC_DGE") \ + CC(CC_DGTmode, "CC_DGT") \ + CC(CC_DLEUmode, "CC_DLEU") \ + CC(CC_DLTUmode, "CC_DLTU") \ + CC(CC_DGEUmode, "CC_DGEU") \ + CC(CC_DGTUmode, "CC_DGTU") \ + CC(CC_Cmode, "CC_C") + +#define SELECT_CC_MODE(OP, X, Y) arm_select_cc_mode (OP, X, Y) + +#define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) + +#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ + do \ + { \ + if (GET_CODE (OP1) == CONST_INT \ + && ! (const_ok_for_arm (INTVAL (OP1)) \ + || (const_ok_for_arm (- INTVAL (OP1))))) \ + { \ + rtx const_op = OP1; \ + CODE = arm_canonicalize_comparison ((CODE), &const_op); \ + OP1 = const_op; \ + } \ + } \ + while (0) + +#define STORE_FLAG_VALUE 1 + + + +/* Gcc puts the pool in the wrong place for ARM, since we can only + load addresses a limited distance around the pc. We do some + special munging to move the constant pool values to the correct + point in the code. */ +#define MACHINE_DEPENDENT_REORG(INSN) \ + arm_reorg (INSN); \ + +#undef ASM_APP_OFF +#define ASM_APP_OFF (TARGET_THUMB ? "\t.code\t16\n" : "") + +/* Output an internal label definition. */ +#ifndef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(STREAM, PREFIX, NUM) \ + do \ + { \ + char * s = (char *) alloca (40 + strlen (PREFIX)); \ + \ + if (arm_ccfsm_state == 3 && arm_target_label == (NUM) \ + && !strcmp (PREFIX, "L")) \ + { \ + arm_ccfsm_state = 0; \ + arm_target_insn = NULL; \ + } \ + ASM_GENERATE_INTERNAL_LABEL (s, (PREFIX), (NUM)); \ + ASM_OUTPUT_LABEL (STREAM, s); \ + } \ + while (0) +#endif + +/* Output a push or a pop instruction (only used when profiling). */ +#define ASM_OUTPUT_REG_PUSH(STREAM, REGNO) \ + if (TARGET_ARM) \ + asm_fprintf (STREAM,"\tstmfd\t%r!,{%r}\n", \ + STACK_POINTER_REGNUM, REGNO); \ + else \ + asm_fprintf (STREAM, "\tpush {%r}\n", REGNO) + + +#define ASM_OUTPUT_REG_POP(STREAM, REGNO) \ + if (TARGET_ARM) \ + asm_fprintf (STREAM, "\tldmfd\t%r!,{%r}\n", \ + STACK_POINTER_REGNUM, REGNO); \ + else \ + asm_fprintf (STREAM, "\tpop {%r}\n", REGNO) + +/* This is how to output a label which precedes a jumptable. Since + Thumb instructions are 2 bytes, we may need explicit alignment here. */ +#undef ASM_OUTPUT_CASE_LABEL +#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE) \ + do \ + { \ + if (TARGET_THUMB) \ + ASM_OUTPUT_ALIGN (FILE, 2); \ + ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \ + } \ + while (0) + +#define ARM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do \ + { \ + if (TARGET_THUMB) \ + { \ + if (is_called_in_ARM_mode (DECL)) \ + fprintf (STREAM, "\t.code 32\n") ; \ + else \ + fprintf (STREAM, "\t.thumb_func\n") ; \ + } \ + if (TARGET_POKE_FUNCTION_NAME) \ + arm_poke_function_name (STREAM, (char *) NAME); \ + } \ + while (0) + +/* For aliases of functions we use .thumb_set instead. */ +#define ASM_OUTPUT_DEF_FROM_DECLS(FILE, DECL1, DECL2) \ + do \ + { \ + const char *const LABEL1 = XSTR (XEXP (DECL_RTL (decl), 0), 0); \ + const char *const LABEL2 = IDENTIFIER_POINTER (DECL2); \ + \ + if (TARGET_THUMB && TREE_CODE (DECL1) == FUNCTION_DECL) \ + { \ + fprintf (FILE, "\t.thumb_set "); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, ","); \ + assemble_name (FILE, LABEL2); \ + fprintf (FILE, "\n"); \ + } \ + else \ + ASM_OUTPUT_DEF (FILE, LABEL1, LABEL2); \ + } \ + while (0) + +#ifdef HAVE_GAS_MAX_SKIP_P2ALIGN +/* To support -falign-* switches we need to use .p2align so + that alignment directives in code sections will be padded + with no-op instructions, rather than zeroes. */ +#define ASM_OUTPUT_MAX_SKIP_ALIGN(FILE,LOG,MAX_SKIP) \ + if ((LOG) != 0) \ + { \ + if ((MAX_SKIP) == 0) \ + fprintf ((FILE), "\t.p2align %d\n", (LOG)); \ + else \ + fprintf ((FILE), "\t.p2align %d,,%d\n", \ + (LOG), (MAX_SKIP)); \ + } +#endif + +/* Only perform branch elimination (by making instructions conditional) if + we're optimising. Otherwise it's of no use anyway. */ +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ + if (TARGET_ARM && optimize) \ + arm_final_prescan_insn (INSN); \ + else if (TARGET_THUMB) \ + thumb_final_prescan_insn (INSN) + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \ + (CODE == '@' || CODE == '|' \ + || (TARGET_ARM && (CODE == '?')) \ + || (TARGET_THUMB && (CODE == '_'))) + +/* Output an operand of an instruction. */ +#define PRINT_OPERAND(STREAM, X, CODE) \ + arm_print_operand (STREAM, X, CODE) + +#define ARM_SIGN_EXTEND(x) ((HOST_WIDE_INT) \ + (HOST_BITS_PER_WIDE_INT <= 32 ? (unsigned HOST_WIDE_INT) (x) \ + : ((((unsigned HOST_WIDE_INT)(x)) & (unsigned HOST_WIDE_INT) 0xffffffff) |\ + ((((unsigned HOST_WIDE_INT)(x)) & (unsigned HOST_WIDE_INT) 0x80000000) \ + ? ((~ (unsigned HOST_WIDE_INT) 0) \ + & ~ (unsigned HOST_WIDE_INT) 0xffffffff) \ + : 0)))) + +/* Output the address of an operand. */ +#define ARM_PRINT_OPERAND_ADDRESS(STREAM, X) \ +{ \ + int is_minus = GET_CODE (X) == MINUS; \ + \ + if (GET_CODE (X) == REG) \ + asm_fprintf (STREAM, "[%r, #0]", REGNO (X)); \ + else if (GET_CODE (X) == PLUS || is_minus) \ + { \ + rtx base = XEXP (X, 0); \ + rtx index = XEXP (X, 1); \ + HOST_WIDE_INT offset = 0; \ + if (GET_CODE (base) != REG) \ + { \ + /* Ensure that BASE is a register */ \ + /* (one of them must be). */ \ + rtx temp = base; \ + base = index; \ + index = temp; \ + } \ + switch (GET_CODE (index)) \ + { \ + case CONST_INT: \ + offset = INTVAL (index); \ + if (is_minus) \ + offset = -offset; \ + asm_fprintf (STREAM, "[%r, #%d]", \ + REGNO (base), offset); \ + break; \ + \ + case REG: \ + asm_fprintf (STREAM, "[%r, %s%r]", \ + REGNO (base), is_minus ? "-" : "", \ + REGNO (index)); \ + break; \ + \ + case MULT: \ + case ASHIFTRT: \ + case LSHIFTRT: \ + case ASHIFT: \ + case ROTATERT: \ + { \ + asm_fprintf (STREAM, "[%r, %s%r", \ + REGNO (base), is_minus ? "-" : "", \ + REGNO (XEXP (index, 0))); \ + arm_print_operand (STREAM, index, 'S'); \ + fputs ("]", STREAM); \ + break; \ + } \ + \ + default: \ + abort(); \ + } \ + } \ + else if ( GET_CODE (X) == PRE_INC || GET_CODE (X) == POST_INC\ + || GET_CODE (X) == PRE_DEC || GET_CODE (X) == POST_DEC)\ + { \ + extern int output_memory_reference_mode; \ + \ + if (GET_CODE (XEXP (X, 0)) != REG) \ + abort (); \ + \ + if (GET_CODE (X) == PRE_DEC || GET_CODE (X) == PRE_INC) \ + asm_fprintf (STREAM, "[%r, #%s%d]!", \ + REGNO (XEXP (X, 0)), \ + GET_CODE (X) == PRE_DEC ? "-" : "", \ + GET_MODE_SIZE (output_memory_reference_mode));\ + else \ + asm_fprintf (STREAM, "[%r], #%s%d", \ + REGNO (XEXP (X, 0)), \ + GET_CODE (X) == POST_DEC ? "-" : "", \ + GET_MODE_SIZE (output_memory_reference_mode));\ + } \ + else output_addr_const (STREAM, X); \ +} + +#define THUMB_PRINT_OPERAND_ADDRESS(STREAM, X) \ +{ \ + if (GET_CODE (X) == REG) \ + asm_fprintf (STREAM, "[%r]", REGNO (X)); \ + else if (GET_CODE (X) == POST_INC) \ + asm_fprintf (STREAM, "%r!", REGNO (XEXP (X, 0))); \ + else if (GET_CODE (X) == PLUS) \ + { \ + if (GET_CODE (XEXP (X, 1)) == CONST_INT) \ + asm_fprintf (STREAM, "[%r, #%d]", \ + REGNO (XEXP (X, 0)), \ + (int) INTVAL (XEXP (X, 1))); \ + else \ + asm_fprintf (STREAM, "[%r, %r]", \ + REGNO (XEXP (X, 0)), \ + REGNO (XEXP (X, 1))); \ + } \ + else \ + output_addr_const (STREAM, X); \ +} + +#define PRINT_OPERAND_ADDRESS(STREAM, X) \ + if (TARGET_ARM) \ + ARM_PRINT_OPERAND_ADDRESS (STREAM, X) \ + else \ + THUMB_PRINT_OPERAND_ADDRESS (STREAM, X) + +/* Output code to add DELTA to the first argument, and then jump to FUNCTION. + Used for C++ multiple inheritance. */ +#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \ + do \ + { \ + int mi_delta = (DELTA); \ + const char *const mi_op = mi_delta < 0 ? "sub" : "add"; \ + int shift = 0; \ + int this_regno = (aggregate_value_p (TREE_TYPE (TREE_TYPE (FUNCTION))) \ + ? 1 : 0); \ + if (mi_delta < 0) \ + mi_delta = - mi_delta; \ + while (mi_delta != 0) \ + { \ + if ((mi_delta & (3 << shift)) == 0) \ + shift += 2; \ + else \ + { \ + asm_fprintf (FILE, "\t%s\t%r, %r, #%d\n", \ + mi_op, this_regno, this_regno, \ + mi_delta & (0xff << shift)); \ + mi_delta &= ~(0xff << shift); \ + shift += 8; \ + } \ + } \ + fputs ("\tb\t", FILE); \ + assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ + if (NEED_PLT_RELOC) \ + fputs ("(PLT)", FILE); \ + fputc ('\n', FILE); \ + } \ + while (0) + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame. */ + +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + arm_return_addr (COUNT, FRAME) + +/* Mask of the bits in the PC that contain the real return address + when running in 26-bit mode. */ +#define RETURN_ADDR_MASK26 (0x03fffffc) + +/* Pick up the return address upon entry to a procedure. Used for + dwarf2 unwind information. This also enables the table driven + mechanism. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, LR_REGNUM) +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (LR_REGNUM) + +/* Used to mask out junk bits from the return address, such as + processor state, interrupt status, condition codes and the like. */ +#define MASK_RETURN_ADDR \ + /* If we are generating code for an ARM2/ARM3 machine or for an ARM6 \ + in 26 bit mode, the condition codes must be masked out of the \ + return address. This does not apply to ARM6 and later processors \ + when running in 32 bit mode. */ \ + ((!TARGET_APCS_32) ? (GEN_INT (RETURN_ADDR_MASK26)) \ + : (GEN_INT ((unsigned long)0xffffffff))) + + +/* Define the codes that are matched by predicates in arm.c */ +#define PREDICATE_CODES \ + {"s_register_operand", {SUBREG, REG}}, \ + {"arm_hard_register_operand", {REG}}, \ + {"f_register_operand", {SUBREG, REG}}, \ + {"arm_add_operand", {SUBREG, REG, CONST_INT}}, \ + {"fpu_add_operand", {SUBREG, REG, CONST_DOUBLE}}, \ + {"fpu_rhs_operand", {SUBREG, REG, CONST_DOUBLE}}, \ + {"arm_rhs_operand", {SUBREG, REG, CONST_INT}}, \ + {"arm_not_operand", {SUBREG, REG, CONST_INT}}, \ + {"reg_or_int_operand", {SUBREG, REG, CONST_INT}}, \ + {"index_operand", {SUBREG, REG, CONST_INT}}, \ + {"thumb_cmp_operand", {SUBREG, REG, CONST_INT}}, \ + {"offsettable_memory_operand", {MEM}}, \ + {"bad_signed_byte_operand", {MEM}}, \ + {"alignable_memory_operand", {MEM}}, \ + {"shiftable_operator", {PLUS, MINUS, AND, IOR, XOR}}, \ + {"minmax_operator", {SMIN, SMAX, UMIN, UMAX}}, \ + {"shift_operator", {ASHIFT, ASHIFTRT, LSHIFTRT, ROTATERT, MULT}}, \ + {"di_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE, MEM}}, \ + {"nonimmediate_di_operand", {SUBREG, REG, MEM}}, \ + {"soft_df_operand", {SUBREG, REG, CONST_DOUBLE, MEM}}, \ + {"nonimmediate_soft_df_operand", {SUBREG, REG, MEM}}, \ + {"load_multiple_operation", {PARALLEL}}, \ + {"store_multiple_operation", {PARALLEL}}, \ + {"equality_operator", {EQ, NE}}, \ + {"arm_comparison_operator", {EQ, NE, LE, LT, GE, GT, GEU, GTU, LEU, \ + LTU, UNORDERED, ORDERED, UNLT, UNLE, \ + UNGE, UNGT}}, \ + {"arm_rhsm_operand", {SUBREG, REG, CONST_INT, MEM}}, \ + {"const_shift_operand", {CONST_INT}}, \ + {"multi_register_push", {PARALLEL}}, \ + {"cc_register", {REG}}, \ + {"logical_binary_operator", {AND, IOR, XOR}}, \ + {"dominant_cc_register", {REG}}, + +/* Define this if you have special predicates that know special things + about modes. Genrecog will warn about certain forms of + match_operand without a mode; if the operand predicate is listed in + SPECIAL_MODE_PREDICATES, the warning will be suppressed. */ +#define SPECIAL_MODE_PREDICATES \ + "cc_register", "dominant_cc_register", + +enum arm_builtins +{ + ARM_BUILTIN_CLZ, + ARM_BUILTIN_MAX +}; +#endif /* ! GCC_ARM_H */ diff --git a/contrib/gcc/config/arm/arm.md b/contrib/gcc/config/arm/arm.md new file mode 100644 index 000000000000..3646fe5890f6 --- /dev/null +++ b/contrib/gcc/config/arm/arm.md @@ -0,0 +1,9199 @@ +;;- Machine description for ARM for GNU compiler +;; Copyright 1991, 1993, 1994, 1995, 1996, 1996, 1997, 1998, 1999, 2000, +;; 2001, 2002 Free Software Foundation, Inc. +;; Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) +;; and Martin Simmons (@harleqn.co.uk). +;; More major hacks by Richard Earnshaw (rearnsha@arm.com). + +;; This file is part of GNU CC. + +;; GNU CC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU CC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU CC; see the file COPYING. If not, write to +;; the Free Software Foundation, 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; There are patterns in this file to support XFmode arithmetic. +;; Unfortunately RISC iX doesn't work well with these so they are disabled. +;; (See arm.h) + +;;--------------------------------------------------------------------------- +;; Constants + +;; Register numbers +(define_constants + [(IP_REGNUM 12) ; Scratch register + (SP_REGNUM 13) ; Stack pointer + (LR_REGNUM 14) ; Return address register + (PC_REGNUM 15) ; Program counter + (CC_REGNUM 24) ; Condition code pseudo register + (LAST_ARM_REGNUM 15) + ] +) + +;; UNSPEC Usage: +;; Note: sin and cos are no-longer used. + +(define_constants + [(UNSPEC_SIN 0) ; `sin' operation (MODE_FLOAT): + ; operand 0 is the result, + ; operand 1 the parameter. + (UNPSEC_COS 1) ; `cos' operation (MODE_FLOAT): + ; operand 0 is the result, + ; operand 1 the parameter. + (UNSPEC_PUSH_MULT 2) ; `push multiple' operation: + ; operand 0 is the first register, + ; subsequent registers are in parallel (use ...) + ; expressions. + (UNSPEC_PIC_SYM 3) ; A symbol that has been treated properly for pic + ; usage, that is, we will add the pic_register + ; value to it before trying to dereference it. + (UNSPEC_PRLG_STK 4) ; A special barrier that prevents frame accesses + ; being scheduled before the stack adjustment insn. + (UNSPEC_CLZ 5) ; `clz' instruction, count leading zeros (SImode): + ; operand 0 is the result, + ; operand 1 is the parameter. + (UNSPEC_PROLOGUE_USE 6) ; As USE insns are not meaningful after reload, + ; this unspec is used to prevent the deletion of + ; instructions setting registers for EH handling + ; and stack frame generation. Operand 0 is the + ; register to "use". + ] +) + +;; UNSPEC_VOLATILE Usage: + +(define_constants + [(VUNSPEC_BLOCKAGE 0) ; `blockage' insn to prevent scheduling across an + ; insn in the code. + (VUNSPEC_EPILOGUE 1) ; `epilogue' insn, used to represent any part of the + ; instruction epilogue sequence that isn't expanded + ; into normal RTL. Used for both normal and sibcall + ; epilogues. + (VUNSPEC_ALIGN 2) ; `align' insn. Used at the head of a minipool table + ; for inlined constants. + (VUNSPEC_POOL_END 3) ; `end-of-table'. Used to mark the end of a minipool + ; table. + (VUNSPEC_POOL_1 4) ; `pool-entry(1)'. An entry in the constant pool for + ; an 8-bit object. + (VUNSPEC_POOL_2 5) ; `pool-entry(2)'. An entry in the constant pool for + ; a 16-bit object. + (VUNSPEC_POOL_4 6) ; `pool-entry(4)'. An entry in the constant pool for + ; a 32-bit object. + (VUNSPEC_POOL_8 7) ; `pool-entry(8)'. An entry in the constant pool for + ; a 64-bit object. + ] +) + +;;--------------------------------------------------------------------------- +;; Attributes + +; IS_THUMB is set to 'yes' when we are generating Thumb code, and 'no' when +; generating ARM code. This is used to control the length of some insn +; patterns that share the same RTL in both ARM and Thumb code. +(define_attr "is_thumb" "no,yes" (const (symbol_ref "thumb_code"))) + +; PROG_MODE attribute is used to determine whether condition codes are +; clobbered by a call insn: they are if in prog32 mode. This is controlled +; by the -mapcs-{32,26} flag, and possibly the -mcpu=... option. +(define_attr "prog_mode" "prog26,prog32" (const (symbol_ref "arm_prog_mode"))) + +; IS_STRONGARM is set to 'yes' when compiling for StrongARM, it affects +; scheduling decisions for the load unit and the multiplier. +(define_attr "is_strongarm" "no,yes" (const (symbol_ref "arm_is_strong"))) + +;; Operand number of an input operand that is shifted. Zero if the +;; given instruction does not shift one of its input operands. +(define_attr "is_xscale" "no,yes" (const (symbol_ref "arm_is_xscale"))) +(define_attr "shift" "" (const_int 0)) + +; Floating Point Unit. If we only have floating point emulation, then there +; is no point in scheduling the floating point insns. (Well, for best +; performance we should try and group them together). +(define_attr "fpu" "fpa,fpe2,fpe3" (const (symbol_ref "arm_fpu_attr"))) + +; LENGTH of an instruction (in bytes) +(define_attr "length" "" (const_int 4)) + +; POOL_RANGE is how far away from a constant pool entry that this insn +; can be placed. If the distance is zero, then this insn will never +; reference the pool. +; NEG_POOL_RANGE is nonzero for insns that can reference a constant pool entry +; before its address. +(define_attr "pool_range" "" (const_int 0)) +(define_attr "neg_pool_range" "" (const_int 0)) + +; An assembler sequence may clobber the condition codes without us knowing. +(define_asm_attributes + [(set_attr "conds" "clob")]) + +; TYPE attribute is used to detect floating point instructions which, if +; running on a co-processor can run in parallel with other, basic instructions +; If write-buffer scheduling is enabled then it can also be used in the +; scheduling of writes. + +; Classification of each insn +; normal any data instruction that doesn't hit memory or fp regs +; mult a multiply instruction +; block blockage insn, this blocks all functional units +; float a floating point arithmetic operation (subject to expansion) +; fdivx XFmode floating point division +; fdivd DFmode floating point division +; fdivs SFmode floating point division +; fmul Floating point multiply +; ffmul Fast floating point multiply +; farith Floating point arithmetic (4 cycle) +; ffarith Fast floating point arithmetic (2 cycle) +; float_em a floating point arithmetic operation that is normally emulated +; even on a machine with an fpa. +; f_load a floating point load from memory +; f_store a floating point store to memory +; f_mem_r a transfer of a floating point register to a real reg via mem +; r_mem_f the reverse of f_mem_r +; f_2_r fast transfer float to arm (no memory needed) +; r_2_f fast transfer arm to float +; call a subroutine call +; load any load from memory +; store1 store 1 word to memory from arm registers +; store2 store 2 words +; store3 store 3 words +; store4 store 4 words +; +(define_attr "type" + "normal,mult,block,float,fdivx,fdivd,fdivs,fmul,ffmul,farith,ffarith,float_em,f_load,f_store,f_mem_r,r_mem_f,f_2_r,r_2_f,call,load,store1,store2,store3,store4" + (const_string "normal")) + +; Load scheduling, set from the arm_ld_sched variable +; initialised by arm_override_options() +(define_attr "ldsched" "no,yes" (const (symbol_ref "arm_ld_sched"))) + +; condition codes: this one is used by final_prescan_insn to speed up +; conditionalizing instructions. It saves having to scan the rtl to see if +; it uses or alters the condition codes. +; +; USE means that the condition codes are used by the insn in the process of +; outputting code, this means (at present) that we can't use the insn in +; inlined branches +; +; SET means that the purpose of the insn is to set the condition codes in a +; well defined manner. +; +; CLOB means that the condition codes are altered in an undefined manner, if +; they are altered at all +; +; JUMP_CLOB is used when the condition cannot be represented by a single +; instruction (UNEQ and LTGT). These cannot be predicated. +; +; NOCOND means that the condition codes are neither altered nor affect the +; output of this insn + +(define_attr "conds" "use,set,clob,jump_clob,nocond" + (if_then_else (eq_attr "type" "call") + (if_then_else (eq_attr "prog_mode" "prog32") + (const_string "clob") (const_string "nocond")) + (const_string "nocond"))) + +; Predicable means that the insn can be conditionally executed based on +; an automatically added predicate (additional patterns are generated by +; gen...). We default to 'no' because no Thumb patterns match this rule +; and not all ARM patterns do. +(define_attr "predicable" "no,yes" (const_string "no")) + +; Only model the write buffer for ARM6 and ARM7. Earlier processors don't +; have one. Later ones, such as StrongARM, have write-back caches, so don't +; suffer blockages enough to warrent modelling this (and it can adversely +; affect the schedule). +(define_attr "model_wbuf" "no,yes" (const (symbol_ref "arm_is_6_or_7"))) + +; WRITE_CONFLICT implies that a read following an unrelated write is likely +; to stall the processor. Used with model_wbuf above. +(define_attr "write_conflict" "no,yes" + (if_then_else (eq_attr "type" + "block,float_em,f_load,f_store,f_mem_r,r_mem_f,call,load") + (const_string "yes") + (const_string "no"))) + +; Classify the insns into those that take one cycle and those that take more +; than one on the main cpu execution unit. +(define_attr "core_cycles" "single,multi" + (if_then_else (eq_attr "type" + "normal,float,fdivx,fdivd,fdivs,fmul,ffmul,farith,ffarith") + (const_string "single") + (const_string "multi"))) + +;; FAR_JUMP is "yes" if a BL instruction is used to generate a branch to a +;; distant label. Only applicable to Thumb code. +(define_attr "far_jump" "yes,no" (const_string "no")) + +;; (define_function_unit {name} {num-units} {n-users} {test} +;; {ready-delay} {issue-delay} [{conflict-list}]) + +;;-------------------------------------------------------------------- +;; Floating point unit (FPA) +;;-------------------------------------------------------------------- +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "fdivx")) 71 69) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "fdivd")) 59 57) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "fdivs")) 31 29) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "fmul")) 9 7) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "ffmul")) 6 4) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "farith")) 4 2) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "ffarith")) 2 2) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "r_2_f")) 5 3) + +(define_function_unit "fpa" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "f_2_r")) 1 2) + +; The fpa10 doesn't really have a memory read unit, but it can start to +; speculatively execute the instruction in the pipeline, provided the data +; is already loaded, so pretend reads have a delay of 2 (and that the +; pipeline is infinite). + +(define_function_unit "fpa_mem" 1 0 (and (eq_attr "fpu" "fpa") + (eq_attr "type" "f_load")) 3 1) + +;;-------------------------------------------------------------------- +;; Write buffer +;;-------------------------------------------------------------------- +; Strictly, we should model a 4-deep write buffer for ARM7xx based chips +; +; The write buffer on some of the arm6 processors is hard to model exactly. +; There is room in the buffer for up to two addresses and up to eight words +; of memory, but the two needn't be split evenly. When writing the two +; addresses are fully pipelined. However, a read from memory that is not +; currently in the cache will block until the writes have completed. +; It is normally the case that FCLK and MCLK will be in the ratio 2:1, so +; writes will take 2 FCLK cycles per word, if FCLK and MCLK are asynchronous +; (they aren't allowed to be at present) then there is a startup cost of 1MCLK +; cycle to add as well. + +(define_function_unit "write_buf" 1 2 + (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store1,r_mem_f")) 5 3) +(define_function_unit "write_buf" 1 2 + (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store2")) 7 4) +(define_function_unit "write_buf" 1 2 + (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store3")) 9 5) +(define_function_unit "write_buf" 1 2 + (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store4")) 11 6) + +;;-------------------------------------------------------------------- +;; Write blockage unit +;;-------------------------------------------------------------------- +; The write_blockage unit models (partially), the fact that reads will stall +; until the write buffer empties. +; The f_mem_r and r_mem_f could also block, but they are to the stack, +; so we don't model them here +(define_function_unit "write_blockage" 1 0 (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store1")) 5 5 + [(eq_attr "write_conflict" "yes")]) +(define_function_unit "write_blockage" 1 0 (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store2")) 7 7 + [(eq_attr "write_conflict" "yes")]) +(define_function_unit "write_blockage" 1 0 (and (eq_attr "model_wbuf" "yes") + (eq_attr "type" "store3")) 9 9 + [(eq_attr "write_conflict" "yes")]) +(define_function_unit "write_blockage" 1 0 + (and (eq_attr "model_wbuf" "yes") (eq_attr "type" "store4")) 11 11 + [(eq_attr "write_conflict" "yes")]) +(define_function_unit "write_blockage" 1 0 + (and (eq_attr "model_wbuf" "yes") + (eq_attr "write_conflict" "yes")) 1 1) + +;;-------------------------------------------------------------------- +;; Core unit +;;-------------------------------------------------------------------- +; Everything must spend at least one cycle in the core unit +(define_function_unit "core" 1 0 (eq_attr "core_cycles" "single") 1 1) + +(define_function_unit "core" 1 0 + (and (eq_attr "ldsched" "yes") (eq_attr "type" "store1")) 1 1) + +(define_function_unit "core" 1 0 + (and (eq_attr "ldsched" "yes") (eq_attr "type" "load")) 2 1) + +;; We do not need to conditionalize the define_function_unit immediately +;; above. This one will be ignored for anything other than xscale +;; compiles and for xscale compiles it provides a larger delay +;; and the scheduler will DTRT. +;; FIXME: this test needs to be revamped to not depend on this feature +;; of the scheduler. + +(define_function_unit "core" 1 0 + (and (and (eq_attr "ldsched" "yes") (eq_attr "type" "load")) + (eq_attr "is_xscale" "yes")) + 3 1) + +(define_function_unit "core" 1 0 + (and (eq_attr "ldsched" "!yes") (eq_attr "type" "load,store1")) 2 2) + +(define_function_unit "core" 1 0 + (and (eq_attr "fpu" "fpa") (eq_attr "type" "f_load")) 3 3) + +(define_function_unit "core" 1 0 + (and (eq_attr "fpu" "fpa") (eq_attr "type" "f_store")) 4 4) + +(define_function_unit "core" 1 0 + (and (eq_attr "fpu" "fpa") (eq_attr "type" "r_mem_f")) 6 6) + +(define_function_unit "core" 1 0 + (and (eq_attr "fpu" "fpa") (eq_attr "type" "f_mem_r")) 7 7) + +(define_function_unit "core" 1 0 + (and (eq_attr "ldsched" "no") (eq_attr "type" "mult")) 16 16) + +(define_function_unit "core" 1 0 + (and (and (eq_attr "ldsched" "yes") (eq_attr "is_strongarm" "no")) + (eq_attr "type" "mult")) 4 4) + +(define_function_unit "core" 1 0 + (and (and (eq_attr "ldsched" "yes") (eq_attr "is_strongarm" "yes")) + (eq_attr "type" "mult")) 3 2) + +(define_function_unit "core" 1 0 (eq_attr "type" "store2") 3 3) + +(define_function_unit "core" 1 0 (eq_attr "type" "store3") 4 4) + +(define_function_unit "core" 1 0 (eq_attr "type" "store4") 5 5) + +(define_function_unit "core" 1 0 + (and (eq_attr "core_cycles" "multi") + (eq_attr "type" "!mult,load,store1,store2,store3,store4")) 32 32) + +;;--------------------------------------------------------------------------- +;; Insn patterns +;; +;; Addition insns. + +;; Note: For DImode insns, there is normally no reason why operands should +;; not be in the same register, what we don't want is for something being +;; written to partially overlap something that is an input. + +(define_expand "adddi3" + [(parallel + [(set (match_operand:DI 0 "s_register_operand" "") + (plus:DI (match_operand:DI 1 "s_register_operand" "") + (match_operand:DI 2 "s_register_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_EITHER" + " + if (TARGET_THUMB) + { + if (GET_CODE (operands[1]) != REG) + operands[1] = force_reg (SImode, operands[1]); + if (GET_CODE (operands[2]) != REG) + operands[2] = force_reg (SImode, operands[2]); + } + " +) + +(define_insn "*thumb_adddi3" + [(set (match_operand:DI 0 "register_operand" "=l") + (plus:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "l"))) + (clobber (reg:CC CC_REGNUM)) + ] + "TARGET_THUMB" + "add\\t%Q0, %Q0, %Q2\;adc\\t%R0, %R0, %R2" + [(set_attr "length" "4")] +) + +(define_insn_and_split "*arm_adddi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (plus:DI (match_operand:DI 1 "s_register_operand" "%0, 0") + (match_operand:DI 2 "s_register_operand" "r, 0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(parallel [(set (reg:CC_C CC_REGNUM) + (compare:CC_C (plus:SI (match_dup 1) (match_dup 2)) + (match_dup 1))) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))]) + (set (match_dup 3) (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (plus:SI (match_dup 4) (match_dup 5))))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn_and_split "*adddi_sesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (plus:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "r,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(parallel [(set (reg:CC_C CC_REGNUM) + (compare:CC_C (plus:SI (match_dup 1) (match_dup 2)) + (match_dup 1))) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))]) + (set (match_dup 3) (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (plus:SI (ashiftrt:SI (match_dup 2) + (const_int 31)) + (match_dup 4))))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn_and_split "*adddi_zesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (plus:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "r,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(parallel [(set (reg:CC_C CC_REGNUM) + (compare:CC_C (plus:SI (match_dup 1) (match_dup 2)) + (match_dup 1))) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))]) + (set (match_dup 3) (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (plus:SI (match_dup 4) (const_int 0))))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_expand "addsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (plus:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "reg_or_int_operand" "")))] + "TARGET_EITHER" + " + if (TARGET_ARM && GET_CODE (operands[2]) == CONST_INT) + { + arm_split_constant (PLUS, SImode, INTVAL (operands[2]), operands[0], + operands[1], + (no_new_pseudos ? 0 : preserve_subexpressions_p ())); + DONE; + } + " +) + +; If there is a scratch available, this will be faster than synthesising the +; addition. +(define_peephole2 + [(match_scratch:SI 3 "r") + (set (match_operand:SI 0 "s_register_operand" "") + (plus:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_ARM && + !(const_ok_for_arm (INTVAL (operands[2])) + || const_ok_for_arm (-INTVAL (operands[2]))) + && const_ok_for_arm (~INTVAL (operands[2]))" + [(set (match_dup 3) (match_dup 2)) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 3)))] + "" +) + +(define_insn_and_split "*arm_addsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (plus:SI (match_operand:SI 1 "s_register_operand" "%r,r,r") + (match_operand:SI 2 "reg_or_int_operand" "rI,L,?n")))] + "TARGET_ARM" + "@ + add%?\\t%0, %1, %2 + sub%?\\t%0, %1, #%n2 + #" + "TARGET_ARM && + GET_CODE (operands[2]) == CONST_INT + && !(const_ok_for_arm (INTVAL (operands[2])) + || const_ok_for_arm (-INTVAL (operands[2])))" + [(clobber (const_int 0))] + " + arm_split_constant (PLUS, SImode, INTVAL (operands[2]), operands[0], + operands[1], 0); + DONE; + " + [(set_attr "length" "4,4,16") + (set_attr "predicable" "yes")] +) + +;; Register group 'k' is a single register group containing only the stack +;; register. Trying to reload it will always fail catastrophically, +;; so never allow those alternatives to match if reloading is needed. + +(define_insn "*thumb_addsi3" + [(set (match_operand:SI 0 "register_operand" "=l,l,l,*r,*h,l,!k") + (plus:SI (match_operand:SI 1 "register_operand" "%0,0,l,*0,*0,!k,!k") + (match_operand:SI 2 "nonmemory_operand" "I,J,lL,*h,*r,!M,!O")))] + "TARGET_THUMB" + "* + static const char * const asms[] = + { + \"add\\t%0, %0, %2\", + \"sub\\t%0, %0, #%n2\", + \"add\\t%0, %1, %2\", + \"add\\t%0, %0, %2\", + \"add\\t%0, %0, %2\", + \"add\\t%0, %1, %2\", + \"add\\t%0, %1, %2\" + }; + if ((which_alternative == 2 || which_alternative == 6) + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) < 0) + return \"sub\\t%0, %1, #%n2\"; + return asms[which_alternative]; + " + [(set_attr "length" "2")] +) + +;; Reloading and elimination of the frame pointer can +;; sometimes cause this optimization to be missed. +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "=l") + (match_operand:SI 1 "const_int_operand" "M")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_operand:SI 2 "register_operand" "k")))] + "TARGET_THUMB + && REGNO (operands[2]) == STACK_POINTER_REGNUM + && (unsigned HOST_WIDE_INT) (INTVAL (operands[1])) < 1024 + && (INTVAL (operands[1]) & 3) == 0" + [(set (match_dup 0) (plus:SI (match_dup 2) (match_dup 1)))] + "" +) + +(define_insn "*addsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (plus:SI (match_operand:SI 1 "s_register_operand" "r, r") + (match_operand:SI 2 "arm_add_operand" "rI,L")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + add%?s\\t%0, %1, %2 + sub%?s\\t%0, %1, #%n2" + [(set_attr "conds" "set")] +) + +(define_insn "*addsi3_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (plus:SI (match_operand:SI 0 "s_register_operand" "r, r") + (match_operand:SI 1 "arm_add_operand" "rI,L")) + (const_int 0)))] + "TARGET_ARM" + "@ + cmn%?\\t%0, %1 + cmp%?\\t%0, #%n1" + [(set_attr "conds" "set")] +) + +;; These patterns are the same ones as the two regular addsi3_compare0 +;; patterns, except we write them slightly different - the combiner +;; tends to generate them this way. +(define_insn "*addsi3_compare0_for_combiner" + [(set (reg:CC CC_REGNUM) + (compare:CC + (match_operand:SI 1 "s_register_operand" "r,r") + (neg:SI (match_operand:SI 2 "arm_add_operand" "rI,L")))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + add%?s\\t%0, %1, %2 + sub%?s\\t%0, %1, #%n2" + [(set_attr "conds" "set")] +) + +(define_insn "*addsi3_compare0_scratch_for_combiner" + [(set (reg:CC CC_REGNUM) + (compare:CC + (match_operand:SI 0 "s_register_operand" "r,r") + (neg:SI (match_operand:SI 1 "arm_add_operand" "rI,L"))))] + "TARGET_ARM" + "@ + cmn%?\\t%0, %1 + cmp%?\\t%0, #%n1" + [(set_attr "conds" "set")] +) + +;; The next four insns work because they compare the result with one of +;; the operands, and we know that the use of the condition code is +;; either GEU or LTU, so we can use the carry flag from the addition +;; instead of doing the compare a second time. +(define_insn "*addsi3_compare_op1" + [(set (reg:CC_C CC_REGNUM) + (compare:CC_C + (plus:SI (match_operand:SI 1 "s_register_operand" "r,r") + (match_operand:SI 2 "arm_add_operand" "rI,L")) + (match_dup 1))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + add%?s\\t%0, %1, %2 + sub%?s\\t%0, %1, #%n2" + [(set_attr "conds" "set")] +) + +(define_insn "*addsi3_compare_op2" + [(set (reg:CC_C CC_REGNUM) + (compare:CC_C + (plus:SI (match_operand:SI 1 "s_register_operand" "r,r") + (match_operand:SI 2 "arm_add_operand" "rI,L")) + (match_dup 2))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + add%?s\\t%0, %1, %2 + sub%?s\\t%0, %1, #%n2" + [(set_attr "conds" "set")] +) + +(define_insn "*compare_addsi2_op0" + [(set (reg:CC_C CC_REGNUM) + (compare:CC_C + (plus:SI (match_operand:SI 0 "s_register_operand" "r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L")) + (match_dup 0)))] + "TARGET_ARM" + "@ + cmn%?\\t%0, %1 + cmp%?\\t%0, #%n1" + [(set_attr "conds" "set")] +) + +(define_insn "*compare_addsi2_op1" + [(set (reg:CC_C CC_REGNUM) + (compare:CC_C + (plus:SI (match_operand:SI 0 "s_register_operand" "r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L")) + (match_dup 1)))] + "TARGET_ARM" + "@ + cmn%?\\t%0, %1 + cmp%?\\t%0, #%n1" + [(set_attr "conds" "set")] +) + +(define_insn "*addsi3_carryin" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (plus:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI"))))] + "TARGET_ARM" + "adc%?\\t%0, %1, %2" + [(set_attr "conds" "use")] +) + +(define_insn "*addsi3_carryin_shift" + [(set (match_operand:SI 0 "s_register_operand" "") + (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (plus:SI + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "") + (match_operand:SI 4 "reg_or_int_operand" "")]) + (match_operand:SI 1 "s_register_operand" ""))))] + "TARGET_ARM" + "adc%?\\t%0, %1, %3%S2" + [(set_attr "conds" "use")] +) + +(define_insn "*addsi3_carryin_alt1" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (plus:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")) + (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0))))] + "TARGET_ARM" + "adc%?\\t%0, %1, %2" + [(set_attr "conds" "use")] +) + +(define_insn "*addsi3_carryin_alt2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (match_operand:SI 1 "s_register_operand" "r")) + (match_operand:SI 2 "arm_rhs_operand" "rI")))] + "TARGET_ARM" + "adc%?\\t%0, %1, %2" + [(set_attr "conds" "use")] +) + +(define_insn "*addsi3_carryin_alt3" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (plus:SI (ltu:SI (reg:CC_C CC_REGNUM) (const_int 0)) + (match_operand:SI 2 "arm_rhs_operand" "rI")) + (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "adc%?\\t%0, %1, %2" + [(set_attr "conds" "use")] +) + +(define_insn "incscc" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (plus:SI (match_operator:SI 2 "arm_comparison_operator" + [(match_operand:CC 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "s_register_operand" "0,?r")))] + "TARGET_ARM" + "@ + add%d2\\t%0, %1, #1 + mov%D2\\t%0, %1\;add%d2\\t%0, %1, #1" + [(set_attr "conds" "use") + (set_attr "length" "4,8")] +) + +(define_insn "addsf3" + [(set (match_operand:SF 0 "s_register_operand" "=f,f") + (plus:SF (match_operand:SF 1 "s_register_operand" "%f,f") + (match_operand:SF 2 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + adf%?s\\t%0, %1, %2 + suf%?s\\t%0, %1, #%N2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "adddf3" + [(set (match_operand:DF 0 "s_register_operand" "=f,f") + (plus:DF (match_operand:DF 1 "s_register_operand" "%f,f") + (match_operand:DF 2 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + adf%?d\\t%0, %1, %2 + suf%?d\\t%0, %1, #%N2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*adddf_esfdf_df" + [(set (match_operand:DF 0 "s_register_operand" "=f,f") + (plus:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f,f")) + (match_operand:DF 2 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + adf%?d\\t%0, %1, %2 + suf%?d\\t%0, %1, #%N2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*adddf_df_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (plus:DF (match_operand:DF 1 "s_register_operand" "f") + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "adf%?d\\t%0, %1, %2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*adddf_esfdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (plus:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "adf%?d\\t%0, %1, %2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "addxf3" + [(set (match_operand:XF 0 "s_register_operand" "=f,f") + (plus:XF (match_operand:XF 1 "s_register_operand" "f,f") + (match_operand:XF 2 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "@ + adf%?e\\t%0, %1, %2 + suf%?e\\t%0, %1, #%N2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_expand "subdi3" + [(parallel + [(set (match_operand:DI 0 "s_register_operand" "") + (minus:DI (match_operand:DI 1 "s_register_operand" "") + (match_operand:DI 2 "s_register_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_EITHER" + " + if (TARGET_THUMB) + { + if (GET_CODE (operands[1]) != REG) + operands[1] = force_reg (SImode, operands[1]); + if (GET_CODE (operands[2]) != REG) + operands[2] = force_reg (SImode, operands[2]); + } + " +) + +(define_insn "*arm_subdi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r,&r") + (minus:DI (match_operand:DI 1 "s_register_operand" "0,r,0") + (match_operand:DI 2 "s_register_operand" "r,0,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "subs\\t%Q0, %Q1, %Q2\;sbc\\t%R0, %R1, %R2" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*thumb_subdi3" + [(set (match_operand:DI 0 "register_operand" "=l") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "register_operand" "l"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_THUMB" + "sub\\t%Q0, %Q0, %Q2\;sbc\\t%R0, %R0, %R2" + [(set_attr "length" "4")] +) + +(define_insn "*subdi_di_zesidi" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (minus:DI (match_operand:DI 1 "s_register_operand" "?r,0") + (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "subs\\t%Q0, %Q1, %2\;sbc\\t%R0, %R1, #0" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*subdi_di_sesidi" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (minus:DI (match_operand:DI 1 "s_register_operand" "r,0") + (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "subs\\t%Q0, %Q1, %2\;sbc\\t%R0, %R1, %2, asr #31" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*subdi_zesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (minus:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "rsbs\\t%Q0, %Q1, %2\;rsc\\t%R0, %R1, #0" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*subdi_sesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (minus:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "rsbs\\t%Q0, %Q1, %2\;rsc\\t%R0, %R1, %2, asr #31" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*subdi_zesidi_zesidi" + [(set (match_operand:DI 0 "s_register_operand" "=r") + (minus:DI (zero_extend:DI + (match_operand:SI 1 "s_register_operand" "r")) + (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "subs\\t%Q0, %1, %2\;rsc\\t%R0, %1, %1" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_expand "subsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (minus:SI (match_operand:SI 1 "reg_or_int_operand" "") + (match_operand:SI 2 "s_register_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[1]) == CONST_INT) + { + if (TARGET_ARM) + { + arm_split_constant (MINUS, SImode, INTVAL (operands[1]), operands[0], + operands[2], + (no_new_pseudos ? 0 + : preserve_subexpressions_p ())); + DONE; + } + else /* TARGET_THUMB */ + operands[1] = force_reg (SImode, operands[1]); + } + " +) + +(define_insn "*thumb_subsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=l") + (minus:SI (match_operand:SI 1 "register_operand" "l") + (match_operand:SI 2 "register_operand" "l")))] + "TARGET_THUMB" + "sub\\t%0, %1, %2" + [(set_attr "length" "2")] +) + +(define_insn_and_split "*arm_subsi3_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "reg_or_int_operand" "rI,?n") + (match_operand:SI 2 "s_register_operand" "r,r")))] + "TARGET_ARM" + "@ + rsb%?\\t%0, %2, %1 + #" + "TARGET_ARM + && GET_CODE (operands[1]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[1]))" + [(clobber (const_int 0))] + " + arm_split_constant (MINUS, SImode, INTVAL (operands[1]), operands[0], + operands[2], 0); + DONE; + " + [(set_attr "length" "4,16") + (set_attr "predicable" "yes")] +) + +(define_peephole2 + [(match_scratch:SI 3 "r") + (set (match_operand:SI 0 "s_register_operand" "") + (minus:SI (match_operand:SI 1 "const_int_operand" "") + (match_operand:SI 2 "s_register_operand" "")))] + "TARGET_ARM + && !const_ok_for_arm (INTVAL (operands[1])) + && const_ok_for_arm (~INTVAL (operands[1]))" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 0) (minus:SI (match_dup 3) (match_dup 2)))] + "" +) + +(define_insn "*subsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (minus:SI (match_operand:SI 1 "arm_rhs_operand" "r,I") + (match_operand:SI 2 "arm_rhs_operand" "rI,r")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + sub%?s\\t%0, %1, %2 + rsb%?s\\t%0, %2, %1" + [(set_attr "conds" "set")] +) + +(define_insn "decscc" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "s_register_operand" "0,?r") + (match_operator:SI 2 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)])))] + "TARGET_ARM" + "@ + sub%d2\\t%0, %1, #1 + mov%D2\\t%0, %1\;sub%d2\\t%0, %1, #1" + [(set_attr "conds" "use") + (set_attr "length" "*,8")] +) + +(define_insn "subsf3" + [(set (match_operand:SF 0 "s_register_operand" "=f,f") + (minus:SF (match_operand:SF 1 "fpu_rhs_operand" "f,G") + (match_operand:SF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + suf%?s\\t%0, %1, %2 + rsf%?s\\t%0, %2, %1" + [(set_attr "type" "farith")] +) + +(define_insn "subdf3" + [(set (match_operand:DF 0 "s_register_operand" "=f,f") + (minus:DF (match_operand:DF 1 "fpu_rhs_operand" "f,G") + (match_operand:DF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + suf%?d\\t%0, %1, %2 + rsf%?d\\t%0, %2, %1" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*subdf_esfdf_df" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (minus:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "suf%?d\\t%0, %1, %2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*subdf_df_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f,f") + (minus:DF (match_operand:DF 1 "fpu_rhs_operand" "f,G") + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f,f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + suf%?d\\t%0, %1, %2 + rsf%?d\\t%0, %2, %1" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "*subdf_esfdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (minus:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "suf%?d\\t%0, %1, %2" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +(define_insn "subxf3" + [(set (match_operand:XF 0 "s_register_operand" "=f,f") + (minus:XF (match_operand:XF 1 "fpu_rhs_operand" "f,G") + (match_operand:XF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "@ + suf%?e\\t%0, %1, %2 + rsf%?e\\t%0, %2, %1" + [(set_attr "type" "farith") + (set_attr "predicable" "yes")] +) + +;; Multiplication insns + +(define_expand "mulsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (mult:SI (match_operand:SI 2 "s_register_operand" "") + (match_operand:SI 1 "s_register_operand" "")))] + "TARGET_EITHER" + "" +) + +;; Use `&' and then `0' to prevent the operands 0 and 1 being the same +(define_insn "*arm_mulsi3" + [(set (match_operand:SI 0 "s_register_operand" "=&r,&r") + (mult:SI (match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 1 "s_register_operand" "%?r,0")))] + "TARGET_ARM" + "mul%?\\t%0, %2, %1" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +; Unfortunately with the Thumb the '&'/'0' trick can fails when operands +; 1 and 2; are the same, because reload will make operand 0 match +; operand 1 without realizing that this conflicts with operand 2. We fix +; this by adding another alternative to match this case, and then `reload' +; it ourselves. This alternative must come first. +(define_insn "*thumb_mulsi3" + [(set (match_operand:SI 0 "register_operand" "=&l,&l,&l") + (mult:SI (match_operand:SI 1 "register_operand" "%l,*h,0") + (match_operand:SI 2 "register_operand" "l,l,l")))] + "TARGET_THUMB" + "* + if (which_alternative < 2) + return \"mov\\t%0, %1\;mul\\t%0, %0, %2\"; + else + return \"mul\\t%0, %0, %2\"; + " + [(set_attr "length" "4,4,2") + (set_attr "type" "mult")] +) + +(define_insn "*mulsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (mult:SI + (match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 1 "s_register_operand" "%?r,0")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=&r,&r") + (mult:SI (match_dup 2) (match_dup 1)))] + "TARGET_ARM && !arm_is_xscale" + "mul%?s\\t%0, %2, %1" + [(set_attr "conds" "set") + (set_attr "type" "mult")] +) + +(define_insn "*mulsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (mult:SI + (match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 1 "s_register_operand" "%?r,0")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=&r,&r"))] + "TARGET_ARM && !arm_is_xscale" + "mul%?s\\t%0, %2, %1" + [(set_attr "conds" "set") + (set_attr "type" "mult")] +) + +;; Unnamed templates to match MLA instruction. + +(define_insn "*mulsi3addsi" + [(set (match_operand:SI 0 "s_register_operand" "=&r,&r,&r,&r") + (plus:SI + (mult:SI (match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "s_register_operand" "%r,0,r,0")) + (match_operand:SI 3 "s_register_operand" "?r,r,0,0")))] + "TARGET_ARM" + "mla%?\\t%0, %2, %1, %3" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "*mulsi3addsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (plus:SI (mult:SI + (match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "s_register_operand" "%r,0,r,0")) + (match_operand:SI 3 "s_register_operand" "?r,r,0,0")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=&r,&r,&r,&r") + (plus:SI (mult:SI (match_dup 2) (match_dup 1)) + (match_dup 3)))] + "TARGET_ARM && !arm_is_xscale" + "mla%?s\\t%0, %2, %1, %3" + [(set_attr "conds" "set") + (set_attr "type" "mult")] +) + +(define_insn "*mulsi3addsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (plus:SI (mult:SI + (match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "s_register_operand" "%r,0,r,0")) + (match_operand:SI 3 "s_register_operand" "?r,r,0,0")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=&r,&r,&r,&r"))] + "TARGET_ARM && !arm_is_xscale" + "mla%?s\\t%0, %2, %1, %3" + [(set_attr "conds" "set") + (set_attr "type" "mult")] +) + +;; Unnamed template to match long long multiply-accumlate (smlal) + +(define_insn "*mulsidi3adddi" + [(set (match_operand:DI 0 "s_register_operand" "=&r") + (plus:DI + (mult:DI + (sign_extend:DI (match_operand:SI 2 "s_register_operand" "%r")) + (sign_extend:DI (match_operand:SI 3 "s_register_operand" "r"))) + (match_operand:DI 1 "s_register_operand" "0")))] + "TARGET_ARM && arm_fast_multiply" + "smlal%?\\t%Q0, %R0, %3, %2" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r") + (mult:DI + (sign_extend:DI (match_operand:SI 1 "s_register_operand" "%r")) + (sign_extend:DI (match_operand:SI 2 "s_register_operand" "r"))))] + "TARGET_ARM && arm_fast_multiply" + "smull%?\\t%Q0, %R0, %1, %2" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r") + (mult:DI + (zero_extend:DI (match_operand:SI 1 "s_register_operand" "%r")) + (zero_extend:DI (match_operand:SI 2 "s_register_operand" "r"))))] + "TARGET_ARM && arm_fast_multiply" + "umull%?\\t%Q0, %R0, %1, %2" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +;; Unnamed template to match long long unsigned multiply-accumlate (umlal) + +(define_insn "*umulsidi3adddi" + [(set (match_operand:DI 0 "s_register_operand" "=&r") + (plus:DI + (mult:DI + (zero_extend:DI (match_operand:SI 2 "s_register_operand" "%r")) + (zero_extend:DI (match_operand:SI 3 "s_register_operand" "r"))) + (match_operand:DI 1 "s_register_operand" "0")))] + "TARGET_ARM && arm_fast_multiply" + "umlal%?\\t%Q0, %R0, %3, %2" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "smulsi3_highpart" + [(set (match_operand:SI 0 "s_register_operand" "=&r,&r") + (truncate:SI + (lshiftrt:DI + (mult:DI + (sign_extend:DI (match_operand:SI 1 "s_register_operand" "%r,0")) + (sign_extend:DI (match_operand:SI 2 "s_register_operand" "r,r"))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=&r,&r"))] + "TARGET_ARM && arm_fast_multiply" + "smull%?\\t%3, %0, %2, %1" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "umulsi3_highpart" + [(set (match_operand:SI 0 "s_register_operand" "=&r,&r") + (truncate:SI + (lshiftrt:DI + (mult:DI + (zero_extend:DI (match_operand:SI 1 "s_register_operand" "%r,0")) + (zero_extend:DI (match_operand:SI 2 "s_register_operand" "r,r"))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=&r,&r"))] + "TARGET_ARM && arm_fast_multiply" + "umull%?\\t%3, %0, %2, %1" + [(set_attr "type" "mult") + (set_attr "predicable" "yes")] +) + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (mult:SI (sign_extend:SI + (match_operand:HI 1 "s_register_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "s_register_operand" "r"))))] + "TARGET_ARM && arm_is_xscale" + "smulbb%?\\t%0, %1, %2" + [(set_attr "type" "mult")] +) + +(define_insn "*mulhisi3addsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_operand:SI 1 "s_register_operand" "r") + (mult:SI (sign_extend:SI + (match_operand:HI 2 "s_register_operand" "%r")) + (sign_extend:SI + (match_operand:HI 3 "s_register_operand" "r")))))] + "TARGET_ARM && arm_is_xscale" + "smlabb%?\\t%0, %2, %3, %1" + [(set_attr "type" "mult")] +) + +(define_insn "*mulhidi3adddi" + [(set (match_operand:DI 0 "s_register_operand" "=r") + (plus:DI + (match_operand:DI 1 "s_register_operand" "0") + (mult:DI (sign_extend:DI + (match_operand:HI 2 "s_register_operand" "%r")) + (sign_extend:DI + (match_operand:HI 3 "s_register_operand" "r")))))] + "TARGET_ARM && arm_is_xscale" + "smlalbb%?\\t%Q0, %R0, %2, %3" +[(set_attr "type" "mult")]) + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (mult:SF (match_operand:SF 1 "s_register_operand" "f") + (match_operand:SF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "fml%?s\\t%0, %1, %2" + [(set_attr "type" "ffmul") + (set_attr "predicable" "yes")] +) + +(define_insn "muldf3" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mult:DF (match_operand:DF 1 "s_register_operand" "f") + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "muf%?d\\t%0, %1, %2" + [(set_attr "type" "fmul") + (set_attr "predicable" "yes")] +) + +(define_insn "*muldf_esfdf_df" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mult:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "muf%?d\\t%0, %1, %2" + [(set_attr "type" "fmul") + (set_attr "predicable" "yes")] +) + +(define_insn "*muldf_df_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mult:DF (match_operand:DF 1 "s_register_operand" "f") + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "muf%?d\\t%0, %1, %2" + [(set_attr "type" "fmul") + (set_attr "predicable" "yes")] +) + +(define_insn "*muldf_esfdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mult:DF + (float_extend:DF (match_operand:SF 1 "s_register_operand" "f")) + (float_extend:DF (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "muf%?d\\t%0, %1, %2" + [(set_attr "type" "fmul") + (set_attr "predicable" "yes")] +) + +(define_insn "mulxf3" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (mult:XF (match_operand:XF 1 "s_register_operand" "f") + (match_operand:XF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "muf%?e\\t%0, %1, %2" + [(set_attr "type" "fmul") + (set_attr "predicable" "yes")] +) + +;; Division insns + +(define_insn "divsf3" + [(set (match_operand:SF 0 "s_register_operand" "=f,f") + (div:SF (match_operand:SF 1 "fpu_rhs_operand" "f,G") + (match_operand:SF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + fdv%?s\\t%0, %1, %2 + frd%?s\\t%0, %2, %1" + [(set_attr "type" "fdivs") + (set_attr "predicable" "yes")] +) + +(define_insn "divdf3" + [(set (match_operand:DF 0 "s_register_operand" "=f,f") + (div:DF (match_operand:DF 1 "fpu_rhs_operand" "f,G") + (match_operand:DF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + dvf%?d\\t%0, %1, %2 + rdf%?d\\t%0, %2, %1" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*divdf_esfdf_df" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (div:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "dvf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*divdf_df_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (div:DF (match_operand:DF 1 "fpu_rhs_operand" "fG") + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rdf%?d\\t%0, %2, %1" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*divdf_esfdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (div:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "dvf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "divxf3" + [(set (match_operand:XF 0 "s_register_operand" "=f,f") + (div:XF (match_operand:XF 1 "fpu_rhs_operand" "f,G") + (match_operand:XF 2 "fpu_rhs_operand" "fG,f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "@ + dvf%?e\\t%0, %1, %2 + rdf%?e\\t%0, %2, %1" + [(set_attr "type" "fdivx") + (set_attr "predicable" "yes")] +) + +;; Modulo insns + +(define_insn "modsf3" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (mod:SF (match_operand:SF 1 "s_register_operand" "f") + (match_operand:SF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rmf%?s\\t%0, %1, %2" + [(set_attr "type" "fdivs") + (set_attr "predicable" "yes")] +) + +(define_insn "moddf3" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mod:DF (match_operand:DF 1 "s_register_operand" "f") + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rmf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*moddf_esfdf_df" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mod:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (match_operand:DF 2 "fpu_rhs_operand" "fG")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rmf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*moddf_df_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mod:DF (match_operand:DF 1 "s_register_operand" "f") + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rmf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "*moddf_esfdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (mod:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f")) + (float_extend:DF + (match_operand:SF 2 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "rmf%?d\\t%0, %1, %2" + [(set_attr "type" "fdivd") + (set_attr "predicable" "yes")] +) + +(define_insn "modxf3" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (mod:XF (match_operand:XF 1 "s_register_operand" "f") + (match_operand:XF 2 "fpu_rhs_operand" "fG")))] + "ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "rmf%?e\\t%0, %1, %2" + [(set_attr "type" "fdivx") + (set_attr "predicable" "yes")] +) + +;; Boolean and,ior,xor insns + +;; Split up double word logical operations + +;; Split up simple DImode logical operations. Simply perform the logical +;; operation on the upper and lower halves of the registers. +(define_split + [(set (match_operand:DI 0 "s_register_operand" "") + (match_operator:DI 6 "logical_binary_operator" + [(match_operand:DI 1 "s_register_operand" "") + (match_operand:DI 2 "s_register_operand" "")]))] + "TARGET_ARM && reload_completed" + [(set (match_dup 0) (match_op_dup:SI 6 [(match_dup 1) (match_dup 2)])) + (set (match_dup 3) (match_op_dup:SI 6 [(match_dup 4) (match_dup 5)]))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" +) + +(define_split + [(set (match_operand:DI 0 "s_register_operand" "") + (match_operator:DI 6 "logical_binary_operator" + [(sign_extend:DI (match_operand:SI 2 "s_register_operand" "")) + (match_operand:DI 1 "s_register_operand" "")]))] + "TARGET_ARM && reload_completed" + [(set (match_dup 0) (match_op_dup:SI 6 [(match_dup 1) (match_dup 2)])) + (set (match_dup 3) (match_op_dup:SI 6 + [(ashiftrt:SI (match_dup 2) (const_int 31)) + (match_dup 4)]))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" +) + +;; The zero extend of operand 2 means we can just copy the high part of +;; operand1 into operand0. +(define_split + [(set (match_operand:DI 0 "s_register_operand" "") + (ior:DI + (zero_extend:DI (match_operand:SI 2 "s_register_operand" "")) + (match_operand:DI 1 "s_register_operand" "")))] + "TARGET_ARM && operands[0] != operands[1] && reload_completed" + [(set (match_dup 0) (ior:SI (match_dup 1) (match_dup 2))) + (set (match_dup 3) (match_dup 4))] + " + { + operands[4] = gen_highpart (SImode, operands[1]); + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" +) + +;; The zero extend of operand 2 means we can just copy the high part of +;; operand1 into operand0. +(define_split + [(set (match_operand:DI 0 "s_register_operand" "") + (xor:DI + (zero_extend:DI (match_operand:SI 2 "s_register_operand" "")) + (match_operand:DI 1 "s_register_operand" "")))] + "TARGET_ARM && operands[0] != operands[1] && reload_completed" + [(set (match_dup 0) (xor:SI (match_dup 1) (match_dup 2))) + (set (match_dup 3) (match_dup 4))] + " + { + operands[4] = gen_highpart (SImode, operands[1]); + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" +) + +(define_insn "anddi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (match_operand:DI 1 "s_register_operand" "%0,r") + (match_operand:DI 2 "s_register_operand" "r,r")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8")] +) + +(define_insn_and_split "*anddi_zesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + ; The zero extend of operand 2 clears the high word of the output + ; operand. + [(set (match_dup 0) (and:SI (match_dup 1) (match_dup 2))) + (set (match_dup 3) (const_int 0))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" + [(set_attr "length" "8")] +) + +(define_insn "*anddi_sesdi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8")] +) + +(define_expand "andsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (and:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "reg_or_int_operand" "")))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (GET_CODE (operands[2]) == CONST_INT) + { + arm_split_constant (AND, SImode, INTVAL (operands[2]), operands[0], + operands[1], + (no_new_pseudos + ? 0 : preserve_subexpressions_p ())); + DONE; + } + } + else /* TARGET_THUMB */ + { + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = force_reg (SImode, operands[2]); + else + { + int i; + + if (((unsigned HOST_WIDE_INT) ~INTVAL (operands[2])) < 256) + { + operands[2] = force_reg (SImode, + GEN_INT (~INTVAL (operands[2]))); + + emit_insn (gen_bicsi3 (operands[0], operands[2], operands[1])); + + DONE; + } + + for (i = 9; i <= 31; i++) + { + if ((((HOST_WIDE_INT) 1) << i) - 1 == INTVAL (operands[2])) + { + emit_insn (gen_extzv (operands[0], operands[1], GEN_INT (i), + const0_rtx)); + DONE; + } + else if ((((HOST_WIDE_INT) 1) << i) - 1 + == ~INTVAL (operands[2])) + { + rtx shift = GEN_INT (i); + rtx reg = gen_reg_rtx (SImode); + + emit_insn (gen_lshrsi3 (reg, operands[1], shift)); + emit_insn (gen_ashlsi3 (operands[0], reg, shift)); + + DONE; + } + } + + operands[2] = force_reg (SImode, operands[2]); + } + } + " +) + +(define_insn_and_split "*arm_andsi3_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (and:SI (match_operand:SI 1 "s_register_operand" "r,r,r") + (match_operand:SI 2 "reg_or_int_operand" "rI,K,?n")))] + "TARGET_ARM" + "@ + and%?\\t%0, %1, %2 + bic%?\\t%0, %1, #%B2 + #" + "TARGET_ARM + && GET_CODE (operands[2]) == CONST_INT + && !(const_ok_for_arm (INTVAL (operands[2])) + || const_ok_for_arm (~INTVAL (operands[2])))" + [(clobber (const_int 0))] + " + arm_split_constant (AND, SImode, INTVAL (operands[2]), operands[0], + operands[1], 0); + DONE; + " + [(set_attr "length" "4,4,16") + (set_attr "predicable" "yes")] +) + +(define_insn "*thumb_andsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=l") + (and:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "l")))] + "TARGET_THUMB" + "and\\t%0, %0, %2" + [(set_attr "length" "2")] +) + +(define_insn "*andsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (and:SI (match_operand:SI 1 "s_register_operand" "r,r") + (match_operand:SI 2 "arm_not_operand" "rI,K")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (and:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "@ + and%?s\\t%0, %1, %2 + bic%?s\\t%0, %1, #%B2" + [(set_attr "conds" "set")] +) + +(define_insn "*andsi3_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (and:SI (match_operand:SI 0 "s_register_operand" "r,r") + (match_operand:SI 1 "arm_not_operand" "rI,K")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=X,r"))] + "TARGET_ARM" + "@ + tst%?\\t%0, %1 + bic%?s\\t%2, %0, #%B1" + [(set_attr "conds" "set")] +) + +(define_insn "*zeroextractsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (zero_extract:SI + (match_operand:SI 0 "s_register_operand" "r") + (match_operand 1 "const_int_operand" "n") + (match_operand 2 "const_int_operand" "n")) + (const_int 0)))] + "TARGET_ARM + && (INTVAL (operands[2]) >= 0 && INTVAL (operands[2]) < 32 + && INTVAL (operands[1]) > 0 + && INTVAL (operands[1]) + (INTVAL (operands[2]) & 1) <= 8 + && INTVAL (operands[1]) + INTVAL (operands[2]) <= 32)" + "* + operands[1] = GEN_INT (((1 << INTVAL (operands[1])) - 1) + << INTVAL (operands[2])); + output_asm_insn (\"tst%?\\t%0, %1\", operands); + return \"\"; + " + [(set_attr "conds" "set")] +) + +(define_insn "*ne_zeroextractsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ne:SI (zero_extract:SI + (match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "const_int_operand" "n") + (match_operand:SI 3 "const_int_operand" "n")) + (const_int 0)))] + "TARGET_ARM + && (INTVAL (operands[3]) >= 0 && INTVAL (operands[3]) < 32 + && INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) + (INTVAL (operands[3]) & 1) <= 8 + && INTVAL (operands[2]) + INTVAL (operands[3]) <= 32)" + "* + operands[2] = GEN_INT (((1 << INTVAL (operands[2])) - 1) + << INTVAL (operands[3])); + output_asm_insn (\"ands\\t%0, %1, %2\", operands); + return \"movne\\t%0, #1\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +;;; ??? This pattern is bogus. If operand3 has bits outside the range +;;; represented by the bitfield, then this will produce incorrect results. +;;; Somewhere, the value needs to be truncated. On targets like the m68k, +;;; which have a real bitfield insert instruction, the truncation happens +;;; in the bitfield insert instruction itself. Since arm does not have a +;;; bitfield insert instruction, we would have to emit code here to truncate +;;; the value before we insert. This loses some of the advantage of having +;;; this insv pattern, so this pattern needs to be reevalutated. + +(define_expand "insv" + [(set (zero_extract:SI (match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" "")) + (match_operand:SI 3 "nonmemory_operand" ""))] + "TARGET_ARM" + " + { + int start_bit = INTVAL (operands[2]); + int width = INTVAL (operands[1]); + HOST_WIDE_INT mask = (((HOST_WIDE_INT)1) << width) - 1; + rtx target, subtarget; + + target = operands[0]; + /* Avoid using a subreg as a subtarget, and avoid writing a paradoxical + subreg as the final target. */ + if (GET_CODE (target) == SUBREG) + { + subtarget = gen_reg_rtx (SImode); + if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (target))) + < GET_MODE_SIZE (SImode)) + target = SUBREG_REG (target); + } + else + subtarget = target; + + if (GET_CODE (operands[3]) == CONST_INT) + { + /* Since we are inserting a known constant, we may be able to + reduce the number of bits that we have to clear so that + the mask becomes simple. */ + /* ??? This code does not check to see if the new mask is actually + simpler. It may not be. */ + rtx op1 = gen_reg_rtx (SImode); + /* ??? Truncate operand3 to fit in the bitfield. See comment before + start of this pattern. */ + HOST_WIDE_INT op3_value = mask & INTVAL (operands[3]); + HOST_WIDE_INT mask2 = ((mask & ~op3_value) << start_bit); + + emit_insn (gen_andsi3 (op1, operands[0], GEN_INT (~mask2))); + emit_insn (gen_iorsi3 (subtarget, op1, + GEN_INT (op3_value << start_bit))); + } + else if (start_bit == 0 + && !(const_ok_for_arm (mask) + || const_ok_for_arm (~mask))) + { + /* A Trick, since we are setting the bottom bits in the word, + we can shift operand[3] up, operand[0] down, OR them together + and rotate the result back again. This takes 3 insns, and + the third might be mergable into another op. */ + /* The shift up copes with the possibility that operand[3] is + wider than the bitfield. */ + rtx op0 = gen_reg_rtx (SImode); + rtx op1 = gen_reg_rtx (SImode); + + emit_insn (gen_ashlsi3 (op0, operands[3], GEN_INT (32 - width))); + emit_insn (gen_lshrsi3 (op1, operands[0], operands[1])); + emit_insn (gen_iorsi3 (op1, op1, op0)); + emit_insn (gen_rotlsi3 (subtarget, op1, operands[1])); + } + else if ((width + start_bit == 32) + && !(const_ok_for_arm (mask) + || const_ok_for_arm (~mask))) + { + /* Similar trick, but slightly less efficient. */ + + rtx op0 = gen_reg_rtx (SImode); + rtx op1 = gen_reg_rtx (SImode); + + emit_insn (gen_ashlsi3 (op0, operands[3], GEN_INT (32 - width))); + emit_insn (gen_ashlsi3 (op1, operands[0], operands[1])); + emit_insn (gen_lshrsi3 (op1, op1, operands[1])); + emit_insn (gen_iorsi3 (subtarget, op1, op0)); + } + else + { + rtx op0 = GEN_INT (mask); + rtx op1 = gen_reg_rtx (SImode); + rtx op2 = gen_reg_rtx (SImode); + + if (!(const_ok_for_arm (mask) || const_ok_for_arm (~mask))) + { + rtx tmp = gen_reg_rtx (SImode); + + emit_insn (gen_movsi (tmp, op0)); + op0 = tmp; + } + + /* Mask out any bits in operand[3] that are not needed. */ + emit_insn (gen_andsi3 (op1, operands[3], op0)); + + if (GET_CODE (op0) == CONST_INT + && (const_ok_for_arm (mask << start_bit) + || const_ok_for_arm (~(mask << start_bit)))) + { + op0 = GEN_INT (~(mask << start_bit)); + emit_insn (gen_andsi3 (op2, operands[0], op0)); + } + else + { + if (GET_CODE (op0) == CONST_INT) + { + rtx tmp = gen_reg_rtx (SImode); + + emit_insn (gen_movsi (tmp, op0)); + op0 = tmp; + } + + if (start_bit != 0) + emit_insn (gen_ashlsi3 (op0, op0, operands[2])); + + emit_insn (gen_andsi_notsi_si (op2, operands[0], op0)); + } + + if (start_bit != 0) + emit_insn (gen_ashlsi3 (op1, op1, operands[2])); + + emit_insn (gen_iorsi3 (subtarget, op1, op2)); + } + + if (subtarget != target) + { + /* If TARGET is still a SUBREG, then it must be wider than a word, + so we must be careful only to set the subword we were asked to. */ + if (GET_CODE (target) == SUBREG) + emit_move_insn (target, subtarget); + else + emit_move_insn (target, gen_lowpart (GET_MODE (target), subtarget)); + } + + DONE; + }" +) + +; constants for op 2 will never be given to these patterns. +(define_insn_and_split "*anddi_notdi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (not:DI (match_operand:DI 1 "s_register_operand" "r,0")) + (match_operand:DI 2 "s_register_operand" "0,r")))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(set (match_dup 0) (and:SI (not:SI (match_dup 1)) (match_dup 2))) + (set (match_dup 3) (and:SI (not:SI (match_dup 4)) (match_dup 5)))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[2]); + operands[2] = gen_lowpart (SImode, operands[2]); + }" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_insn_and_split "*anddi_notzesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (not:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r"))) + (match_operand:DI 1 "s_register_operand" "0,?r")))] + "TARGET_ARM" + "@ + bic%?\\t%Q0, %Q1, %2 + #" + ; (not (zero_extend ...)) allows us to just copy the high word from + ; operand1 to operand0. + "TARGET_ARM + && reload_completed + && operands[0] != operands[1]" + [(set (match_dup 0) (and:SI (not:SI (match_dup 1)) (match_dup 2))) + (set (match_dup 3) (match_dup 4))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" + [(set_attr "length" "4,8") + (set_attr "predicable" "yes")] +) + +(define_insn_and_split "*anddi_notsesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (and:DI (not:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r"))) + (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(set (match_dup 0) (and:SI (not:SI (match_dup 1)) (match_dup 2))) + (set (match_dup 3) (and:SI (not:SI + (ashiftrt:SI (match_dup 2) (const_int 31))) + (match_dup 4)))] + " + { + operands[3] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_insn "andsi_notsi_si" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (and:SI (not:SI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "bic%?\\t%0, %1, %2" + [(set_attr "predicable" "yes")] +) + +(define_insn "bicsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "l")) + (match_operand:SI 2 "register_operand" "0")))] + "TARGET_THUMB" + "bic\\t%0, %0, %1" + [(set_attr "length" "2")] +) + +(define_insn "andsi_not_shiftsi_si" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (and:SI (not:SI (match_operator:SI 4 "shift_operator" + [(match_operand:SI 2 "s_register_operand" "r") + (match_operand:SI 3 "arm_rhs_operand" "rM")])) + (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "bic%?\\t%0, %1, %2%S4" + [(set_attr "predicable" "yes") + (set_attr "shift" "2") + ] +) + +(define_insn "*andsi_notsi_si_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (and:SI (not:SI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand:SI 1 "s_register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (and:SI (not:SI (match_dup 2)) (match_dup 1)))] + "TARGET_ARM" + "bic%?s\\t%0, %1, %2" + [(set_attr "conds" "set")] +) + +(define_insn "*andsi_notsi_si_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (and:SI (not:SI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand:SI 1 "s_register_operand" "r")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "bic%?s\\t%0, %1, %2" + [(set_attr "conds" "set")] +) + +(define_insn "iordi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (ior:DI (match_operand:DI 1 "s_register_operand" "%0,r") + (match_operand:DI 2 "s_register_operand" "r,r")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_insn "*iordi_zesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (ior:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "0,?r")))] + "TARGET_ARM" + "@ + orr%?\\t%Q0, %Q1, %2 + #" + [(set_attr "length" "4,8") + (set_attr "predicable" "yes")] +) + +(define_insn "*iordi_sesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (ior:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_expand "iorsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (ior:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "reg_or_int_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[2]) == CONST_INT) + { + if (TARGET_ARM) + { + arm_split_constant (IOR, SImode, INTVAL (operands[2]), operands[0], + operands[1], + (no_new_pseudos + ? 0 : preserve_subexpressions_p ())); + DONE; + } + else /* TARGET_THUMB */ + operands [2] = force_reg (SImode, operands [2]); + } + " +) + +(define_insn_and_split "*arm_iorsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (ior:SI (match_operand:SI 1 "s_register_operand" "r,r") + (match_operand:SI 2 "reg_or_int_operand" "rI,?n")))] + "TARGET_ARM" + "@ + orr%?\\t%0, %1, %2 + #" + "TARGET_ARM + && GET_CODE (operands[2]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[2]))" + [(clobber (const_int 0))] + " + arm_split_constant (IOR, SImode, INTVAL (operands[2]), operands[0], + operands[1], 0); + DONE; + " + [(set_attr "length" "4,16") + (set_attr "predicable" "yes")] +) + +(define_insn "*thumb_iorsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (ior:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "l")))] + "TARGET_THUMB" + "orr\\t%0, %0, %2" + [(set_attr "length" "2")] +) + +(define_peephole2 + [(match_scratch:SI 3 "r") + (set (match_operand:SI 0 "s_register_operand" "") + (ior:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "const_int_operand" "")))] + "TARGET_ARM + && !const_ok_for_arm (INTVAL (operands[2])) + && const_ok_for_arm (~INTVAL (operands[2]))" + [(set (match_dup 3) (match_dup 2)) + (set (match_dup 0) (ior:SI (match_dup 1) (match_dup 3)))] + "" +) + +(define_insn "*iorsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (ior:SI (match_operand:SI 1 "s_register_operand" "%r") + (match_operand:SI 2 "arm_rhs_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (ior:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "orr%?s\\t%0, %1, %2" + [(set_attr "conds" "set")] +) + +(define_insn "*iorsi3_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (ior:SI (match_operand:SI 1 "s_register_operand" "%r") + (match_operand:SI 2 "arm_rhs_operand" "rI")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "orr%?s\\t%0, %1, %2" + [(set_attr "conds" "set")] +) + +(define_insn "xordi3" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (xor:DI (match_operand:DI 1 "s_register_operand" "%0,r") + (match_operand:DI 2 "s_register_operand" "r,r")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_insn "*xordi_zesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (xor:DI (zero_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "0,?r")))] + "TARGET_ARM" + "@ + eor%?\\t%Q0, %Q1, %2 + #" + [(set_attr "length" "4,8") + (set_attr "predicable" "yes")] +) + +(define_insn "*xordi_sesidi_di" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (xor:DI (sign_extend:DI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_expand "xorsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (xor:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "arm_rhs_operand" "")))] + "TARGET_EITHER" + "if (TARGET_THUMB) + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = force_reg (SImode, operands[2]); + " +) + +(define_insn "*arm_xorsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (xor:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")))] + "TARGET_ARM" + "eor%?\\t%0, %1, %2" + [(set_attr "predicable" "yes")] +) + +(define_insn "*thumb_xorsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (xor:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "l")))] + "TARGET_THUMB" + "eor\\t%0, %0, %2" + [(set_attr "length" "2")] +) + +(define_insn "*xorsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (xor:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (xor:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM" + "eor%?s\\t%0, %1, %2" + [(set_attr "conds" "set")] +) + +(define_insn "*xorsi3_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (xor:SI (match_operand:SI 0 "s_register_operand" "r") + (match_operand:SI 1 "arm_rhs_operand" "rI")) + (const_int 0)))] + "TARGET_ARM" + "teq%?\\t%0, %1" + [(set_attr "conds" "set")] +) + +; By splitting (IOR (AND (NOT A) (NOT B)) C) as D = AND (IOR A B) (NOT C), +; (NOT D) we can sometimes merge the final NOT into one of the following +; insns. + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ior:SI (and:SI (not:SI (match_operand:SI 1 "s_register_operand" "r")) + (not:SI (match_operand:SI 2 "arm_rhs_operand" "rI"))) + (match_operand:SI 3 "arm_rhs_operand" "rI"))) + (clobber (match_operand:SI 4 "s_register_operand" "=r"))] + "TARGET_ARM" + [(set (match_dup 4) (and:SI (ior:SI (match_dup 1) (match_dup 2)) + (not:SI (match_dup 3)))) + (set (match_dup 0) (not:SI (match_dup 4)))] + "" +) + +(define_insn "*andsi_iorsi3_notsi" + [(set (match_operand:SI 0 "s_register_operand" "=&r,&r,&r") + (and:SI (ior:SI (match_operand:SI 1 "s_register_operand" "r,r,0") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI")) + (not:SI (match_operand:SI 3 "arm_rhs_operand" "rI,rI,rI"))))] + "TARGET_ARM" + "orr%?\\t%0, %1, %2\;bic%?\\t%0, %0, %3" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + + + +;; Minimum and maximum insns + +(define_insn "smaxsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (smax:SI (match_operand:SI 1 "s_register_operand" "0,r,?r") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%1, %2\;movlt\\t%0, %2 + cmp\\t%1, %2\;movge\\t%0, %1 + cmp\\t%1, %2\;movge\\t%0, %1\;movlt\\t%0, %2" + [(set_attr "conds" "clob") + (set_attr "length" "8,8,12")] +) + +(define_insn "sminsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (smin:SI (match_operand:SI 1 "s_register_operand" "0,r,?r") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%1, %2\;movge\\t%0, %2 + cmp\\t%1, %2\;movlt\\t%0, %1 + cmp\\t%1, %2\;movlt\\t%0, %1\;movge\\t%0, %2" + [(set_attr "conds" "clob") + (set_attr "length" "8,8,12")] +) + +(define_insn "umaxsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (umax:SI (match_operand:SI 1 "s_register_operand" "0,r,?r") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%1, %2\;movcc\\t%0, %2 + cmp\\t%1, %2\;movcs\\t%0, %1 + cmp\\t%1, %2\;movcs\\t%0, %1\;movcc\\t%0, %2" + [(set_attr "conds" "clob") + (set_attr "length" "8,8,12")] +) + +(define_insn "uminsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (umin:SI (match_operand:SI 1 "s_register_operand" "0,r,?r") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%1, %2\;movcs\\t%0, %2 + cmp\\t%1, %2\;movcc\\t%0, %1 + cmp\\t%1, %2\;movcc\\t%0, %1\;movcs\\t%0, %2" + [(set_attr "conds" "clob") + (set_attr "length" "8,8,12")] +) + +(define_insn "*store_minmaxsi" + [(set (match_operand:SI 0 "memory_operand" "=m") + (match_operator:SI 3 "minmax_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "s_register_operand" "r")])) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + operands[3] = gen_rtx (minmax_code (operands[3]), SImode, operands[1], + operands[2]); + output_asm_insn (\"cmp\\t%1, %2\", operands); + output_asm_insn (\"str%d3\\t%1, %0\", operands); + output_asm_insn (\"str%D3\\t%2, %0\", operands); + return \"\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12") + (set_attr "type" "store1")] +) + +; Reject the frame pointer in operand[1], since reloading this after +; it has been eliminated can cause carnage. +(define_insn "*minmax_arithsi" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (match_operator:SI 4 "shiftable_operator" + [(match_operator:SI 5 "minmax_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")]) + (match_operand:SI 1 "s_register_operand" "0,?r")])) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM + && (GET_CODE (operands[1]) != REG + || (REGNO(operands[1]) != FRAME_POINTER_REGNUM + && REGNO(operands[1]) != ARG_POINTER_REGNUM))" + "* + { + enum rtx_code code = GET_CODE (operands[4]); + + operands[5] = gen_rtx (minmax_code (operands[5]), SImode, operands[2], + operands[3]); + output_asm_insn (\"cmp\\t%2, %3\", operands); + output_asm_insn (\"%i4%d5\\t%0, %1, %2\", operands); + if (which_alternative != 0 || operands[3] != const0_rtx + || (code != PLUS && code != MINUS && code != IOR && code != XOR)) + output_asm_insn (\"%i4%D5\\t%0, %1, %3\", operands); + return \"\"; + }" + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + + +;; Shift and rotation insns + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (ashift:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "arm_rhs_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[2]) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31) + { + emit_insn (gen_movsi (operands[0], const0_rtx)); + DONE; + } + " +) + +(define_insn "*thumb_ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=l,l") + (ashift:SI (match_operand:SI 1 "register_operand" "l,0") + (match_operand:SI 2 "nonmemory_operand" "N,l")))] + "TARGET_THUMB" + "lsl\\t%0, %1, %2" + [(set_attr "length" "2")] +) + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (ashiftrt:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "arm_rhs_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[2]) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31) + operands[2] = GEN_INT (31); + " +) + +(define_insn "*thumb_ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=l,l") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "l,0") + (match_operand:SI 2 "nonmemory_operand" "N,l")))] + "TARGET_THUMB" + "asr\\t%0, %1, %2" + [(set_attr "length" "2")] +) + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (lshiftrt:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "arm_rhs_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[2]) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31) + { + emit_insn (gen_movsi (operands[0], const0_rtx)); + DONE; + } + " +) + +(define_insn "*thumb_lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=l,l") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "l,0") + (match_operand:SI 2 "nonmemory_operand" "N,l")))] + "TARGET_THUMB" + "lsr\\t%0, %1, %2" + [(set_attr "length" "2")] +) + +(define_expand "rotlsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (rotatert:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "reg_or_int_operand" "")))] + "TARGET_ARM" + " + if (GET_CODE (operands[2]) == CONST_INT) + operands[2] = GEN_INT ((32 - INTVAL (operands[2])) % 32); + else + { + rtx reg = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (reg, GEN_INT (32), operands[2])); + operands[2] = reg; + } + " +) + +(define_expand "rotrsi3" + [(set (match_operand:SI 0 "s_register_operand" "") + (rotatert:SI (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 2 "arm_rhs_operand" "")))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (GET_CODE (operands[2]) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31) + operands[2] = GEN_INT (INTVAL (operands[2]) % 32); + } + else /* TARGET_THUMB */ + { + if (GET_CODE (operands [2]) == CONST_INT) + operands [2] = force_reg (SImode, operands[2]); + } + " +) + +(define_insn "*thumb_rotrsi3" + [(set (match_operand:SI 0 "register_operand" "=l") + (rotatert:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "l")))] + "TARGET_THUMB" + "ror\\t%0, %0, %2" + [(set_attr "length" "2")] +) + +(define_insn "*arm_shiftsi3" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "reg_or_int_operand" "rM")]))] + "TARGET_ARM" + "mov%?\\t%0, %1%S3" + [(set_attr "predicable" "yes") + (set_attr "shift" "1") + ] +) + +(define_insn "*shiftsi3_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")]) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (match_op_dup 3 [(match_dup 1) (match_dup 2)]))] + "TARGET_ARM" + "mov%?s\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*shiftsi3_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")]) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "mov%?s\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*notsi_shiftsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")])))] + "TARGET_ARM" + "mvn%?\\t%0, %1%S3" + [(set_attr "predicable" "yes") + (set_attr "shift" "1") + ] +) + +(define_insn "*notsi_shiftsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (not:SI (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")])) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI (match_op_dup 3 [(match_dup 1) (match_dup 2)])))] + "TARGET_ARM" + "mvn%?s\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*not_shiftsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (not:SI (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")])) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "mvn%?s\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +;; We don't really have extzv, but defining this using shifts helps +;; to reduce register pressure later on. + +(define_expand "extzv" + [(set (match_dup 4) + (ashift:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" ""))) + (set (match_operand:SI 0 "register_operand" "") + (lshiftrt:SI (match_dup 4) + (match_operand:SI 3 "const_int_operand" "")))] + "TARGET_THUMB" + " + { + HOST_WIDE_INT lshift = 32 - INTVAL (operands[2]) - INTVAL (operands[3]); + HOST_WIDE_INT rshift = 32 - INTVAL (operands[2]); + + operands[3] = GEN_INT (rshift); + + if (lshift == 0) + { + emit_insn (gen_lshrsi3 (operands[0], operands[1], operands[3])); + DONE; + } + + operands[2] = GEN_INT (lshift); + operands[4] = gen_reg_rtx (SImode); + }" +) + + +;; Unary arithmetic insns + +(define_expand "negdi2" + [(parallel + [(set (match_operand:DI 0 "s_register_operand" "") + (neg:DI (match_operand:DI 1 "s_register_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_EITHER" + " + if (TARGET_THUMB) + { + if (GET_CODE (operands[1]) != REG) + operands[1] = force_reg (SImode, operands[1]); + } + " +) + +;; The constraints here are to prevent a *partial* overlap (where %Q0 == %R1). +;; The second alternative is to allow the common case of a *full* overlap. +(define_insn "*arm_negdi2" + [(set (match_operand:DI 0 "s_register_operand" "=&r,r") + (neg:DI (match_operand:DI 1 "s_register_operand" "?r,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "rsbs\\t%Q0, %Q1, #0\;rsc\\t%R0, %R1, #0" + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*thumb_negdi2" + [(set (match_operand:DI 0 "register_operand" "=&l") + (neg:DI (match_operand:DI 1 "register_operand" "l"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_THUMB" + "mov\\t%R0, #0\;neg\\t%Q0, %Q1\;sbc\\t%R0, %R1" + [(set_attr "length" "6")] +) + +(define_expand "negsi2" + [(set (match_operand:SI 0 "s_register_operand" "") + (neg:SI (match_operand:SI 1 "s_register_operand" "")))] + "TARGET_EITHER" + "" +) + +(define_insn "*arm_negsi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (neg:SI (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "rsb%?\\t%0, %1, #0" + [(set_attr "predicable" "yes")] +) + +(define_insn "*thumb_negsi2" + [(set (match_operand:SI 0 "register_operand" "=l") + (neg:SI (match_operand:SI 1 "register_operand" "l")))] + "TARGET_THUMB" + "neg\\t%0, %1" + [(set_attr "length" "2")] +) + +(define_insn "negsf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (neg:SF (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "mnf%?s\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "negdf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (neg:DF (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "mnf%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "*negdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (neg:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "mnf%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "negxf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (neg:XF (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "mnf%?e\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +;; abssi2 doesn't really clobber the condition codes if a different register +;; is being set. To keep things simple, assume during rtl manipulations that +;; it does, but tell the final scan operator the truth. Similarly for +;; (neg (abs...)) + +(define_insn "abssi2" + [(set (match_operand:SI 0 "s_register_operand" "=r,&r") + (abs:SI (match_operand:SI 1 "s_register_operand" "0,r"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%0, #0\;rsblt\\t%0, %0, #0 + eor%?\\t%0, %1, %1, asr #31\;sub%?\\t%0, %0, %1, asr #31" + [(set_attr "conds" "clob,*") + (set_attr "shift" "1") + ;; predicable can't be set based on the variant, so left as no + (set_attr "length" "8")] +) + +(define_insn "*neg_abssi2" + [(set (match_operand:SI 0 "s_register_operand" "=r,&r") + (neg:SI (abs:SI (match_operand:SI 1 "s_register_operand" "0,r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "@ + cmp\\t%0, #0\;rsbgt\\t%0, %0, #0 + eor%?\\t%0, %1, %1, asr #31\;rsb%?\\t%0, %0, %1, asr #31" + [(set_attr "conds" "clob,*") + (set_attr "shift" "1") + ;; predicable can't be set based on the variant, so left as no + (set_attr "length" "8")] +) + +(define_insn "abssf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (abs:SF (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "abs%?s\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "absdf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (abs:DF (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "abs%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "*absdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (abs:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "abs%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "absxf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (abs:XF (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "abs%?e\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "sqrtsf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (sqrt:SF (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "sqt%?s\\t%0, %1" + [(set_attr "type" "float_em") + (set_attr "predicable" "yes")] +) + +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (sqrt:DF (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "sqt%?d\\t%0, %1" + [(set_attr "type" "float_em") + (set_attr "predicable" "yes")] +) + +(define_insn "*sqrtdf_esfdf" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (sqrt:DF (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "sqt%?d\\t%0, %1" + [(set_attr "type" "float_em") + (set_attr "predicable" "yes")] +) + +(define_insn "sqrtxf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (sqrt:XF (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "sqt%?e\\t%0, %1" + [(set_attr "type" "float_em") + (set_attr "predicable" "yes")] +) + +;; SIN COS TAN and family are always emulated, so it's probably better +;; to always call a library function. +;(define_insn "sinsf2" +; [(set (match_operand:SF 0 "s_register_operand" "=f") +; (unspec:SF [(match_operand:SF 1 "s_register_operand" "f")] +; UNSPEC_SIN))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "sin%?s\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "sindf2" +; [(set (match_operand:DF 0 "s_register_operand" "=f") +; (unspec:DF [(match_operand:DF 1 "s_register_operand" "f")] +; UNSPEC_SIN))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "sin%?d\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "*sindf_esfdf" +; [(set (match_operand:DF 0 "s_register_operand" "=f") +; (unspec:DF [(float_extend:DF +; (match_operand:SF 1 "s_register_operand" "f"))] +; UNSPEC_SIN))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "sin%?d\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "sinxf2" +; [(set (match_operand:XF 0 "s_register_operand" "=f") +; (unspec:XF [(match_operand:XF 1 "s_register_operand" "f")] +; UNSPEC_SIN))] +; "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" +; "sin%?e\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "cossf2" +; [(set (match_operand:SF 0 "s_register_operand" "=f") +; (unspec:SF [(match_operand:SF 1 "s_register_operand" "f")] +; UNSPEC_COS))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "cos%?s\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "cosdf2" +; [(set (match_operand:DF 0 "s_register_operand" "=f") +; (unspec:DF [(match_operand:DF 1 "s_register_operand" "f")] +; UNSPEC_COS))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "cos%?d\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "*cosdf_esfdf" +; [(set (match_operand:DF 0 "s_register_operand" "=f") +; (unspec:DF [(float_extend:DF +; (match_operand:SF 1 "s_register_operand" "f"))] +; UNSPEC_COS))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "cos%?d\\t%0, %1" +;[(set_attr "type" "float_em")]) +; +;(define_insn "cosxf2" +; [(set (match_operand:XF 0 "s_register_operand" "=f") +; (unspec:XF [(match_operand:XF 1 "s_register_operand" "f")] +; UNSEPC_COS))] +; "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" +; "cos%?e\\t%0, %1" +;[(set_attr "type" "float_em")]) + +(define_insn_and_split "one_cmpldi2" + [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") + (not:DI (match_operand:DI 1 "s_register_operand" "?r,0")))] + "TARGET_ARM" + "#" + "TARGET_ARM && reload_completed" + [(set (match_dup 0) (not:SI (match_dup 1))) + (set (match_dup 2) (not:SI (match_dup 3)))] + " + { + operands[2] = gen_highpart (SImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + }" + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_expand "one_cmplsi2" + [(set (match_operand:SI 0 "s_register_operand" "") + (not:SI (match_operand:SI 1 "s_register_operand" "")))] + "TARGET_EITHER" + "" +) + +(define_insn "*arm_one_cmplsi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "mvn%?\\t%0, %1" + [(set_attr "predicable" "yes")] +) + +(define_insn "*thumb_one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=l") + (not:SI (match_operand:SI 1 "register_operand" "l")))] + "TARGET_THUMB" + "mvn\\t%0, %1" + [(set_attr "length" "2")] +) + +(define_insn "*notsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (not:SI (match_operand:SI 1 "s_register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI (match_dup 1)))] + "TARGET_ARM" + "mvn%?s\\t%0, %1" + [(set_attr "conds" "set")] +) + +(define_insn "*notsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (not:SI (match_operand:SI 1 "s_register_operand" "r")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "mvn%?s\\t%0, %1" + [(set_attr "conds" "set")] +) + +;; Fixed <--> Floating conversion insns + +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (float:SF (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "flt%?s\\t%0, %1" + [(set_attr "type" "r_2_f") + (set_attr "predicable" "yes")] +) + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (float:DF (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "flt%?d\\t%0, %1" + [(set_attr "type" "r_2_f") + (set_attr "predicable" "yes")] +) + +(define_insn "floatsixf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (float:XF (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "flt%?e\\t%0, %1" + [(set_attr "type" "r_2_f") + (set_attr "predicable" "yes")] +) + +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (fix:SI (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "fix%?z\\t%0, %1" + [(set_attr "type" "f_2_r") + (set_attr "predicable" "yes")] +) + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (fix:SI (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "fix%?z\\t%0, %1" + [(set_attr "type" "f_2_r") + (set_attr "predicable" "yes")] +) + +(define_insn "fix_truncxfsi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (fix:SI (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "fix%?z\\t%0, %1" + [(set_attr "type" "f_2_r") + (set_attr "predicable" "yes")] +) + +;; Truncation insns + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (float_truncate:SF + (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "mvf%?s\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "truncxfsf2" + [(set (match_operand:SF 0 "s_register_operand" "=f") + (float_truncate:SF + (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "mvf%?s\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "truncxfdf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (float_truncate:DF + (match_operand:XF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "mvf%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +;; Zero and sign extension instructions. + +(define_insn "zero_extendsidi2" + [(set (match_operand:DI 0 "s_register_operand" "=r") + (zero_extend:DI (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "* + if (REGNO (operands[1]) + != REGNO (operands[0]) + (WORDS_BIG_ENDIAN ? 1 : 0)) + output_asm_insn (\"mov%?\\t%Q0, %1\", operands); + return \"mov%?\\t%R0, #0\"; + " + [(set_attr "length" "8") + (set_attr "predicable" "yes")] +) + +(define_insn "zero_extendqidi2" + [(set (match_operand:DI 0 "s_register_operand" "=r,r") + (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] + "TARGET_ARM" + "@ + and%?\\t%Q0, %1, #255\;mov%?\\t%R0, #0 + ldr%?b\\t%Q0, %1\;mov%?\\t%R0, #0" + [(set_attr "length" "8") + (set_attr "predicable" "yes") + (set_attr "type" "*,load") + (set_attr "pool_range" "*,4092") + (set_attr "neg_pool_range" "*,4084")] +) + +(define_insn "extendsidi2" + [(set (match_operand:DI 0 "s_register_operand" "=r") + (sign_extend:DI (match_operand:SI 1 "s_register_operand" "r")))] + "TARGET_ARM" + "* + if (REGNO (operands[1]) + != REGNO (operands[0]) + (WORDS_BIG_ENDIAN ? 1 : 0)) + output_asm_insn (\"mov%?\\t%Q0, %1\", operands); + return \"mov%?\\t%R0, %Q0, asr #31\"; + " + [(set_attr "length" "8") + (set_attr "shift" "1") + (set_attr "predicable" "yes")] +) + +(define_expand "zero_extendhisi2" + [(set (match_dup 2) + (ashift:SI (match_operand:HI 1 "nonimmediate_operand" "") + (const_int 16))) + (set (match_operand:SI 0 "s_register_operand" "") + (lshiftrt:SI (match_dup 2) (const_int 16)))] + "TARGET_EITHER" + " + { + if (TARGET_ARM) + { + if (arm_arch4 && GET_CODE (operands[1]) == MEM) + { + /* Note: We do not have to worry about TARGET_MMU_TRAPS + here because the insn below will generate an LDRH instruction + rather than an LDR instruction, so we cannot get an unaligned + word access. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_ZERO_EXTEND (SImode, + operands[1]))); + DONE; + } + if (TARGET_MMU_TRAPS && GET_CODE (operands[1]) == MEM) + { + emit_insn (gen_movhi_bytes (operands[0], operands[1])); + DONE; + } + if (!s_register_operand (operands[1], HImode)) + operands[1] = copy_to_mode_reg (HImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_reg_rtx (SImode); + } + else /* TARGET_THUMB */ + { + if (GET_CODE (operands[1]) == MEM) + { + rtx tmp; + + tmp = gen_rtx_ZERO_EXTEND (SImode, operands[1]); + tmp = gen_rtx_SET (VOIDmode, operands[0], tmp); + emit_insn (tmp); + } + else + { + rtx ops[3]; + + if (!s_register_operand (operands[1], HImode)) + operands[1] = copy_to_mode_reg (HImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_reg_rtx (SImode); + + ops[0] = operands[2]; + ops[1] = operands[1]; + ops[2] = GEN_INT (16); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFT (SImode, ops[1], ops[2]))); + + ops[0] = operands[0]; + ops[1] = operands[2]; + ops[2] = GEN_INT (16); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_LSHIFTRT (SImode, ops[1], + ops[2]))); + } + DONE; + } + }" +) + +(define_insn "*thumb_zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=l") + (zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))] + "TARGET_THUMB" + "* + rtx mem = XEXP (operands[1], 0); + + if (GET_CODE (mem) == CONST) + mem = XEXP (mem, 0); + + if (GET_CODE (mem) == LABEL_REF) + return \"ldr\\t%0, %1\"; + + if (GET_CODE (mem) == PLUS) + { + rtx a = XEXP (mem, 0); + rtx b = XEXP (mem, 1); + + /* This can happen due to bugs in reload. */ + if (GET_CODE (a) == REG && REGNO (a) == SP_REGNUM) + { + rtx ops[2]; + ops[0] = operands[0]; + ops[1] = a; + + output_asm_insn (\"mov %0, %1\", ops); + + XEXP (mem, 0) = operands[0]; + } + + else if ( GET_CODE (a) == LABEL_REF + && GET_CODE (b) == CONST_INT) + return \"ldr\\t%0, %1\"; + } + + return \"ldrh\\t%0, %1\"; + " + [(set_attr "length" "4") + (set_attr "type" "load") + (set_attr "pool_range" "60")] +) + +(define_insn "*arm_zero_extendhisi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))] + "TARGET_ARM && arm_arch4" + "ldr%?h\\t%0, %1" + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "256") + (set_attr "neg_pool_range" "244")] +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (zero_extend:SI (match_operand:HI 1 "alignable_memory_operand" ""))) + (clobber (match_operand:SI 2 "s_register_operand" ""))] + "TARGET_ARM && (!arm_arch4)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (lshiftrt:SI (match_dup 2) (const_int 16)))] + " + if ((operands[1] = arm_gen_rotated_half_load (operands[1])) == NULL) + FAIL; + " +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operator:SI 3 "shiftable_operator" + [(zero_extend:SI (match_operand:HI 1 "alignable_memory_operand" "")) + (match_operand:SI 4 "s_register_operand" "")])) + (clobber (match_operand:SI 2 "s_register_operand" ""))] + "TARGET_ARM && (!arm_arch4)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) + (match_op_dup 3 + [(lshiftrt:SI (match_dup 2) (const_int 16)) (match_dup 4)]))] + " + if ((operands[1] = arm_gen_rotated_half_load (operands[1])) == NULL) + FAIL; + " +) + +(define_expand "zero_extendqisi2" + [(set (match_operand:SI 0 "s_register_operand" "") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))] + "TARGET_EITHER" + " + if (GET_CODE (operands[1]) != MEM) + { + if (TARGET_ARM) + { + emit_insn (gen_andsi3 (operands[0], + gen_lowpart (SImode, operands[1]), + GEN_INT (255))); + } + else /* TARGET_THUMB */ + { + rtx temp = gen_reg_rtx (SImode); + rtx ops[3]; + + operands[1] = copy_to_mode_reg (QImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + + ops[0] = temp; + ops[1] = operands[1]; + ops[2] = GEN_INT (24); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFT (SImode, ops[1], ops[2]))); + + ops[0] = operands[0]; + ops[1] = temp; + ops[2] = GEN_INT (24); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_LSHIFTRT (SImode, ops[1], ops[2]))); + } + DONE; + } + " +) + +(define_insn "*thumb_zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=l") + (zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))] + "TARGET_THUMB" + "ldrb\\t%0, %1" + [(set_attr "length" "2") + (set_attr "type" "load") + (set_attr "pool_range" "32")] +) + +(define_insn "*arm_zero_extendqisi2" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))] + "TARGET_ARM" + "ldr%?b\\t%0, %1\\t%@ zero_extendqisi2" + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "4096") + (set_attr "neg_pool_range" "4084")] +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (zero_extend:SI (subreg:QI (match_operand:SI 1 "" "") 0))) + (clobber (match_operand:SI 2 "s_register_operand" ""))] + "TARGET_ARM && (GET_CODE (operands[1]) != MEM)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (and:SI (match_dup 2) (const_int 255)))] + "" +) + +(define_insn "*compareqi_eq0" + [(set (reg:CC_Z CC_REGNUM) + (compare:CC_Z (match_operand:QI 0 "s_register_operand" "r") + (const_int 0)))] + "TARGET_ARM" + "tst\\t%0, #255" + [(set_attr "conds" "set")] +) + +(define_expand "extendhisi2" + [(set (match_dup 2) + (ashift:SI (match_operand:HI 1 "nonimmediate_operand" "") + (const_int 16))) + (set (match_operand:SI 0 "s_register_operand" "") + (ashiftrt:SI (match_dup 2) + (const_int 16)))] + "TARGET_EITHER" + " + { + if (TARGET_ARM && arm_arch4 && GET_CODE (operands[1]) == MEM) + { + /* Note: We do not have to worry about TARGET_MMU_TRAPS + here because the insn below will generate an LDRH instruction + rather than an LDR instruction, so we cannot get an unaligned + word access. */ + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_SIGN_EXTEND (SImode, operands[1]))); + DONE; + } + + if (TARGET_ARM && TARGET_MMU_TRAPS && GET_CODE (operands[1]) == MEM) + { + emit_insn (gen_extendhisi2_mem (operands[0], operands[1])); + DONE; + } + if (!s_register_operand (operands[1], HImode)) + operands[1] = copy_to_mode_reg (HImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_reg_rtx (SImode); + + if (TARGET_THUMB) + { + rtx ops[3]; + + ops[0] = operands[2]; + ops[1] = operands[1]; + ops[2] = GEN_INT (16); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFT (SImode, ops[1], ops[2]))); + + ops[0] = operands[0]; + ops[1] = operands[2]; + ops[2] = GEN_INT (16); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFTRT (SImode, ops[1], ops[2]))); + + DONE; + } + }" +) + +(define_insn "*thumb_extendhisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=l") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "m"))) + (clobber (match_scratch:SI 2 "=&l"))] + "TARGET_THUMB" + "* + { + rtx ops[4]; + rtx mem = XEXP (operands[1], 0); + + /* This code used to try to use 'V', and fix the address only if it was + offsettable, but this fails for e.g. REG+48 because 48 is outside the + range of QImode offsets, and offsettable_address_p does a QImode + address check. */ + + if (GET_CODE (mem) == CONST) + mem = XEXP (mem, 0); + + if (GET_CODE (mem) == LABEL_REF) + return \"ldr\\t%0, %1\"; + + if (GET_CODE (mem) == PLUS) + { + rtx a = XEXP (mem, 0); + rtx b = XEXP (mem, 1); + + if (GET_CODE (a) == LABEL_REF + && GET_CODE (b) == CONST_INT) + return \"ldr\\t%0, %1\"; + + if (GET_CODE (b) == REG) + return \"ldrsh\\t%0, %1\"; + + ops[1] = a; + ops[2] = b; + } + else + { + ops[1] = mem; + ops[2] = const0_rtx; + } + + if (GET_CODE (ops[1]) != REG) + { + debug_rtx (ops[1]); + abort (); + } + + ops[0] = operands[0]; + ops[3] = operands[2]; + output_asm_insn (\"mov\\t%3, %2\;ldrsh\\t%0, [%1, %3]\", ops); + return \"\"; + }" + [(set_attr "length" "4") + (set_attr "type" "load") + (set_attr "pool_range" "1020")] +) + +(define_expand "extendhisi2_mem" + [(set (match_dup 2) (zero_extend:SI (match_operand:HI 1 "" ""))) + (set (match_dup 3) + (zero_extend:SI (match_dup 7))) + (set (match_dup 6) (ashift:SI (match_dup 4) (const_int 24))) + (set (match_operand:SI 0 "" "") + (ior:SI (ashiftrt:SI (match_dup 6) (const_int 16)) (match_dup 5)))] + "TARGET_ARM" + " + { + rtx mem1, mem2; + rtx addr = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); + + mem1 = gen_rtx_MEM (QImode, addr); + MEM_COPY_ATTRIBUTES (mem1, operands[1]); + mem2 = gen_rtx_MEM (QImode, plus_constant (addr, 1)); + MEM_COPY_ATTRIBUTES (mem2, operands[1]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = mem1; + operands[2] = gen_reg_rtx (SImode); + operands[3] = gen_reg_rtx (SImode); + operands[6] = gen_reg_rtx (SImode); + operands[7] = mem2; + + if (BYTES_BIG_ENDIAN) + { + operands[4] = operands[2]; + operands[5] = operands[3]; + } + else + { + operands[4] = operands[3]; + operands[5] = operands[2]; + } + }" +) + +(define_insn "*arm_extendhisi_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))] + "TARGET_ARM && arm_arch4" + "ldr%?sh\\t%0, %1" + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "256") + (set_attr "neg_pool_range" "244")] +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (sign_extend:SI (match_operand:HI 1 "alignable_memory_operand" ""))) + (clobber (match_operand:SI 2 "s_register_operand" ""))] + "TARGET_ARM && (!arm_arch4)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (ashiftrt:SI (match_dup 2) (const_int 16)))] + " + if ((operands[1] = arm_gen_rotated_half_load (operands[1])) == NULL) + FAIL; + " +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operator:SI 3 "shiftable_operator" + [(sign_extend:SI (match_operand:HI 1 "alignable_memory_operand" "")) + (match_operand:SI 4 "s_register_operand" "")])) + (clobber (match_operand:SI 2 "s_register_operand" ""))] + "TARGET_ARM && (!arm_arch4)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) + (match_op_dup 3 + [(ashiftrt:SI (match_dup 2) (const_int 16)) (match_dup 4)]))] + "if ((operands[1] = arm_gen_rotated_half_load (operands[1])) == NULL) + FAIL; + " +) + +(define_expand "extendqihi2" + [(set (match_dup 2) + (ashift:SI (match_operand:QI 1 "general_operand" "") + (const_int 24))) + (set (match_operand:HI 0 "s_register_operand" "") + (ashiftrt:SI (match_dup 2) + (const_int 24)))] + "TARGET_ARM" + " + { + if (arm_arch4 && GET_CODE (operands[1]) == MEM) + { + emit_insn (gen_rtx_SET (VOIDmode, + operands[0], + gen_rtx_SIGN_EXTEND (HImode, operands[1]))); + DONE; + } + if (!s_register_operand (operands[1], QImode)) + operands[1] = copy_to_mode_reg (QImode, operands[1]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_reg_rtx (SImode); + }" +) + +; Rather than restricting all byte accesses to memory addresses that ldrsb +; can handle, we fix up the ones that ldrsb can't grok with a split. +(define_insn "*extendqihi_insn" + [(set (match_operand:HI 0 "s_register_operand" "=r") + (sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))] + "TARGET_ARM && arm_arch4" + "* + /* If the address is invalid, this will split the instruction into two. */ + if (bad_signed_byte_operand (operands[1], VOIDmode)) + return \"#\"; + return \"ldr%?sb\\t%0, %1\"; + " + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "length" "8") + (set_attr "pool_range" "256") + (set_attr "neg_pool_range" "244")] +) + +(define_split + [(set (match_operand:HI 0 "s_register_operand" "") + (sign_extend:HI (match_operand:QI 1 "bad_signed_byte_operand" "")))] + "TARGET_ARM && arm_arch4 && reload_completed" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 0) (sign_extend:HI (match_dup 2)))] + " + { + HOST_WIDE_INT offset; + + operands[3] = gen_rtx_REG (SImode, REGNO (operands[0])); + operands[2] = gen_rtx_MEM (QImode, operands[3]); + MEM_COPY_ATTRIBUTES (operands[2], operands[1]); + operands[1] = XEXP (operands[1], 0); + if (GET_CODE (operands[1]) == PLUS + && GET_CODE (XEXP (operands[1], 1)) == CONST_INT + && !(const_ok_for_arm (offset = INTVAL (XEXP (operands[1], 1))) + || const_ok_for_arm (-offset))) + { + HOST_WIDE_INT low = (offset > 0 + ? (offset & 0xff) : -((-offset) & 0xff)); + XEXP (operands[2], 0) = plus_constant (operands[3], low); + operands[1] = plus_constant (XEXP (operands[1], 0), offset - low); + } + /* Ensure the sum is in correct canonical form */ + else if (GET_CODE (operands[1]) == PLUS + && GET_CODE (XEXP (operands[1], 1)) != CONST_INT + && !s_register_operand (XEXP (operands[1], 1), VOIDmode)) + operands[1] = gen_rtx_PLUS (GET_MODE (operands[1]), + XEXP (operands[1], 1), + XEXP (operands[1], 0)); + }" +) + +(define_expand "extendqisi2" + [(set (match_dup 2) + (ashift:SI (match_operand:QI 1 "general_operand" "") + (const_int 24))) + (set (match_operand:SI 0 "s_register_operand" "") + (ashiftrt:SI (match_dup 2) + (const_int 24)))] + "TARGET_EITHER" + " + { + if (TARGET_ARM && arm_arch4 && GET_CODE (operands[1]) == MEM) + { + emit_insn (gen_rtx_SET (VOIDmode, + operands[0], + gen_rtx_SIGN_EXTEND (SImode, operands[1]))); + DONE; + } + if (!s_register_operand (operands[1], QImode)) + operands[1] = copy_to_mode_reg (QImode, operands[1]); + operands[1] = gen_lowpart (SImode, operands[1]); + operands[2] = gen_reg_rtx (SImode); + + if (TARGET_THUMB) + { + rtx ops[3]; + + ops[0] = operands[2]; + ops[1] = operands[1]; + ops[2] = GEN_INT (24); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFT (SImode, ops[1], ops[2]))); + + ops[0] = operands[0]; + ops[1] = operands[2]; + ops[2] = GEN_INT (24); + + emit_insn (gen_rtx_SET (VOIDmode, ops[0], + gen_rtx_ASHIFTRT (SImode, ops[1], ops[2]))); + + DONE; + } + }" +) + +; Rather than restricting all byte accesses to memory addresses that ldrsb +; can handle, we fix up the ones that ldrsb can't grok with a split. +(define_insn "*arm_extendqisi_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))] + "TARGET_ARM && arm_arch4" + "* + /* If the address is invalid, this will split the instruction into two. */ + if (bad_signed_byte_operand (operands[1], VOIDmode)) + return \"#\"; + return \"ldr%?sb\\t%0, %1\"; + " + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "length" "8") + (set_attr "pool_range" "256") + (set_attr "neg_pool_range" "244")] +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (sign_extend:SI (match_operand:QI 1 "bad_signed_byte_operand" "")))] + "TARGET_ARM && arm_arch4 && reload_completed" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (sign_extend:SI (match_dup 2)))] + " + { + HOST_WIDE_INT offset; + + operands[2] = gen_rtx_MEM (QImode, operands[0]); + MEM_COPY_ATTRIBUTES (operands[2], operands[1]); + operands[1] = XEXP (operands[1], 0); + if (GET_CODE (operands[1]) == PLUS + && GET_CODE (XEXP (operands[1], 1)) == CONST_INT + && !(const_ok_for_arm (offset = INTVAL (XEXP (operands[1], 1))) + || const_ok_for_arm (-offset))) + { + HOST_WIDE_INT low = (offset > 0 + ? (offset & 0xff) : -((-offset) & 0xff)); + XEXP (operands[2], 0) = plus_constant (operands[0], low); + operands[1] = plus_constant (XEXP (operands[1], 0), offset - low); + } + /* Ensure the sum is in correct canonical form */ + else if (GET_CODE (operands[1]) == PLUS + && GET_CODE (XEXP (operands[1], 1)) != CONST_INT + && !s_register_operand (XEXP (operands[1], 1), VOIDmode)) + operands[1] = gen_rtx_PLUS (GET_MODE (operands[1]), + XEXP (operands[1], 1), + XEXP (operands[1], 0)); + }" +) + +(define_insn "*thumb_extendqisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=l,l") + (sign_extend:SI (match_operand:QI 1 "memory_operand" "V,m")))] + "TARGET_THUMB" + "* + { + rtx ops[3]; + rtx mem = XEXP (operands[1], 0); + + if (GET_CODE (mem) == CONST) + mem = XEXP (mem, 0); + + if (GET_CODE (mem) == LABEL_REF) + return \"ldr\\t%0, %1\"; + + if (GET_CODE (mem) == PLUS + && GET_CODE (XEXP (mem, 0)) == LABEL_REF) + return \"ldr\\t%0, %1\"; + + if (which_alternative == 0) + return \"ldrsb\\t%0, %1\"; + + ops[0] = operands[0]; + + if (GET_CODE (mem) == PLUS) + { + rtx a = XEXP (mem, 0); + rtx b = XEXP (mem, 1); + + ops[1] = a; + ops[2] = b; + + if (GET_CODE (a) == REG) + { + if (GET_CODE (b) == REG) + output_asm_insn (\"ldrsb\\t%0, [%1, %2]\", ops); + else if (REGNO (a) == REGNO (ops[0])) + { + output_asm_insn (\"ldrb\\t%0, [%1, %2]\", ops); + output_asm_insn (\"lsl\\t%0, %0, #24\", ops); + output_asm_insn (\"asr\\t%0, %0, #24\", ops); + } + else + output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops); + } + else if (GET_CODE (b) != REG) + abort (); + else + { + if (REGNO (b) == REGNO (ops[0])) + { + output_asm_insn (\"ldrb\\t%0, [%2, %1]\", ops); + output_asm_insn (\"lsl\\t%0, %0, #24\", ops); + output_asm_insn (\"asr\\t%0, %0, #24\", ops); + } + else + output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops); + } + } + else if (GET_CODE (mem) == REG && REGNO (ops[0]) == REGNO (mem)) + { + output_asm_insn (\"ldrb\\t%0, [%0, #0]\", ops); + output_asm_insn (\"lsl\\t%0, %0, #24\", ops); + output_asm_insn (\"asr\\t%0, %0, #24\", ops); + } + else + { + ops[1] = mem; + ops[2] = const0_rtx; + + output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops); + } + return \"\"; + }" + [(set_attr "length" "2,6") + (set_attr "type" "load,load") + (set_attr "pool_range" "32,32")] +) + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "s_register_operand" "=f") + (float_extend:DF (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "mvf%?d\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "extendsfxf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (float_extend:XF (match_operand:SF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "mvf%?e\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + +(define_insn "extenddfxf2" + [(set (match_operand:XF 0 "s_register_operand" "=f") + (float_extend:XF (match_operand:DF 1 "s_register_operand" "f")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "mvf%?e\\t%0, %1" + [(set_attr "type" "ffarith") + (set_attr "predicable" "yes")] +) + + +;; Move insns (including loads and stores) + +;; XXX Just some ideas about movti. +;; I don't think these are a good idea on the arm, there just aren't enough +;; registers +;;(define_expand "loadti" +;; [(set (match_operand:TI 0 "s_register_operand" "") +;; (mem:TI (match_operand:SI 1 "address_operand" "")))] +;; "" "") + +;;(define_expand "storeti" +;; [(set (mem:TI (match_operand:TI 0 "address_operand" "")) +;; (match_operand:TI 1 "s_register_operand" ""))] +;; "" "") + +;;(define_expand "movti" +;; [(set (match_operand:TI 0 "general_operand" "") +;; (match_operand:TI 1 "general_operand" ""))] +;; "" +;; " +;;{ +;; rtx insn; +;; +;; if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) +;; operands[1] = copy_to_reg (operands[1]); +;; if (GET_CODE (operands[0]) == MEM) +;; insn = gen_storeti (XEXP (operands[0], 0), operands[1]); +;; else if (GET_CODE (operands[1]) == MEM) +;; insn = gen_loadti (operands[0], XEXP (operands[1], 0)); +;; else +;; FAIL; +;; +;; emit_insn (insn); +;; DONE; +;;}") + +;; Recognise garbage generated above. + +;;(define_insn "" +;; [(set (match_operand:TI 0 "general_operand" "=r,r,r,<,>,m") +;; (match_operand:TI 1 "general_operand" "<,>,m,r,r,r"))] +;; "" +;; "* +;; { +;; register mem = (which_alternative < 3); +;; register const char *template; +;; +;; operands[mem] = XEXP (operands[mem], 0); +;; switch (which_alternative) +;; { +;; case 0: template = \"ldmdb\\t%1!, %M0\"; break; +;; case 1: template = \"ldmia\\t%1!, %M0\"; break; +;; case 2: template = \"ldmia\\t%1, %M0\"; break; +;; case 3: template = \"stmdb\\t%0!, %M1\"; break; +;; case 4: template = \"stmia\\t%0!, %M1\"; break; +;; case 5: template = \"stmia\\t%0, %M1\"; break; +;; } +;; output_asm_insn (template, operands); +;; return \"\"; +;; }") + +(define_expand "movdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_THUMB) + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (DImode, operands[1]); + } + } + " +) + +(define_insn "*arm_movdi" + [(set (match_operand:DI 0 "nonimmediate_di_operand" "=r, r, o<>") + (match_operand:DI 1 "di_operand" "rIK,mi,r"))] + "TARGET_ARM" + "* + return (output_move_double (operands)); + " + [(set_attr "length" "8") + (set_attr "type" "*,load,store2") + (set_attr "pool_range" "*,1020,*") + (set_attr "neg_pool_range" "*,1012,*")] +) + +;;; ??? This should have alternatives for constants. +;;; ??? This was originally identical to the movdf_insn pattern. +;;; ??? The 'i' constraint looks funny, but it should always be replaced by +;;; thumb_reorg with a memory reference. +(define_insn "*thumb_movdi_insn" + [(set (match_operand:DI 0 "nonimmediate_operand" "=l,l,l,l,>,l, m,*r") + (match_operand:DI 1 "general_operand" "l, I,J,>,l,mi,l,*r"))] + "TARGET_THUMB + && ( register_operand (operands[0], DImode) + || register_operand (operands[1], DImode))" + "* + { + switch (which_alternative) + { + default: + case 0: + if (REGNO (operands[1]) == REGNO (operands[0]) + 1) + return \"add\\t%0, %1, #0\;add\\t%H0, %H1, #0\"; + return \"add\\t%H0, %H1, #0\;add\\t%0, %1, #0\"; + case 1: + return \"mov\\t%Q0, %1\;mov\\t%R0, #0\"; + case 2: + operands[1] = GEN_INT (- INTVAL (operands[1])); + return \"mov\\t%Q0, %1\;neg\\t%Q0, %Q0\;asr\\t%R0, %Q0, #31\"; + case 3: + return \"ldmia\\t%1, {%0, %H0}\"; + case 4: + return \"stmia\\t%0, {%1, %H1}\"; + case 5: + return thumb_load_double_from_address (operands); + case 6: + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[0], 0), 4)); + output_asm_insn (\"str\\t%1, %0\;str\\t%H1, %2\", operands); + return \"\"; + case 7: + if (REGNO (operands[1]) == REGNO (operands[0]) + 1) + return \"mov\\t%0, %1\;mov\\t%H0, %H1\"; + return \"mov\\t%H0, %H1\;mov\\t%0, %1\"; + } + }" + [(set_attr "length" "4,4,6,2,2,6,4,4") + (set_attr "type" "*,*,*,load,store2,load,store2,*") + (set_attr "pool_range" "*,*,*,*,*,1020,*,*")] +) + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + /* Everything except mem = const or mem = mem can be done easily */ + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (SImode, operands[1]); + if (GET_CODE (operands[1]) == CONST_INT + && !(const_ok_for_arm (INTVAL (operands[1])) + || const_ok_for_arm (~INTVAL (operands[1])))) + { + arm_split_constant (SET, SImode, INTVAL (operands[1]), operands[0], + NULL_RTX, + (no_new_pseudos ? 0 + : preserve_subexpressions_p ())); + DONE; + } + } + else /* TARGET_THUMB.... */ + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (SImode, operands[1]); + } + } + + if (flag_pic + && (CONSTANT_P (operands[1]) + || symbol_mentioned_p (operands[1]) + || label_mentioned_p (operands[1]))) + operands[1] = legitimize_pic_address (operands[1], SImode, + (no_new_pseudos ? operands[0] : 0)); + " +) + +(define_insn "*arm_movsi_insn" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r, m") + (match_operand:SI 1 "general_operand" "rI,K,mi,r"))] + "TARGET_ARM + && ( register_operand (operands[0], SImode) + || register_operand (operands[1], SImode))" + "@ + mov%?\\t%0, %1 + mvn%?\\t%0, #%B1 + ldr%?\\t%0, %1 + str%?\\t%1, %0" + [(set_attr "type" "*,*,load,store1") + (set_attr "predicable" "yes") + (set_attr "pool_range" "*,*,4096,*") + (set_attr "neg_pool_range" "*,*,4084,*")] +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "TARGET_ARM + && (!(const_ok_for_arm (INTVAL (operands[1])) + || const_ok_for_arm (~INTVAL (operands[1]))))" + [(clobber (const_int 0))] + " + arm_split_constant (SET, SImode, INTVAL (operands[1]), operands[0], + NULL_RTX, 0); + DONE; + " +) + +(define_insn "*thumb_movsi_insn" + [(set (match_operand:SI 0 "nonimmediate_operand" "=l,l,l,l,l,>,l, m,*lh") + (match_operand:SI 1 "general_operand" "l, I,J,K,>,l,mi,l,*lh"))] + "TARGET_THUMB + && ( register_operand (operands[0], SImode) + || register_operand (operands[1], SImode))" + "@ + mov %0, %1 + mov %0, %1 + # + # + ldmia\\t%1, {%0} + stmia\\t%0, {%1} + ldr\\t%0, %1 + str\\t%1, %0 + mov\\t%0, %1" + [(set_attr "length" "2,2,4,4,2,2,2,2,2") + (set_attr "type" "*,*,*,*,load,store1,load,store1,*") + (set_attr "pool_range" "*,*,*,*,*,*,1020,*,*")] +) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "TARGET_THUMB && CONST_OK_FOR_THUMB_LETTER (INTVAL (operands[1]), 'J')" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (neg:SI (match_dup 0)))] + "operands[1] = GEN_INT (- INTVAL (operands[1]));" +) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "TARGET_THUMB && CONST_OK_FOR_THUMB_LETTER (INTVAL (operands[1]), 'K')" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))] + " + { + unsigned HOST_WIDE_INT val = INTVAL (operands[1]); + unsigned HOST_WIDE_INT mask = 0xff; + int i; + + for (i = 0; i < 25; i++) + if ((val & (mask << i)) == val) + break; + + if (i == 0) + FAIL; + + operands[1] = GEN_INT (val >> i); + operands[2] = GEN_INT (i); + }" +) + +(define_expand "movaddr" + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operand:DI 1 "address_operand" ""))] + "TARGET_ARM" + "" +) + +(define_insn "*movaddr_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operand:DI 1 "address_operand" "p"))] + "TARGET_ARM + && reload_completed + && (GET_CODE (operands[1]) == LABEL_REF + || (GET_CODE (operands[1]) == CONST + && GET_CODE (XEXP (operands[1], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT))" + "adr%?\\t%0, %a1" + [(set_attr "predicable" "yes")] +) + +;; When generating pic, we need to load the symbol offset into a register. +;; So that the optimizer does not confuse this with a normal symbol load +;; we use an unspec. The offset will be loaded from a constant pool entry, +;; since that is the only type of relocation we can use. + +;; The rather odd constraints on the following are to force reload to leave +;; the insn alone, and to force the minipool generation pass to then move +;; the GOT symbol to memory. + +(define_insn "pic_load_addr_arm" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "mX")] UNSPEC_PIC_SYM))] + "TARGET_ARM && flag_pic" + "ldr%?\\t%0, %1" + [(set_attr "type" "load") + (set (attr "pool_range") (const_int 4096)) + (set (attr "neg_pool_range") (const_int 4084))] +) + +(define_insn "pic_load_addr_thumb" + [(set (match_operand:SI 0 "s_register_operand" "=l") + (unspec:SI [(match_operand:SI 1 "" "mX")] UNSPEC_PIC_SYM))] + "TARGET_THUMB && flag_pic" + "ldr\\t%0, %1" + [(set_attr "type" "load") + (set (attr "pool_range") (const_int 1024))] +) + +;; This variant is used for AOF assembly, since it needs to mention the +;; pic register in the rtl. +(define_expand "pic_load_addr_based" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unspec:SI [(match_operand 1 "" "") (match_dup 2)] UNSPEC_PIC_SYM))] + "TARGET_ARM && flag_pic" + "operands[2] = pic_offset_table_rtx;" +) + +(define_insn "*pic_load_addr_based_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unspec:SI [(match_operand 1 "" "") + (match_operand 2 "s_register_operand" "r")] + UNSPEC_PIC_SYM))] + "TARGET_EITHER && flag_pic && operands[2] == pic_offset_table_rtx" + "* +#ifdef AOF_ASSEMBLER + operands[1] = aof_pic_entry (operands[1]); +#endif + output_asm_insn (\"ldr%?\\t%0, %a1\", operands); + return \"\"; + " + [(set_attr "type" "load") + (set (attr "pool_range") + (if_then_else (eq_attr "is_thumb" "yes") + (const_int 1024) + (const_int 4096))) + (set (attr "neg_pool_range") + (if_then_else (eq_attr "is_thumb" "yes") + (const_int 0) + (const_int 4084)))] +) + +(define_insn "pic_add_dot_plus_four" + [(set (match_operand:SI 0 "register_operand" "+r") + (plus:SI (match_dup 0) (const (plus:SI (pc) (const_int 4))))) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_THUMB && flag_pic" + "* + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (operands[1])); + return \"add\\t%0, %|pc\"; + " + [(set_attr "length" "2")] +) + +(define_insn "pic_add_dot_plus_eight" + [(set (match_operand:SI 0 "register_operand" "+r") + (plus:SI (match_dup 0) (const (plus:SI (pc) (const_int 8))))) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_ARM && flag_pic" + "* + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", + CODE_LABEL_NUMBER (operands[1])); + return \"add%?\\t%0, %|pc, %0\"; + " + [(set_attr "predicable" "yes")] +) + +(define_expand "builtin_setjmp_receiver" + [(label_ref (match_operand 0 "" ""))] + "flag_pic" + " +{ + arm_finalize_pic (0); + DONE; +}") + +;; If copying one reg to another we can set the condition codes according to +;; its value. Such a move is common after a return from subroutine and the +;; result is being tested against zero. + +(define_insn "*movsi_compare0" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_operand:SI 1 "s_register_operand" "0,r") + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r,r") + (match_dup 1))] + "TARGET_ARM" + "@ + cmp%?\\t%0, #0 + sub%?s\\t%0, %1, #0" + [(set_attr "conds" "set")] +) + +;; Subroutine to store a half word from a register into memory. +;; Operand 0 is the source register (HImode) +;; Operand 1 is the destination address in a register (SImode) + +;; In both this routine and the next, we must be careful not to spill +;; a memory address of reg+large_const into a separate PLUS insn, since this +;; can generate unrecognizable rtl. + +(define_expand "storehi" + [;; store the low byte + (set (match_operand 1 "" "") (match_dup 3)) + ;; extract the high byte + (set (match_dup 2) + (ashiftrt:SI (match_operand 0 "" "") (const_int 8))) + ;; store the high byte + (set (match_dup 4) (subreg:QI (match_dup 2) 0))] ;explicit subreg safe + "TARGET_ARM" + " + { + rtx op1 = operands[1]; + rtx addr = XEXP (op1, 0); + enum rtx_code code = GET_CODE (addr); + + if ((code == PLUS && GET_CODE (XEXP (addr, 1)) != CONST_INT) + || code == MINUS) + op1 = replace_equiv_address (operands[1], force_reg (SImode, addr)); + + operands[4] = adjust_address (op1, QImode, 1); + operands[1] = adjust_address (operands[1], QImode, 0); + operands[3] = gen_lowpart (QImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[2] = gen_reg_rtx (SImode); + }" +) + +(define_expand "storehi_bigend" + [(set (match_dup 4) (match_dup 3)) + (set (match_dup 2) + (ashiftrt:SI (match_operand 0 "" "") (const_int 8))) + (set (match_operand 1 "" "") (subreg:QI (match_dup 2) 0))] + "TARGET_ARM" + " + { + rtx op1 = operands[1]; + rtx addr = XEXP (op1, 0); + enum rtx_code code = GET_CODE (addr); + + if ((code == PLUS && GET_CODE (XEXP (addr, 1)) != CONST_INT) + || code == MINUS) + op1 = replace_equiv_address (op1, force_reg (SImode, addr)); + + operands[4] = adjust_address (op1, QImode, 1); + operands[1] = adjust_address (operands[1], QImode, 0); + operands[3] = gen_lowpart (QImode, operands[0]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[2] = gen_reg_rtx (SImode); + }" +) + +;; Subroutine to store a half word integer constant into memory. +(define_expand "storeinthi" + [(set (match_operand 0 "" "") + (subreg:QI (match_operand 1 "" "") 0)) + (set (match_dup 3) (subreg:QI (match_dup 2) 0))] + "TARGET_ARM" + " + { + HOST_WIDE_INT value = INTVAL (operands[1]); + rtx addr = XEXP (operands[0], 0); + rtx op0 = operands[0]; + enum rtx_code code = GET_CODE (addr); + + if ((code == PLUS && GET_CODE (XEXP (addr, 1)) != CONST_INT) + || code == MINUS) + op0 = replace_equiv_address (op0, force_reg (SImode, addr)); + + operands[1] = gen_reg_rtx (SImode); + if (BYTES_BIG_ENDIAN) + { + emit_insn (gen_movsi (operands[1], GEN_INT ((value >> 8) & 255))); + if ((value & 255) == ((value >> 8) & 255)) + operands[2] = operands[1]; + else + { + operands[2] = gen_reg_rtx (SImode); + emit_insn (gen_movsi (operands[2], GEN_INT (value & 255))); + } + } + else + { + emit_insn (gen_movsi (operands[1], GEN_INT (value & 255))); + if ((value & 255) == ((value >> 8) & 255)) + operands[2] = operands[1]; + else + { + operands[2] = gen_reg_rtx (SImode); + emit_insn (gen_movsi (operands[2], GEN_INT ((value >> 8) & 255))); + } + } + + operands[3] = adjust_address (op0, QImode, 1); + operands[0] = adjust_address (operands[0], QImode, 0); + }" +) + +(define_expand "storehi_single_op" + [(set (match_operand:HI 0 "memory_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "TARGET_ARM && arm_arch4" + " + if (!s_register_operand (operands[1], HImode)) + operands[1] = copy_to_mode_reg (HImode, operands[1]); + " +) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) == MEM) + { + if (arm_arch4) + { + emit_insn (gen_storehi_single_op (operands[0], operands[1])); + DONE; + } + if (GET_CODE (operands[1]) == CONST_INT) + emit_insn (gen_storeinthi (operands[0], operands[1])); + else + { + if (GET_CODE (operands[1]) == MEM) + operands[1] = force_reg (HImode, operands[1]); + if (BYTES_BIG_ENDIAN) + emit_insn (gen_storehi_bigend (operands[1], operands[0])); + else + emit_insn (gen_storehi (operands[1], operands[0])); + } + DONE; + } + /* Sign extend a constant, and keep it in an SImode reg. */ + else if (GET_CODE (operands[1]) == CONST_INT) + { + rtx reg = gen_reg_rtx (SImode); + HOST_WIDE_INT val = INTVAL (operands[1]) & 0xffff; + + /* If the constant is already valid, leave it alone. */ + if (!const_ok_for_arm (val)) + { + /* If setting all the top bits will make the constant + loadable in a single instruction, then set them. + Otherwise, sign extend the number. */ + + if (const_ok_for_arm (~(val | ~0xffff))) + val |= ~0xffff; + else if (val & 0x8000) + val |= ~0xffff; + } + + emit_insn (gen_movsi (reg, GEN_INT (val))); + operands[1] = gen_rtx_SUBREG (HImode, reg, 0); + } + else if (!arm_arch4) + { + /* Note: We do not have to worry about TARGET_MMU_TRAPS + for v4 and up architectures because LDRH instructions will + be used to access the HI values, and these cannot generate + unaligned word access faults in the MMU. */ + if (GET_CODE (operands[1]) == MEM) + { + if (TARGET_MMU_TRAPS) + { + rtx base; + rtx offset = const0_rtx; + rtx reg = gen_reg_rtx (SImode); + + if ((GET_CODE (base = XEXP (operands[1], 0)) == REG + || (GET_CODE (base) == PLUS + && (GET_CODE (offset = XEXP (base, 1)) + == CONST_INT) + && ((INTVAL(offset) & 1) != 1) + && GET_CODE (base = XEXP (base, 0)) == REG)) + && REGNO_POINTER_ALIGN (REGNO (base)) >= 32) + { + HOST_WIDE_INT new_offset = INTVAL (offset) & ~3; + rtx new; + + new = gen_rtx_MEM (SImode, + plus_constant (base, new_offset)); + MEM_COPY_ATTRIBUTES (new, operands[1]); + emit_insn (gen_movsi (reg, new)); + if (((INTVAL (offset) & 2) != 0) + ^ (BYTES_BIG_ENDIAN ? 1 : 0)) + { + rtx reg2 = gen_reg_rtx (SImode); + + emit_insn (gen_lshrsi3 (reg2, reg, + GEN_INT (16))); + reg = reg2; + } + } + else + emit_insn (gen_movhi_bytes (reg, operands[1])); + + operands[1] = gen_lowpart (HImode, reg); + } + else if (BYTES_BIG_ENDIAN) + { + rtx base; + rtx offset = const0_rtx; + + if ((GET_CODE (base = XEXP (operands[1], 0)) == REG + || (GET_CODE (base) == PLUS + && (GET_CODE (offset = XEXP (base, 1)) + == CONST_INT) + && GET_CODE (base = XEXP (base, 0)) == REG)) + && REGNO_POINTER_ALIGN (REGNO (base)) >= 32) + { + rtx reg = gen_reg_rtx (SImode); + rtx new; + + if ((INTVAL (offset) & 2) == 2) + { + HOST_WIDE_INT new_offset = INTVAL (offset) ^ 2; + new = gen_rtx_MEM (SImode, + plus_constant (base, + new_offset)); + MEM_COPY_ATTRIBUTES (new, operands[1]); + emit_insn (gen_movsi (reg, new)); + } + else + { + new = gen_rtx_MEM (SImode, + XEXP (operands[1], 0)); + MEM_COPY_ATTRIBUTES (new, operands[1]); + emit_insn (gen_rotated_loadsi (reg, new)); + } + + operands[1] = gen_lowpart (HImode, reg); + } + else + { + emit_insn (gen_movhi_bigend (operands[0], + operands[1])); + DONE; + } + } + } + } + } + /* Handle loading a large integer during reload */ + else if (GET_CODE (operands[1]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[1])) + && !const_ok_for_arm (~INTVAL (operands[1]))) + { + /* Writing a constant to memory needs a scratch, which should + be handled with SECONDARY_RELOADs. */ + if (GET_CODE (operands[0]) != REG) + abort (); + + operands[0] = gen_rtx_SUBREG (SImode, operands[0], 0); + emit_insn (gen_movsi (operands[0], operands[1])); + DONE; + } + } + else /* TARGET_THUMB */ + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (HImode, operands[1]); + + /* ??? We shouldn't really get invalid addresses here, but this can + happen if we are passed a SP (never OK for HImode/QImode) or + virtual register (rejected by GO_IF_LEGITIMATE_ADDRESS for + HImode/QImode) relative address. */ + /* ??? This should perhaps be fixed elsewhere, for instance, in + fixup_stack_1, by checking for other kinds of invalid addresses, + e.g. a bare reference to a virtual register. This may confuse the + alpha though, which must handle this case differently. */ + if (GET_CODE (operands[0]) == MEM + && !memory_address_p (GET_MODE (operands[0]), + XEXP (operands[0], 0))) + operands[0] + = replace_equiv_address (operands[0], + copy_to_reg (XEXP (operands[0], 0))); + + if (GET_CODE (operands[1]) == MEM + && !memory_address_p (GET_MODE (operands[1]), + XEXP (operands[1], 0))) + operands[1] + = replace_equiv_address (operands[1], + copy_to_reg (XEXP (operands[1], 0))); + } + /* Handle loading a large integer during reload */ + else if (GET_CODE (operands[1]) == CONST_INT + && !CONST_OK_FOR_THUMB_LETTER (INTVAL (operands[1]), 'I')) + { + /* Writing a constant to memory needs a scratch, which should + be handled with SECONDARY_RELOADs. */ + if (GET_CODE (operands[0]) != REG) + abort (); + + operands[0] = gen_rtx (SUBREG, SImode, operands[0], 0); + emit_insn (gen_movsi (operands[0], operands[1])); + DONE; + } + } + " +) + +(define_insn "*thumb_movhi_insn" + [(set (match_operand:HI 0 "nonimmediate_operand" "=l,l, m,*r,*h,l") + (match_operand:HI 1 "general_operand" "l,mn,l,*h,*r,I"))] + "TARGET_THUMB + && ( register_operand (operands[0], HImode) + || register_operand (operands[1], HImode))" + "* + switch (which_alternative) + { + case 0: return \"add %0, %1, #0\"; + case 2: return \"strh %1, %0\"; + case 3: return \"mov %0, %1\"; + case 4: return \"mov %0, %1\"; + case 5: return \"mov %0, %1\"; + default: abort (); + case 1: + /* The stack pointer can end up being taken as an index register. + Catch this case here and deal with it. */ + if (GET_CODE (XEXP (operands[1], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == REG + && REGNO (XEXP (XEXP (operands[1], 0), 0)) == SP_REGNUM) + { + rtx ops[2]; + ops[0] = operands[0]; + ops[1] = XEXP (XEXP (operands[1], 0), 0); + + output_asm_insn (\"mov %0, %1\", ops); + + XEXP (XEXP (operands[1], 0), 0) = operands[0]; + + } + return \"ldrh %0, %1\"; + }" + [(set_attr "length" "2,4,2,2,2,2") + (set_attr "type" "*,load,store1,*,*,*") + (set_attr "pool_range" "*,64,*,*,*,*")] +) + + +(define_insn "rotated_loadsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (rotate:SI (match_operand:SI 1 "offsettable_memory_operand" "o") + (const_int 16)))] + "TARGET_ARM && (!TARGET_MMU_TRAPS)" + "* + { + rtx ops[2]; + + ops[0] = operands[0]; + ops[1] = gen_rtx_MEM (SImode, plus_constant (XEXP (operands[1], 0), 2)); + output_asm_insn (\"ldr%?\\t%0, %1\\t%@ load-rotate\", ops); + return \"\"; + }" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_expand "movhi_bytes" + [(set (match_dup 2) (zero_extend:SI (match_operand:HI 1 "" ""))) + (set (match_dup 3) + (zero_extend:SI (match_dup 6))) + (set (match_operand:SI 0 "" "") + (ior:SI (ashift:SI (match_dup 4) (const_int 8)) (match_dup 5)))] + "TARGET_ARM" + " + { + rtx mem1, mem2; + rtx addr = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); + + mem1 = gen_rtx_MEM (QImode, addr); + MEM_COPY_ATTRIBUTES (mem1, operands[1]); + mem2 = gen_rtx_MEM (QImode, plus_constant (addr, 1)); + MEM_COPY_ATTRIBUTES (mem2, operands[1]); + operands[0] = gen_lowpart (SImode, operands[0]); + operands[1] = mem1; + operands[2] = gen_reg_rtx (SImode); + operands[3] = gen_reg_rtx (SImode); + operands[6] = mem2; + + if (BYTES_BIG_ENDIAN) + { + operands[4] = operands[2]; + operands[5] = operands[3]; + } + else + { + operands[4] = operands[3]; + operands[5] = operands[2]; + } + }" +) + +(define_expand "movhi_bigend" + [(set (match_dup 2) + (rotate:SI (subreg:SI (match_operand:HI 1 "memory_operand" "") 0) + (const_int 16))) + (set (match_dup 3) + (ashiftrt:SI (match_dup 2) (const_int 16))) + (set (match_operand:HI 0 "s_register_operand" "") + (subreg:HI (match_dup 3) 0))] + "TARGET_ARM" + " + operands[2] = gen_reg_rtx (SImode); + operands[3] = gen_reg_rtx (SImode); + " +) + +;; Pattern to recognise insn generated default case above +(define_insn "*movhi_insn_arch4" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,m,r") + (match_operand:HI 1 "general_operand" "rI,K,r,m"))] + "TARGET_ARM + && arm_arch4 + && (GET_CODE (operands[1]) != CONST_INT + || const_ok_for_arm (INTVAL (operands[1])) + || const_ok_for_arm (~INTVAL (operands[1])))" + "@ + mov%?\\t%0, %1\\t%@ movhi + mvn%?\\t%0, #%B1\\t%@ movhi + str%?h\\t%1, %0\\t%@ movhi + ldr%?h\\t%0, %1\\t%@ movhi" + [(set_attr "type" "*,*,store1,load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "*,*,*,256") + (set_attr "neg_pool_range" "*,*,*,244")] +) + +(define_insn "*movhi_insn_littleend" + [(set (match_operand:HI 0 "s_register_operand" "=r,r,r") + (match_operand:HI 1 "general_operand" "rI,K,m"))] + "TARGET_ARM + && !arm_arch4 + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && (GET_CODE (operands[1]) != CONST_INT + || const_ok_for_arm (INTVAL (operands[1])) + || const_ok_for_arm (~INTVAL (operands[1])))" + "@ + mov%?\\t%0, %1\\t%@ movhi + mvn%?\\t%0, #%B1\\t%@ movhi + ldr%?\\t%0, %1\\t%@ movhi" + [(set_attr "type" "*,*,load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "4096") + (set_attr "neg_pool_range" "4084")] +) + +(define_insn "*movhi_insn_bigend" + [(set (match_operand:HI 0 "s_register_operand" "=r,r,r") + (match_operand:HI 1 "general_operand" "rI,K,m"))] + "TARGET_ARM + && !arm_arch4 + && BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && (GET_CODE (operands[1]) != CONST_INT + || const_ok_for_arm (INTVAL (operands[1])) + || const_ok_for_arm (~INTVAL (operands[1])))" + "@ + mov%?\\t%0, %1\\t%@ movhi + mvn%?\\t%0, #%B1\\t%@ movhi + ldr%?\\t%0, %1\\t%@ movhi_bigend\;mov%?\\t%0, %0, asr #16" + [(set_attr "type" "*,*,load") + (set_attr "predicable" "yes") + (set_attr "length" "4,4,8") + (set_attr "pool_range" "*,*,4092") + (set_attr "neg_pool_range" "*,*,4084")] +) + +(define_insn "*loadhi_si_bigend" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (rotate:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0) + (const_int 16)))] + "TARGET_ARM + && BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS" + "ldr%?\\t%0, %1\\t%@ movhi_bigend" + [(set_attr "type" "load") + (set_attr "predicable" "yes") + (set_attr "pool_range" "4096") + (set_attr "neg_pool_range" "4084")] +) + +(define_insn "*movhi_bytes" + [(set (match_operand:HI 0 "s_register_operand" "=r,r") + (match_operand:HI 1 "arm_rhs_operand" "rI,K"))] + "TARGET_ARM && TARGET_MMU_TRAPS" + "@ + mov%?\\t%0, %1\\t%@ movhi + mvn%?\\t%0, #%B1\\t%@ movhi" + [(set_attr "predicable" "yes")] +) + +(define_insn "thumb_movhi_clobber" + [(set (match_operand:HI 0 "memory_operand" "=m") + (match_operand:HI 1 "register_operand" "l")) + (clobber (match_operand:SI 2 "register_operand" "=&l"))] + "TARGET_THUMB" + "* + abort ();" +) + +;; We use a DImode scratch because we may occasionally need an additional +;; temporary if the address isn't offsettable -- push_reload doesn't seem +;; to take any notice of the "o" constraints on reload_memory_operand operand. +(define_expand "reload_outhi" + [(parallel [(match_operand:HI 0 "arm_reload_memory_operand" "=o") + (match_operand:HI 1 "s_register_operand" "r") + (match_operand:DI 2 "s_register_operand" "=&l")])] + "TARGET_EITHER" + "if (TARGET_ARM) + arm_reload_out_hi (operands); + else + thumb_reload_out_hi (operands); + DONE; + " +) + +(define_expand "reload_inhi" + [(parallel [(match_operand:HI 0 "s_register_operand" "=r") + (match_operand:HI 1 "arm_reload_memory_operand" "o") + (match_operand:DI 2 "s_register_operand" "=&r")])] + "TARGET_THUMB || (TARGET_ARM && TARGET_MMU_TRAPS)" + " + if (TARGET_ARM) + arm_reload_in_hi (operands); + else + thumb_reload_out_hi (operands); + DONE; +") + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + /* Everything except mem = const or mem = mem can be done easily */ + + if (!no_new_pseudos) + { + if (GET_CODE (operands[1]) == CONST_INT) + { + rtx reg = gen_reg_rtx (SImode); + + emit_insn (gen_movsi (reg, operands[1])); + operands[1] = gen_rtx_SUBREG (QImode, reg, 0); + } + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (QImode, operands[1]); + } + } + else /* TARGET_THUMB */ + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (QImode, operands[1]); + + /* ??? We shouldn't really get invalid addresses here, but this can + happen if we are passed a SP (never OK for HImode/QImode) or + virtual register (rejected by GO_IF_LEGITIMATE_ADDRESS for + HImode/QImode) relative address. */ + /* ??? This should perhaps be fixed elsewhere, for instance, in + fixup_stack_1, by checking for other kinds of invalid addresses, + e.g. a bare reference to a virtual register. This may confuse the + alpha though, which must handle this case differently. */ + if (GET_CODE (operands[0]) == MEM + && !memory_address_p (GET_MODE (operands[0]), + XEXP (operands[0], 0))) + operands[0] + = replace_equiv_address (operands[0], + copy_to_reg (XEXP (operands[0], 0))); + if (GET_CODE (operands[1]) == MEM + && !memory_address_p (GET_MODE (operands[1]), + XEXP (operands[1], 0))) + operands[1] + = replace_equiv_address (operands[1], + copy_to_reg (XEXP (operands[1], 0))); + } + /* Handle loading a large integer during reload */ + else if (GET_CODE (operands[1]) == CONST_INT + && !CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I')) + { + /* Writing a constant to memory needs a scratch, which should + be handled with SECONDARY_RELOADs. */ + if (GET_CODE (operands[0]) != REG) + abort (); + + operands[0] = gen_rtx (SUBREG, SImode, operands[0], 0); + emit_insn (gen_movsi (operands[0], operands[1])); + DONE; + } + } + " +) + + +(define_insn "*arm_movqi_insn" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r,m") + (match_operand:QI 1 "general_operand" "rI,K,m,r"))] + "TARGET_ARM + && ( register_operand (operands[0], QImode) + || register_operand (operands[1], QImode))" + "@ + mov%?\\t%0, %1 + mvn%?\\t%0, #%B1 + ldr%?b\\t%0, %1 + str%?b\\t%1, %0" + [(set_attr "type" "*,*,load,store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*thumb_movqi_insn" + [(set (match_operand:QI 0 "nonimmediate_operand" "=l,l,m,*r,*h,l") + (match_operand:QI 1 "general_operand" "l, m,l,*h,*r,I"))] + "TARGET_THUMB + && ( register_operand (operands[0], QImode) + || register_operand (operands[1], QImode))" + "@ + add\\t%0, %1, #0 + ldrb\\t%0, %1 + strb\\t%1, %0 + mov\\t%0, %1 + mov\\t%0, %1 + mov\\t%0, %1" + [(set_attr "length" "2") + (set_attr "type" "*,load,store1,*,*,*") + (set_attr "pool_range" "*,32,*,*,*,*")] +) + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (SFmode, operands[1]); + } + else /* TARGET_THUMB */ + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (SFmode, operands[1]); + } + } + " +) + +(define_split + [(set (match_operand:SF 0 "nonimmediate_operand" "") + (match_operand:SF 1 "immediate_operand" ""))] + "TARGET_ARM + && !TARGET_HARD_FLOAT + && reload_completed + && GET_CODE (operands[1]) == CONST_DOUBLE" + [(set (match_dup 2) (match_dup 3))] + " + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_lowpart (SImode, operands[1]); + if (operands[2] == 0 || operands[3] == 0) + FAIL; + " +) + +(define_insn "*arm_movsf_hard_insn" + [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f, m,f,r,r,r, m") + (match_operand:SF 1 "general_operand" "fG,H,mE,f,r,f,r,mE,r"))] + "TARGET_ARM + && TARGET_HARD_FLOAT + && (GET_CODE (operands[0]) != MEM + || register_operand (operands[1], SFmode))" + "@ + mvf%?s\\t%0, %1 + mnf%?s\\t%0, #%N1 + ldf%?s\\t%0, %1 + stf%?s\\t%1, %0 + str%?\\t%1, [%|sp, #-4]!\;ldf%?s\\t%0, [%|sp], #4 + stf%?s\\t%1, [%|sp, #-4]!\;ldr%?\\t%0, [%|sp], #4 + mov%?\\t%0, %1 + ldr%?\\t%0, %1\\t%@ float + str%?\\t%1, %0\\t%@ float" + [(set_attr "length" "4,4,4,4,8,8,4,4,4") + (set_attr "predicable" "yes") + (set_attr "type" + "ffarith,ffarith,f_load,f_store,r_mem_f,f_mem_r,*,load,store1") + (set_attr "pool_range" "*,*,1024,*,*,*,*,4096,*") + (set_attr "neg_pool_range" "*,*,1012,*,*,*,*,4084,*")] +) + +;; Exactly the same as above, except that all `f' cases are deleted. +;; This is necessary to prevent reload from ever trying to use a `f' reg +;; when -msoft-float. + +(define_insn "*arm_movsf_soft_insn" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m") + (match_operand:SF 1 "general_operand" "r,mE,r"))] + "TARGET_ARM + && TARGET_SOFT_FLOAT + && (GET_CODE (operands[0]) != MEM + || register_operand (operands[1], SFmode))" + "@ + mov%?\\t%0, %1 + ldr%?\\t%0, %1\\t%@ float + str%?\\t%1, %0\\t%@ float" + [(set_attr "length" "4,4,4") + (set_attr "predicable" "yes") + (set_attr "type" "*,load,store1") + (set_attr "pool_range" "*,4096,*") + (set_attr "neg_pool_range" "*,4084,*")] +) + +;;; ??? This should have alternatives for constants. +(define_insn "*thumb_movsf_insn" + [(set (match_operand:SF 0 "nonimmediate_operand" "=l,l,>,l, m,*r,*h") + (match_operand:SF 1 "general_operand" "l, >,l,mF,l,*h,*r"))] + "TARGET_THUMB + && ( register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" + "@ + add\\t%0, %1, #0 + ldmia\\t%1, {%0} + stmia\\t%0, {%1} + ldr\\t%0, %1 + str\\t%1, %0 + mov\\t%0, %1 + mov\\t%0, %1" + [(set_attr "length" "2") + (set_attr "type" "*,load,store1,load,store1,*,*") + (set_attr "pool_range" "*,*,*,1020,*,*,*")] +) + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (DFmode, operands[1]); + } + else /* TARGET_THUMB */ + { + if (!no_new_pseudos) + { + if (GET_CODE (operands[0]) != REG) + operands[1] = force_reg (DFmode, operands[1]); + } + } + " +) + +;; Reloading a df mode value stored in integer regs to memory can require a +;; scratch reg. +(define_expand "reload_outdf" + [(match_operand:DF 0 "arm_reload_memory_operand" "=o") + (match_operand:DF 1 "s_register_operand" "r") + (match_operand:SI 2 "s_register_operand" "=&r")] + "TARGET_ARM" + " + { + enum rtx_code code = GET_CODE (XEXP (operands[0], 0)); + + if (code == REG) + operands[2] = XEXP (operands[0], 0); + else if (code == POST_INC || code == PRE_DEC) + { + operands[0] = gen_rtx_SUBREG (DImode, operands[0], 0); + operands[1] = gen_rtx_SUBREG (DImode, operands[1], 0); + emit_insn (gen_movdi (operands[0], operands[1])); + DONE; + } + else if (code == PRE_INC) + { + rtx reg = XEXP (XEXP (operands[0], 0), 0); + + emit_insn (gen_addsi3 (reg, reg, GEN_INT (8))); + operands[2] = reg; + } + else if (code == POST_DEC) + operands[2] = XEXP (XEXP (operands[0], 0), 0); + else + emit_insn (gen_addsi3 (operands[2], XEXP (XEXP (operands[0], 0), 0), + XEXP (XEXP (operands[0], 0), 1))); + + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (DFmode, operands[2]), + operands[1])); + + if (code == POST_DEC) + emit_insn (gen_addsi3 (operands[2], operands[2], GEN_INT (-8))); + + DONE; + }" +) + +(define_insn "*movdf_hard_insn" + [(set (match_operand:DF 0 "nonimmediate_operand" + "=r,Q,r,m,r, f, f,f, m,!f,!r") + (match_operand:DF 1 "general_operand" + "Q, r,r,r,mF,fG,H,mF,f,r, f"))] + "TARGET_ARM + && TARGET_HARD_FLOAT + && (GET_CODE (operands[0]) != MEM + || register_operand (operands[1], DFmode))" + "* + { + switch (which_alternative) + { + default: + case 0: return \"ldm%?ia\\t%m1, %M0\\t%@ double\"; + case 1: return \"stm%?ia\\t%m0, %M1\\t%@ double\"; + case 2: case 3: case 4: return output_move_double (operands); + case 5: return \"mvf%?d\\t%0, %1\"; + case 6: return \"mnf%?d\\t%0, #%N1\"; + case 7: return \"ldf%?d\\t%0, %1\"; + case 8: return \"stf%?d\\t%1, %0\"; + case 9: return output_mov_double_fpu_from_arm (operands); + case 10: return output_mov_double_arm_from_fpu (operands); + } + } + " + [(set_attr "length" "4,4,8,8,8,4,4,4,4,8,8") + (set_attr "predicable" "yes") + (set_attr "type" + "load,store2,*,store2,load,ffarith,ffarith,f_load,f_store,r_mem_f,f_mem_r") + (set_attr "pool_range" "*,*,*,*,252,*,*,1024,*,*,*") + (set_attr "neg_pool_range" "*,*,*,*,244,*,*,1012,*,*,*")] +) + +;; Software floating point version. This is essentially the same as movdi. +;; Do not use `f' as a constraint to prevent reload from ever trying to use +;; an `f' reg. + +(define_insn "*movdf_soft_insn" + [(set (match_operand:DF 0 "nonimmediate_soft_df_operand" "=r,r,m") + (match_operand:DF 1 "soft_df_operand" "r,mF,r"))] + "TARGET_ARM && TARGET_SOFT_FLOAT + " + "* return output_move_double (operands);" + [(set_attr "length" "8,8,8") + (set_attr "type" "*,load,store2") + (set_attr "pool_range" "252") + (set_attr "neg_pool_range" "244")] +) + +;;; ??? This should have alternatives for constants. +;;; ??? This was originally identical to the movdi_insn pattern. +;;; ??? The 'F' constraint looks funny, but it should always be replaced by +;;; thumb_reorg with a memory reference. +(define_insn "*thumb_movdf_insn" + [(set (match_operand:DF 0 "nonimmediate_operand" "=l,l,>,l, m,*r") + (match_operand:DF 1 "general_operand" "l, >,l,mF,l,*r"))] + "TARGET_THUMB + && ( register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode))" + "* + switch (which_alternative) + { + default: + case 0: + if (REGNO (operands[1]) == REGNO (operands[0]) + 1) + return \"add\\t%0, %1, #0\;add\\t%H0, %H1, #0\"; + return \"add\\t%H0, %H1, #0\;add\\t%0, %1, #0\"; + case 1: + return \"ldmia\\t%1, {%0, %H0}\"; + case 2: + return \"stmia\\t%0, {%1, %H1}\"; + case 3: + return thumb_load_double_from_address (operands); + case 4: + operands[2] = gen_rtx (MEM, SImode, + plus_constant (XEXP (operands[0], 0), 4)); + output_asm_insn (\"str\\t%1, %0\;str\\t%H1, %2\", operands); + return \"\"; + case 5: + if (REGNO (operands[1]) == REGNO (operands[0]) + 1) + return \"mov\\t%0, %1\;mov\\t%H0, %H1\"; + return \"mov\\t%H0, %H1\;mov\\t%0, %1\"; + } + " + [(set_attr "length" "4,2,2,6,4,4") + (set_attr "type" "*,load,store2,load,store2,*") + (set_attr "pool_range" "*,*,*,1020,*,*")] +) + + +(define_expand "movxf" + [(set (match_operand:XF 0 "general_operand" "") + (match_operand:XF 1 "general_operand" ""))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "") + +;; Even when the XFmode patterns aren't enabled, we enable this after +;; reloading so that we can push floating point registers in the prologue. + +(define_insn "*movxf_hard_insn" + [(set (match_operand:XF 0 "nonimmediate_operand" "=f,f,f,m,f,r,r") + (match_operand:XF 1 "general_operand" "fG,H,m,f,r,f,r"))] + "TARGET_ARM && TARGET_HARD_FLOAT && (ENABLE_XF_PATTERNS || reload_completed)" + "* + switch (which_alternative) + { + default: + case 0: return \"mvf%?e\\t%0, %1\"; + case 1: return \"mnf%?e\\t%0, #%N1\"; + case 2: return \"ldf%?e\\t%0, %1\"; + case 3: return \"stf%?e\\t%1, %0\"; + case 4: return output_mov_long_double_fpu_from_arm (operands); + case 5: return output_mov_long_double_arm_from_fpu (operands); + case 6: return output_mov_long_double_arm_from_arm (operands); + } + " + [(set_attr "length" "4,4,4,4,8,8,12") + (set_attr "predicable" "yes") + (set_attr "type" "ffarith,ffarith,f_load,f_store,r_mem_f,f_mem_r,*") + (set_attr "pool_range" "*,*,1024,*,*,*,*") + (set_attr "neg_pool_range" "*,*,1012,*,*,*,*")] +) + + +;; load- and store-multiple insns +;; The arm can load/store any set of registers, provided that they are in +;; ascending order; but that is beyond GCC so stick with what it knows. + +(define_expand "load_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "" "") + (match_operand:SI 1 "" "")) + (use (match_operand:SI 2 "" ""))])] + "TARGET_ARM" + " + /* Support only fixed point registers. */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 14 + || INTVAL (operands[2]) < 2 + || GET_CODE (operands[1]) != MEM + || GET_CODE (operands[0]) != REG + || REGNO (operands[0]) > (LAST_ARM_REGNUM - 1) + || REGNO (operands[0]) + INTVAL (operands[2]) > LAST_ARM_REGNUM) + FAIL; + + operands[3] + = arm_gen_load_multiple (REGNO (operands[0]), INTVAL (operands[2]), + force_reg (SImode, XEXP (operands[1], 0)), + TRUE, FALSE, RTX_UNCHANGING_P(operands[1]), + MEM_IN_STRUCT_P(operands[1]), + MEM_SCALAR_P (operands[1])); + " +) + +;; Load multiple with write-back + +(define_insn "*ldmsi_postinc4" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 16))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (match_dup 2))) + (set (match_operand:SI 4 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 5 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 6 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 12))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 5" + "ldm%?ia\\t%1!, {%3, %4, %5, %6}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*ldmsi_postinc3" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 12))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (match_dup 2))) + (set (match_operand:SI 4 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 5 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 8))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 4" + "ldm%?ia\\t%1!, {%3, %4, %5}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*ldmsi_postinc2" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 8))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (match_dup 2))) + (set (match_operand:SI 4 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 2) (const_int 4))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 3" + "ldm%?ia\\t%1!, {%3, %4}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +;; Ordinary load multiple + +(define_insn "*ldmsi4" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "arm_hard_register_operand" "") + (mem:SI (match_operand:SI 1 "s_register_operand" "r"))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 4" + "ldm%?ia\\t%1, {%2, %3, %4, %5}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*ldmsi3" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "arm_hard_register_operand" "") + (mem:SI (match_operand:SI 1 "s_register_operand" "r"))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 3" + "ldm%?ia\\t%1, {%2, %3, %4}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*ldmsi2" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "arm_hard_register_operand" "") + (mem:SI (match_operand:SI 1 "s_register_operand" "r"))) + (set (match_operand:SI 3 "arm_hard_register_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4))))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 2" + "ldm%?ia\\t%1, {%2, %3}" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_expand "store_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "" "") + (match_operand:SI 1 "" "")) + (use (match_operand:SI 2 "" ""))])] + "TARGET_ARM" + " + /* Support only fixed point registers */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 14 + || INTVAL (operands[2]) < 2 + || GET_CODE (operands[1]) != REG + || GET_CODE (operands[0]) != MEM + || REGNO (operands[1]) > (LAST_ARM_REGNUM - 1) + || REGNO (operands[1]) + INTVAL (operands[2]) > LAST_ARM_REGNUM) + FAIL; + + operands[3] + = arm_gen_store_multiple (REGNO (operands[1]), INTVAL (operands[2]), + force_reg (SImode, XEXP (operands[0], 0)), + TRUE, FALSE, RTX_UNCHANGING_P (operands[0]), + MEM_IN_STRUCT_P(operands[0]), + MEM_SCALAR_P (operands[0])); + " +) + +;; Store multiple with write-back + +(define_insn "*stmsi_postinc4" + [(match_parallel 0 "store_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 16))) + (set (mem:SI (match_dup 2)) + (match_operand:SI 3 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 4))) + (match_operand:SI 4 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 8))) + (match_operand:SI 5 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 12))) + (match_operand:SI 6 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 5" + "stm%?ia\\t%1!, {%3, %4, %5, %6}" + [(set_attr "predicable" "yes") + (set_attr "type" "store4")] +) + +(define_insn "*stmsi_postinc3" + [(match_parallel 0 "store_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 12))) + (set (mem:SI (match_dup 2)) + (match_operand:SI 3 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 4))) + (match_operand:SI 4 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 8))) + (match_operand:SI 5 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 4" + "stm%?ia\\t%1!, {%3, %4, %5}" + [(set_attr "predicable" "yes") + (set_attr "type" "store3")] +) + +(define_insn "*stmsi_postinc2" + [(match_parallel 0 "store_multiple_operation" + [(set (match_operand:SI 1 "s_register_operand" "=r") + (plus:SI (match_operand:SI 2 "s_register_operand" "1") + (const_int 8))) + (set (mem:SI (match_dup 2)) + (match_operand:SI 3 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 2) (const_int 4))) + (match_operand:SI 4 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 3" + "stm%?ia\\t%1!, {%3, %4}" + [(set_attr "predicable" "yes") + (set_attr "type" "store2")] +) + +;; Ordinary store multiple + +(define_insn "*stmsi4" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r")) + (match_operand:SI 2 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 4" + "stm%?ia\\t%1, {%2, %3, %4, %5}" + [(set_attr "predicable" "yes") + (set_attr "type" "store4")] +) + +(define_insn "*stmsi3" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r")) + (match_operand:SI 2 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 3" + "stm%?ia\\t%1, {%2, %3, %4}" + [(set_attr "predicable" "yes") + (set_attr "type" "store3")] +) + +(define_insn "*stmsi2" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r")) + (match_operand:SI 2 "arm_hard_register_operand" "")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "arm_hard_register_operand" ""))])] + "TARGET_ARM && XVECLEN (operands[0], 0) == 2" + "stm%?ia\\t%1, {%2, %3}" + [(set_attr "predicable" "yes") + (set_attr "type" "store2")] +) + +;; Move a block of memory if it is word aligned and MORE than 2 words long. +;; We could let this apply for blocks of less than this, but it clobbers so +;; many registers that there is then probably a better way. + +(define_expand "movstrqi" + [(match_operand:BLK 0 "general_operand" "") + (match_operand:BLK 1 "general_operand" "") + (match_operand:SI 2 "const_int_operand" "") + (match_operand:SI 3 "const_int_operand" "")] + "TARGET_EITHER" + " + if (TARGET_ARM) + { + if (arm_gen_movstrqi (operands)) + DONE; + FAIL; + } + else /* TARGET_THUMB */ + { + if ( INTVAL (operands[3]) != 4 + || INTVAL (operands[2]) > 48) + FAIL; + + thumb_expand_movstrqi (operands); + DONE; + } + " +) + +;; Thumb block-move insns + +(define_insn "movmem12b" + [(set (mem:SI (match_operand:SI 2 "register_operand" "0")) + (mem:SI (match_operand:SI 3 "register_operand" "1"))) + (set (mem:SI (plus:SI (match_dup 2) (const_int 4))) + (mem:SI (plus:SI (match_dup 3) (const_int 4)))) + (set (mem:SI (plus:SI (match_dup 2) (const_int 8))) + (mem:SI (plus:SI (match_dup 3) (const_int 8)))) + (set (match_operand:SI 0 "register_operand" "=l") + (plus:SI (match_dup 2) (const_int 12))) + (set (match_operand:SI 1 "register_operand" "=l") + (plus:SI (match_dup 3) (const_int 12))) + (clobber (match_scratch:SI 4 "=&l")) + (clobber (match_scratch:SI 5 "=&l")) + (clobber (match_scratch:SI 6 "=&l"))] + "TARGET_THUMB" + "* return thumb_output_move_mem_multiple (3, operands);" + [(set_attr "length" "4") + ; This isn't entirely accurate... It loads as well, but in terms of + ; scheduling the following insn it is better to consider it as a store + (set_attr "type" "store3")] +) + +(define_insn "movmem8b" + [(set (mem:SI (match_operand:SI 2 "register_operand" "0")) + (mem:SI (match_operand:SI 3 "register_operand" "1"))) + (set (mem:SI (plus:SI (match_dup 2) (const_int 4))) + (mem:SI (plus:SI (match_dup 3) (const_int 4)))) + (set (match_operand:SI 0 "register_operand" "=l") + (plus:SI (match_dup 2) (const_int 8))) + (set (match_operand:SI 1 "register_operand" "=l") + (plus:SI (match_dup 3) (const_int 8))) + (clobber (match_scratch:SI 4 "=&l")) + (clobber (match_scratch:SI 5 "=&l"))] + "TARGET_THUMB" + "* return thumb_output_move_mem_multiple (2, operands);" + [(set_attr "length" "4") + ; This isn't entirely accurate... It loads as well, but in terms of + ; scheduling the following insn it is better to consider it as a store + (set_attr "type" "store2")] +) + + + +;; Compare & branch insns +;; The range calcualations are based as follows: +;; For forward branches, the address calculation returns the address of +;; the next instruction. This is 2 beyond the branch instruction. +;; For backward branches, the address calculation returns the address of +;; the first instruction in this pattern (cmp). This is 2 before the branch +;; instruction for the shortest sequence, and 4 before the branch instruction +;; if we have to jump around an unconditional branch. +;; To the basic branch range the PC offset must be added (this is +4). +;; So for forward branches we have +;; (pos_range - pos_base_offs + pc_offs) = (pos_range - 2 + 4). +;; And for backward branches we have +;; (neg_range - neg_base_offs + pc_offs) = (neg_range - (-2 or -4) + 4). +;; +;; For a 'b' pos_range = 2046, neg_range = -2048 giving (-2040->2048). +;; For a 'b<cond>' pos_range = 254, neg_range = -256 giving (-250 ->256). + +(define_insn "cbranchsi4" + [(set (pc) + (if_then_else + (match_operator 0 "arm_comparison_operator" + [(match_operand:SI 1 "register_operand" "l,r") + (match_operand:SI 2 "nonmemory_operand" "rI,r")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "TARGET_THUMB" + "* + output_asm_insn (\"cmp\\t%1, %2\", operands); + switch (get_attr_length (insn)) + { + case 4: return \"b%d0\\t%l3\"; + case 6: return \"b%D0\\t.LCB%=\;b\\t%l3\\t%@long jump\\n.LCB%=:\"; + default: return \"b%D0\\t.LCB%=\;bl\\t%l3\\t%@far jump\\n.LCB%=:\"; + } + " + [(set (attr "far_jump") + (if_then_else + (eq_attr "length" "8") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 3) (pc)) (const_int -250)) + (le (minus (match_dup 3) (pc)) (const_int 256))) + (const_int 4) + (if_then_else + (and (ge (minus (match_dup 3) (pc)) (const_int -2040)) + (le (minus (match_dup 3) (pc)) (const_int 2048))) + (const_int 6) + (const_int 8))))] +) + +(define_insn "*negated_cbranchsi4" + [(set (pc) + (if_then_else + (match_operator 0 "arm_comparison_operator" + [(match_operand:SI 1 "register_operand" "l") + (neg:SI (match_operand:SI 2 "nonmemory_operand" "l"))]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "TARGET_THUMB" + "* + output_asm_insn (\"cmn\\t%1, %2\", operands); + switch (get_attr_length (insn)) + { + case 4: return \"b%d0\\t%l3\"; + case 6: return \"b%D0\\t.LCB%=\;b\\t%l3\\t%@long jump\\n.LCB%=:\"; + default: return \"b%D0\\t.LCB%=\;bl\\t%l3\\t%@far jump\\n.LCB%=:\"; + } + " + [(set (attr "far_jump") + (if_then_else + (eq_attr "length" "8") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 3) (pc)) (const_int -250)) + (le (minus (match_dup 3) (pc)) (const_int 256))) + (const_int 4) + (if_then_else + (and (ge (minus (match_dup 3) (pc)) (const_int -2040)) + (le (minus (match_dup 3) (pc)) (const_int 2048))) + (const_int 6) + (const_int 8))))] +) + + +;; Comparison and test insns + +(define_expand "cmpsi" + [(match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 1 "arm_add_operand" "")] + "TARGET_ARM" + "{ + arm_compare_op0 = operands[0]; + arm_compare_op1 = operands[1]; + DONE; + }" +) + +(define_expand "cmpsf" + [(match_operand:SF 0 "s_register_operand" "") + (match_operand:SF 1 "fpu_rhs_operand" "")] + "TARGET_ARM && TARGET_HARD_FLOAT" + " + arm_compare_op0 = operands[0]; + arm_compare_op1 = operands[1]; + DONE; + " +) + +(define_expand "cmpdf" + [(match_operand:DF 0 "s_register_operand" "") + (match_operand:DF 1 "fpu_rhs_operand" "")] + "TARGET_ARM && TARGET_HARD_FLOAT" + " + arm_compare_op0 = operands[0]; + arm_compare_op1 = operands[1]; + DONE; + " +) + +(define_expand "cmpxf" + [(match_operand:XF 0 "s_register_operand" "") + (match_operand:XF 1 "fpu_rhs_operand" "")] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + " + arm_compare_op0 = operands[0]; + arm_compare_op1 = operands[1]; + DONE; + " +) + +(define_insn "*arm_cmpsi_insn" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_operand:SI 0 "s_register_operand" "r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L")))] + "TARGET_ARM" + "@ + cmp%?\\t%0, %1 + cmn%?\\t%0, #%n1" + [(set_attr "conds" "set")] +) + +(define_insn "*cmpsi_shiftsi" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_operand:SI 0 "s_register_operand" "r") + (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")])))] + "TARGET_ARM" + "cmp%?\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*cmpsi_shiftsi_swp" + [(set (reg:CC_SWP CC_REGNUM) + (compare:CC_SWP (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "reg_or_int_operand" "rM")]) + (match_operand:SI 0 "s_register_operand" "r")))] + "TARGET_ARM" + "cmp%?\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*cmpsi_neg_shiftsi" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_operand:SI 0 "s_register_operand" "r") + (neg:SI (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")]))))] + "TARGET_ARM" + "cmn%?\\t%0, %1%S3" + [(set_attr "conds" "set") + (set_attr "shift" "1") + ] +) + +(define_insn "*cmpsf_insn" + [(set (reg:CCFP CC_REGNUM) + (compare:CCFP (match_operand:SF 0 "s_register_operand" "f,f") + (match_operand:SF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?\\t%0, %1 + cnf%?\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpdf_insn" + [(set (reg:CCFP CC_REGNUM) + (compare:CCFP (match_operand:DF 0 "s_register_operand" "f,f") + (match_operand:DF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?\\t%0, %1 + cnf%?\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpesfdf_df" + [(set (reg:CCFP CC_REGNUM) + (compare:CCFP (float_extend:DF + (match_operand:SF 0 "s_register_operand" "f,f")) + (match_operand:DF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?\\t%0, %1 + cnf%?\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpdf_esfdf" + [(set (reg:CCFP CC_REGNUM) + (compare:CCFP (match_operand:DF 0 "s_register_operand" "f") + (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "cmf%?\\t%0, %1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpxf_insn" + [(set (reg:CCFP CC_REGNUM) + (compare:CCFP (match_operand:XF 0 "s_register_operand" "f,f") + (match_operand:XF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "@ + cmf%?\\t%0, %1 + cnf%?\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpsf_trap" + [(set (reg:CCFPE CC_REGNUM) + (compare:CCFPE (match_operand:SF 0 "s_register_operand" "f,f") + (match_operand:SF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?e\\t%0, %1 + cnf%?e\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpdf_trap" + [(set (reg:CCFPE CC_REGNUM) + (compare:CCFPE (match_operand:DF 0 "s_register_operand" "f,f") + (match_operand:DF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?e\\t%0, %1 + cnf%?e\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmp_esfdf_df_trap" + [(set (reg:CCFPE CC_REGNUM) + (compare:CCFPE (float_extend:DF + (match_operand:SF 0 "s_register_operand" "f,f")) + (match_operand:DF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + cmf%?e\\t%0, %1 + cnf%?e\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmp_df_esfdf_trap" + [(set (reg:CCFPE CC_REGNUM) + (compare:CCFPE (match_operand:DF 0 "s_register_operand" "f") + (float_extend:DF + (match_operand:SF 1 "s_register_operand" "f"))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "cmf%?e\\t%0, %1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +(define_insn "*cmpxf_trap" + [(set (reg:CCFPE CC_REGNUM) + (compare:CCFPE (match_operand:XF 0 "s_register_operand" "f,f") + (match_operand:XF 1 "fpu_add_operand" "fG,H")))] + "TARGET_ARM && ENABLE_XF_PATTERNS && TARGET_HARD_FLOAT" + "@ + cmf%?e\\t%0, %1 + cnf%?e\\t%0, #%N1" + [(set_attr "conds" "set") + (set_attr "type" "f_2_r")] +) + +; This insn allows redundant compares to be removed by cse, nothing should +; ever appear in the output file since (set (reg x) (reg x)) is a no-op that +; is deleted later on. The match_dup will match the mode here, so that +; mode changes of the condition codes aren't lost by this even though we don't +; specify what they are. + +(define_insn "*deleted_compare" + [(set (match_operand 0 "cc_register" "") (match_dup 0))] + "TARGET_ARM" + "\\t%@ deleted compare" + [(set_attr "conds" "set") + (set_attr "length" "0")] +) + + +;; Conditional branch insns + +(define_expand "beq" + [(set (pc) + (if_then_else (eq (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (EQ, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bne" + [(set (pc) + (if_then_else (ne (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (NE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "ble" + [(set (pc) + (if_then_else (le (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bge" + [(set (pc) + (if_then_else (ge (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "blt" + [(set (pc) + (if_then_else (lt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GTU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LEU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GEU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LTU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bunordered" + [(set (pc) + (if_then_else (unordered (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNORDERED, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "bordered" + [(set (pc) + (if_then_else (ordered (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (ORDERED, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "bungt" + [(set (pc) + (if_then_else (ungt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNGT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bunlt" + [(set (pc) + (if_then_else (unlt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNLT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bunge" + [(set (pc) + (if_then_else (unge (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNGE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bunle" + [(set (pc) + (if_then_else (unle (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNLE, arm_compare_op0, arm_compare_op1);" +) + +;; The following two patterns need two branch instructions, since there is +;; no single instruction that will handle all cases. +(define_expand "buneq" + [(set (pc) + (if_then_else (uneq (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNEQ, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "bltgt" + [(set (pc) + (if_then_else (ltgt (match_dup 1) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (LTGT, arm_compare_op0, arm_compare_op1);" +) + +;; +;; Patterns to match conditional branch insns. +;; + +; Special pattern to match UNEQ. +(define_insn "*arm_buneq" + [(set (pc) + (if_then_else (uneq (match_operand 1 "cc_register" "") (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "* + if (arm_ccfsm_state != 0) + abort (); + + return \"bvs\\t%l0;beq\\t%l0\"; + " + [(set_attr "conds" "jump_clob") + (set_attr "length" "8")] +) + +; Special pattern to match LTGT. +(define_insn "*arm_bltgt" + [(set (pc) + (if_then_else (ltgt (match_operand 1 "cc_register" "") (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "* + if (arm_ccfsm_state != 0) + abort (); + + return \"bmi\\t%l0;bgt\\t%l0\"; + " + [(set_attr "conds" "jump_clob") + (set_attr "length" "8")] +) + +(define_insn "*arm_cond_branch" + [(set (pc) + (if_then_else (match_operator 1 "arm_comparison_operator" + [(match_operand 2 "cc_register" "") (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ARM" + "* + if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return \"b%d1\\t%l0\"; + " + [(set_attr "conds" "use")] +) + +; Special pattern to match reversed UNEQ. +(define_insn "*arm_buneq_reversed" + [(set (pc) + (if_then_else (uneq (match_operand 1 "cc_register" "") (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "* + if (arm_ccfsm_state != 0) + abort (); + + return \"bmi\\t%l0;bgt\\t%l0\"; + " + [(set_attr "conds" "jump_clob") + (set_attr "length" "8")] +) + +; Special pattern to match reversed LTGT. +(define_insn "*arm_bltgt_reversed" + [(set (pc) + (if_then_else (ltgt (match_operand 1 "cc_register" "") (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "* + if (arm_ccfsm_state != 0) + abort (); + + return \"bvs\\t%l0;beq\\t%l0\"; + " + [(set_attr "conds" "jump_clob") + (set_attr "length" "8")] +) + +(define_insn "*arm_cond_branch_reversed" + [(set (pc) + (if_then_else (match_operator 1 "arm_comparison_operator" + [(match_operand 2 "cc_register" "") (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "TARGET_ARM" + "* + if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return \"b%D1\\t%l0\"; + " + [(set_attr "conds" "use")] +) + + + +; scc insns + +(define_expand "seq" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (eq:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (EQ, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sne" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ne:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (NE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sgt" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (gt:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sle" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (le:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sge" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ge:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GE, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "slt" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (lt:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LT, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sgtu" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (gtu:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GTU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sleu" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (leu:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LEU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sgeu" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (geu:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (GEU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sltu" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ltu:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM" + "operands[1] = arm_gen_compare_reg (LTU, arm_compare_op0, arm_compare_op1);" +) + +(define_expand "sunordered" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unordered:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNORDERED, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "sordered" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ordered:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (ORDERED, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "sungt" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (ungt:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNGT, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "sunge" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unge:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNGE, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "sunlt" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unlt:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNLT, arm_compare_op0, + arm_compare_op1);" +) + +(define_expand "sunle" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unle:SI (match_dup 1) (const_int 0)))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "operands[1] = arm_gen_compare_reg (UNLE, arm_compare_op0, + arm_compare_op1);" +) + +;;; DO NOT add patterns for SUNEQ or SLTGT, these can't be represented with +;;; simple ARM instructions. +; +; (define_expand "suneq" +; [(set (match_operand:SI 0 "s_register_operand" "=r") +; (uneq:SI (match_dup 1) (const_int 0)))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "abort ();" +; ) +; +; (define_expand "sltgt" +; [(set (match_operand:SI 0 "s_register_operand" "=r") +; (ltgt:SI (match_dup 1) (const_int 0)))] +; "TARGET_ARM && TARGET_HARD_FLOAT" +; "abort ();" +; ) + +(define_insn "*mov_scc" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operator:SI 1 "arm_comparison_operator" + [(match_operand 2 "cc_register" "") (const_int 0)]))] + "TARGET_ARM" + "mov%D1\\t%0, #0\;mov%d1\\t%0, #1" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*mov_negscc" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (neg:SI (match_operator:SI 1 "arm_comparison_operator" + [(match_operand 2 "cc_register" "") (const_int 0)])))] + "TARGET_ARM" + "mov%D1\\t%0, #0\;mvn%d1\\t%0, #0" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*mov_notscc" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI (match_operator:SI 1 "arm_comparison_operator" + [(match_operand 2 "cc_register" "") (const_int 0)])))] + "TARGET_ARM" + "mov%D1\\t%0, #0\;mvn%d1\\t%0, #1" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + + +;; Conditional move insns + +(define_expand "movsicc" + [(set (match_operand:SI 0 "s_register_operand" "") + (if_then_else:SI (match_operand 1 "arm_comparison_operator" "") + (match_operand:SI 2 "arm_not_operand" "") + (match_operand:SI 3 "arm_not_operand" "")))] + "TARGET_ARM" + " + { + enum rtx_code code = GET_CODE (operands[1]); + rtx ccreg = arm_gen_compare_reg (code, arm_compare_op0, arm_compare_op1); + + operands[1] = gen_rtx (code, VOIDmode, ccreg, const0_rtx); + }" +) + +(define_expand "movsfcc" + [(set (match_operand:SF 0 "s_register_operand" "") + (if_then_else:SF (match_operand 1 "arm_comparison_operator" "") + (match_operand:SF 2 "s_register_operand" "") + (match_operand:SF 3 "nonmemory_operand" "")))] + "TARGET_ARM" + " + { + enum rtx_code code = GET_CODE (operands[1]); + rtx ccreg; + + /* When compiling for SOFT_FLOAT, ensure both arms are in registers. + Otherwise, ensure it is a valid FP add operand */ + if ((!TARGET_HARD_FLOAT) + || (!fpu_add_operand (operands[3], SFmode))) + operands[3] = force_reg (SFmode, operands[3]); + + ccreg = arm_gen_compare_reg (code, arm_compare_op0, arm_compare_op1); + operands[1] = gen_rtx (code, VOIDmode, ccreg, const0_rtx); + }" +) + +(define_expand "movdfcc" + [(set (match_operand:DF 0 "s_register_operand" "") + (if_then_else:DF (match_operand 1 "arm_comparison_operator" "") + (match_operand:DF 2 "s_register_operand" "") + (match_operand:DF 3 "fpu_add_operand" "")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + " + { + enum rtx_code code = GET_CODE (operands[1]); + rtx ccreg = arm_gen_compare_reg (code, arm_compare_op0, arm_compare_op1); + + operands[1] = gen_rtx (code, VOIDmode, ccreg, const0_rtx); + }" +) + +(define_insn "*movsicc_insn" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r,r,r,r,r,r") + (if_then_else:SI + (match_operator 3 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_not_operand" "0,0,rI,K,rI,rI,K,K") + (match_operand:SI 2 "arm_not_operand" "rI,K,0,0,rI,K,rI,K")))] + "TARGET_ARM" + "@ + mov%D3\\t%0, %2 + mvn%D3\\t%0, #%B2 + mov%d3\\t%0, %1 + mvn%d3\\t%0, #%B1 + mov%d3\\t%0, %1\;mov%D3\\t%0, %2 + mov%d3\\t%0, %1\;mvn%D3\\t%0, #%B2 + mvn%d3\\t%0, #%B1\;mov%D3\\t%0, %2 + mvn%d3\\t%0, #%B1\;mvn%D3\\t%0, #%B2" + [(set_attr "length" "4,4,4,4,8,8,8,8") + (set_attr "conds" "use")] +) + +(define_insn "*movsfcc_hard_insn" + [(set (match_operand:SF 0 "s_register_operand" "=f,f,f,f,f,f,f,f") + (if_then_else:SF + (match_operator 3 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (match_operand:SF 1 "fpu_add_operand" "0,0,fG,H,fG,fG,H,H") + (match_operand:SF 2 "fpu_add_operand" "fG,H,0,0,fG,H,fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + mvf%D3s\\t%0, %2 + mnf%D3s\\t%0, #%N2 + mvf%d3s\\t%0, %1 + mnf%d3s\\t%0, #%N1 + mvf%d3s\\t%0, %1\;mvf%D3s\\t%0, %2 + mvf%d3s\\t%0, %1\;mnf%D3s\\t%0, #%N2 + mnf%d3s\\t%0, #%N1\;mvf%D3s\\t%0, %2 + mnf%d3s\\t%0, #%N1\;mnf%D3s\\t%0, #%N2" + [(set_attr "length" "4,4,4,4,8,8,8,8") + (set_attr "type" "ffarith") + (set_attr "conds" "use")] +) + +(define_insn "*movsfcc_soft_insn" + [(set (match_operand:SF 0 "s_register_operand" "=r,r") + (if_then_else:SF (match_operator 3 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (match_operand:SF 1 "s_register_operand" "0,r") + (match_operand:SF 2 "s_register_operand" "r,0")))] + "TARGET_ARM && TARGET_SOFT_FLOAT" + "@ + mov%D3\\t%0, %2 + mov%d3\\t%0, %1" + [(set_attr "conds" "use")] +) + +(define_insn "*movdfcc_insn" + [(set (match_operand:DF 0 "s_register_operand" "=f,f,f,f,f,f,f,f") + (if_then_else:DF + (match_operator 3 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (match_operand:DF 1 "fpu_add_operand" "0,0,fG,H,fG,fG,H,H") + (match_operand:DF 2 "fpu_add_operand" "fG,H,0,0,fG,H,fG,H")))] + "TARGET_ARM && TARGET_HARD_FLOAT" + "@ + mvf%D3d\\t%0, %2 + mnf%D3d\\t%0, #%N2 + mvf%d3d\\t%0, %1 + mnf%d3d\\t%0, #%N1 + mvf%d3d\\t%0, %1\;mvf%D3d\\t%0, %2 + mvf%d3d\\t%0, %1\;mnf%D3d\\t%0, #%N2 + mnf%d3d\\t%0, #%N1\;mvf%D3d\\t%0, %2 + mnf%d3d\\t%0, #%N1\;mnf%D3d\\t%0, #%N2" + [(set_attr "length" "4,4,4,4,8,8,8,8") + (set_attr "type" "ffarith") + (set_attr "conds" "use")] +) + + +;; Jump and linkage insns + +(define_expand "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "TARGET_EITHER" + "" +) + +(define_insn "*arm_jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "TARGET_ARM" + "* + { + if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return \"b%?\\t%l0\"; + } + " + [(set_attr "predicable" "yes")] +) + +(define_insn "*thumb_jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "TARGET_THUMB" + "* + if (get_attr_length (insn) == 2) + return \"b\\t%l0\"; + return \"bl\\t%l0\\t%@ far jump\"; + " + [(set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -2048)) + (le (minus (match_dup 0) (pc)) (const_int 2044))) + (const_int 2) + (const_int 4)))] +) + +(define_expand "call" + [(parallel [(call (match_operand 0 "memory_operand" "") + (match_operand 1 "general_operand" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))])] + "TARGET_EITHER" + " + { + rtx callee; + + /* In an untyped call, we can get NULL for operand 2. */ + if (operands[2] == NULL_RTX) + operands[2] = const0_rtx; + + /* This is to decide if we should generate indirect calls by loading the + 32 bit address of the callee into a register before performing the + branch and link. operand[2] encodes the long_call/short_call + attribute of the function being called. This attribute is set whenever + __attribute__((long_call/short_call)) or #pragma long_call/no_long_call + is used, and the short_call attribute can also be set if function is + declared as static or if it has already been defined in the current + compilation unit. See arm.c and arm.h for info about this. The third + parameter to arm_is_longcall_p is used to tell it which pattern + invoked it. */ + callee = XEXP (operands[0], 0); + + if (GET_CODE (callee) != REG + && arm_is_longcall_p (operands[0], INTVAL (operands[2]), 0)) + XEXP (operands[0], 0) = force_reg (Pmode, callee); + }" +) + +(define_insn "*call_reg" + [(call (mem:SI (match_operand:SI 0 "s_register_operand" "r")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM" + "* + return output_call (operands); + " + ;; length is worst case, normally it is only two + [(set_attr "length" "12") + (set_attr "type" "call")] +) + +(define_insn "*call_mem" + [(call (mem:SI (match_operand:SI 0 "memory_operand" "m")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM" + "* + return output_call_mem (operands); + " + [(set_attr "length" "12") + (set_attr "type" "call")] +) + +(define_insn "*call_indirect" + [(call (mem:SI (match_operand:SI 0 "register_operand" "l*r")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_THUMB" + "* + { + if (TARGET_CALLER_INTERWORKING) + return \"bl\\t%__interwork_call_via_%0\"; + else + return \"bl\\t%__call_via_%0\"; + }" + [(set_attr "type" "call")] +) + +(define_insn "*call_value_indirect" + [(set (match_operand 0 "" "=l") + (call (mem:SI (match_operand:SI 1 "register_operand" "l*r")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_THUMB" + "* + { + if (TARGET_CALLER_INTERWORKING) + return \"bl\\t%__interwork_call_via_%1\"; + else + return \"bl\\t%__call_via_%1\"; + }" + [(set_attr "type" "call")] +) + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "memory_operand" "") + (match_operand 2 "general_operand" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))])] + "TARGET_EITHER" + " + { + rtx callee = XEXP (operands[1], 0); + + /* In an untyped call, we can get NULL for operand 2. */ + if (operands[3] == 0) + operands[3] = const0_rtx; + + /* See the comment in define_expand \"call\". */ + if (GET_CODE (callee) != REG + && arm_is_longcall_p (operands[1], INTVAL (operands[3]), 0)) + XEXP (operands[1], 0) = force_reg (Pmode, callee); + }" +) + +(define_insn "*call_value_reg" + [(set (match_operand 0 "" "=r,f") + (call (mem:SI (match_operand:SI 1 "s_register_operand" "r,r")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM" + "* + return output_call (&operands[1]); + " + [(set_attr "length" "12") + (set_attr "type" "call")] +) + +(define_insn "*call_value_mem" + [(set (match_operand 0 "" "=r,f") + (call (mem:SI (match_operand:SI 1 "memory_operand" "m,m")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM && (!CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))" + "* + return output_call_mem (&operands[1]); + " + [(set_attr "length" "12") + (set_attr "type" "call")] +) + +;; Allow calls to SYMBOL_REFs specially as they are not valid general addresses +;; The 'a' causes the operand to be treated as an address, i.e. no '#' output. + +(define_insn "*call_symbol" + [(call (mem:SI (match_operand:SI 0 "" "X")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM + && (GET_CODE (operands[0]) == SYMBOL_REF) + && !arm_is_longcall_p (operands[0], INTVAL (operands[2]), 1)" + "* + { + return NEED_PLT_RELOC ? \"bl%?\\t%a0(PLT)\" : \"bl%?\\t%a0\"; + }" + [(set_attr "type" "call")] +) + +(define_insn "*call_value_symbol" + [(set (match_operand 0 "s_register_operand" "=r,f") + (call (mem:SI (match_operand:SI 1 "" "X,X")) + (match_operand:SI 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_ARM + && (GET_CODE (operands[1]) == SYMBOL_REF) + && !arm_is_longcall_p (operands[1], INTVAL (operands[3]), 1)" + "* + { + return NEED_PLT_RELOC ? \"bl%?\\t%a1(PLT)\" : \"bl%?\\t%a1\"; + }" + [(set_attr "type" "call")] +) + +(define_insn "*call_insn" + [(call (mem:SI (match_operand:SI 0 "" "X")) + (match_operand:SI 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_THUMB + && operands[2] == const0_rtx && (GET_CODE (operands[0]) == SYMBOL_REF)" + "bl\\t%a0" + [(set_attr "length" "4") + (set_attr "type" "call")] +) + +(define_insn "*call_value_insn" + [(set (match_operand 0 "register_operand" "=l") + (call (mem:SI (match_operand 1 "" "X")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNUM))] + "TARGET_THUMB + && operands[3] == const0_rtx && (GET_CODE (operands[1]) == SYMBOL_REF)" + "bl\\t%a1" + [(set_attr "length" "4") + (set_attr "type" "call")] +) + +;; We may also be able to do sibcalls for Thumb, but it's much harder... +(define_expand "sibcall" + [(parallel [(call (match_operand 0 "memory_operand" "") + (match_operand 1 "general_operand" "")) + (use (match_operand 2 "" "")) + (use (reg:SI LR_REGNUM))])] + "TARGET_ARM" + " + { + if (operands[2] == NULL_RTX) + operands[2] = const0_rtx; + }" +) + +(define_expand "sibcall_value" + [(parallel [(set (match_operand 0 "register_operand" "") + (call (match_operand 1 "memory_operand" "") + (match_operand 2 "general_operand" ""))) + (use (match_operand 3 "" "")) + (use (reg:SI LR_REGNUM))])] + "TARGET_ARM" + " + { + if (operands[3] == NULL_RTX) + operands[3] = const0_rtx; + }" +) + +(define_insn "*sibcall_insn" + [(call (mem:SI (match_operand:SI 0 "" "X")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (reg:SI LR_REGNUM))] + "TARGET_ARM && GET_CODE (operands[0]) == SYMBOL_REF" + "* + return NEED_PLT_RELOC ? \"b%?\\t%a0(PLT)\" : \"b%?\\t%a0\"; + " + [(set_attr "type" "call")] +) + +(define_insn "*sibcall_value_insn" + [(set (match_operand 0 "s_register_operand" "=r,f") + (call (mem:SI (match_operand:SI 1 "" "X,X")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (use (reg:SI LR_REGNUM))] + "TARGET_ARM && GET_CODE (operands[1]) == SYMBOL_REF" + "* + return NEED_PLT_RELOC ? \"b%?\\t%a1(PLT)\" : \"b%?\\t%a1\"; + " + [(set_attr "type" "call")] +) + +;; Often the return insn will be the same as loading from memory, so set attr +(define_insn "return" + [(return)] + "TARGET_ARM && USE_RETURN_INSN (FALSE)" + "* + { + if (arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return output_return_instruction (NULL, TRUE, FALSE); + }" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*cond_return" + [(set (pc) + (if_then_else (match_operator 0 "arm_comparison_operator" + [(match_operand 1 "cc_register" "") (const_int 0)]) + (return) + (pc)))] + "TARGET_ARM && USE_RETURN_INSN (TRUE)" + "* + { + if (arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return output_return_instruction (operands[0], TRUE, FALSE); + }" + [(set_attr "conds" "use") + (set_attr "type" "load")] +) + +(define_insn "*cond_return_inverted" + [(set (pc) + (if_then_else (match_operator 0 "arm_comparison_operator" + [(match_operand 1 "cc_register" "") (const_int 0)]) + (pc) + (return)))] + "TARGET_ARM && USE_RETURN_INSN (TRUE)" + "* + { + if (arm_ccfsm_state == 2) + { + arm_ccfsm_state += 2; + return \"\"; + } + return output_return_instruction (operands[0], TRUE, TRUE); + }" + [(set_attr "conds" "use") + (set_attr "type" "load")] +) + +;; Call subroutine returning any type. + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "TARGET_ARM" + " + { + int i; + + emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + /* The optimizer does not know that the call sets the function value + registers we stored in the result block. We avoid problems by + claiming that all hard registers are used and clobbered at this + point. */ + emit_insn (gen_blockage ()); + + DONE; + }" +) + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] VUNSPEC_BLOCKAGE)] + "TARGET_EITHER" + "" + [(set_attr "length" "0") + (set_attr "type" "block")] +) + +(define_expand "casesi" + [(match_operand:SI 0 "s_register_operand" "") ; index to jump on + (match_operand:SI 1 "const_int_operand" "") ; lower bound + (match_operand:SI 2 "const_int_operand" "") ; total range + (match_operand:SI 3 "" "") ; table label + (match_operand:SI 4 "" "")] ; Out of range label + "TARGET_ARM" + " + { + rtx reg; + if (operands[1] != const0_rtx) + { + reg = gen_reg_rtx (SImode); + + emit_insn (gen_addsi3 (reg, operands[0], + GEN_INT (-INTVAL (operands[1])))); + operands[0] = reg; + } + + if (!const_ok_for_arm (INTVAL (operands[2]))) + operands[2] = force_reg (SImode, operands[2]); + + emit_jump_insn (gen_casesi_internal (operands[0], operands[2], operands[3], + operands[4])); + DONE; + }" +) + +;; The USE in this pattern is needed to tell flow analysis that this is +;; a CASESI insn. It has no other purpose. +(define_insn "casesi_internal" + [(parallel [(set (pc) + (if_then_else + (leu (match_operand:SI 0 "s_register_operand" "r") + (match_operand:SI 1 "arm_rhs_operand" "rI")) + (mem:SI (plus:SI (mult:SI (match_dup 0) (const_int 4)) + (label_ref (match_operand 2 "" "")))) + (label_ref (match_operand 3 "" "")))) + (clobber (reg:CC CC_REGNUM)) + (use (label_ref (match_dup 2)))])] + "TARGET_ARM" + "* + if (flag_pic) + return \"cmp\\t%0, %1\;addls\\t%|pc, %|pc, %0, asl #2\;b\\t%l3\"; + return \"cmp\\t%0, %1\;ldrls\\t%|pc, [%|pc, %0, asl #2]\;b\\t%l3\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_expand "indirect_jump" + [(set (pc) + (match_operand:SI 0 "s_register_operand" ""))] + "TARGET_EITHER" + "" +) + +(define_insn "*arm_indirect_jump" + [(set (pc) + (match_operand:SI 0 "s_register_operand" "r"))] + "TARGET_ARM" + "mov%?\\t%|pc, %0\\t%@ indirect register jump" + [(set_attr "predicable" "yes")] +) + +;; Although not supported by the define_expand above, +;; cse/combine may generate this form. +(define_insn "*load_indirect_jump" + [(set (pc) + (match_operand:SI 0 "memory_operand" "m"))] + "TARGET_ARM" + "ldr%?\\t%|pc, %0\\t%@ indirect memory jump" + [(set_attr "type" "load") + (set_attr "pool_range" "4096") + (set_attr "neg_pool_range" "4084") + (set_attr "predicable" "yes")] +) + +(define_insn "*thumb_indirect_jump" + [(set (pc) + (match_operand:SI 0 "register_operand" "l*r"))] + "TARGET_THUMB" + "mov\\tpc, %0" + [(set_attr "conds" "clob") + (set_attr "length" "2")] +) + + +;; Misc insns + +(define_insn "nop" + [(const_int 0)] + "TARGET_EITHER" + "* + if (TARGET_ARM) + return \"mov%?\\t%|r0, %|r0\\t%@ nop\"; + return \"mov\\tr8, r8\"; + " + [(set (attr "length") + (if_then_else (eq_attr "is_thumb" "yes") + (const_int 2) + (const_int 4)))] +) + + +;; Patterns to allow combination of arithmetic, cond code and shifts + +(define_insn "*arith_shiftsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operator:SI 1 "shiftable_operator" + [(match_operator:SI 3 "shift_operator" + [(match_operand:SI 4 "s_register_operand" "r") + (match_operand:SI 5 "reg_or_int_operand" "rI")]) + (match_operand:SI 2 "s_register_operand" "r")]))] + "TARGET_ARM" + "%i1%?\\t%0, %2, %4%S3" + [(set_attr "predicable" "yes") + (set_attr "shift" "4") + ] +) + +(define_insn "*arith_shiftsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (match_operator:SI 1 "shiftable_operator" + [(match_operator:SI 3 "shift_operator" + [(match_operand:SI 4 "s_register_operand" "r") + (match_operand:SI 5 "reg_or_int_operand" "rI")]) + (match_operand:SI 2 "s_register_operand" "r")]) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (match_op_dup 1 [(match_op_dup 3 [(match_dup 4) (match_dup 5)]) + (match_dup 2)]))] + "TARGET_ARM" + "%i1%?s\\t%0, %2, %4%S3" + [(set_attr "conds" "set") + (set_attr "shift" "4") + ] +) + +(define_insn "*arith_shiftsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (match_operator:SI 1 "shiftable_operator" + [(match_operator:SI 3 "shift_operator" + [(match_operand:SI 4 "s_register_operand" "r") + (match_operand:SI 5 "reg_or_int_operand" "rI")]) + (match_operand:SI 2 "s_register_operand" "r")]) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "%i1%?s\\t%0, %2, %4%S3" + [(set_attr "conds" "set") + (set_attr "shift" "4") + ] +) + +(define_insn "*sub_shiftsi" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "reg_or_int_operand" "rM")])))] + "TARGET_ARM" + "sub%?\\t%0, %1, %3%S2" + [(set_attr "predicable" "yes") + (set_attr "shift" "3") + ] +) + +(define_insn "*sub_shiftsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (minus:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "reg_or_int_operand" "rM")])) + (const_int 0))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM" + "sub%?s\\t%0, %1, %3%S2" + [(set_attr "conds" "set") + (set_attr "shift" "3") + ] +) + +(define_insn "*sub_shiftsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV + (minus:SI (match_operand:SI 1 "s_register_operand" "r") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "reg_or_int_operand" "rM")])) + (const_int 0))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "sub%?s\\t%0, %1, %3%S2" + [(set_attr "conds" "set") + (set_attr "shift" "3") + ] +) + +;; These variants of the above insns can occur if the first operand is the +;; frame pointer and we eliminate that. This is a kludge, but there doesn't +;; seem to be a way around it. Most of the predicates have to be null +;; because the format can be generated part way through reload, so +;; if we don't match it as soon as it becomes available, reload doesn't know +;; how to reload pseudos that haven't got hard registers; the constraints will +;; sort everything out. + +(define_insn "*reload_mulsi3" + [(set (match_operand:SI 0 "" "=&r") + (plus:SI (plus:SI (match_operator:SI 5 "shift_operator" + [(match_operand:SI 3 "" "r") + (match_operand:SI 4 "" "rM")]) + (match_operand:SI 2 "" "r")) + (match_operand:SI 1 "const_int_operand" "n")))] + "TARGET_ARM && reload_in_progress" + "* + output_asm_insn (\"add%?\\t%0, %2, %3%S5\", operands); + operands[2] = operands[1]; + operands[1] = operands[0]; + return output_add_immediate (operands); + " + [ + ; we have no idea how long the add_immediate is, it could be up to 4. + (set_attr "length" "20")] +) + +(define_insn "*reload_mulsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (plus:SI + (plus:SI + (match_operator:SI 5 "shift_operator" + [(match_operand:SI 3 "" "r") + (match_operand:SI 4 "" "rM")]) + (match_operand:SI 1 "" "r")) + (match_operand:SI 2 "const_int_operand" "n")) + (const_int 0))) + (set (match_operand:SI 0 "" "=&r") + (plus:SI (plus:SI (match_op_dup 5 [(match_dup 3) (match_dup 4)]) + (match_dup 1)) + (match_dup 2)))] + "TARGET_ARM && reload_in_progress && !arm_is_xscale" + "* + output_add_immediate (operands); + return \"add%?s\\t%0, %0, %3%S5\"; + " + [(set_attr "conds" "set") + (set_attr "shift" "3") + (set_attr "length" "20")] +) + +(define_insn "*reload_mulsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (plus:SI + (plus:SI + (match_operator:SI 5 "shift_operator" + [(match_operand:SI 3 "" "r") + (match_operand:SI 4 "" "rM")]) + (match_operand:SI 1 "" "r")) + (match_operand:SI 2 "const_int_operand" "n")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=&r"))] + "TARGET_ARM && reload_in_progress && !arm_is_xscale" + "* + output_add_immediate (operands); + return \"add%?s\\t%0, %0, %3%S5\"; + " + [(set_attr "conds" "set") + (set_attr "shift" "3") + (set_attr "length" "20")] +) + +;; These are similar, but are needed when the mla pattern contains the +;; eliminated register as operand 3. + +(define_insn "*reload_muladdsi" + [(set (match_operand:SI 0 "" "=&r,&r") + (plus:SI (plus:SI (mult:SI (match_operand:SI 1 "" "%0,r") + (match_operand:SI 2 "" "r,r")) + (match_operand:SI 3 "" "r,r")) + (match_operand:SI 4 "const_int_operand" "n,n")))] + "TARGET_ARM && reload_in_progress" + "* + output_asm_insn (\"mla%?\\t%0, %2, %1, %3\", operands); + operands[2] = operands[4]; + operands[1] = operands[0]; + return output_add_immediate (operands); + " + [(set_attr "length" "20") + (set_attr "type" "mult")] +) + +(define_insn "*reload_muladdsi_compare0" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (plus:SI (plus:SI (mult:SI + (match_operand:SI 3 "" "r") + (match_operand:SI 4 "" "r")) + (match_operand:SI 1 "" "r")) + (match_operand:SI 2 "const_int_operand" "n")) + (const_int 0))) + (set (match_operand:SI 0 "" "=&r") + (plus:SI (plus:SI (mult:SI (match_dup 3) (match_dup 4)) (match_dup 1)) + (match_dup 2)))] + "TARGET_ARM && reload_in_progress && !arm_is_xscale" + "* + output_add_immediate (operands); + output_asm_insn (\"mla%?s\\t%0, %3, %4, %0\", operands); + return \"\"; + " + [(set_attr "length" "20") + (set_attr "conds" "set") + (set_attr "type" "mult")] +) + +(define_insn "*reload_muladdsi_compare0_scratch" + [(set (reg:CC_NOOV CC_REGNUM) + (compare:CC_NOOV (plus:SI (plus:SI (mult:SI + (match_operand:SI 3 "" "r") + (match_operand:SI 4 "" "r")) + (match_operand:SI 1 "" "r")) + (match_operand:SI 2 "const_int_operand" "n")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=&r"))] + "TARGET_ARM && reload_in_progress" + "* + output_add_immediate (operands); + return \"mla%?s\\t%0, %3, %4, %0\"; + " + [(set_attr "length" "20") + (set_attr "conds" "set") + (set_attr "type" "mult")] +) + + + +(define_insn "*and_scc" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (and:SI (match_operator:SI 1 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 2 "s_register_operand" "r")))] + "TARGET_ARM" + "mov%D1\\t%0, #0\;and%d1\\t%0, %2, #1" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*ior_scc" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (ior:SI (match_operator:SI 2 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "s_register_operand" "0,?r")))] + "TARGET_ARM" + "@ + orr%d2\\t%0, %1, #1 + mov%D2\\t%0, %1\;orr%d2\\t%0, %1, #1" + [(set_attr "conds" "use") + (set_attr "length" "4,8")] +) + +(define_insn "*compare_scc" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (match_operator:SI 1 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_add_operand" "rI,L")])) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + if (GET_CODE (operands[1]) == LT && operands[3] == const0_rtx) + return \"mov\\t%0, %2, lsr #31\"; + + if (GET_CODE (operands[1]) == GE && operands[3] == const0_rtx) + return \"mvn\\t%0, %2\;mov\\t%0, %0, lsr #31\"; + + if (GET_CODE (operands[1]) == NE) + { + if (which_alternative == 1) + return \"adds\\t%0, %2, #%n3\;movne\\t%0, #1\"; + return \"subs\\t%0, %2, %3\;movne\\t%0, #1\"; + } + if (which_alternative == 1) + output_asm_insn (\"cmn\\t%2, #%n3\", operands); + else + output_asm_insn (\"cmp\\t%2, %3\", operands); + return \"mov%D1\\t%0, #0\;mov%d1\\t%0, #1\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*cond_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI (match_operator 3 "equality_operator" + [(match_operator 4 "arm_comparison_operator" + [(match_operand 5 "cc_register" "") (const_int 0)]) + (const_int 0)]) + (match_operand:SI 1 "arm_rhs_operand" "0,rI,?rI") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI")))] + "TARGET_ARM" + "* + if (GET_CODE (operands[3]) == NE) + { + if (which_alternative != 1) + output_asm_insn (\"mov%D4\\t%0, %2\", operands); + if (which_alternative != 0) + output_asm_insn (\"mov%d4\\t%0, %1\", operands); + return \"\"; + } + if (which_alternative != 0) + output_asm_insn (\"mov%D4\\t%0, %1\", operands); + if (which_alternative != 1) + output_asm_insn (\"mov%d4\\t%0, %2\", operands); + return \"\"; + " + [(set_attr "conds" "use") + (set_attr "length" "4,4,8")] +) + +(define_insn "*cond_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (match_operator:SI 5 "shiftable_operator" + [(match_operator:SI 4 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")]) + (match_operand:SI 1 "s_register_operand" "0,?r")])) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + if (GET_CODE (operands[4]) == LT && operands[3] == const0_rtx) + return \"%i5\\t%0, %1, %2, lsr #31\"; + + output_asm_insn (\"cmp\\t%2, %3\", operands); + if (GET_CODE (operands[5]) == AND) + output_asm_insn (\"mov%D4\\t%0, #0\", operands); + else if (GET_CODE (operands[5]) == MINUS) + output_asm_insn (\"rsb%D4\\t%0, %1, #0\", operands); + else if (which_alternative != 0) + output_asm_insn (\"mov%D4\\t%0, %1\", operands); + return \"%i5%d4\\t%0, %1, #1\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*cond_sub" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "s_register_operand" "0,?r") + (match_operator:SI 4 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + output_asm_insn (\"cmp\\t%2, %3\", operands); + if (which_alternative != 0) + output_asm_insn (\"mov%D4\\t%0, %1\", operands); + return \"sub%d4\\t%0, %1, #1\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*cmp_ite0" + [(set (match_operand 6 "dominant_cc_register" "") + (compare + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand:SI 0 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L,rI,L")]) + (match_operator:SI 5 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,rI,L,L")]) + (const_int 0)) + (const_int 0)))] + "TARGET_ARM" + "* + { + static const char * const opcodes[4][2] = + { + {\"cmp\\t%2, %3\;cmp%d5\\t%0, %1\", + \"cmp\\t%0, %1\;cmp%d4\\t%2, %3\"}, + {\"cmp\\t%2, %3\;cmn%d5\\t%0, #%n1\", + \"cmn\\t%0, #%n1\;cmp%d4\\t%2, %3\"}, + {\"cmn\\t%2, #%n3\;cmp%d5\\t%0, %1\", + \"cmp\\t%0, %1\;cmn%d4\\t%2, #%n3\"}, + {\"cmn\\t%2, #%n3\;cmn%d5\\t%0, #%n1\", + \"cmn\\t%0, #%n1\;cmn%d4\\t%2, #%n3\"} + }; + int swap = + comparison_dominates_p (GET_CODE (operands[5]), GET_CODE (operands[4])); + + return opcodes[which_alternative][swap]; + }" + [(set_attr "conds" "set") + (set_attr "length" "8")] +) + +(define_insn "*cmp_ite1" + [(set (match_operand 6 "dominant_cc_register" "") + (compare + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand:SI 0 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L,rI,L")]) + (match_operator:SI 5 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,rI,L,L")]) + (const_int 1)) + (const_int 0)))] + "TARGET_ARM" + "* + { + static const char * const opcodes[4][2] = + { + {\"cmp\\t%0, %1\;cmp%d4\\t%2, %3\", + \"cmp\\t%2, %3\;cmp%D5\\t%0, %1\"}, + {\"cmn\\t%0, #%n1\;cmp%d4\\t%2, %3\", + \"cmp\\t%2, %3\;cmn%D5\\t%0, #%n1\"}, + {\"cmp\\t%0, %1\;cmn%d4\\t%2, #%n3\", + \"cmn\\t%2, #%n3\;cmp%D5\\t%0, %1\"}, + {\"cmn\\t%0, #%n1\;cmn%d4\\t%2, #%n3\", + \"cmn\\t%2, #%n3\;cmn%D5\\t%0, #%n1\"} + }; + int swap = + comparison_dominates_p (GET_CODE (operands[5]), + reverse_condition (GET_CODE (operands[4]))); + + return opcodes[which_alternative][swap]; + }" + [(set_attr "conds" "set") + (set_attr "length" "8")] +) + +(define_insn "*cmp_and" + [(set (match_operand 6 "dominant_cc_register" "") + (compare + (and:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand:SI 0 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L,rI,L")]) + (match_operator:SI 5 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,rI,L,L")])) + (const_int 0)))] + "TARGET_ARM" + "* + { + static const char *const opcodes[4][2] = + { + {\"cmp\\t%2, %3\;cmp%d5\\t%0, %1\", + \"cmp\\t%0, %1\;cmp%d4\\t%2, %3\"}, + {\"cmp\\t%2, %3\;cmn%d5\\t%0, #%n1\", + \"cmn\\t%0, #%n1\;cmp%d4\\t%2, %3\"}, + {\"cmn\\t%2, #%n3\;cmp%d5\\t%0, %1\", + \"cmp\\t%0, %1\;cmn%d4\\t%2, #%n3\"}, + {\"cmn\\t%2, #%n3\;cmn%d5\\t%0, #%n1\", + \"cmn\\t%0, #%n1\;cmn%d4\\t%2, #%n3\"} + }; + int swap = + comparison_dominates_p (GET_CODE (operands[5]), GET_CODE (operands[4])); + + return opcodes[which_alternative][swap]; + }" + [(set_attr "conds" "set") + (set_attr "predicable" "no") + (set_attr "length" "8")] +) + +(define_insn "*cmp_ior" + [(set (match_operand 6 "dominant_cc_register" "") + (compare + (ior:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand:SI 0 "s_register_operand" "r,r,r,r") + (match_operand:SI 1 "arm_add_operand" "rI,L,rI,L")]) + (match_operator:SI 5 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,rI,L,L")])) + (const_int 0)))] + "TARGET_ARM" + "* +{ + static const char *const opcodes[4][2] = + { + {\"cmp\\t%0, %1\;cmp%D4\\t%2, %3\", + \"cmp\\t%2, %3\;cmp%D5\\t%0, %1\"}, + {\"cmn\\t%0, #%n1\;cmp%D4\\t%2, %3\", + \"cmp\\t%2, %3\;cmn%D5\\t%0, #%n1\"}, + {\"cmp\\t%0, %1\;cmn%D4\\t%2, #%n3\", + \"cmn\\t%2, #%n3\;cmp%D5\\t%0, %1\"}, + {\"cmn\\t%0, #%n1\;cmn%D4\\t%2, #%n3\", + \"cmn\\t%2, #%n3\;cmn%D5\\t%0, #%n1\"} + }; + int swap = + comparison_dominates_p (GET_CODE (operands[5]), GET_CODE (operands[4])); + + return opcodes[which_alternative][swap]; +} +" + [(set_attr "conds" "set") + (set_attr "length" "8")] +) + +(define_insn "*negscc" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (neg:SI (match_operator 3 "arm_comparison_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + if (GET_CODE (operands[3]) == LT && operands[3] == const0_rtx) + return \"mov\\t%0, %1, asr #31\"; + + if (GET_CODE (operands[3]) == NE) + return \"subs\\t%0, %1, %2\;mvnne\\t%0, #0\"; + + if (GET_CODE (operands[3]) == GT) + return \"subs\\t%0, %1, %2\;mvnne\\t%0, %0, asr #31\"; + + output_asm_insn (\"cmp\\t%1, %2\", operands); + output_asm_insn (\"mov%D3\\t%0, #0\", operands); + return \"mvn%d3\\t%0, #0\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "movcond" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "r,r,r") + (match_operand:SI 4 "arm_add_operand" "rIL,rIL,rIL")]) + (match_operand:SI 1 "arm_rhs_operand" "0,rI,?rI") + (match_operand:SI 2 "arm_rhs_operand" "rI,0,rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + if (GET_CODE (operands[5]) == LT + && (operands[4] == const0_rtx)) + { + if (which_alternative != 1 && GET_CODE (operands[1]) == REG) + { + if (operands[2] == const0_rtx) + return \"and\\t%0, %1, %3, asr #31\"; + return \"ands\\t%0, %1, %3, asr #32\;movcc\\t%0, %2\"; + } + else if (which_alternative != 0 && GET_CODE (operands[2]) == REG) + { + if (operands[1] == const0_rtx) + return \"bic\\t%0, %2, %3, asr #31\"; + return \"bics\\t%0, %2, %3, asr #32\;movcs\\t%0, %1\"; + } + /* The only case that falls through to here is when both ops 1 & 2 + are constants */ + } + + if (GET_CODE (operands[5]) == GE + && (operands[4] == const0_rtx)) + { + if (which_alternative != 1 && GET_CODE (operands[1]) == REG) + { + if (operands[2] == const0_rtx) + return \"bic\\t%0, %1, %3, asr #31\"; + return \"bics\\t%0, %1, %3, asr #32\;movcs\\t%0, %2\"; + } + else if (which_alternative != 0 && GET_CODE (operands[2]) == REG) + { + if (operands[1] == const0_rtx) + return \"and\\t%0, %2, %3, asr #31\"; + return \"ands\\t%0, %2, %3, asr #32\;movcc\\t%0, %1\"; + } + /* The only case that falls through to here is when both ops 1 & 2 + are constants */ + } + if (GET_CODE (operands[4]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[4]))) + output_asm_insn (\"cmn\\t%3, #%n4\", operands); + else + output_asm_insn (\"cmp\\t%3, %4\", operands); + if (which_alternative != 0) + output_asm_insn (\"mov%d5\\t%0, %1\", operands); + if (which_alternative != 1) + output_asm_insn (\"mov%D5\\t%0, %2\", operands); + return \"\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8,8,12")] +) + +(define_insn "*ifcompare_plus_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_add_operand" "rIL,rIL")]) + (plus:SI + (match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_add_operand" "rIL,rIL")) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_plus_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 5 "cc_register" "") (const_int 0)]) + (plus:SI + (match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,L,rI,L")) + (match_operand:SI 1 "arm_rhs_operand" "0,0,?rI,?rI")))] + "TARGET_ARM" + "@ + add%d4\\t%0, %2, %3 + sub%d4\\t%0, %2, #%n3 + add%d4\\t%0, %2, %3\;mov%D4\\t%0, %1 + sub%d4\\t%0, %2, #%n3\;mov%D4\\t%0, %1" + [(set_attr "conds" "use") + (set_attr "length" "4,4,8,8") + (set_attr "type" "*,*,*,*")] +) + +(define_insn "*ifcompare_move_plus" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_add_operand" "rIL,rIL")]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI") + (plus:SI + (match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_add_operand" "rIL,rIL")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_move_plus" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 5 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_rhs_operand" "0,0,?rI,?rI") + (plus:SI + (match_operand:SI 2 "s_register_operand" "r,r,r,r") + (match_operand:SI 3 "arm_add_operand" "rI,L,rI,L"))))] + "TARGET_ARM" + "@ + add%D4\\t%0, %2, %3 + sub%D4\\t%0, %2, #%n3 + add%D4\\t%0, %2, %3\;mov%d4\\t%0, %1 + sub%D4\\t%0, %2, #%n3\;mov%d4\\t%0, %1" + [(set_attr "conds" "use") + (set_attr "length" "4,4,8,8") + (set_attr "type" "*,*,*,*")] +) + +(define_insn "*ifcompare_arith_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI (match_operator 9 "arm_comparison_operator" + [(match_operand:SI 5 "s_register_operand" "r") + (match_operand:SI 6 "arm_add_operand" "rIL")]) + (match_operator:SI 8 "shiftable_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")]) + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "arm_rhs_operand" "rI")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*if_arith_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI (match_operator 5 "arm_comparison_operator" + [(match_operand 8 "cc_register" "") (const_int 0)]) + (match_operator:SI 6 "shiftable_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rI")]) + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "arm_rhs_operand" "rI")])))] + "TARGET_ARM" + "%I6%d5\\t%0, %1, %2\;%I7%D5\\t%0, %3, %4" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*ifcompare_arith_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_add_operand" "rIL,rIL")]) + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_rhs_operand" "rI,rI")]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + /* If we have an operation where (op x 0) is the identity operation and + the conditional operator is LT or GE and we are comparing against zero and + everything is in registers then we can do this in two instructions */ + if (operands[3] == const0_rtx + && GET_CODE (operands[7]) != AND + && GET_CODE (operands[5]) == REG + && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == REGNO (operands[4]) + && REGNO (operands[4]) != REGNO (operands[0])) + { + if (GET_CODE (operands[6]) == LT) + return \"and\\t%0, %5, %2, asr #31\;%I7\\t%0, %4, %0\"; + else if (GET_CODE (operands[6]) == GE) + return \"bic\\t%0, %5, %2, asr #31\;%I7\\t%0, %4, %0\"; + } + if (GET_CODE (operands[3]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[3]))) + output_asm_insn (\"cmn\\t%2, #%n3\", operands); + else + output_asm_insn (\"cmp\\t%2, %3\", operands); + output_asm_insn (\"%I7%d6\\t%0, %4, %5\", operands); + if (which_alternative != 0) + return \"mov%D6\\t%0, %1\"; + return \"\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_arith_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 4 "arm_comparison_operator" + [(match_operand 6 "cc_register" "") (const_int 0)]) + (match_operator:SI 5 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI")))] + "TARGET_ARM" + "@ + %I5%d4\\t%0, %2, %3 + %I5%d4\\t%0, %2, %3\;mov%D4\\t%0, %1" + [(set_attr "conds" "use") + (set_attr "length" "4,8") + (set_attr "type" "*,*")] +) + +(define_insn "*ifcompare_move_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_add_operand" "rIL,rIL")]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI") + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "* + /* If we have an operation where (op x 0) is the identity operation and + the conditional operator is LT or GE and we are comparing against zero and + everything is in registers then we can do this in two instructions */ + if (operands[5] == const0_rtx + && GET_CODE (operands[7]) != AND + && GET_CODE (operands[3]) == REG + && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == REGNO (operands[2]) + && REGNO (operands[2]) != REGNO (operands[0])) + { + if (GET_CODE (operands[6]) == GE) + return \"and\\t%0, %3, %4, asr #31\;%I7\\t%0, %2, %0\"; + else if (GET_CODE (operands[6]) == LT) + return \"bic\\t%0, %3, %4, asr #31\;%I7\\t%0, %2, %0\"; + } + + if (GET_CODE (operands[5]) == CONST_INT + && !const_ok_for_arm (INTVAL (operands[5]))) + output_asm_insn (\"cmn\\t%4, #%n5\", operands); + else + output_asm_insn (\"cmp\\t%4, %5\", operands); + + if (which_alternative != 0) + output_asm_insn (\"mov%d6\\t%0, %1\", operands); + return \"%I7%D6\\t%0, %2, %3\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_move_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 6 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI") + (match_operator:SI 5 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rI,rI")])))] + "TARGET_ARM" + "@ + %I5%D4\\t%0, %2, %3 + %I5%D4\\t%0, %2, %3\;mov%d4\\t%0, %1" + [(set_attr "conds" "use") + (set_attr "length" "4,8") + (set_attr "type" "*,*")] +) + +(define_insn "*ifcompare_move_not" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "r,r") + (match_operand:SI 4 "arm_add_operand" "rIL,rIL")]) + (match_operand:SI 1 "arm_not_operand" "0,?rIK") + (not:SI + (match_operand:SI 2 "s_register_operand" "r,r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_move_not" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K") + (not:SI (match_operand:SI 2 "s_register_operand" "r,r,r"))))] + "TARGET_ARM" + "@ + mvn%D4\\t%0, %2 + mov%d4\\t%0, %1\;mvn%D4\\t%0, %2 + mvn%d4\\t%0, #%B1\;mvn%D4\\t%0, %2" + [(set_attr "conds" "use") + (set_attr "length" "4,8,8")] +) + +(define_insn "*ifcompare_not_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "r,r") + (match_operand:SI 4 "arm_add_operand" "rIL,rIL")]) + (not:SI + (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:SI 1 "arm_not_operand" "0,?rIK"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_not_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (not:SI (match_operand:SI 2 "s_register_operand" "r,r,r")) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K")))] + "TARGET_ARM" + "@ + mvn%d4\\t%0, %2 + mov%D4\\t%0, %1\;mvn%d4\\t%0, %2 + mvn%D4\\t%0, #%B1\;mvn%d4\\t%0, %2" + [(set_attr "conds" "use") + (set_attr "length" "4,8,8")] +) + +(define_insn "*ifcompare_shift_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_add_operand" "rIL,rIL")]) + (match_operator:SI 7 "shift_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rM,rM")]) + (match_operand:SI 1 "arm_not_operand" "0,?rIK"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_shift_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand 6 "cc_register" "") (const_int 0)]) + (match_operator:SI 4 "shift_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r") + (match_operand:SI 3 "arm_rhs_operand" "rM,rM,rM")]) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K")))] + "TARGET_ARM" + "@ + mov%d5\\t%0, %2%S4 + mov%D5\\t%0, %1\;mov%d5\\t%0, %2%S4 + mvn%D5\\t%0, #%B1\;mov%d5\\t%0, %2%S4" + [(set_attr "conds" "use") + (set_attr "shift" "2") + (set_attr "length" "4,8,8")] +) + +(define_insn "*ifcompare_move_shift" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r,r") + (match_operand:SI 5 "arm_add_operand" "rIL,rIL")]) + (match_operand:SI 1 "arm_not_operand" "0,?rIK") + (match_operator:SI 7 "shift_operator" + [(match_operand:SI 2 "s_register_operand" "r,r") + (match_operand:SI 3 "arm_rhs_operand" "rM,rM")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_move_shift" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand 6 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K") + (match_operator:SI 4 "shift_operator" + [(match_operand:SI 2 "s_register_operand" "r,r,r") + (match_operand:SI 3 "arm_rhs_operand" "rM,rM,rM")])))] + "TARGET_ARM" + "@ + mov%D5\\t%0, %2%S4 + mov%d5\\t%0, %1\;mov%D5\\t%0, %2%S4 + mvn%d5\\t%0, #%B1\;mov%D5\\t%0, %2%S4" + [(set_attr "conds" "use") + (set_attr "shift" "2") + (set_attr "length" "4,8,8")] +) + +(define_insn "*ifcompare_shift_shift" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 7 "arm_comparison_operator" + [(match_operand:SI 5 "s_register_operand" "r") + (match_operand:SI 6 "arm_add_operand" "rIL")]) + (match_operator:SI 8 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")]) + (match_operator:SI 9 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "arm_rhs_operand" "rM")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*if_shift_shift" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand 8 "cc_register" "") (const_int 0)]) + (match_operator:SI 6 "shift_operator" + [(match_operand:SI 1 "s_register_operand" "r") + (match_operand:SI 2 "arm_rhs_operand" "rM")]) + (match_operator:SI 7 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "arm_rhs_operand" "rM")])))] + "TARGET_ARM" + "mov%d5\\t%0, %1%S6\;mov%D5\\t%0, %3%S7" + [(set_attr "conds" "use") + (set_attr "shift" "1") + (set_attr "length" "8")] +) + +(define_insn "*ifcompare_not_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r") + (match_operand:SI 5 "arm_add_operand" "rIL")]) + (not:SI (match_operand:SI 1 "s_register_operand" "r")) + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r") + (match_operand:SI 3 "arm_rhs_operand" "rI")]))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*if_not_arith" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (not:SI (match_operand:SI 1 "s_register_operand" "r")) + (match_operator:SI 6 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r") + (match_operand:SI 3 "arm_rhs_operand" "rI")])))] + "TARGET_ARM" + "mvn%d5\\t%0, %1\;%I6%D5\\t%0, %2, %3" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*ifcompare_arith_not" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 6 "arm_comparison_operator" + [(match_operand:SI 4 "s_register_operand" "r") + (match_operand:SI 5 "arm_add_operand" "rIL")]) + (match_operator:SI 7 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r") + (match_operand:SI 3 "arm_rhs_operand" "rI")]) + (not:SI (match_operand:SI 1 "s_register_operand" "r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +(define_insn "*if_arith_not" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand 4 "cc_register" "") (const_int 0)]) + (match_operator:SI 6 "shiftable_operator" + [(match_operand:SI 2 "s_register_operand" "r") + (match_operand:SI 3 "arm_rhs_operand" "rI")]) + (not:SI (match_operand:SI 1 "s_register_operand" "r"))))] + "TARGET_ARM" + "mvn%D5\\t%0, %1\;%I6%d5\\t%0, %2, %3" + [(set_attr "conds" "use") + (set_attr "length" "8")] +) + +(define_insn "*ifcompare_neg_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "r,r") + (match_operand:SI 4 "arm_add_operand" "rIL,rIL")]) + (neg:SI (match_operand:SI 2 "s_register_operand" "r,r")) + (match_operand:SI 1 "arm_not_operand" "0,?rIK"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_neg_move" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (neg:SI (match_operand:SI 2 "s_register_operand" "r,r,r")) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K")))] + "TARGET_ARM" + "@ + rsb%d4\\t%0, %2, #0 + mov%D4\\t%0, %1\;rsb%d4\\t%0, %2, #0 + mvn%D4\\t%0, #%B1\;rsb%d4\\t%0, %2, #0" + [(set_attr "conds" "use") + (set_attr "length" "4,8,8")] +) + +(define_insn "*ifcompare_move_neg" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI + (match_operator 5 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "r,r") + (match_operand:SI 4 "arm_add_operand" "rIL,rIL")]) + (match_operand:SI 1 "arm_not_operand" "0,?rIK") + (neg:SI (match_operand:SI 2 "s_register_operand" "r,r")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM" + "#" + [(set_attr "conds" "clob") + (set_attr "length" "8,12")] +) + +(define_insn "*if_move_neg" + [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") + (if_then_else:SI + (match_operator 4 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_not_operand" "0,?rI,K") + (neg:SI (match_operand:SI 2 "s_register_operand" "r,r,r"))))] + "TARGET_ARM" + "@ + rsb%D4\\t%0, %2, #0 + mov%d4\\t%0, %1\;rsb%D4\\t%0, %2, #0 + mvn%d4\\t%0, #%B1\;rsb%D4\\t%0, %2, #0" + [(set_attr "conds" "use") + (set_attr "length" "4,8,8")] +) + +(define_insn "*arith_adjacentmem" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operator:SI 1 "shiftable_operator" + [(match_operand:SI 2 "memory_operand" "m") + (match_operand:SI 3 "memory_operand" "m")])) + (clobber (match_scratch:SI 4 "=r"))] + "TARGET_ARM && adjacent_mem_locations (operands[2], operands[3])" + "* + { + rtx ldm[3]; + rtx arith[4]; + int val1 = 0, val2 = 0; + + if (REGNO (operands[0]) > REGNO (operands[4])) + { + ldm[1] = operands[4]; + ldm[2] = operands[0]; + } + else + { + ldm[1] = operands[0]; + ldm[2] = operands[4]; + } + if (GET_CODE (XEXP (operands[2], 0)) != REG) + val1 = INTVAL (XEXP (XEXP (operands[2], 0), 1)); + if (GET_CODE (XEXP (operands[3], 0)) != REG) + val2 = INTVAL (XEXP (XEXP (operands[3], 0), 1)); + arith[0] = operands[0]; + arith[3] = operands[1]; + if (val1 < val2) + { + arith[1] = ldm[1]; + arith[2] = ldm[2]; + } + else + { + arith[1] = ldm[2]; + arith[2] = ldm[1]; + } + if (val1 && val2) + { + rtx ops[3]; + ldm[0] = ops[0] = operands[4]; + ops[1] = XEXP (XEXP (operands[2], 0), 0); + ops[2] = XEXP (XEXP (operands[2], 0), 1); + output_add_immediate (ops); + if (val1 < val2) + output_asm_insn (\"ldm%?ia\\t%0, {%1, %2}\", ldm); + else + output_asm_insn (\"ldm%?da\\t%0, {%1, %2}\", ldm); + } + else if (val1) + { + ldm[0] = XEXP (operands[3], 0); + if (val1 < val2) + output_asm_insn (\"ldm%?da\\t%0, {%1, %2}\", ldm); + else + output_asm_insn (\"ldm%?ia\\t%0, {%1, %2}\", ldm); + } + else + { + ldm[0] = XEXP (operands[2], 0); + if (val1 < val2) + output_asm_insn (\"ldm%?ia\\t%0, {%1, %2}\", ldm); + else + output_asm_insn (\"ldm%?da\\t%0, {%1, %2}\", ldm); + } + output_asm_insn (\"%I3%?\\t%0, %1, %2\", arith); + return \"\"; + }" + [(set_attr "length" "12") + (set_attr "predicable" "yes") + (set_attr "type" "load")] +) + +;; the arm can support extended pre-inc instructions + +;; In all these cases, we use operands 0 and 1 for the register being +;; incremented because those are the operands that local-alloc will +;; tie and these are the pair most likely to be tieable (and the ones +;; that will benefit the most). + +;; We reject the frame pointer if it occurs anywhere in these patterns since +;; elimination will cause too many headaches. + +(define_insn "*strqi_preinc" + [(set (mem:QI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ"))) + (match_operand:QI 3 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "str%?b\\t%3, [%0, %2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*strqi_predec" + [(set (mem:QI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r"))) + (match_operand:QI 3 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "str%?b\\t%3, [%0, -%2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqi_preinc" + [(set (match_operand:QI 3 "s_register_operand" "=r") + (mem:QI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?b\\t%3, [%0, %2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqi_predec" + [(set (match_operand:QI 3 "s_register_operand" "=r") + (mem:QI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?b\\t%3, [%0, -%2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqisi_preinc" + [(set (match_operand:SI 3 "s_register_operand" "=r") + (zero_extend:SI + (mem:QI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ"))))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?b\\t%3, [%0, %2]!\\t%@ z_extendqisi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqisi_predec" + [(set (match_operand:SI 3 "s_register_operand" "=r") + (zero_extend:SI + (mem:QI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r"))))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?b\\t%3, [%0, -%2]!\\t%@ z_extendqisi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*strsi_preinc" + [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ"))) + (match_operand:SI 3 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "str%?\\t%3, [%0, %2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*strsi_predec" + [(set (mem:SI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r"))) + (match_operand:SI 3 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "str%?\\t%3, [%0, -%2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadsi_preinc" + [(set (match_operand:SI 3 "s_register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?\\t%3, [%0, %2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadsi_predec" + [(set (match_operand:SI 3 "s_register_operand" "=r") + (mem:SI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?\\t%3, [%0, -%2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadhi_preinc" + [(set (match_operand:HI 3 "s_register_operand" "=r") + (mem:HI (plus:SI (match_operand:SI 1 "s_register_operand" "%0") + (match_operand:SI 2 "index_operand" "rJ")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?\\t%3, [%0, %2]!\\t%@ loadhi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadhi_predec" + [(set (match_operand:HI 3 "s_register_operand" "=r") + (mem:HI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operand:SI 2 "s_register_operand" "r")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_dup 2)))] + "TARGET_ARM + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && (GET_CODE (operands[2]) != REG + || REGNO (operands[2]) != FRAME_POINTER_REGNUM)" + "ldr%?\\t%3, [%0, -%2]!\\t%@ loadhi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*strqi_shiftpreinc" + [(set (mem:QI (plus:SI (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]) + (match_operand:SI 1 "s_register_operand" "0"))) + (match_operand:QI 5 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 1)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "str%?b\\t%5, [%0, %3%S2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*strqi_shiftpredec" + [(set (mem:QI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]))) + (match_operand:QI 5 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "str%?b\\t%5, [%0, -%3%S2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqi_shiftpreinc" + [(set (match_operand:QI 5 "s_register_operand" "=r") + (mem:QI (plus:SI (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]) + (match_operand:SI 1 "s_register_operand" "0")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 1)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?b\\t%5, [%0, %3%S2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadqi_shiftpredec" + [(set (match_operand:QI 5 "s_register_operand" "=r") + (mem:QI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")])))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?b\\t%5, [%0, -%3%S2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*strsi_shiftpreinc" + [(set (mem:SI (plus:SI (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]) + (match_operand:SI 1 "s_register_operand" "0"))) + (match_operand:SI 5 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 1)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "str%?\\t%5, [%0, %3%S2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*strsi_shiftpredec" + [(set (mem:SI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]))) + (match_operand:SI 5 "s_register_operand" "r")) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "str%?\\t%5, [%0, -%3%S2]!" + [(set_attr "type" "store1") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadsi_shiftpreinc" + [(set (match_operand:SI 5 "s_register_operand" "=r") + (mem:SI (plus:SI (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]) + (match_operand:SI 1 "s_register_operand" "0")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 1)))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?\\t%5, [%0, %3%S2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadsi_shiftpredec" + [(set (match_operand:SI 5 "s_register_operand" "=r") + (mem:SI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")])))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?\\t%5, [%0, -%3%S2]!" + [(set_attr "type" "load") + (set_attr "predicable" "yes")]) + +(define_insn "*loadhi_shiftpreinc" + [(set (match_operand:HI 5 "s_register_operand" "=r") + (mem:HI (plus:SI (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")]) + (match_operand:SI 1 "s_register_operand" "0")))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (plus:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 1)))] + "TARGET_ARM + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?\\t%5, [%0, %3%S2]!\\t%@ loadhi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +(define_insn "*loadhi_shiftpredec" + [(set (match_operand:HI 5 "s_register_operand" "=r") + (mem:HI (minus:SI (match_operand:SI 1 "s_register_operand" "0") + (match_operator:SI 2 "shift_operator" + [(match_operand:SI 3 "s_register_operand" "r") + (match_operand:SI 4 "const_shift_operand" "n")])))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_dup 1) (match_op_dup 2 [(match_dup 3) + (match_dup 4)])))] + "TARGET_ARM + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && REGNO (operands[0]) != FRAME_POINTER_REGNUM + && REGNO (operands[1]) != FRAME_POINTER_REGNUM + && REGNO (operands[3]) != FRAME_POINTER_REGNUM" + "ldr%?\\t%5, [%0, -%3%S2]!\\t%@ loadhi" + [(set_attr "type" "load") + (set_attr "predicable" "yes")] +) + +; It can also support extended post-inc expressions, but combine doesn't +; try these.... +; It doesn't seem worth adding peepholes for anything but the most common +; cases since, unlike combine, the increment must immediately follow the load +; for this pattern to match. +; We must watch to see that the source/destination register isn't also the +; same as the base address register, and that if the index is a register, +; that it is not the same as the base address register. In such cases the +; instruction that we would generate would have UNPREDICTABLE behaviour so +; we cannot use it. + +(define_peephole + [(set (mem:QI (match_operand:SI 0 "s_register_operand" "+r")) + (match_operand:QI 2 "s_register_operand" "r")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_operand:SI 1 "index_operand" "rJ")))] + "TARGET_ARM + && (REGNO (operands[2]) != REGNO (operands[0])) + && (GET_CODE (operands[1]) != REG + || (REGNO (operands[1]) != REGNO (operands[0])))" + "str%?b\\t%2, [%0], %1" +) + +(define_peephole + [(set (match_operand:QI 0 "s_register_operand" "=r") + (mem:QI (match_operand:SI 1 "s_register_operand" "+r"))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_operand:SI 2 "index_operand" "rJ")))] + "TARGET_ARM + && REGNO (operands[0]) != REGNO(operands[1]) + && (GET_CODE (operands[2]) != REG + || REGNO(operands[0]) != REGNO (operands[2]))" + "ldr%?b\\t%0, [%1], %2" +) + +(define_peephole + [(set (mem:SI (match_operand:SI 0 "s_register_operand" "+r")) + (match_operand:SI 2 "s_register_operand" "r")) + (set (match_dup 0) + (plus:SI (match_dup 0) (match_operand:SI 1 "index_operand" "rJ")))] + "TARGET_ARM + && (REGNO (operands[2]) != REGNO (operands[0])) + && (GET_CODE (operands[1]) != REG + || (REGNO (operands[1]) != REGNO (operands[0])))" + "str%?\\t%2, [%0], %1" +) + +(define_peephole + [(set (match_operand:HI 0 "s_register_operand" "=r") + (mem:HI (match_operand:SI 1 "s_register_operand" "+r"))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_operand:SI 2 "index_operand" "rJ")))] + "TARGET_ARM + && !BYTES_BIG_ENDIAN + && !TARGET_MMU_TRAPS + && REGNO (operands[0]) != REGNO(operands[1]) + && (GET_CODE (operands[2]) != REG + || REGNO(operands[0]) != REGNO (operands[2]))" + "ldr%?\\t%0, [%1], %2\\t%@ loadhi" +) + +(define_peephole + [(set (match_operand:SI 0 "s_register_operand" "=r") + (mem:SI (match_operand:SI 1 "s_register_operand" "+r"))) + (set (match_dup 1) + (plus:SI (match_dup 1) (match_operand:SI 2 "index_operand" "rJ")))] + "TARGET_ARM + && REGNO (operands[0]) != REGNO(operands[1]) + && (GET_CODE (operands[2]) != REG + || REGNO(operands[0]) != REGNO (operands[2]))" + "ldr%?\\t%0, [%1], %2" +) + +(define_peephole + [(set (mem:QI (plus:SI (match_operand:SI 0 "s_register_operand" "+r") + (match_operand:SI 1 "index_operand" "rJ"))) + (match_operand:QI 2 "s_register_operand" "r")) + (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))] + "TARGET_ARM + && (REGNO (operands[2]) != REGNO (operands[0])) + && (GET_CODE (operands[1]) != REG + || (REGNO (operands[1]) != REGNO (operands[0])))" + "str%?b\\t%2, [%0, %1]!" +) + +(define_peephole + [(set (mem:QI (plus:SI (match_operator:SI 4 "shift_operator" + [(match_operand:SI 0 "s_register_operand" "r") + (match_operand:SI 1 "const_int_operand" "n")]) + (match_operand:SI 2 "s_register_operand" "+r"))) + (match_operand:QI 3 "s_register_operand" "r")) + (set (match_dup 2) (plus:SI (match_op_dup 4 [(match_dup 0) (match_dup 1)]) + (match_dup 2)))] + "TARGET_ARM + && (REGNO (operands[3]) != REGNO (operands[2])) + && (REGNO (operands[0]) != REGNO (operands[2]))" + "str%?b\\t%3, [%2, %0%S4]!" +) + +; This pattern is never tried by combine, so do it as a peephole + +(define_peephole2 + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 1 "s_register_operand" "")) + (set (reg:CC CC_REGNUM) + (compare:CC (match_dup 1) (const_int 0)))] + "TARGET_ARM + " + [(parallel [(set (reg:CC CC_REGNUM) (compare:CC (match_dup 1) (const_int 0))) + (set (match_dup 0) (match_dup 1))])] + "" +) + +; Peepholes to spot possible load- and store-multiples, if the ordering is +; reversed, check that the memory references aren't volatile. + +(define_peephole + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operand:SI 4 "memory_operand" "m")) + (set (match_operand:SI 1 "s_register_operand" "=r") + (match_operand:SI 5 "memory_operand" "m")) + (set (match_operand:SI 2 "s_register_operand" "=r") + (match_operand:SI 6 "memory_operand" "m")) + (set (match_operand:SI 3 "s_register_operand" "=r") + (match_operand:SI 7 "memory_operand" "m"))] + "TARGET_ARM && load_multiple_sequence (operands, 4, NULL, NULL, NULL)" + "* + return emit_ldm_seq (operands, 4); + " +) + +(define_peephole + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operand:SI 3 "memory_operand" "m")) + (set (match_operand:SI 1 "s_register_operand" "=r") + (match_operand:SI 4 "memory_operand" "m")) + (set (match_operand:SI 2 "s_register_operand" "=r") + (match_operand:SI 5 "memory_operand" "m"))] + "TARGET_ARM && load_multiple_sequence (operands, 3, NULL, NULL, NULL)" + "* + return emit_ldm_seq (operands, 3); + " +) + +(define_peephole + [(set (match_operand:SI 0 "s_register_operand" "=r") + (match_operand:SI 2 "memory_operand" "m")) + (set (match_operand:SI 1 "s_register_operand" "=r") + (match_operand:SI 3 "memory_operand" "m"))] + "TARGET_ARM && load_multiple_sequence (operands, 2, NULL, NULL, NULL)" + "* + return emit_ldm_seq (operands, 2); + " +) + +(define_peephole + [(set (match_operand:SI 4 "memory_operand" "=m") + (match_operand:SI 0 "s_register_operand" "r")) + (set (match_operand:SI 5 "memory_operand" "=m") + (match_operand:SI 1 "s_register_operand" "r")) + (set (match_operand:SI 6 "memory_operand" "=m") + (match_operand:SI 2 "s_register_operand" "r")) + (set (match_operand:SI 7 "memory_operand" "=m") + (match_operand:SI 3 "s_register_operand" "r"))] + "TARGET_ARM && store_multiple_sequence (operands, 4, NULL, NULL, NULL)" + "* + return emit_stm_seq (operands, 4); + " +) + +(define_peephole + [(set (match_operand:SI 3 "memory_operand" "=m") + (match_operand:SI 0 "s_register_operand" "r")) + (set (match_operand:SI 4 "memory_operand" "=m") + (match_operand:SI 1 "s_register_operand" "r")) + (set (match_operand:SI 5 "memory_operand" "=m") + (match_operand:SI 2 "s_register_operand" "r"))] + "TARGET_ARM && store_multiple_sequence (operands, 3, NULL, NULL, NULL)" + "* + return emit_stm_seq (operands, 3); + " +) + +(define_peephole + [(set (match_operand:SI 2 "memory_operand" "=m") + (match_operand:SI 0 "s_register_operand" "r")) + (set (match_operand:SI 3 "memory_operand" "=m") + (match_operand:SI 1 "s_register_operand" "r"))] + "TARGET_ARM && store_multiple_sequence (operands, 2, NULL, NULL, NULL)" + "* + return emit_stm_seq (operands, 2); + " +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (and:SI (ge:SI (match_operand:SI 1 "s_register_operand" "") + (const_int 0)) + (neg:SI (match_operator:SI 2 "arm_comparison_operator" + [(match_operand:SI 3 "s_register_operand" "") + (match_operand:SI 4 "arm_rhs_operand" "")])))) + (clobber (match_operand:SI 5 "s_register_operand" ""))] + "TARGET_ARM" + [(set (match_dup 5) (not:SI (ashiftrt:SI (match_dup 1) (const_int 31)))) + (set (match_dup 0) (and:SI (match_op_dup 2 [(match_dup 3) (match_dup 4)]) + (match_dup 5)))] + "" +) + +;; This split can be used because CC_Z mode implies that the following +;; branch will be an equality, or an unsigned inequality, so the sign +;; extension is not needed. + +(define_split + [(set (reg:CC_Z CC_REGNUM) + (compare:CC_Z + (ashift:SI (subreg:SI (match_operand:QI 0 "memory_operand" "") 0) + (const_int 24)) + (match_operand 1 "const_int_operand" ""))) + (clobber (match_scratch:SI 2 ""))] + "TARGET_ARM + && (((unsigned HOST_WIDE_INT) INTVAL (operands[1])) + == (((unsigned HOST_WIDE_INT) INTVAL (operands[1])) >> 24) << 24)" + [(set (match_dup 2) (zero_extend:SI (match_dup 0))) + (set (reg:CC CC_REGNUM) (compare:CC (match_dup 2) (match_dup 1)))] + " + operands[1] = GEN_INT (((unsigned long) INTVAL (operands[1])) >> 24); + " +) + +(define_expand "prologue" + [(clobber (const_int 0))] + "TARGET_EITHER" + "if (TARGET_ARM) + arm_expand_prologue (); + else + thumb_expand_prologue (); + DONE; + " +) + +(define_expand "epilogue" + [(unspec_volatile [(return)] VUNSPEC_EPILOGUE)] + "TARGET_EITHER" + " + if (TARGET_THUMB) + thumb_expand_epilogue (); + else if (USE_RETURN_INSN (FALSE)) + { + emit_jump_insn (gen_return ()); + DONE; + } + emit_jump_insn (gen_rtx_UNSPEC_VOLATILE (VOIDmode, + gen_rtvec (1, + gen_rtx_RETURN (VOIDmode)), + VUNSPEC_EPILOGUE)); + DONE; + " +) + +(define_insn "sibcall_epilogue" + [(unspec_volatile [(const_int 0)] VUNSPEC_EPILOGUE)] + "TARGET_ARM" + "* + output_asm_insn (\"%@ Sibcall epilogue\", operands); + if (USE_RETURN_INSN (FALSE)) + return output_return_instruction (NULL, FALSE, FALSE); + return arm_output_epilogue (FALSE); + " +;; Length is absolute worst case + [(set_attr "length" "44") + (set_attr "type" "block")] +) + +(define_insn "*epilogue_insns" + [(unspec_volatile [(return)] VUNSPEC_EPILOGUE)] + "TARGET_EITHER" + "* + if (TARGET_ARM) + return arm_output_epilogue (TRUE); + else /* TARGET_THUMB */ + return thumb_unexpanded_epilogue (); + " + ; Length is absolute worst case + [(set_attr "length" "44") + (set_attr "type" "block")] +) + +(define_expand "eh_epilogue" + [(use (match_operand:SI 0 "register_operand" "r")) + (use (match_operand:SI 1 "register_operand" "r")) + (use (match_operand:SI 2 "register_operand" "r"))] + "TARGET_EITHER" + " + { + cfun->machine->eh_epilogue_sp_ofs = operands[1]; + if (GET_CODE (operands[2]) != REG || REGNO (operands[2]) != 2) + { + rtx ra = gen_rtx_REG (Pmode, 2); + + emit_move_insn (ra, operands[2]); + operands[2] = ra; + } + /* This is a hack -- we may have crystalized the function type too + early. */ + cfun->machine->func_type = 0; + }" +) + +;; This split is only used during output to reduce the number of patterns +;; that need assembler instructions adding to them. We allowed the setting +;; of the conditions to be implicit during rtl generation so that +;; the conditional compare patterns would work. However this conflicts to +;; some extent with the conditional data operations, so we have to split them +;; up again here. + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (if_then_else:SI (match_operator 1 "arm_comparison_operator" + [(match_operand 2 "" "") (match_operand 3 "" "")]) + (match_dup 0) + (match_operand 4 "" ""))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM && reload_completed" + [(set (match_dup 5) (match_dup 6)) + (cond_exec (match_dup 7) + (set (match_dup 0) (match_dup 4)))] + " + { + enum machine_mode mode = SELECT_CC_MODE (GET_CODE (operands[1]), + operands[2], operands[3]); + enum rtx_code rc = GET_CODE (operands[1]); + + operands[5] = gen_rtx_REG (mode, CC_REGNUM); + operands[6] = gen_rtx_COMPARE (mode, operands[2], operands[3]); + if (mode == CCFPmode || mode == CCFPEmode) + rc = reverse_condition_maybe_unordered (rc); + else + rc = reverse_condition (rc); + + operands[7] = gen_rtx_fmt_ee (rc, VOIDmode, operands[5], const0_rtx); + }" +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (if_then_else:SI (match_operator 1 "arm_comparison_operator" + [(match_operand 2 "" "") (match_operand 3 "" "")]) + (match_operand 4 "" "") + (match_dup 0))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM && reload_completed" + [(set (match_dup 5) (match_dup 6)) + (cond_exec (match_op_dup 1 [(match_dup 5) (const_int 0)]) + (set (match_dup 0) (match_dup 4)))] + " + { + enum machine_mode mode = SELECT_CC_MODE (GET_CODE (operands[1]), + operands[2], operands[3]); + + operands[5] = gen_rtx_REG (mode, CC_REGNUM); + operands[6] = gen_rtx_COMPARE (mode, operands[2], operands[3]); + }" +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (if_then_else:SI (match_operator 1 "arm_comparison_operator" + [(match_operand 2 "" "") (match_operand 3 "" "")]) + (match_operand 4 "" "") + (match_operand 5 "" ""))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM && reload_completed" + [(set (match_dup 6) (match_dup 7)) + (cond_exec (match_op_dup 1 [(match_dup 6) (const_int 0)]) + (set (match_dup 0) (match_dup 4))) + (cond_exec (match_dup 8) + (set (match_dup 0) (match_dup 5)))] + " + { + enum machine_mode mode = SELECT_CC_MODE (GET_CODE (operands[1]), + operands[2], operands[3]); + enum rtx_code rc = GET_CODE (operands[1]); + + operands[6] = gen_rtx_REG (mode, CC_REGNUM); + operands[7] = gen_rtx_COMPARE (mode, operands[2], operands[3]); + if (mode == CCFPmode || mode == CCFPEmode) + rc = reverse_condition_maybe_unordered (rc); + else + rc = reverse_condition (rc); + + operands[8] = gen_rtx_fmt_ee (rc, VOIDmode, operands[6], const0_rtx); + }" +) + +(define_split + [(set (match_operand:SI 0 "s_register_operand" "") + (if_then_else:SI (match_operator 1 "arm_comparison_operator" + [(match_operand:SI 2 "s_register_operand" "") + (match_operand:SI 3 "arm_add_operand" "")]) + (match_operand:SI 4 "arm_rhs_operand" "") + (not:SI + (match_operand:SI 5 "s_register_operand" "")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ARM && reload_completed" + [(set (match_dup 6) (match_dup 7)) + (cond_exec (match_op_dup 1 [(match_dup 6) (const_int 0)]) + (set (match_dup 0) (match_dup 4))) + (cond_exec (match_dup 8) + (set (match_dup 0) (not:SI (match_dup 5))))] + " + { + enum machine_mode mode = SELECT_CC_MODE (GET_CODE (operands[1]), + operands[2], operands[3]); + enum rtx_code rc = GET_CODE (operands[1]); + + operands[6] = gen_rtx_REG (mode, CC_REGNUM); + operands[7] = gen_rtx (COMPARE, mode, operands[2], operands[3]); + if (mode == CCFPmode || mode == CCFPEmode) + rc = reverse_condition_maybe_unordered (rc); + else + rc = reverse_condition (rc); + + operands[8] = gen_rtx_fmt_ee (rc, VOIDmode, operands[6], const0_rtx); + }" +) + +(define_insn "*cond_move_not" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (if_then_else:SI (match_operator 4 "arm_comparison_operator" + [(match_operand 3 "cc_register" "") (const_int 0)]) + (match_operand:SI 1 "arm_rhs_operand" "0,?rI") + (not:SI + (match_operand:SI 2 "s_register_operand" "r,r"))))] + "TARGET_ARM" + "@ + mvn%D4\\t%0, %2 + mov%d4\\t%0, %1\;mvn%D4\\t%0, %2" + [(set_attr "conds" "use") + (set_attr "length" "4,8")] +) + +;; The next two patterns occur when an AND operation is followed by a +;; scc insn sequence + +(define_insn "*sign_extract_onebit" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (sign_extract:SI (match_operand:SI 1 "s_register_operand" "r") + (const_int 1) + (match_operand:SI 2 "const_int_operand" "n")))] + "TARGET_ARM" + "* + operands[2] = GEN_INT (1 << INTVAL (operands[2])); + output_asm_insn (\"ands\\t%0, %1, %2\", operands); + return \"mvnne\\t%0, #0\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "8")] +) + +(define_insn "*not_signextract_onebit" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (not:SI + (sign_extract:SI (match_operand:SI 1 "s_register_operand" "r") + (const_int 1) + (match_operand:SI 2 "const_int_operand" "n"))))] + "TARGET_ARM" + "* + operands[2] = GEN_INT (1 << INTVAL (operands[2])); + output_asm_insn (\"tst\\t%1, %2\", operands); + output_asm_insn (\"mvneq\\t%0, #0\", operands); + return \"movne\\t%0, #0\"; + " + [(set_attr "conds" "clob") + (set_attr "length" "12")] +) + +;; Push multiple registers to the stack. Registers are in parallel (use ...) +;; expressions. For simplicity, the first register is also in the unspec +;; part. +(define_insn "*push_multi" + [(match_parallel 2 "multi_register_push" + [(set (match_operand:BLK 0 "memory_operand" "=m") + (unspec:BLK [(match_operand:SI 1 "s_register_operand" "r")] + UNSPEC_PUSH_MULT))])] + "TARGET_ARM" + "* + { + int num_saves = XVECLEN (operands[2], 0); + + /* For the StrongARM at least it is faster to + use STR to store only a single register. */ + if (num_saves == 1) + output_asm_insn (\"str\\t%1, [%m0, #-4]!\", operands); + else + { + int i; + char pattern[100]; + + strcpy (pattern, \"stmfd\\t%m0!, {%1\"); + + for (i = 1; i < num_saves; i++) + { + strcat (pattern, \", %|\"); + strcat (pattern, + reg_names[REGNO (XEXP (XVECEXP (operands[2], 0, i), 0))]); + } + + strcat (pattern, \"}\"); + output_asm_insn (pattern, operands); + } + + return \"\"; + }" + [(set_attr "type" "store4")] +) + +;; Similarly for the floating point registers +(define_insn "*push_fp_multi" + [(match_parallel 2 "multi_register_push" + [(set (match_operand:BLK 0 "memory_operand" "=m") + (unspec:BLK [(match_operand:XF 1 "f_register_operand" "f")] + UNSPEC_PUSH_MULT))])] + "TARGET_ARM" + "* + { + char pattern[100]; + + sprintf (pattern, \"sfmfd\\t%%1, %d, [%%m0]!\", XVECLEN (operands[2], 0)); + output_asm_insn (pattern, operands); + return \"\"; + }" + [(set_attr "type" "f_store")] +) + +;; Special patterns for dealing with the constant pool + +(define_insn "align_4" + [(unspec_volatile [(const_int 0)] VUNSPEC_ALIGN)] + "TARGET_EITHER" + "* + assemble_align (32); + return \"\"; + " +) + +(define_insn "consttable_end" + [(unspec_volatile [(const_int 0)] VUNSPEC_POOL_END)] + "TARGET_EITHER" + "* + making_const_table = FALSE; + return \"\"; + " +) + +(define_insn "consttable_1" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_1)] + "TARGET_THUMB" + "* + making_const_table = TRUE; + assemble_integer (operands[0], 1, BITS_PER_WORD, 1); + assemble_zeros (3); + return \"\"; + " + [(set_attr "length" "4")] +) + +(define_insn "consttable_2" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_2)] + "TARGET_THUMB" + "* + making_const_table = TRUE; + assemble_integer (operands[0], 2, BITS_PER_WORD, 1); + assemble_zeros (2); + return \"\"; + " + [(set_attr "length" "4")] +) + +(define_insn "consttable_4" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_4)] + "TARGET_EITHER" + "* + { + making_const_table = TRUE; + switch (GET_MODE_CLASS (GET_MODE (operands[0]))) + { + case MODE_FLOAT: + { + union real_extract u; + memcpy (&u, &CONST_DOUBLE_LOW (operands[0]), sizeof u); + assemble_real (u.d, GET_MODE (operands[0]), BITS_PER_WORD); + break; + } + default: + assemble_integer (operands[0], 4, BITS_PER_WORD, 1); + break; + } + return \"\"; + }" + [(set_attr "length" "4")] +) + +(define_insn "consttable_8" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_8)] + "TARGET_EITHER" + "* + { + making_const_table = TRUE; + switch (GET_MODE_CLASS (GET_MODE (operands[0]))) + { + case MODE_FLOAT: + { + union real_extract u; + memcpy (&u, &CONST_DOUBLE_LOW (operands[0]), sizeof u); + assemble_real (u.d, GET_MODE (operands[0]), BITS_PER_WORD); + break; + } + default: + assemble_integer (operands[0], 8, BITS_PER_WORD, 1); + break; + } + return \"\"; + }" + [(set_attr "length" "8")] +) + +;; Miscellaneous Thumb patterns + +(define_insn "tablejump" + [(set (pc) (match_operand:SI 0 "register_operand" "l*r")) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_THUMB" + "mov pc, %0" + [(set_attr "length" "2")] +) + +;; V5 Instructions, + +(define_insn "clz" + [(set (match_operand:SI 0 "s_register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "s_register_operand" "r")] + UNSPEC_CLZ))] + "TARGET_ARM && arm_arch5" + "clz\\t%0, %1") + +(define_expand "ffssi2" + [(set (match_operand:SI 0 "s_register_operand" "") + (ffs:SI (match_operand:SI 1 "s_register_operand" "")))] + "TARGET_ARM && arm_arch5" + " + { + rtx t1, t2, t3; + + t1 = gen_reg_rtx (SImode); + t2 = gen_reg_rtx (SImode); + t3 = gen_reg_rtx (SImode); + + emit_insn (gen_negsi2 (t1, operands[1])); + emit_insn (gen_andsi3 (t2, operands[1], t1)); + emit_insn (gen_clz (t3, t2)); + emit_insn (gen_subsi3 (operands[0], GEN_INT (32), t3)); + DONE; + }" +) + +;; V5E instructions. + +(define_insn "prefetch" + [(prefetch (match_operand:SI 0 "address_operand" "p") + (match_operand:SI 1 "" "") + (match_operand:SI 2 "" ""))] + "TARGET_ARM && arm_arch5e" + "pld\\t%a0") + +;; General predication pattern + +(define_cond_exec + [(match_operator 0 "arm_comparison_operator" + [(match_operand 1 "cc_register" "") + (const_int 0)])] + "TARGET_ARM" + "" +) + +(define_insn "prologue_use" + [(unspec:SI [(match_operand:SI 0 "register_operand" "")] UNSPEC_PROLOGUE_USE)] + "" + "%@ %0 needed for prologue" +) diff --git a/contrib/gcc/config/arm/coff.h b/contrib/gcc/config/arm/coff.h new file mode 100644 index 000000000000..f53dace9056d --- /dev/null +++ b/contrib/gcc/config/arm/coff.h @@ -0,0 +1,126 @@ +/* Definitions of target machine for GNU compiler. + For ARM with COFF object format. + Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 + Free Software Foundation, Inc. + Contributed by Doug Evans (devans@cygnus.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* Note - it is important that this definition matches the one in tcoff.h */ +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/coff)", stderr) + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME) + +#ifndef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + { "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork" } +#endif + +/* This is COFF, but prefer stabs. */ +#define SDB_DEBUGGING_INFO + +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#include "dbxcoff.h" + +/* A C statement to output assembler commands which will identify the + object file as having been compiled with GNU CC (or another GNU + compiler). */ + +/* This outputs a lot of .req's to define alias for various registers. + Let's try to avoid this. */ +#undef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ + do \ + { \ + fprintf (STREAM, "%s Generated by gcc %s for ARM/coff\n", \ + ASM_COMMENT_START, version_string); \ + fprintf (STREAM, ASM_APP_OFF); \ + } \ + while (0) + +/* Switch into a generic section. */ +#define TARGET_ASM_NAMED_SECTION default_coff_asm_named_section + +/* Support the ctors/dtors and other sections. */ + +#undef INIT_SECTION_ASM_OP + +/* Define this macro if jump tables (for `tablejump' insns) should be + output in the text section, along with the assembler instructions. + Otherwise, the readonly data section is used. */ +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +#undef READONLY_DATA_SECTION +#define READONLY_DATA_SECTION rdata_section +#undef RDATA_SECTION_ASM_OP +#define RDATA_SECTION_ASM_OP "\t.section .rdata" +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP "\t.section .ctors,\"x\"" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP "\t.section .dtors,\"x\"" + +/* A list of other sections which the compiler might be "in" at any + given time. */ + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS SUBTARGET_EXTRA_SECTIONS in_rdata + +#define SUBTARGET_EXTRA_SECTIONS + +/* A list of extra section function definitions. */ + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + RDATA_SECTION_FUNCTION \ + SUBTARGET_EXTRA_SECTION_FUNCTIONS + +#define SUBTARGET_EXTRA_SECTION_FUNCTIONS + +#define RDATA_SECTION_FUNCTION \ +void \ +rdata_section () \ +{ \ + if (in_section != in_rdata) \ + { \ + fprintf (asm_out_file, "%s\n", RDATA_SECTION_ASM_OP); \ + in_section = in_rdata; \ + } \ +} + +/* Support the ctors/dtors sections for g++. */ + +/* __CTOR_LIST__ and __DTOR_LIST__ must be defined by the linker script. */ +#define CTOR_LISTS_DEFINED_EXTERNALLY + +#undef DO_GLOBAL_CTORS_BODY +#undef DO_GLOBAL_DTORS_BODY + +/* The ARM development system defines __main. */ +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain + +#define SUPPORTS_INIT_PRIORITY 0 diff --git a/contrib/gcc/config/arm/conix-elf.h b/contrib/gcc/config/arm/conix-elf.h new file mode 100644 index 000000000000..b74afdfc006d --- /dev/null +++ b/contrib/gcc/config/arm/conix-elf.h @@ -0,0 +1,44 @@ +/* Definitions of target machine for GNU compiler, + for ARM with ConiX OS. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Philip Blundell <pb@futuretv.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +`Boston, MA 02111-1307, USA. */ + +/* elfos.h should have already been included. Now just override + any conflicting definitions and add any extras. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF ConiX)", stderr); + +/* Default to using APCS-32 and software floating point. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32) + +#ifndef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" +#endif + +#ifndef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm7tdmi +#endif + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-D__arm__ -D__CONIX__ -D__ELF__" diff --git a/contrib/gcc/config/arm/crti.asm b/contrib/gcc/config/arm/crti.asm new file mode 100644 index 000000000000..f3741db2bed3 --- /dev/null +++ b/contrib/gcc/config/arm/crti.asm @@ -0,0 +1,76 @@ +# Copyright (C) 2001 Free Software Foundation, Inc. +# Written By Nick Clifton +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any +# later version. +# +# In addition to the permissions in the GNU General Public License, the +# Free Software Foundation gives you unlimited permission to link the +# compiled version of this file with other programs, and to distribute +# those programs without any restriction coming from the use of this +# file. (The General Public License restrictions do apply in other +# respects; for example, they cover modification of the file, and +# distribution when not linked into another program.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# As a special exception, if you link this library with files +# compiled with GCC to produce an executable, this does not cause +# the resulting executable to be covered by the GNU General Public License. +# This exception does not however invalidate any other reasons why +# the executable file might be covered by the GNU General Public License. +# + +# This file just make a stack frame for the contents of the .fini and +# .init sections. Users may put any desired instructions in those +# sections. + + # Note - this macro is complimented by the FUNC_END macro + # in crtn.asm. If you change this macro you must also change + # that macro match. +.macro FUNC_START +#ifdef __thumb__ + .thumb + + push {r4, r5, r6, r7, lr} +#else + .arm + # Create a stack frame and save any call-preserved registers + mov ip, sp + stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc} + sub fp, ip, #4 +#endif +.endm + + .file "crti.asm" + + .section ".init" + .align 2 + .global _init +#ifdef __thumb__ + .thumb_func +#endif +_init: + FUNC_START + + + .section ".fini" + .align 2 + .global _fini +#ifdef __thumb__ + .thumb_func +#endif +_fini: + FUNC_START + +# end of crti.asm diff --git a/contrib/gcc/config/arm/crtn.asm b/contrib/gcc/config/arm/crtn.asm new file mode 100644 index 000000000000..a7f0e9e2c713 --- /dev/null +++ b/contrib/gcc/config/arm/crtn.asm @@ -0,0 +1,81 @@ +# Copyright (C) 2001 Free Software Foundation, Inc. +# Written By Nick Clifton +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any +# later version. +# +# In addition to the permissions in the GNU General Public License, the +# Free Software Foundation gives you unlimited permission to link the +# compiled version of this file with other programs, and to distribute +# those programs without any restriction coming from the use of this +# file. (The General Public License restrictions do apply in other +# respects; for example, they cover modification of the file, and +# distribution when not linked into another program.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# As a special exception, if you link this library with files +# compiled with GCC to produce an executable, this does not cause +# the resulting executable to be covered by the GNU General Public License. +# This exception does not however invalidate any other reasons why +# the executable file might be covered by the GNU General Public License. +# + +# This file just makes sure that the .fini and .init sections do in +# fact return. Users may put any desired instructions in those sections. +# This file is the last thing linked into any executable. + + # Note - this macro is complimented by the FUNC_START macro + # in crti.asm. If you change this macro you must also change + # that macro match. + # + # Note - we do not try any fancy optimisations of the return + # sequences here, it is just not worth it. Instead keep things + # simple. Restore all the save resgisters, including the link + # register and then perform the correct function return instruction. +.macro FUNC_END +#ifdef __thumb__ + .thumb + + pop {r4, r5, r6, r7} + pop {r3} + mov lr, r3 +#else + .arm + + ldmdb fp, {r4, r5, r6, r7, r8, r9, sl, fp, sp, lr} +#endif + +#if defined __THUMB_INTERWORK__ || defined __thumb__ + bx lr +#else +#ifdef __APCS_26__ + movs pc, lr +#else + mov pc, lr +#endif +#endif +.endm + + + .file "crtn.asm" + + .section ".init" + ;; + FUNC_END + + .section ".fini" + ;; + FUNC_END + +# end of crtn.asm diff --git a/contrib/gcc/config/arm/ecos-elf.h b/contrib/gcc/config/arm/ecos-elf.h new file mode 100644 index 000000000000..f1377a9eca24 --- /dev/null +++ b/contrib/gcc/config/arm/ecos-elf.h @@ -0,0 +1,28 @@ +/* Definitions for ecos based ARM systems using ELF + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF Ecos)", stderr); + +#define HAS_INIT_SECTION + +#undef INVOKE_main + diff --git a/contrib/gcc/config/arm/elf.h b/contrib/gcc/config/arm/elf.h new file mode 100644 index 000000000000..c27dacaa58bf --- /dev/null +++ b/contrib/gcc/config/arm/elf.h @@ -0,0 +1,197 @@ +/* Definitions of target machine for GNU compiler. + For ARM with ELF obj format. + Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + Contributed by Philip Blundell <philb@gnu.org> and + Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef OBJECT_FORMAT_ELF + #error elf.h included before elfos.h +#endif + +#ifndef LOCAL_LABEL_PREFIX +#define LOCAL_LABEL_PREFIX "." +#endif + +#ifndef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "-D__ELF__" +#endif + +#ifndef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS \ + { "subtarget_extra_asm_spec", SUBTARGET_EXTRA_ASM_SPEC }, +#endif + +#ifndef SUBTARGET_EXTRA_ASM_SPEC +#define SUBTARGET_EXTRA_ASM_SPEC "" +#endif + +#ifndef ASM_SPEC +#define ASM_SPEC "\ +%{mbig-endian:-EB} \ +%{mcpu=*:-m%*} \ +%{march=*:-m%*} \ +%{mapcs-*:-mapcs-%*} \ +%{mapcs-float:-mfloat} \ +%{msoft-float:-mno-fpu} \ +%{mthumb-interwork:-mthumb-interwork} \ +%(subtarget_extra_asm_spec)" +#endif + +/* The ARM uses @ are a comment character so we need to redefine + TYPE_OPERAND_FMT. */ +#undef TYPE_OPERAND_FMT +#define TYPE_OPERAND_FMT "%s" + +/* We might need a ARM specific header to function declarations. */ +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + do \ + { \ + ARM_DECLARE_FUNCTION_NAME (FILE, NAME, DECL); \ + fprintf (FILE, "%s", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "function"); \ + putc ('\n', FILE); \ + ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ + ASM_OUTPUT_LABEL(FILE, NAME); \ + } \ + while (0) + +/* We might need an ARM specific trailer for function declarations. */ +#undef ASM_DECLARE_FUNCTION_SIZE +#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \ + do \ + { \ + ARM_DECLARE_FUNCTION_SIZE (FILE, FNAME, DECL); \ + if (!flag_inhibit_size_directive) \ + { \ + char label[256]; \ + static int labelno; \ + labelno ++; \ + ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \ + ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, (FNAME)); \ + fprintf (FILE, ","); \ + assemble_name (FILE, label); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, (FNAME)); \ + putc ('\n', FILE); \ + } \ + } \ + while (0) + +/* Define this macro if jump tables (for `tablejump' insns) should be + output in the text section, along with the assembler instructions. + Otherwise, the readonly data section is used. */ +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +#ifndef LINK_SPEC +#define LINK_SPEC "%{mbig-endian:-EB} -X" +#endif + +/* Run-time Target Specification. */ +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/elf)", stderr) +#endif + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME) +#endif + +#ifndef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + { "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork", "fno-leading-underscore" } +#endif + + +/* This outputs a lot of .req's to define alias for various registers. + Let's try to avoid this. */ +#ifndef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ + do \ + { \ + fprintf (STREAM, "%s Generated by gcc %s for ARM/elf\n", \ + ASM_COMMENT_START, version_string); \ + output_file_directive (STREAM, main_input_filename); \ + fprintf (STREAM, ASM_APP_OFF); \ + } \ + while (0) +#endif + +/* Output an internal label definition. */ +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(STREAM, PREFIX, NUM) \ + do \ + { \ + char * s = (char *) alloca (40 + strlen (PREFIX)); \ + extern int arm_target_label, arm_ccfsm_state; \ + extern rtx arm_target_insn; \ + \ + if (arm_ccfsm_state == 3 && arm_target_label == (NUM) \ + && !strcmp (PREFIX, "L")) \ + { \ + arm_ccfsm_state = 0; \ + arm_target_insn = NULL; \ + } \ + ASM_GENERATE_INTERNAL_LABEL (s, (PREFIX), (NUM)); \ + ASM_OUTPUT_LABEL (STREAM, s); \ + } \ + while (0) + +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION arm_elf_asm_named_section + +#undef ASM_OUTPUT_ALIGNED_COMMON +#define ASM_OUTPUT_ALIGNED_COMMON(STREAM, NAME, SIZE, ALIGN) \ + do \ + { \ + fprintf (STREAM, "\t.comm\t"); \ + assemble_name (STREAM, NAME); \ + fprintf (STREAM, ", %d, %d\n", SIZE, ALIGN); \ + } \ + while (0) + +/* For PIC code we need to explicitly specify (PLT) and (GOT) relocs. */ +#define NEED_PLT_RELOC flag_pic +#define NEED_GOT_RELOC flag_pic + +/* The ELF assembler handles GOT addressing differently to NetBSD. */ +#define GOT_PCREL 0 + +/* Biggest alignment supported by the object file format of this + machine. Use this macro to limit the alignment which can be + specified using the `__attribute__ ((aligned (N)))' construct. If + not defined, the default value is `BIGGEST_ALIGNMENT'. */ +#define MAX_OFILE_ALIGNMENT (32768 * 8) + +/* Align output to a power of two. Note ".align 0" is redundant, + and also GAS will treat it as ".align 2" which we do not want. */ +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + do \ + { \ + if ((POWER) > 0) \ + fprintf (STREAM, "\t.align\t%d\n", POWER); \ + } \ + while (0) + +#define SUPPORTS_INIT_PRIORITY 1 diff --git a/contrib/gcc/config/arm/freebsd.h b/contrib/gcc/config/arm/freebsd.h new file mode 100644 index 000000000000..a9b268c0a48e --- /dev/null +++ b/contrib/gcc/config/arm/freebsd.h @@ -0,0 +1,57 @@ +/* Definitions for StrongARM running FreeBSD using the ELF format + Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by David E. O'Brien <obrien@FreeBSD.org> and BSDi. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC FBSD_CPP_SPEC + + +/************************[ Target stuff ]***********************************/ + +/* Define the actual types of some ANSI-mandated types. + Needs to agree with <machine/ansi.h>. GCC defaults come from c-decl.c, + c-common.c, and config/<arch>/<arch>.h. */ + +/* arm.h gets this wrong for FreeBSD. We use the GCC defaults instead. */ + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +/* We use the GCC defaults here. */ +#undef WCHAR_TYPE + +#undef WCHAR_UNSIGNED +#define WCHAR_UNSIGNED 0 + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +#undef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_strongarm + +#undef ARM_OS_NAME +#define ARM_OS_NAME "FreeBSD" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (FreeBSD/StrongARM ELF)"); diff --git a/contrib/gcc/config/arm/lib1funcs.asm b/contrib/gcc/config/arm/lib1funcs.asm new file mode 100644 index 000000000000..ec706ece127c --- /dev/null +++ b/contrib/gcc/config/arm/lib1funcs.asm @@ -0,0 +1,781 @@ +@ libgcc routines for ARM cpu. +@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) + +/* Copyright 1995, 1996, 1998, 1999, 2000 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ +/* ------------------------------------------------------------------------ */ + +/* We need to know what prefix to add to function names. */ + +#ifndef __USER_LABEL_PREFIX__ +#error __USER_LABEL_PREFIX__ not defined +#endif + +/* ANSI concatenation macros. */ + +#define CONCAT1(a, b) CONCAT2(a, b) +#define CONCAT2(a, b) a ## b + +/* Use the right prefix for global labels. */ + +#define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x) + +#ifdef __ELF__ +#ifdef __thumb__ +#define __PLT__ /* Not supported in Thumb assembler (for now). */ +#else +#define __PLT__ (PLT) +#endif +#define TYPE(x) .type SYM(x),function +#define SIZE(x) .size SYM(x), . - SYM(x) +#else +#define __PLT__ +#define TYPE(x) +#define SIZE(x) +#endif + +/* Function end macros. Variants for 26 bit APCS and interworking. */ + +#ifdef __APCS_26__ +# define RET movs pc, lr +# define RETc(x) mov##x##s pc, lr +# define RETCOND ^ +.macro ARM_LDIV0 +Ldiv0: + str lr, [sp, #-4]! + bl SYM (__div0) __PLT__ + mov r0, #0 @ About as wrong as it could be. + ldmia sp!, {pc}^ +.endm +#else +# ifdef __THUMB_INTERWORK__ +# define RET bx lr +# define RETc(x) bx##x lr +.macro THUMB_LDIV0 +Ldiv0: + push { lr } + bl SYM (__div0) + mov r0, #0 @ About as wrong as it could be. + pop { r1 } + bx r1 +.endm +.macro ARM_LDIV0 +Ldiv0: + str lr, [sp, #-4]! + bl SYM (__div0) __PLT__ + mov r0, #0 @ About as wrong as it could be. + ldr lr, [sp], #4 + bx lr +.endm +# else +# define RET mov pc, lr +# define RETc(x) mov##x pc, lr +.macro THUMB_LDIV0 +Ldiv0: + push { lr } + bl SYM (__div0) + mov r0, #0 @ About as wrong as it could be. + pop { pc } +.endm +.macro ARM_LDIV0 +Ldiv0: + str lr, [sp, #-4]! + bl SYM (__div0) __PLT__ + mov r0, #0 @ About as wrong as it could be. + ldmia sp!, {pc} +.endm +# endif +# define RETCOND +#endif + +.macro FUNC_END name +Ldiv0: +#ifdef __thumb__ + THUMB_LDIV0 +#else + ARM_LDIV0 +#endif + SIZE (__\name) +.endm + +.macro THUMB_FUNC_START name + .globl SYM (\name) + TYPE (\name) + .thumb_func +SYM (\name): +.endm + +/* Function start macros. Variants for ARM and Thumb. */ + +#ifdef __thumb__ +#define THUMB_FUNC .thumb_func +#define THUMB_CODE .force_thumb +#else +#define THUMB_FUNC +#define THUMB_CODE +#endif + +.macro FUNC_START name + .text + .globl SYM (__\name) + TYPE (__\name) + .align 0 + THUMB_CODE + THUMB_FUNC +SYM (__\name): +.endm + +/* Register aliases. */ + +work .req r4 @ XXXX is this safe ? +dividend .req r0 +divisor .req r1 +overdone .req r2 +result .req r2 +curbit .req r3 +ip .req r12 +sp .req r13 +lr .req r14 +pc .req r15 + +/* ------------------------------------------------------------------------ */ +/* Bodies of the divsion and modulo routines. */ +/* ------------------------------------------------------------------------ */ +.macro ARM_DIV_MOD_BODY modulo +Loop1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmplo divisor, dividend + movlo divisor, divisor, lsl #4 + movlo curbit, curbit, lsl #4 + blo Loop1 + +Lbignum: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmplo divisor, dividend + movlo divisor, divisor, lsl #1 + movlo curbit, curbit, lsl #1 + blo Lbignum + +Loop3: + @ Test for possible subtractions. On the final pass, this may + @ subtract too much from the dividend ... + + .if \modulo + @ ... so keep track of which subtractions are done in OVERDONE. + @ We can fix them up afterwards. + mov overdone, #0 + cmp dividend, divisor + subhs dividend, dividend, divisor + cmp dividend, divisor, lsr #1 + subhs dividend, dividend, divisor, lsr #1 + orrhs overdone, overdone, curbit, ror #1 + cmp dividend, divisor, lsr #2 + subhs dividend, dividend, divisor, lsr #2 + orrhs overdone, overdone, curbit, ror #2 + cmp dividend, divisor, lsr #3 + subhs dividend, dividend, divisor, lsr #3 + orrhs overdone, overdone, curbit, ror #3 + mov ip, curbit + .else + @ ... so keep track of which subtractions are done in RESULT. + @ The result will be ok, since the "bit" will have been + @ shifted out at the bottom. + cmp dividend, divisor + subhs dividend, dividend, divisor + orrhs result, result, curbit + cmp dividend, divisor, lsr #1 + subhs dividend, dividend, divisor, lsr #1 + orrhs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subhs dividend, dividend, divisor, lsr #2 + orrhs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subhs dividend, dividend, divisor, lsr #3 + orrhs result, result, curbit, lsr #3 + .endif + + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne Loop3 + + .if \modulo +Lfixup_dividend: + @ Any subtractions that we should not have done will be recorded in + @ the top three bits of OVERDONE. Exactly which were not needed + @ are governed by the position of the bit, stored in IP. + ands overdone, overdone, #0xe0000000 + @ If we terminated early, because dividend became zero, then the + @ bit in ip will not be in the bottom nibble, and we should not + @ perform the additions below. We must test for this though + @ (rather relying upon the TSTs to prevent the additions) since + @ the bit in ip could be in the top two bits which might then match + @ with one of the smaller RORs. + tstne ip, #0x7 + beq Lgot_result + tst overdone, ip, ror #3 + addne dividend, dividend, divisor, lsr #3 + tst overdone, ip, ror #2 + addne dividend, dividend, divisor, lsr #2 + tst overdone, ip, ror #1 + addne dividend, dividend, divisor, lsr #1 + .endif + +Lgot_result: +.endm +/* ------------------------------------------------------------------------ */ +.macro THUMB_DIV_MOD_BODY modulo + @ Load the constant 0x10000000 into our work register. + mov work, #1 + lsl work, #28 +Loop1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, work + bhs Lbignum + cmp divisor, dividend + bhs Lbignum + lsl divisor, #4 + lsl curbit, #4 + b Loop1 +Lbignum: + @ Set work to 0x80000000 + lsl work, #3 +Loop2: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, work + bhs Loop3 + cmp divisor, dividend + bhs Loop3 + lsl divisor, #1 + lsl curbit, #1 + b Loop2 +Loop3: + @ Test for possible subtractions ... + .if \modulo + @ ... On the final pass, this may subtract too much from the dividend, + @ so keep track of which subtractions are done, we can fix them up + @ afterwards. + mov overdone, #0 + cmp dividend, divisor + blo Lover1 + sub dividend, dividend, divisor +Lover1: + lsr work, divisor, #1 + cmp dividend, work + blo Lover2 + sub dividend, dividend, work + mov ip, curbit + mov work, #1 + ror curbit, work + orr overdone, curbit + mov curbit, ip +Lover2: + lsr work, divisor, #2 + cmp dividend, work + blo Lover3 + sub dividend, dividend, work + mov ip, curbit + mov work, #2 + ror curbit, work + orr overdone, curbit + mov curbit, ip +Lover3: + lsr work, divisor, #3 + cmp dividend, work + blo Lover4 + sub dividend, dividend, work + mov ip, curbit + mov work, #3 + ror curbit, work + orr overdone, curbit + mov curbit, ip +Lover4: + mov ip, curbit + .else + @ ... and note which bits are done in the result. On the final pass, + @ this may subtract too much from the dividend, but the result will be ok, + @ since the "bit" will have been shifted out at the bottom. + cmp dividend, divisor + blo Lover1 + sub dividend, dividend, divisor + orr result, result, curbit +Lover1: + lsr work, divisor, #1 + cmp dividend, work + blo Lover2 + sub dividend, dividend, work + lsr work, curbit, #1 + orr result, work +Lover2: + lsr work, divisor, #2 + cmp dividend, work + blo Lover3 + sub dividend, dividend, work + lsr work, curbit, #2 + orr result, work +Lover3: + lsr work, divisor, #3 + cmp dividend, work + blo Lover4 + sub dividend, dividend, work + lsr work, curbit, #3 + orr result, work +Lover4: + .endif + + cmp dividend, #0 @ Early termination? + beq Lover5 + lsr curbit, #4 @ No, any more bits to do? + beq Lover5 + lsr divisor, #4 + b Loop3 +Lover5: + .if \modulo + @ Any subtractions that we should not have done will be recorded in + @ the top three bits of "overdone". Exactly which were not needed + @ are governed by the position of the bit, stored in ip. + mov work, #0xe + lsl work, #28 + and overdone, work + beq Lgot_result + + @ If we terminated early, because dividend became zero, then the + @ bit in ip will not be in the bottom nibble, and we should not + @ perform the additions below. We must test for this though + @ (rather relying upon the TSTs to prevent the additions) since + @ the bit in ip could be in the top two bits which might then match + @ with one of the smaller RORs. + mov curbit, ip + mov work, #0x7 + tst curbit, work + beq Lgot_result + + mov curbit, ip + mov work, #3 + ror curbit, work + tst overdone, curbit + beq Lover6 + lsr work, divisor, #3 + add dividend, work +Lover6: + mov curbit, ip + mov work, #2 + ror curbit, work + tst overdone, curbit + beq Lover7 + lsr work, divisor, #2 + add dividend, work +Lover7: + mov curbit, ip + mov work, #1 + ror curbit, work + tst overdone, curbit + beq Lgot_result + lsr work, divisor, #1 + add dividend, work + .endif +Lgot_result: +.endm +/* ------------------------------------------------------------------------ */ +/* Start of the Real Functions */ +/* ------------------------------------------------------------------------ */ +#ifdef L_udivsi3 + + FUNC_START udivsi3 + +#ifdef __thumb__ + + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + mov result, #0 + + push { work } + cmp dividend, divisor + blo Lgot_result + + THUMB_DIV_MOD_BODY 0 + + mov r0, result + pop { work } + RET + +#else /* ARM version. */ + + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + mov result, #0 + cmp dividend, divisor + blo Lgot_result + + ARM_DIV_MOD_BODY 0 + + mov r0, result + RET + +#endif /* ARM version */ + + FUNC_END udivsi3 + +#endif /* L_udivsi3 */ +/* ------------------------------------------------------------------------ */ +#ifdef L_umodsi3 + + FUNC_START umodsi3 + +#ifdef __thumb__ + + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + cmp dividend, divisor + bhs Lover10 + RET + +Lover10: + push { work } + + THUMB_DIV_MOD_BODY 1 + + pop { work } + RET + +#else /* ARM version. */ + + cmp divisor, #0 + beq Ldiv0 + cmp divisor, #1 + cmpne dividend, divisor + moveq dividend, #0 + RETc(lo) + mov curbit, #1 + + ARM_DIV_MOD_BODY 1 + + RET + +#endif /* ARM version. */ + + FUNC_END umodsi3 + +#endif /* L_umodsi3 */ +/* ------------------------------------------------------------------------ */ +#ifdef L_divsi3 + + FUNC_START divsi3 + +#ifdef __thumb__ + cmp divisor, #0 + beq Ldiv0 + + push { work } + mov work, dividend + eor work, divisor @ Save the sign of the result. + mov ip, work + mov curbit, #1 + mov result, #0 + cmp divisor, #0 + bpl Lover10 + neg divisor, divisor @ Loops below use unsigned. +Lover10: + cmp dividend, #0 + bpl Lover11 + neg dividend, dividend +Lover11: + cmp dividend, divisor + blo Lgot_result + + THUMB_DIV_MOD_BODY 0 + + mov r0, result + mov work, ip + cmp work, #0 + bpl Lover12 + neg r0, r0 +Lover12: + pop { work } + RET + +#else /* ARM version. */ + + eor ip, dividend, divisor @ Save the sign of the result. + mov curbit, #1 + mov result, #0 + cmp divisor, #0 + rsbmi divisor, divisor, #0 @ Loops below use unsigned. + beq Ldiv0 + cmp dividend, #0 + rsbmi dividend, dividend, #0 + cmp dividend, divisor + blo Lgot_result + + ARM_DIV_MOD_BODY 0 + + mov r0, result + cmp ip, #0 + rsbmi r0, r0, #0 + RET + +#endif /* ARM version */ + + FUNC_END divsi3 + +#endif /* L_divsi3 */ +/* ------------------------------------------------------------------------ */ +#ifdef L_modsi3 + + FUNC_START modsi3 + +#ifdef __thumb__ + + mov curbit, #1 + cmp divisor, #0 + beq Ldiv0 + bpl Lover10 + neg divisor, divisor @ Loops below use unsigned. +Lover10: + push { work } + @ Need to save the sign of the dividend, unfortunately, we need + @ work later on. Must do this after saving the original value of + @ the work register, because we will pop this value off first. + push { dividend } + cmp dividend, #0 + bpl Lover11 + neg dividend, dividend +Lover11: + cmp dividend, divisor + blo Lgot_result + + THUMB_DIV_MOD_BODY 1 + + pop { work } + cmp work, #0 + bpl Lover12 + neg dividend, dividend +Lover12: + pop { work } + RET + +#else /* ARM version. */ + + cmp divisor, #0 + rsbmi divisor, divisor, #0 @ Loops below use unsigned. + beq Ldiv0 + @ Need to save the sign of the dividend, unfortunately, we need + @ ip later on; this is faster than pushing lr and using that. + str dividend, [sp, #-4]! + cmp dividend, #0 @ Test dividend against zero + rsbmi dividend, dividend, #0 @ If negative make positive + cmp dividend, divisor @ else if zero return zero + blo Lgot_result @ if smaller return dividend + mov curbit, #1 + + ARM_DIV_MOD_BODY 1 + + ldr ip, [sp], #4 + cmp ip, #0 + rsbmi dividend, dividend, #0 + RET + +#endif /* ARM version */ + + FUNC_END modsi3 + +#endif /* L_modsi3 */ +/* ------------------------------------------------------------------------ */ +#ifdef L_dvmd_tls + + FUNC_START div0 + + RET + + SIZE (__div0) + +#endif /* L_divmodsi_tools */ +/* ------------------------------------------------------------------------ */ +#ifdef L_dvmd_lnx +@ GNU/Linux division-by zero handler. Used in place of L_dvmd_tls + +/* Constants taken from <asm/unistd.h> and <asm/signal.h> */ +#define SIGFPE 8 +#define __NR_SYSCALL_BASE 0x900000 +#define __NR_getpid (__NR_SYSCALL_BASE+ 20) +#define __NR_kill (__NR_SYSCALL_BASE+ 37) + + FUNC_START div0 + + stmfd sp!, {r1, lr} + swi __NR_getpid + cmn r0, #1000 + ldmhsfd sp!, {r1, pc}RETCOND @ not much we can do + mov r1, #SIGFPE + swi __NR_kill +#ifdef __THUMB_INTERWORK__ + ldmfd sp!, {r1, lr} + bx lr +#else + ldmfd sp!, {r1, pc}RETCOND +#endif + + SIZE (__div0) + +#endif /* L_dvmd_lnx */ +/* ------------------------------------------------------------------------ */ +/* These next two sections are here despite the fact that they contain Thumb + assembler because their presence allows interworked code to be linked even + when the GCC library is this one. */ + +/* Do not build the interworking functions when the target architecture does + not support Thumb instructions. (This can be a multilib option). */ +#if defined L_call_via_rX && (defined __ARM_ARCH_4T__ || defined __ARM_ARCH_5T__ || defined __ARM_ARCH_5TE__) + +/* These labels & instructions are used by the Arm/Thumb interworking code. + The address of function to be called is loaded into a register and then + one of these labels is called via a BL instruction. This puts the + return address into the link register with the bottom bit set, and the + code here switches to the correct mode before executing the function. */ + + .text + .align 0 + .force_thumb + +.macro call_via register + THUMB_FUNC_START _call_via_\register + + bx \register + nop + + SIZE (_call_via_\register) +.endm + + call_via r0 + call_via r1 + call_via r2 + call_via r3 + call_via r4 + call_via r5 + call_via r6 + call_via r7 + call_via r8 + call_via r9 + call_via sl + call_via fp + call_via ip + call_via sp + call_via lr + +#endif /* L_call_via_rX */ +/* ------------------------------------------------------------------------ */ +/* Do not build the interworking functions when the target architecture does + not support Thumb instructions. (This can be a multilib option). */ +#if defined L_interwork_call_via_rX && (defined __ARM_ARCH_4T__ || defined __ARM_ARCH_5T__ || defined __ARM_ARCH_5TE__) + +/* These labels & instructions are used by the Arm/Thumb interworking code, + when the target address is in an unknown instruction set. The address + of function to be called is loaded into a register and then one of these + labels is called via a BL instruction. This puts the return address + into the link register with the bottom bit set, and the code here + switches to the correct mode before executing the function. Unfortunately + the target code cannot be relied upon to return via a BX instruction, so + instead we have to store the resturn address on the stack and allow the + called function to return here instead. Upon return we recover the real + return address and use a BX to get back to Thumb mode. */ + + .text + .align 0 + + .code 32 + .globl _arm_return +_arm_return: + ldmia r13!, {r12} + bx r12 + .code 16 + +.macro interwork register + .code 16 + + THUMB_FUNC_START _interwork_call_via_\register + + bx pc + nop + + .code 32 + .globl .Lchange_\register +.Lchange_\register: + tst \register, #1 + stmeqdb r13!, {lr} + adreq lr, _arm_return + bx \register + + SIZE (_interwork_call_via_\register) +.endm + + interwork r0 + interwork r1 + interwork r2 + interwork r3 + interwork r4 + interwork r5 + interwork r6 + interwork r7 + interwork r8 + interwork r9 + interwork sl + interwork fp + interwork ip + interwork sp + + /* The LR case has to be handled a little differently... */ + .code 16 + + THUMB_FUNC_START _interwork_call_via_lr + + bx pc + nop + + .code 32 + .globl .Lchange_lr +.Lchange_lr: + tst lr, #1 + stmeqdb r13!, {lr} + mov ip, lr + adreq lr, _arm_return + bx ip + + SIZE (_interwork_call_via_lr) + +#endif /* L_interwork_call_via_rX */ diff --git a/contrib/gcc/config/arm/linux-elf.h b/contrib/gcc/config/arm/linux-elf.h new file mode 100644 index 000000000000..74f7e7ef9999 --- /dev/null +++ b/contrib/gcc/config/arm/linux-elf.h @@ -0,0 +1,119 @@ +/* Definitions for ARM running Linux-based GNU systems using ELF + Copyright (C) 1993, 1994, 1997, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + Contributed by Philip Blundell <philb@gnu.org> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* elfos.h should have already been included. Now just override + any conflicting definitions and add any extras. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM GNU/Linux with ELF)", stderr); + +/* Do not assume anything about header files. */ +#define NO_IMPLICIT_EXTERN_C + +/* Default is to use APCS-32 mode. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_MMU_TRAPS) + +#define SUBTARGET_EXTRA_LINK_SPEC " -m armelf_linux -p" + +#undef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + { "marm", "mlittle-endian", "mhard-float", "mapcs-32", "mno-thumb-interwork" } + +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" + +/* Now we define the strings used to build the spec file. */ +#define LIB_SPEC \ + "%{shared: -lc} \ + %{!shared: %{pthread:-lpthread} \ + %{profile:-lc_p} %{!profile: -lc}}" + +#define LIBGCC_SPEC "%{msoft-float:-lfloat} -lgcc" + +/* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add + the GNU/Linux magical crtbegin.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main'. */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: \ + %{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} \ + %{!p:%{profile:gcrt1.o%s} \ + %{!profile:crt1.o%s}}}} \ + crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}" + +/* Provide a ENDFILE_SPEC appropriate for GNU/Linux. Here we tack on + the GNU/Linux magical crtend.o file (see crtstuff.c) which + provides part of the support for getting C++ file-scope static + object constructed before entering `main', followed by a normal + GNU/Linux "finalizer" file, `crtn.o'. */ + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s" + +#undef LINK_SPEC +#define LINK_SPEC "%{h*} %{version:-v} \ + %{b} %{Wl,*:%*} \ + %{static:-Bstatic} \ + %{shared:-shared} \ + %{symbolic:-Bsymbolic} \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2} \ + -X \ + %{mbig-endian:-EB}" \ + SUBTARGET_EXTRA_LINK_SPEC + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ +"-Dunix -Dlinux -D__ELF__ \ +-Asystem=unix -Asystem=posix" + +/* Allow #sccs in preprocessor. */ +#define SCCS_DIRECTIVE + +/* This is how we tell the assembler that two symbols have the same value. */ +#define ASM_OUTPUT_DEF(FILE, NAME1, NAME2) \ + do \ + { \ + assemble_name (FILE, NAME1); \ + fputs (" = ", FILE); \ + assemble_name (FILE, NAME2); \ + fputc ('\n', FILE); \ + } \ + while (0) + +/* NWFPE always understands FPA instructions. */ +#undef FP_DEFAULT +#define FP_DEFAULT FP_SOFT3 + +/* Call the function profiler with a given profile label. */ +#undef ARM_FUNCTION_PROFILER +#define ARM_FUNCTION_PROFILER(STREAM, LABELNO) \ +{ \ + fprintf (STREAM, "\tbl\tmcount%s\n", NEED_PLT_RELOC ? "(PLT)" : ""); \ +} + +#undef CC1_SPEC +#define CC1_SPEC "%{profile:-p}" diff --git a/contrib/gcc/config/arm/linux-gas.h b/contrib/gcc/config/arm/linux-gas.h new file mode 100644 index 000000000000..b4e346dd9ef3 --- /dev/null +++ b/contrib/gcc/config/arm/linux-gas.h @@ -0,0 +1,71 @@ +/* Definitions of target machine for GNU compiler. + ARM Linux-based GNU systems version. + Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Russell King <rmk92@ecs.soton.ac.uk>. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* This is how we tell the assembler that a symbol is weak. + GAS always supports weak symbols. */ + +/* This is used in ASM_FILE_START. */ +#undef ARM_OS_NAME +#define ARM_OS_NAME "Linux" + +/* Unsigned chars produces much better code than signed. */ +#define DEFAULT_SIGNED_CHAR 0 + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__}" + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +/* Emit code to set up a trampoline and synchronise the caches. */ +#undef INITIALIZE_TRAMPOLINE +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 8)), \ + (CXT)); \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 12)), \ + (FNADDR)); \ + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__clear_cache"), \ + 0, VOIDmode, 2, TRAMP, Pmode, \ + plus_constant (TRAMP, TRAMPOLINE_SIZE), Pmode); \ +} + +/* Clear the instruction cache from `beg' to `end'. This makes an + inline system call to SYS_cacheflush. */ +#define CLEAR_INSN_CACHE(BEG, END) \ +{ \ + register unsigned long _beg __asm ("a1") = (unsigned long) (BEG); \ + register unsigned long _end __asm ("a2") = (unsigned long) (END); \ + register unsigned long _flg __asm ("a3") = 0; \ + __asm __volatile ("swi 0x9f0002 @ sys_cacheflush" \ + : "=r" (_beg) \ + : "0" (_beg), "r" (_end), "r" (_flg)); \ +} diff --git a/contrib/gcc/config/arm/netbsd.h b/contrib/gcc/config/arm/netbsd.h new file mode 100644 index 000000000000..74f32e805ea0 --- /dev/null +++ b/contrib/gcc/config/arm/netbsd.h @@ -0,0 +1,153 @@ +/* NetBSD/arm (RiscBSD) version. + Copyright (C) 1993, 1994, 1997, 1998 Free Software Foundation, Inc. + Contributed by Mark Brinicombe (amb@physig.ph.kcl.ac.uk) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/NetBSD)", stderr); + +/* This is used in ASM_FILE_START. */ +#undef ARM_OS_NAME +#define ARM_OS_NAME "NetBSD" + +/* Unsigned chars produces much better code than signed. */ +#define DEFAULT_SIGNED_CHAR 0 + +/* Since we always use GAS as our assembler we support stabs. */ +#define DBX_DEBUGGING_INFO 1 + +/*#undef ASM_DECLARE_FUNCTION_NAME*/ + +/* ARM6 family default cpu. */ +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm6 + +/* Default is to use APCS-32 mode. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_FRAME) + +/* Some defines for CPP. + arm32 is the NetBSD port name, so we always define arm32 and __arm32__. */ +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "\ +-Dunix -Driscbsd -Darm32 -D__arm32__ -D__arm__ -D__NetBSD__ \ +-Asystem=unix -Asystem=NetBSD" + +/* Define _POSIX_SOURCE if necessary. */ +#undef CPP_SPEC +#define CPP_SPEC "\ +%(cpp_cpu_arch) %(cpp_apcs_pc) %(cpp_float) %(cpp_endian) \ +%{posix:-D_POSIX_SOURCE} \ +" + +/* Because TARGET_DEFAULT sets ARM_FLAG_APCS_32 */ +#undef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" + +/* Because TARGET_DEFAULT sets ARM_FLAG_SOFT_FLOAT */ +#undef CPP_FLOAT_DEFAULT_SPEC +#define CPP_FLOAT_DEFAULT_SPEC "-D__SOFTFP__" + +/* Pass -X to the linker so that it will strip symbols starting with 'L' */ +#undef LINK_SPEC +#define LINK_SPEC "\ +-X %{!shared:%{!nostdlib:%{!r*:%{!e*:-e start}}} -dc -dp %{R*} \ +%{static:-Bstatic}} %{shared} %{assert*} \ +" + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + +#undef WCHAR_UNSIGNED +#define WCHAR_UNSIGNED 0 + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +#define HANDLE_SYSV_PRAGMA + +/* We don't have any limit on the length as out debugger is GDB. */ +#undef DBX_CONTIN_LENGTH + +/* NetBSD does its profiling differently to the Acorn compiler. We + don't need a word following the mcount call; and to skip it + requires either an assembly stub or use of fomit-frame-pointer when + compiling the profiling functions. Since we break Acorn CC + compatibility below a little more won't hurt. */ + +#undef ARM_FUNCTION_PROFILER +#define ARM_FUNCTION_PROFILER(STREAM,LABELNO) \ +{ \ + fprintf(STREAM, "\tmov\t%sip, %slr\n", REGISTER_PREFIX, REGISTER_PREFIX); \ + fprintf(STREAM, "\tbl\tmcount\n"); \ +} + +/* On the ARM `@' introduces a comment, so we must use something else + for .type directives. */ +#undef TYPE_OPERAND_FMT +#define TYPE_OPERAND_FMT "%%%s" + +/* NetBSD uses the old PCC style aggregate returning conventions. */ +#undef DEFAULT_PCC_STRUCT_RETURN +#define DEFAULT_PCC_STRUCT_RETURN 1 + +/* Although not normally relevant (since by default, all aggregates + are returned in memory) compiling some parts of libc requires + non-APCS style struct returns. */ +#undef RETURN_IN_MEMORY + +/* VERY BIG NOTE : Change of structure alignment for RiscBSD. + There are consequences you should be aware of... + + Normally GCC/arm uses a structure alignment of 32 for compatibility + with armcc. This means that structures are padded to a word + boundary. However this causes problems with bugged NetBSD kernel + code (possibly userland code as well - I have not checked every + binary). The nature of this bugged code is to rely on sizeof() + returning the correct size of various structures rounded to the + nearest byte (SCSI and ether code are two examples, the vm system + is another). This code breaks when the structure alignment is 32 + as sizeof() will report a word=rounded size. By changing the + structure alignment to 8. GCC will conform to what is expected by + NetBSD. + + This has several side effects that should be considered. + 1. Structures will only be aligned to the size of the largest member. + i.e. structures containing only bytes will be byte aligned. + structures containing shorts will be half word alinged. + structures containing ints will be word aligned. + + This means structures should be padded to a word boundary if + alignment of 32 is required for byte structures etc. + + 2. A potential performance penalty may exist if strings are no longer + word aligned. GCC will not be able to use word load/stores to copy + short strings. + + This modification is not encouraged but with the present state of the + NetBSD source tree it is currently the only solution that meets the + requirements. */ +#undef DEFAULT_STRUCTURE_SIZE_BOUNDARY +#define DEFAULT_STRUCTURE_SIZE_BOUNDARY 8 diff --git a/contrib/gcc/config/arm/pe.c b/contrib/gcc/config/arm/pe.c new file mode 100644 index 000000000000..8360f85c44ca --- /dev/null +++ b/contrib/gcc/config/arm/pe.c @@ -0,0 +1,280 @@ +/* Routines for GCC for ARM/pe. + Copyright (C) 1995, 1996, 2000, 2001 Free Software Foundation, Inc. + Contributed by Doug Evans (dje@cygnus.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "rtl.h" +#include "output.h" +#include "flags.h" +#include "tree.h" +#include "expr.h" +#include "toplev.h" +#include "tm_p.h" + +extern int current_function_anonymous_args; + + +/* Return non-zero if DECL is a dllexport'd object. */ + +tree current_class_type; /* FIXME */ + +int +arm_dllexport_p (decl) + tree decl; +{ + tree exp; + + if (TREE_CODE (decl) != VAR_DECL + && TREE_CODE (decl) != FUNCTION_DECL) + return 0; + exp = lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl)); + if (exp) + return 1; + + return 0; +} + +/* Return non-zero if DECL is a dllimport'd object. */ + +int +arm_dllimport_p (decl) + tree decl; +{ + tree imp; + + if (TREE_CODE (decl) == FUNCTION_DECL + && TARGET_NOP_FUN_DLLIMPORT) + return 0; + + if (TREE_CODE (decl) != VAR_DECL + && TREE_CODE (decl) != FUNCTION_DECL) + return 0; + imp = lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl)); + if (imp) + return 1; + + return 0; +} + +/* Return non-zero if SYMBOL is marked as being dllexport'd. */ + +int +arm_dllexport_name_p (symbol) + const char * symbol; +{ + return symbol[0] == ARM_PE_FLAG_CHAR && symbol[1] == 'e' && symbol[2] == '.'; +} + +/* Return non-zero if SYMBOL is marked as being dllimport'd. */ + +int +arm_dllimport_name_p (symbol) + const char * symbol; +{ + return symbol[0] == ARM_PE_FLAG_CHAR && symbol[1] == 'i' && symbol[2] == '.'; +} + +/* Mark a DECL as being dllexport'd. + Note that we override the previous setting (eg: dllimport). */ + +void +arm_mark_dllexport (decl) + tree decl; +{ + const char * oldname; + char * newname; + rtx rtlname; + tree idp; + + rtlname = XEXP (DECL_RTL (decl), 0); + if (GET_CODE (rtlname) == SYMBOL_REF) + oldname = XSTR (rtlname, 0); + else if (GET_CODE (rtlname) == MEM + && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF) + oldname = XSTR (XEXP (rtlname, 0), 0); + else + abort (); + if (arm_dllimport_name_p (oldname)) + oldname += 9; + else if (arm_dllexport_name_p (oldname)) + return; /* already done */ + + newname = alloca (strlen (oldname) + 4); + sprintf (newname, "%ce.%s", ARM_PE_FLAG_CHAR, oldname); + + /* We pass newname through get_identifier to ensure it has a unique + address. RTL processing can sometimes peek inside the symbol ref + and compare the string's addresses to see if two symbols are + identical. */ + /* ??? At least I think that's why we do this. */ + idp = get_identifier (newname); + + XEXP (DECL_RTL (decl), 0) = + gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp)); +} + +/* Mark a DECL as being dllimport'd. */ + +void +arm_mark_dllimport (decl) + tree decl; +{ + const char * oldname; + char * newname; + tree idp; + rtx rtlname, newrtl; + + rtlname = XEXP (DECL_RTL (decl), 0); + + if (GET_CODE (rtlname) == SYMBOL_REF) + oldname = XSTR (rtlname, 0); + else if (GET_CODE (rtlname) == MEM + && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF) + oldname = XSTR (XEXP (rtlname, 0), 0); + else + abort (); + + if (arm_dllexport_name_p (oldname)) + abort (); /* this shouldn't happen */ + else if (arm_dllimport_name_p (oldname)) + return; /* already done */ + + /* ??? One can well ask why we're making these checks here, + and that would be a good question. */ + + /* Imported variables can't be initialized. */ + if (TREE_CODE (decl) == VAR_DECL + && !DECL_VIRTUAL_P (decl) + && DECL_INITIAL (decl)) + { + error_with_decl (decl, "initialized variable `%s' is marked dllimport"); + return; + } + /* Nor can they be static. */ + if (TREE_CODE (decl) == VAR_DECL + /* ??? Is this test for vtables needed? */ + && !DECL_VIRTUAL_P (decl) + && 0 /*???*/) + { + error_with_decl (decl, "static variable `%s' is marked dllimport"); + return; + } + + /* `extern' needn't be specified with dllimport. + Specify `extern' now and hope for the best. Sigh. */ + if (TREE_CODE (decl) == VAR_DECL + /* ??? Is this test for vtables needed? */ + && !DECL_VIRTUAL_P (decl)) + { + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + } + + newname = alloca (strlen (oldname) + 11); + sprintf (newname, "%ci.__imp_%s", ARM_PE_FLAG_CHAR, oldname); + + /* We pass newname through get_identifier to ensure it has a unique + address. RTL processing can sometimes peek inside the symbol ref + and compare the string's addresses to see if two symbols are + identical. */ + /* ??? At least I think that's why we do this. */ + idp = get_identifier (newname); + + newrtl = gen_rtx (MEM, Pmode, + gen_rtx (SYMBOL_REF, Pmode, + IDENTIFIER_POINTER (idp))); + XEXP (DECL_RTL (decl), 0) = newrtl; +} + +/* Cover function to implement ENCODE_SECTION_INFO. */ + +void +arm_pe_encode_section_info (decl) + tree decl; +{ + /* This bit is copied from arm.h. */ + if (optimize > 0 && TREE_CONSTANT (decl) + && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST)) + { + rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd' + ? TREE_CST_RTL (decl) : DECL_RTL (decl)); + SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1; + } + + /* Mark the decl so we can tell from the rtl whether the object is + dllexport'd or dllimport'd. */ + if (arm_dllexport_p (decl)) + arm_mark_dllexport (decl); + else if (arm_dllimport_p (decl)) + arm_mark_dllimport (decl); + /* It might be that DECL has already been marked as dllimport, but a + subsequent definition nullified that. The attribute is gone but + DECL_RTL still has @i.__imp_foo. We need to remove that. */ + else if ((TREE_CODE (decl) == FUNCTION_DECL + || TREE_CODE (decl) == VAR_DECL) + && DECL_RTL (decl) != NULL_RTX + && GET_CODE (DECL_RTL (decl)) == MEM + && GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM + && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF + && arm_dllimport_name_p (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0))) + { + const char *oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0); + tree idp = get_identifier (oldname + 9); + rtx newrtl = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp)); + + XEXP (DECL_RTL (decl), 0) = newrtl; + + /* We previously set TREE_PUBLIC and DECL_EXTERNAL. + ??? We leave these alone for now. */ + } +} + +/* Cover function for UNIQUE_SECTION. */ + +void +arm_pe_unique_section (decl, reloc) + tree decl; + int reloc; +{ + int len; + const char * name; + char * string; + const char * prefix; + + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + /* Strip off any encoding in fnname. */ + STRIP_NAME_ENCODING (name, name); + + /* The object is put in, for example, section .text$foo. + The linker will then ultimately place them in .text + (everything from the $ on is stripped). */ + if (TREE_CODE (decl) == FUNCTION_DECL) + prefix = ".text$"; + else if (DECL_READONLY_SECTION (decl, reloc)) + prefix = ".rdata$"; + else + prefix = ".data$"; + len = strlen (name) + strlen (prefix); + string = alloca (len + 1); + sprintf (string, "%s%s", prefix, name); + + DECL_SECTION_NAME (decl) = build_string (len, string); +} diff --git a/contrib/gcc/config/arm/pe.h b/contrib/gcc/config/arm/pe.h new file mode 100644 index 000000000000..1182aac12c4d --- /dev/null +++ b/contrib/gcc/config/arm/pe.h @@ -0,0 +1,254 @@ +/* Definitions of target machine for GNU compiler, for ARM with PE obj format. + Copyright (C) 1995, 1996, 1999, 2000 Free Software Foundation, Inc. + Contributed by Doug Evans (dje@cygnus.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Enable PE specific code. */ +#define ARM_PE 1 + +#define ARM_PE_FLAG_CHAR '@' + +/* Ensure that @x. will be stripped from the function name. */ +#undef SUBTARGET_NAME_ENCODING_LENGTHS +#define SUBTARGET_NAME_ENCODING_LENGTHS \ + case ARM_PE_FLAG_CHAR: return 3; + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/pe)", stderr) + +/* Get tree.c to declare a target-specific specialization of + merge_decl_attributes. */ +#define TARGET_DLLIMPORT_DECL_ATTRIBUTES + +/* Support the __declspec keyword by turning them into attributes. + We currently only support: naked, dllimport, and dllexport. + Note that the current way we do this may result in a collision with + predefined attributes later on. This can be solved by using one attribute, + say __declspec__, and passing args to it. The problem with that approach + is that args are not accumulated: each new appearance would clobber any + existing args. */ +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "-D__pe__ -D__declspec(x)=__attribute__((x))" + + +/* Experimental addition for pr 7885. + Ignore dllimport for functions. */ +#define TARGET_FLAG_NOP_FUN (1 << 24) + +#undef TARGET_NOP_FUN_DLLIMPORT +#define TARGET_NOP_FUN_DLLIMPORT (target_flags & TARGET_FLAG_NOP_FUN) + +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ +{ "nop-fun-dllimport", TARGET_FLAG_NOP_FUN, \ + N_("Ignore dllimport attribute for functions") }, \ +{ "no-nop-fun-dllimport", - TARGET_FLAG_NOP_FUN, "" }, + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | TARGET_FLAG_NOP_FUN) + + +#undef WCHAR_TYPE +#define WCHAR_TYPE "short unsigned int" +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 16 + +/* Same as arm.h except r10 is call-saved, not fixed. */ +#undef FIXED_REGISTERS +#define FIXED_REGISTERS \ +{ \ + 0,0,0,0,0,0,0,0, \ + 0,0,0,1,0,1,0,1, \ + 0,0,0,0,0,0,0,0, \ + 1,1,1 \ +} + +/* Same as arm.h except r10 is call-saved, not fixed. */ +#undef CALL_USED_REGISTERS +#define CALL_USED_REGISTERS \ +{ \ + 1,1,1,1,0,0,0,0, \ + 0,0,0,1,1,1,1,1, \ + 1,1,1,1,0,0,0,0, \ + 1,1,1 \ +} + +/* In addition to the stuff done in arm.h, we must mark dll symbols specially. + Definitions of dllexport'd objects install some info in the .drectve + section. References to dllimport'd objects are fetched indirectly via + __imp_. If both are declared, dllexport overrides. + This is also needed to implement one-only vtables: they go into their own + section and we need to set DECL_SECTION_NAME so we do that here. + Note that we can be called twice on the same decl. */ +#undef ENCODE_SECTION_INFO +#define ENCODE_SECTION_INFO(DECL) \ + arm_pe_encode_section_info (DECL) + +/* Used to implement dllexport overriding dllimport semantics. It's also used + to handle vtables - the first pass won't do anything because + DECL_CONTEXT (DECL) will be 0 so arm_dll{ex,im}port_p will return 0. + It's also used to handle dllimport override semantics. */ +#define REDO_SECTION_INFO_P(DECL) 1 + +/* Define this macro if in some cases global symbols from one translation + unit may not be bound to undefined symbols in another translation unit + without user intervention. For instance, under Microsoft Windows + symbols must be explicitly imported from shared libraries (DLLs). */ +#define MULTIPLE_SYMBOL_SPACES + +#define UNIQUE_SECTION(DECL, RELOC) arm_pe_unique_section (DECL, RELOC) + +#define SUPPORTS_ONE_ONLY 1 + +/* Switch into a generic section. */ +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION default_pe_asm_named_section + +/* This outputs a lot of .req's to define alias for various registers. + Let's try to avoid this. */ +#undef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ + do \ + { \ + asm_fprintf (STREAM, "%@ Generated by gcc %s for ARM/pe\n",\ + version_string); \ + output_file_directive ((STREAM), main_input_filename); \ + } \ + while (0) + +/* Output a reference to a label. */ +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ + asm_fprintf (STREAM, "%U%s", arm_strip_name_encoding (NAME)) + +/* Output a function definition label. */ +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do \ + { \ + if (arm_dllexport_name_p (NAME)) \ + { \ + drectve_section (); \ + fprintf (STREAM, "\t.ascii \" -export:%s\"\n", \ + arm_strip_name_encoding (NAME)); \ + function_section (DECL); \ + } \ + ARM_DECLARE_FUNCTION_NAME (STREAM, NAME, DECL); \ + if (TARGET_THUMB) \ + fprintf (STREAM, "\t.code 16\n"); \ + ASM_OUTPUT_LABEL (STREAM, NAME); \ + } \ + while (0) + +/* Output a common block. */ +#undef ASM_OUTPUT_COMMON +#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \ + do \ + { \ + if (arm_dllexport_name_p (NAME)) \ + { \ + drectve_section (); \ + fprintf ((STREAM), "\t.ascii \" -export:%s\"\n",\ + arm_strip_name_encoding (NAME)); \ + } \ + if (! arm_dllimport_name_p (NAME)) \ + { \ + fprintf ((STREAM), "\t.comm\t"); \ + assemble_name ((STREAM), (NAME)); \ + asm_fprintf ((STREAM), ", %d\t%@ %d\n", \ + (ROUNDED), (SIZE)); \ + } \ + } \ + while (0) + +/* Output the label for an initialized variable. */ +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \ + do \ + { \ + if (arm_dllexport_name_p (NAME)) \ + { \ + enum in_section save_section = in_section; \ + drectve_section (); \ + fprintf (STREAM, "\t.ascii \" -export:%s\"\n",\ + arm_strip_name_encoding (NAME)); \ + switch_to_section (save_section, (DECL)); \ + } \ + ASM_OUTPUT_LABEL ((STREAM), (NAME)); \ + } \ + while (0) + +/* Support the ctors/dtors and other sections. */ + +#define DRECTVE_SECTION_ASM_OP "\t.section .drectve" + +/* A list of other sections which the compiler might be "in" at any + given time. */ + +#undef SUBTARGET_EXTRA_SECTIONS +#define SUBTARGET_EXTRA_SECTIONS in_drectve, + +/* A list of extra section function definitions. */ + +#undef SUBTARGET_EXTRA_SECTION_FUNCTIONS +#define SUBTARGET_EXTRA_SECTION_FUNCTIONS \ + DRECTVE_SECTION_FUNCTION \ + SWITCH_TO_SECTION_FUNCTION + +#define DRECTVE_SECTION_FUNCTION \ +void \ +drectve_section () \ +{ \ + if (in_section != in_drectve) \ + { \ + fprintf (asm_out_file, "%s\n", DRECTVE_SECTION_ASM_OP); \ + in_section = in_drectve; \ + } \ +} + +/* Switch to SECTION (an `enum in_section'). + + ??? This facility should be provided by GCC proper. + The problem is that we want to temporarily switch sections in + ASM_DECLARE_OBJECT_NAME and then switch back to the original section + afterwards. */ +#define SWITCH_TO_SECTION_FUNCTION \ +static void switch_to_section PARAMS ((enum in_section, tree)); \ +static void \ +switch_to_section (section, decl) \ + enum in_section section; \ + tree decl; \ +{ \ + switch (section) \ + { \ + case in_text: text_section (); break; \ + case in_data: data_section (); break; \ + case in_named: named_section (decl, NULL, 0); break; \ + case in_rdata: rdata_section (); break; \ + case in_ctors: ctors_section (); break; \ + case in_dtors: dtors_section (); break; \ + case in_drectve: drectve_section (); break; \ + default: abort (); break; \ + } \ +} diff --git a/contrib/gcc/config/arm/riscix.h b/contrib/gcc/config/arm/riscix.h new file mode 100644 index 000000000000..35fc23258d2e --- /dev/null +++ b/contrib/gcc/config/arm/riscix.h @@ -0,0 +1,144 @@ +/* Definitions of target machine for GNU compiler. ARM RISCiX version. + Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 + Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rwe11@cl.cam.ac.uk), based on original + work by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) + and Martin Simmons (@harleqn.co.uk). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Translation to find startup files. On RISC iX boxes, + crt0, mcrt0 and gcrt0.o are in /usr/lib. */ +#define STARTFILE_SPEC "\ + %{pg:/usr/lib/gcrt0.o%s}\ + %{!pg:%{p:/usr/lib/mcrt0.o%s}\ + %{!p:/usr/lib/crt0.o%s}}" + +/* RISC iX has no concept of -lg */ +/* If -static is specified then link with -lc_n */ + +#ifndef LIB_SPEC +#define LIB_SPEC "\ + %{g*:-lg}\ + %{!p:%{!pg:%{!static:-lc}%{static:-lc_n}}}\ + %{p:-lc_p}\ + %{pg:-lc_p}" +#endif + +/* The RISC iX assembler never deletes any symbols from the object module; + and, by default, ld doesn't either. -X causes local symbols starting + with 'L' to be deleted, which is what we want. */ +#ifndef LINK_SPEC +#define LINK_SPEC "-X" +#endif + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Darm -Driscix -Dunix -Asystem=unix" +#endif + + +/* RISCiX has some weird symbol name munging, that is done to the object module + after assembly, which enables multiple libraries to be supported within + one (possibly shared) library. It basically changes the symbol name of + certain symbols (for example _bcopy is converted to _$bcopy if using BSD) + Symrename's parameters are determined as follows: + -mno-symrename Don't run symrename + -mbsd symrename -BSD <file> + -mxopen symrename -XOPEN <file> + -ansi symrename - <file> + <none> symrename -BSD <file> + */ + +#ifndef ASM_FINAL_SPEC +#if !defined (CROSS_COMPILE) +#define ASM_FINAL_SPEC "\ +%{!mno-symrename: \ + \n /usr/bin/symrename \ + -%{mbsd:%{pedantic:%e-mbsd and -pedantic incompatible}BSD}\ +%{mxopen:%{mbsd:%e-mbsd and -mxopen incompatible}\ +%{pedantic:%e-mxopen and -pedantic incompatible}XOPEN}\ +%{!mbsd:%{!mxopen:%{!ansi:BSD}}} %{c:%{o*:%*}%{!o*:%b.o}}%{!c:%U.o}}" +#endif +#endif + +/* None of these is actually used in cc1. If we don't define them in target + switches cc1 complains about them. For the sake of argument lets allocate + bit 31 of target flags for such options. */ +#define SUBTARGET_SWITCHES \ + {"bsd", 0x80000000, N_("Do symbol renaming for BSD")}, \ + {"xopen", 0x80000000, N_("Do symbol renaming for X/OPEN")}, \ + {"no-symrename", 0x80000000, N_("Don't do symbol renaming")}, + + +/* Run-time Target Specification. */ +#define TARGET_VERSION \ + fputs (" (ARM/RISCiX)", stderr); + +/* This is used in ASM_FILE_START */ +#define ARM_OS_NAME "RISCiX" + +/* Unsigned chars produces much better code than signed. */ +#define DEFAULT_SIGNED_CHAR 0 + +/* Some systems use __main in a way incompatible with its use in gcc, in these + cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to + give the same symbol without quotes for an alternative entry point. You + must define both, or neither. */ +#ifndef NAME__MAIN +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain +#endif + +/* size_t is "unsigned int" in RISCiX */ +#define SIZE_TYPE "unsigned int" + +/* ptrdiff_t is "int" in RISCiX */ +#define PTRDIFF_TYPE "int" + +/* Maths operation domain error number, EDOM */ +#define TARGET_EDOM 33 + +/* Override the normal default CPU */ +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm2 + +/* r10 is reserved by RISCiX */ +#define SUBTARGET_CONDITIONAL_REGISTER_USAGE \ + fixed_regs[10] = 1; \ + call_used_regs[10] = 1; + +#include "arm/aout.h" + +/* The RISCiX assembler does not understand .set */ +#undef SET_ASM_OP + +/* Add to CPP_SPEC, we want to add the right #defines when using the include + files. */ +#define SUBTARGET_CPP_SPEC "\ + %{mbsd:%{pedantic:%e-mbsd and -pedantic incompatible} -D_BSD_C} \ + %{mxopen:%{mbsd:%e-mbsd and -mxopen incompatible} \ + %{pedantic:%e-mxopen and -pedantic incompatible} -D_XOPEN_C} \ + %{!mbsd:%{!mxopen:%{!ansi: -D_BSD_C}}}" + +/* The native RISCiX assembler does not support stabs of any kind; because + the native assembler is not used by the compiler, Acorn didn't feel it was + necessary to put them in! */ + +#ifdef DBX_DEBUGGING_INFO +#undef DBX_DEBUGGING_INFO +#endif diff --git a/contrib/gcc/config/arm/riscix1-1.h b/contrib/gcc/config/arm/riscix1-1.h new file mode 100644 index 000000000000..fd93d40f05ea --- /dev/null +++ b/contrib/gcc/config/arm/riscix1-1.h @@ -0,0 +1,103 @@ +/* Definitions of target machine for GNU compiler. ARM RISCiX 1.1x version. + Copyright (C) 1993, 1995, 1997, 1999 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rwe11@cl.cam.ac.uk), based on original + work by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) + and Martin Simmons (@harleqn.co.uk). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* RISCiX 1.1x is basically the same as 1.2x except that it doesn't have + symrename or atexit. */ + +/* Translation to find startup files. On RISCiX boxes, gcrt0.o is in + /usr/lib. */ +#define STARTFILE_SPEC \ + "%{pg:/usr/lib/gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}" + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "-Darm -Driscix -Dunix -Asystem=unix" +#endif + +/* Riscix 1.1 doesn't have X/OPEN support, so only accept -mbsd (but ignore + it). + By not having -mxopen and -mno-symrename, we get warning messages, + but everything still compiles. */ +/* None of these is actually used in cc1, so they modify bit 31 */ +#define SUBTARGET_SWITCHES \ +{"bsd", 0x80000000, ""}, + + +/* Run-time Target Specification. */ +#define TARGET_VERSION \ + fputs (" (ARM/RISCiX)", stderr); + +/* This is used in ASM_FILE_START */ +#define ARM_OS_NAME "RISCiX" + +#ifdef riscos +#define TARGET_WHEN_DEBUGGING 3 +#else +#define TARGET_WHEN_DEBUGGING 1 +#endif + +/* 'char' is signed by default on RISCiX, unsigned on RISCOS. */ +#ifdef riscos +#define DEFAULT_SIGNED_CHAR 0 +#else +#define DEFAULT_SIGNED_CHAR 1 +#endif + +/* Define this if the target system lacks the function atexit from the + ANSI C standard. If this is defined, and ON_EXIT is not + defined, a default exit function will be provided to support C++. + The man page only describes on_exit, but atexit is also there. + This seems to be missing in early versions. + + FIXME Should we define ON_EXIT here? */ +#define NEED_ATEXIT + +/* Some systems use __main in a way incompatible with its use in gcc, in these + cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to + give the same symbol without quotes for an alternative entry point. You + must define both, or neither. */ +#ifndef NAME__MAIN +#define NAME__MAIN "__gccmain" +#define SYMBOL__MAIN __gccmain +#endif + +/* Override the normal default CPU */ +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm2 + +/* r10 is reserved by RISCiX */ +#define SUBTARGET_CONDITIONAL_REGISTER_USAGE \ + fixed_regs[10] = 1; \ + call_used_regs[10] = 1; + + +#include "arm/aout.h" + +#define SUBTARGET_CPP_SPEC "%{!ansi: -D_BSD_C}" + + +/* The native RISCiX assembler does not support stabs of any kind; because + the native assembler is not used by the compiler, Acorn didn't feel it was + necessary to put them in! */ + +#ifdef DBX_DEBUGGING_INFO +#undef DBX_DEBUGGING_INFO +#endif diff --git a/contrib/gcc/config/arm/rix-gas.h b/contrib/gcc/config/arm/rix-gas.h new file mode 100644 index 000000000000..cfb5312f3b6c --- /dev/null +++ b/contrib/gcc/config/arm/rix-gas.h @@ -0,0 +1,42 @@ +/* Definitions of target machine for GNU compiler. ARM RISCiX(stabs) version. + Copyright (C) 1993 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rwe11@cl.cam.ac.uk), based on original + work by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) + and Martin Simmons (@harleqn.co.uk). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Limit the length of a stabs entry (for the broken Acorn assembler) */ +#undef DBX_CONTIN_LENGTH +#define DBX_CONTIN_LENGTH 80 + +/* The native RISCiX assembler does not support stabs of any kind; because + the native assembler is not used by the compiler, Acorn didn't feel it was + necessary to put them in! + However, this file assumes that we have an assembler that does have stabs, + so we put them back in. */ + +#define DBX_DEBUGGING_INFO + +/* Unfortunately dbx doesn't understand these */ +/* Dbx on RISCiX is so broken that I've given up trying to support it. + lets just support gdb. */ +/* #define DEFAULT_GDB_EXTENSIONS 0 */ +/* RISCiX dbx doesn't accept xrefs */ +/* #define DBX_NO_XREFS 1 */ + diff --git a/contrib/gcc/config/arm/rtems-elf.h b/contrib/gcc/config/arm/rtems-elf.h new file mode 100644 index 000000000000..954f72b98820 --- /dev/null +++ b/contrib/gcc/config/arm/rtems-elf.h @@ -0,0 +1,33 @@ +/* Definitions for RTEMS based ARM systems using ELF + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF RTEMS)", stderr); + +#define HAS_INIT_SECTION + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Darm -Darm_elf -Drtems -D__rtems__ -D__ELF__ \ + -Asystem(rtems) -Acpu(arm) -Amachine(arm)" + +/*#undef INVOKE_main*/ + + diff --git a/contrib/gcc/config/arm/semi.h b/contrib/gcc/config/arm/semi.h new file mode 100644 index 000000000000..54ea0e8b0371 --- /dev/null +++ b/contrib/gcc/config/arm/semi.h @@ -0,0 +1,73 @@ +/* Definitions of target machine for GNU compiler. ARM on semi-hosted platform + Copyright (C) 1994, 1995, 1996, 1997, 2001 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (richard.earnshaw@arm.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define STARTFILE_SPEC "crt0.o%s" + +#ifndef LIB_SPEC +#define LIB_SPEC "-lc" +#endif + +#ifndef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "-D__semi__" +#endif + +#ifndef LINK_SPEC +#define LINK_SPEC "%{mbig-endian:-EB} -X" +#endif + +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/semi-hosted)", stderr); +#endif + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME) +#endif + +#ifndef SUBTARGET_EXTRA_SPECS +#define SUBTARGET_EXTRA_SPECS \ + { "subtarget_extra_asm_spec", SUBTARGET_EXTRA_ASM_SPEC }, +#endif + +#ifndef SUBTARGET_EXTRA_ASM_SPEC +#define SUBTARGET_EXTRA_ASM_SPEC "" +#endif + +/* The compiler supports PIC code generation, even though the binutils + may not. If we are asked to compile position independent code, we + always pass -k to the assembler. If it doesn't recognize it, then + it will barf, which probably means that it doesn't know how to + assemble PIC code. This is what we want, since otherwise tools + may incorrectly assume we support PIC compilation even if the + binutils can't. */ +#ifndef ASM_SPEC +#define ASM_SPEC "\ +%{fpic: -k} %{fPIC: -k} \ +%{mbig-endian:-EB} \ +%{mcpu=*:-m%*} \ +%{march=*:-m%*} \ +%{mapcs-float:-mfloat} \ +%{msoft-float:-mno-fpu} \ +%{mthumb-interwork:-mthumb-interwork} \ +%(subtarget_extra_asm_spec)" +#endif + +#undef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" diff --git a/contrib/gcc/config/arm/semiaof.h b/contrib/gcc/config/arm/semiaof.h new file mode 100644 index 000000000000..7ca68bc95c25 --- /dev/null +++ b/contrib/gcc/config/arm/semiaof.h @@ -0,0 +1,39 @@ +/* Definitions of target machine for GNU compiler. ARM on semi-hosted platform + AOF Syntax assembler. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (richard.earnshaw@armltd.co.uk) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define CPP_PREDEFINES \ + "-Darm -Dsemi" + +#define ASM_SPEC "%{g -g} -arch 4 \ +-apcs 3%{mapcs-32:/32bit}%{mapcs-26:/26bit}%{!mapcs-26:%{!macps-32:/32bit}}" + +#define LIB_SPEC "%{Eb: armlib_h.32b%s}%{!Eb: armlib_h.32l%s}" + +#define TARGET_VERSION fputs (" (ARM/semi-hosted)", stderr); + +#define TARGET_DEFAULT ARM_FLAG_APCS_32 + +/* The Norcroft C library defines size_t as "unsigned int" */ +#define SIZE_TYPE "unsigned int" + +#undef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" diff --git a/contrib/gcc/config/arm/strongarm-coff.h b/contrib/gcc/config/arm/strongarm-coff.h new file mode 100644 index 000000000000..4d2f29212b5a --- /dev/null +++ b/contrib/gcc/config/arm/strongarm-coff.h @@ -0,0 +1,28 @@ +/* Definitions for StrongARM systems using COFF + Copyright (C) 1999 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#ifndef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_strongarm +#endif + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (StrongARM/COFF)", stderr); diff --git a/contrib/gcc/config/arm/strongarm-elf.h b/contrib/gcc/config/arm/strongarm-elf.h new file mode 100644 index 000000000000..3fc848718bcc --- /dev/null +++ b/contrib/gcc/config/arm/strongarm-elf.h @@ -0,0 +1,30 @@ +/* Definitions for non-Linux based StrongARM systems using ELF + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (StrongARM/ELF non-Linux)", stderr); +#endif + +#ifndef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_strongarm +#endif + diff --git a/contrib/gcc/config/arm/strongarm-pe.h b/contrib/gcc/config/arm/strongarm-pe.h new file mode 100644 index 000000000000..2938d3e55ac6 --- /dev/null +++ b/contrib/gcc/config/arm/strongarm-pe.h @@ -0,0 +1,23 @@ +/* Definitions of target machine for GNU compiler, for ARM with PE obj format. + Copyright (C) 1999 Free Software Foundation, Inc. + Contributed by Doug Evans (dje@cygnus.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (StrongARM/PE)", stderr); diff --git a/contrib/gcc/config/arm/t-arm-aout b/contrib/gcc/config/arm/t-arm-aout new file mode 100644 index 000000000000..2ce20e4fb744 --- /dev/null +++ b/contrib/gcc/config/arm/t-arm-aout @@ -0,0 +1,29 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +# MULTILIB_OPTIONS = mhard-float/msoft-float mapcs-32/mapcs-26 mno-thumb-interwork/mthumb-interwork arm/thumb +# MULTILIB_DIRNAMES = le be fpu soft 32bit 26bit normal interwork arm thumb +# MULTILIB_MATCHES = +# MULTILIB_EXCEPTIONS = *mapcs-26/*mthumb-interwork* *mpacs-26/*mthumb* + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/contrib/gcc/config/arm/t-arm-coff b/contrib/gcc/config/arm/t-arm-coff new file mode 100644 index 000000000000..bf37a37d4ddc --- /dev/null +++ b/contrib/gcc/config/arm/t-arm-coff @@ -0,0 +1,34 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +MULTILIB_OPTIONS = mlittle-endian/mbig-endian mhard-float/msoft-float marm/mthumb mno-thumb-interwork/mthumb-interwork +MULTILIB_DIRNAMES = le be fpu soft arm thumb normal interwork +MULTILIB_MATCHES = +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somwehere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline diff --git a/contrib/gcc/config/arm/t-arm-elf b/contrib/gcc/config/arm/t-arm-elf new file mode 100644 index 000000000000..d94b5d2ccbc0 --- /dev/null +++ b/contrib/gcc/config/arm/t-arm-elf @@ -0,0 +1,93 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + + +MULTILIB_OPTIONS = marm/mthumb +MULTILIB_DIRNAMES = arm thumb +MULTILIB_EXCEPTIONS = + +# MULTILIB_OPTIONS = mlittle-endian/mbig-endian +# MULTILIB_DIRNAMES = le be +# MULTILIB_EXCEPTIONS = +# MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle +# +# MULTILIB_OPTIONS += mhard-float/msoft-float +# MULTILIB_DIRNAMES += fpu soft +# +# MULTILIB_OPTIONS += mapcs-32/mapcs-26 +# MULTILIB_DIRNAMES += 32bit 26bit +# +# MULTILIB_OPTIONS += mno-thumb-interwork/mthumb-interwork +# MULTILIB_DIRNAMES += normal interwork +# MULTILIB_EXCEPTIONS += *mapcs-26/*mthumb-interwork* +# +# MULTILIB_OPTIONS += fno-leading-underscore/fleading-underscore +# MULTILIB_DIRNAMES += elf under +# +# MULTILIB_OPTIONS += mcpu=arm7 +# MULTILIB_DIRNAMES += nofmult +# MULTILIB_EXCEPTIONS += *mthumb*/*mcpu=arm7* +# # Note: the multilib_exceptions matches both -mthumb and +# # -mthumb-interwork +# # +# # We have to match all the arm cpu variants which do not have the +# # multiply instruction and treat them as if the user had specified +# # -mcpu=arm7. Note that in the following the ? is interpreted as +# # an = for the purposes of matching command line options. +# # FIXME: There ought to be a better way to do this. +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm7d +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm7di +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm70 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm700 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm700i +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm710 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm710c +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm7100 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm7500 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm7500fe +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm6 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm60 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm600 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm610 +# MULTILIB_MATCHES += mcpu?arm7=mcpu?arm620 + +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o + +# If EXTRA_MULTILIB_PARTS is not defined above then define EXTRA_PARTS here +# EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somewhere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline + +# Assemble startup files. +$(T)crti.o: $(srcdir)/config/arm/crti.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/arm/crti.asm + +$(T)crtn.o: $(srcdir)/config/arm/crtn.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/arm/crtn.asm diff --git a/contrib/gcc/config/arm/t-linux b/contrib/gcc/config/arm/t-linux new file mode 100644 index 000000000000..7dbd0c0e2776 --- /dev/null +++ b/contrib/gcc/config/arm/t-linux @@ -0,0 +1,23 @@ +# Just for these, we omit the frame pointer since it makes such a big +# difference. It is then pointless adding debugging. +TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC +LIBGCC2_DEBUG_CFLAGS = -g0 + +# Don't build enquire +ENQUIRE= + +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx + +# MULTILIB_OPTIONS = mhard-float/msoft-float +# MULTILIB_DIRNAMES = hard-float soft-float + +# If you want to build both APCS variants as multilib options this is how +# to do it. +# MULTILIB_OPTIONS += mapcs-32/mapcs-26 +# MULTILIB_DIRNAMES += apcs-32 apcs-26 + +# EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o + +# LIBGCC = stmp-multilib +# INSTALL_LIBGCC = install-multilib diff --git a/contrib/gcc/config/arm/t-netbsd b/contrib/gcc/config/arm/t-netbsd new file mode 100644 index 000000000000..511b0151116c --- /dev/null +++ b/contrib/gcc/config/arm/t-netbsd @@ -0,0 +1,26 @@ +# Just for these, we omit the frame pointer since it makes such a big +# difference. It is then pointless adding debugging. +TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fpic +LIBGCC2_DEBUG_CFLAGS = -g0 + +# Build a shared libgcc library. +SHLIB_EXT = .so +SHLIB_NAME = @shlib_base_name@.so +SHLIB_SONAME = @shlib_base_name@.so.1 +SHLIB_OBJS = @shlib_objs@ + +SHLIB_LINK = $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -shared -nodefaultlibs \ + -Wl,-soname,$(SHLIB_SONAME) \ + -o $(SHLIB_NAME) @multilib_flags@ $(SHLIB_OBJS) -lc && \ + rm -f $(SHLIB_SONAME) && \ + $(LN_S) $(SHLIB_NAME) $(SHLIB_SONAME) +# $(slibdir) double quoted to protect it from expansion while building +# libgcc.mk. We want this delayed until actual install time. +SHLIB_INSTALL = $(INSTALL_DATA) $(SHLIB_NAME) $$(slibdir)/$(SHLIB_SONAME); \ + rm -f $$(slibdir)/$(SHLIB_NAME); \ + $(LN_S) $(SHLIB_SONAME) $$(slibdir)/$(SHLIB_NAME) + +# Don't build enquire +ENQUIRE= + + diff --git a/contrib/gcc/config/arm/t-pe b/contrib/gcc/config/arm/t-pe new file mode 100644 index 000000000000..4de366521c06 --- /dev/null +++ b/contrib/gcc/config/arm/t-pe @@ -0,0 +1,31 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +pe.o: $(srcdir)/config/arm/pe.c + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/arm/pe.c + +MULTILIB_OPTIONS = mhard-float mthumb +MULTILIB_DIRNAMES = fpu thumb + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc diff --git a/contrib/gcc/config/arm/t-riscix b/contrib/gcc/config/arm/t-riscix new file mode 100644 index 000000000000..0d38cb0bb551 --- /dev/null +++ b/contrib/gcc/config/arm/t-riscix @@ -0,0 +1,6 @@ +# Just for these, we omit the frame pointer since it makes such a big +# difference. It is then pointless adding debugging. +TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer +LIBGCC2_DEBUG_CFLAGS = -g0 + +FIXPROTO_DEFINES= -D_POSIX_SOURCE -D_XOPEN_C -D_BSD_C -D_XOPEN_SOURCE diff --git a/contrib/gcc/config/arm/t-semi b/contrib/gcc/config/arm/t-semi new file mode 100644 index 000000000000..ce3946063864 --- /dev/null +++ b/contrib/gcc/config/arm/t-semi @@ -0,0 +1,43 @@ +# Just for these, we omit the frame pointer since it makes such a big +# difference. It is then pointless adding debugging. +TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer +LIBGCC2_DEBUG_CFLAGS = -g0 + +# Don't build enquire +ENQUIRE= + +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX + +#Don't try to run fixproto +STMP_FIXPROTO = + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifdef __SOFTFP__' > fp-bit.c + echo '#define FLOAT' >> fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + echo '#endif' >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifdef __SOFTFP__' > dp-bit.c + echo '#ifndef __ARMEB__' >> dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + echo '#endif' >> dp-bit.c + +MULTILIB_OPTIONS = msoft-float mapcs-26 mbig-endian mwords-little-endian +MULTILIB_DIRNAMES = soft apcs26 big wlittle +MULTILIB_EXCEPTIONS = *mapcs-26/*mbig-endian* mwords-little-endian *mapcs-26/mwords-little-endian msoft-float/mwords-little-endian + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib diff --git a/contrib/gcc/config/arm/t-strongarm-coff b/contrib/gcc/config/arm/t-strongarm-coff new file mode 100644 index 000000000000..0a66360cb277 --- /dev/null +++ b/contrib/gcc/config/arm/t-strongarm-coff @@ -0,0 +1,34 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +MULTILIB_OPTIONS = mlittle-endian/mbig-endian mhard-float/msoft-float +MULTILIB_DIRNAMES = le be fpu soft +MULTILIB_MATCHES = +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somwehere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline diff --git a/contrib/gcc/config/arm/t-strongarm-elf b/contrib/gcc/config/arm/t-strongarm-elf new file mode 100644 index 000000000000..46e2ac4f6158 --- /dev/null +++ b/contrib/gcc/config/arm/t-strongarm-elf @@ -0,0 +1,44 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +MULTILIB_OPTIONS = mlittle-endian/mbig-endian mhard-float/msoft-float +MULTILIB_DIRNAMES = le be fpu soft +MULTILIB_EXCEPTIONS = +MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somewhere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline + +# Assemble startup files. +$(T)crti.o: $(srcdir)/config/arm/crti.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/arm/crti.asm + +$(T)crtn.o: $(srcdir)/config/arm/crtn.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/arm/crtn.asm diff --git a/contrib/gcc/config/arm/t-strongarm-pe b/contrib/gcc/config/arm/t-strongarm-pe new file mode 100644 index 000000000000..6a44132bd07d --- /dev/null +++ b/contrib/gcc/config/arm/t-strongarm-pe @@ -0,0 +1,37 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +pe.o: $(srcdir)/config/arm/pe.c + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/arm/pe.c + +MULTILIB_OPTIONS = mhard-float/msoft-float +MULTILIB_DIRNAMES = fpu soft +MULTILIB_MATCHES = +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somwehere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline diff --git a/contrib/gcc/config/arm/t-xscale-coff b/contrib/gcc/config/arm/t-xscale-coff new file mode 100644 index 000000000000..89f371f4928b --- /dev/null +++ b/contrib/gcc/config/arm/t-xscale-coff @@ -0,0 +1,46 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +MULTILIB_OPTIONS = mlittle-endian/mbig-endian +MULTILIB_DIRNAMES = le be +MULTILIB_EXCEPTIONS = +MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle + +# Note XScale does not support 26 bit APCS. +# Note XScale does not support hard FP + +MULTILIB_OPTIONS += mno-thumb-interwork/mthumb-interwork +MULTILIB_DIRNAMES += normal interwork + +MULTILIB_OPTIONS += marm/mthumb +MULTILIB_DIRNAMES += arm thumb +MULTILIB_EXCEPTIONS += *mhard-float/*mthumb* + +MULTILIB_REDUNDANT_DIRS = interwork/thumb=thumb + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somewhere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline diff --git a/contrib/gcc/config/arm/t-xscale-elf b/contrib/gcc/config/arm/t-xscale-elf new file mode 100644 index 000000000000..0efc1d547788 --- /dev/null +++ b/contrib/gcc/config/arm/t-xscale-elf @@ -0,0 +1,57 @@ +LIB1ASMSRC = arm/lib1funcs.asm +LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __ARMEB__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __ARMEB__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +MULTILIB_OPTIONS = mlittle-endian/mbig-endian +MULTILIB_DIRNAMES = le be +MULTILIB_EXCEPTIONS = +MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle + +# Note XScale does not support 26 bit APCS. +# Note XScale does not support hard FP + +MULTILIB_OPTIONS += mno-thumb-interwork/mthumb-interwork +MULTILIB_DIRNAMES += normal interwork + +MULTILIB_OPTIONS += marm/mthumb +MULTILIB_DIRNAMES += arm thumb +MULTILIB_EXCEPTIONS += *mhard-float/*mthumb* + +MULTILIB_REDUNDANT_DIRS = interwork/thumb=thumb + +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +# Currently there is a bug somewhere in GCC's alias analysis +# or scheduling code that is breaking _fpmul_parts in fp-bit.c. +# Disabling function inlining is a workaround for this problem. +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline + +# Assemble startup files. +$(T)crti.o: $(srcdir)/config/arm/crti.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/arm/crti.asm + +$(T)crtn.o: $(srcdir)/config/arm/crtn.asm $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/arm/crtn.asm diff --git a/contrib/gcc/config/arm/uclinux-elf.h b/contrib/gcc/config/arm/uclinux-elf.h new file mode 100644 index 000000000000..cad61b70bd4f --- /dev/null +++ b/contrib/gcc/config/arm/uclinux-elf.h @@ -0,0 +1,30 @@ +/* Definitions for ARM running ucLinux using ELF + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + Contributed by Philip Blundell <pb@nexus.co.uk> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* We don't want a PLT. */ +#undef NEED_PLT_RELOC +#define NEED_PLT_RELOC 0 + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF ucLinux)", stderr); + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_MMU_TRAPS | ARM_FLAG_SINGLE_PIC_BASE) diff --git a/contrib/gcc/config/arm/unknown-elf-oabi.h b/contrib/gcc/config/arm/unknown-elf-oabi.h new file mode 100644 index 000000000000..cc58f3ae2bed --- /dev/null +++ b/contrib/gcc/config/arm/unknown-elf-oabi.h @@ -0,0 +1,32 @@ +/* Definitions for non-Linux based ARM systems using ELF old abi + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF non-Linux old abi)", stderr); + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Darm_oabi -Darm -Darm_elf -Acpu=arm -Amachine=arm -D__ELF__" + +#undef ASM_SPEC +#define ASM_SPEC "-moabi %{mbig-endian:-EB} %{mcpu=*:-m%*} %{march=*:-m%*} \ + %{mapcs-*:-mapcs-%*} %{mthumb-interwork:-mthumb-interwork}" + diff --git a/contrib/gcc/config/arm/unknown-elf.h b/contrib/gcc/config/arm/unknown-elf.h new file mode 100644 index 000000000000..0741884ee120 --- /dev/null +++ b/contrib/gcc/config/arm/unknown-elf.h @@ -0,0 +1,100 @@ +/* Definitions for non-Linux based ARM systems using ELF + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* elfos.h should have already been included. Now just override + any conflicting definitions and add any extras. */ + +/* Run-time Target Specification. */ +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/ELF)", stderr); +#endif + +/* Default to using APCS-32 and software floating point. */ +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME) +#endif + +/* Now we define the strings used to build the spec file. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC " crti%O%s crtbegin%O%s crt0%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend%O%s crtn%O%s" + +/* The __USES_INITFINI__ define is tested in newlib/libc/sys/arm/crt0.S + to see if it needs to invoked _init() and _fini(). */ +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "-D__ELF__ -D__USES_INITFINI__" + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Return a non-zero value if DECL has a section attribute. */ +#define IN_NAMED_SECTION(DECL) \ + ((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \ + && DECL_SECTION_NAME (DECL) != NULL_TREE) + +#undef ASM_OUTPUT_ALIGNED_BSS +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + do \ + { \ + if (IN_NAMED_SECTION (DECL)) \ + named_section (DECL, NULL, 0); \ + else \ + bss_section (); \ + \ + ASM_GLOBALIZE_LABEL (FILE, NAME); \ + \ + ASM_OUTPUT_ALIGN (FILE, floor_log2 (ALIGN / BITS_PER_UNIT)); \ + \ + last_assemble_variable_decl = DECL; \ + ASM_DECLARE_OBJECT_NAME (FILE, NAME, DECL); \ + ASM_OUTPUT_SKIP (FILE, SIZE ? SIZE : 1); \ + } \ + while (0) + +#undef ASM_OUTPUT_ALIGNED_DECL_LOCAL +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \ + do \ + { \ + if ((DECL) != NULL && IN_NAMED_SECTION (DECL)) \ + named_section (DECL, NULL, 0); \ + else \ + bss_section (); \ + \ + ASM_OUTPUT_ALIGN (FILE, floor_log2 (ALIGN / BITS_PER_UNIT)); \ + ASM_OUTPUT_LABEL (FILE, NAME); \ + fprintf (FILE, "\t.space\t%d\n", SIZE); \ + } \ + while (0) + +#ifndef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__ELF__" +#endif + +#ifndef CPP_APCS_PC_DEFAULT_SPEC +#define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" +#endif + +#ifndef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm7tdmi +#endif + diff --git a/contrib/gcc/config/arm/vxarm.h b/contrib/gcc/config/arm/vxarm.h new file mode 100644 index 000000000000..755936450c88 --- /dev/null +++ b/contrib/gcc/config/arm/vxarm.h @@ -0,0 +1,69 @@ +/* Definitions of target machine for GNU compiler, + for ARM with targetting the VXWorks run time environment. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + + Contributed by: Mike Stump <mrs@wrs.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm710 + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC \ +"%{march=arm710:-DCPU=ARM710A} \ + %{march=arm7tdmi:-DCPU=ARM7TDMI} \ + %{march=arm810:-DCPU=ARM810} \ + %{march=strongarm110:-DCPU=ARMSA110} \ + %{!march=*: \ + %{mcpu=arm710:-DCPU=ARM710A} \ + %{mcpu=arm7tdmi:-DCPU=ARM7TDMI} \ + %{mcpu=arm810:-DCPU=ARM810} \ + %{mcpu=strongarm110:-DCPU=ARMSA110}} \ + %{!mcpu*:%{!march=*:-DCPU=ARM710A}} \ +" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-D__vxworks -Acpu=arm -Amachine=arm" + +/* VxWorks does all the library stuff itself. */ +#undef LIB_SPEC +#define LIB_SPEC "" + +/* VxWorks uses object files, not loadable images. make linker just + combine objects. */ +#undef LINK_SPEC +#define LINK_SPEC "-r" + +/* VxWorks provides the functionality of crt0.o and friends itself. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "" + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (ARM/VxWorks)", stderr); + +#undef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ + do \ + { \ + fprintf (STREAM, "%s Generated by gcc %s for ARM/VxWorks\n", \ + ASM_COMMENT_START, version_string); \ + } \ + while (0) diff --git a/contrib/gcc/config/arm/xscale-coff.h b/contrib/gcc/config/arm/xscale-coff.h new file mode 100644 index 000000000000..51fe6932e6d7 --- /dev/null +++ b/contrib/gcc/config/arm/xscale-coff.h @@ -0,0 +1,34 @@ +/* Definitions for XScale systems using COFF + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#undef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_xscale + +#undef SUBTARGET_EXTRA_ASM_SPEC +#define SUBTARGET_EXTRA_ASM_SPEC "%{!mcpu=*:-mxscale}" + +#undef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + { "mlittle-endian", "mno-thumb-interwork", "marm" } + +#undef TARGET_VERSION +#define TARGET_VERSION fputs (" (XScale/COFF)", stderr); diff --git a/contrib/gcc/config/arm/xscale-elf.h b/contrib/gcc/config/arm/xscale-elf.h new file mode 100644 index 000000000000..b1ce04e103e2 --- /dev/null +++ b/contrib/gcc/config/arm/xscale-elf.h @@ -0,0 +1,36 @@ +/* Definitions for XScale architectures using ELF + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Catherine Moore <clm@cygnus.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Run-time Target Specification. */ +#ifndef TARGET_VERSION +#define TARGET_VERSION fputs (" (XScale/ELF non-Linux)", stderr); +#endif + +#ifndef SUBTARGET_CPU_DEFAULT +#define SUBTARGET_CPU_DEFAULT TARGET_CPU_xscale +#endif + +#define SUBTARGET_EXTRA_ASM_SPEC "%{!mcpu=*:-mxscale} %{!mhard-float:-mno-fpu}" + +#ifndef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + { "mlittle-endian", "mno-thumb-interwork", "marm", "msoft-float" } +#endif |