URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/fr30
- from Rev 154 to Rev 816
- ↔ Reverse comparison
Rev 154 → Rev 816
/crti.asm
0,0 → 1,74
# crti.s for ELF |
|
# Copyright (C) 1992, 1998, 1999 Free Software Foundation, Inc. |
# Written By David Vinayak Henkel-Wallace, June 1992 |
# |
# 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, 51 Franklin Street, Fifth Floor, |
# Boston, MA 02110-1301, 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. |
|
.file "crti.asm" |
|
.section ".init" |
.global _init |
.type _init,#function |
.align 4 |
_init: |
st rp, @-r15 |
enter #4 |
|
# These nops are here to align the end of this code with a 16 byte |
# boundary. The linker will start inserting code into the .init |
# section at such a boundary. |
|
nop |
nop |
nop |
nop |
nop |
nop |
|
|
.section ".fini" |
.global _fini |
.type _fini,#function |
.align 4 |
_fini: |
st rp, @-r15 |
enter #4 |
nop |
nop |
nop |
nop |
nop |
nop |
/fr30.h
0,0 → 1,1130
/*{{{ Comment. */ |
|
/* Definitions of FR30 target. |
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2007 |
Free Software Foundation, Inc. |
Contributed by Cygnus Solutions. |
|
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 3, 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 GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
/*}}}*/ |
/*{{{ Driver configuration. */ |
|
/* Defined in svr4.h. */ |
#undef SWITCH_TAKES_ARG |
|
/* Defined in svr4.h. */ |
#undef WORD_SWITCH_TAKES_ARG |
|
/*}}}*/ |
/*{{{ Run-time target specifications. */ |
|
#undef ASM_SPEC |
#define ASM_SPEC "%{v}" |
|
/* Define this to be a string constant containing `-D' options to define the |
predefined macros that identify this machine and system. These macros will |
be predefined unless the `-ansi' option is specified. */ |
|
#define TARGET_CPU_CPP_BUILTINS() \ |
do \ |
{ \ |
builtin_define_std ("fr30"); \ |
builtin_assert ("machine=fr30"); \ |
} \ |
while (0) |
|
#define TARGET_VERSION fprintf (stderr, " (fr30)"); |
|
#define CAN_DEBUG_WITHOUT_FP |
|
#undef STARTFILE_SPEC |
#define STARTFILE_SPEC "crt0.o%s crti.o%s crtbegin.o%s" |
|
/* Include the OS stub library, so that the code can be simulated. |
This is not the right way to do this. Ideally this kind of thing |
should be done in the linker script - but I have not worked out how |
to specify the location of a linker script in a gcc command line yet... */ |
#undef ENDFILE_SPEC |
#define ENDFILE_SPEC "%{!mno-lsim:-lsim} crtend.o%s crtn.o%s" |
|
/*}}}*/ |
/*{{{ Storage Layout. */ |
|
#define BITS_BIG_ENDIAN 1 |
|
#define BYTES_BIG_ENDIAN 1 |
|
#define WORDS_BIG_ENDIAN 1 |
|
#define UNITS_PER_WORD 4 |
|
#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \ |
do \ |
{ \ |
if (GET_MODE_CLASS (MODE) == MODE_INT \ |
&& GET_MODE_SIZE (MODE) < 4) \ |
(MODE) = SImode; \ |
} \ |
while (0) |
|
#define PARM_BOUNDARY 32 |
|
#define STACK_BOUNDARY 32 |
|
#define FUNCTION_BOUNDARY 32 |
|
#define BIGGEST_ALIGNMENT 32 |
|
#define DATA_ALIGNMENT(TYPE, ALIGN) \ |
(TREE_CODE (TYPE) == ARRAY_TYPE \ |
&& TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ |
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) |
|
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ |
(TREE_CODE (EXP) == STRING_CST \ |
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) |
|
#define STRICT_ALIGNMENT 1 |
|
/* Defined in svr4.h. */ |
#define PCC_BITFIELD_TYPE_MATTERS 1 |
|
/*}}}*/ |
/*{{{ Layout of Source Language Data Types. */ |
|
#define SHORT_TYPE_SIZE 16 |
#define INT_TYPE_SIZE 32 |
#define LONG_TYPE_SIZE 32 |
#define LONG_LONG_TYPE_SIZE 64 |
#define FLOAT_TYPE_SIZE 32 |
#define DOUBLE_TYPE_SIZE 64 |
#define LONG_DOUBLE_TYPE_SIZE 64 |
|
#define DEFAULT_SIGNED_CHAR 1 |
|
/*}}}*/ |
/*{{{ REGISTER BASICS. */ |
|
/* Number of hardware registers known to the compiler. They receive numbers 0 |
through `FIRST_PSEUDO_REGISTER-1'; thus, the first pseudo register's number |
really is assigned the number `FIRST_PSEUDO_REGISTER'. */ |
#define FIRST_PSEUDO_REGISTER 21 |
|
/* Fixed register assignments: */ |
|
/* Here we do a BAD THING - reserve a register for use by the machine |
description file. There are too many places in compiler where it |
assumes that it can issue a branch or jump instruction without |
providing a scratch register for it, and reload just cannot cope, so |
we keep a register back for these situations. */ |
#define COMPILER_SCRATCH_REGISTER 0 |
|
/* The register that contains the result of a function call. */ |
#define RETURN_VALUE_REGNUM 4 |
|
/* The first register that can contain the arguments to a function. */ |
#define FIRST_ARG_REGNUM 4 |
|
/* A call-used register that can be used during the function prologue. */ |
#define PROLOGUE_TMP_REGNUM COMPILER_SCRATCH_REGISTER |
|
/* Register numbers used for passing a function's static chain pointer. If |
register windows are used, the register number as seen by the called |
function is `STATIC_CHAIN_INCOMING_REGNUM', while the register number as |
seen by the calling function is `STATIC_CHAIN_REGNUM'. If these registers |
are the same, `STATIC_CHAIN_INCOMING_REGNUM' need not be defined. |
|
The static chain register need not be a fixed register. |
|
If the static chain is passed in memory, these macros should not be defined; |
instead, the next two macros should be defined. */ |
#define STATIC_CHAIN_REGNUM 12 |
/* #define STATIC_CHAIN_INCOMING_REGNUM */ |
|
/* An FR30 specific hardware register. */ |
#define ACCUMULATOR_REGNUM 13 |
|
/* The register number of the frame pointer register, which is used to access |
automatic variables in the stack frame. On some machines, the hardware |
determines which register this is. On other machines, you can choose any |
register you wish for this purpose. */ |
#define FRAME_POINTER_REGNUM 14 |
|
/* The register number of the stack pointer register, which must also be a |
fixed register according to `FIXED_REGISTERS'. On most machines, the |
hardware determines which register this is. */ |
#define STACK_POINTER_REGNUM 15 |
|
/* The following a fake hard registers that describe some of the dedicated |
registers on the FR30. */ |
#define CONDITION_CODE_REGNUM 16 |
#define RETURN_POINTER_REGNUM 17 |
#define MD_HIGH_REGNUM 18 |
#define MD_LOW_REGNUM 19 |
|
/* An initializer that says which registers are used for fixed purposes all |
throughout the compiled code and are therefore not available for general |
allocation. These would include the stack pointer, the frame pointer |
(except on machines where that can be used as a general register when no |
frame pointer is needed), the program counter on machines where that is |
considered one of the addressable registers, and any other numbered register |
with a standard use. |
|
This information is expressed as a sequence of numbers, separated by commas |
and surrounded by braces. The Nth number is 1 if register N is fixed, 0 |
otherwise. |
|
The table initialized from this macro, and the table initialized by the |
following one, may be overridden at run time either automatically, by the |
actions of the macro `CONDITIONAL_REGISTER_USAGE', or by the user with the |
command options `-ffixed-REG', `-fcall-used-REG' and `-fcall-saved-REG'. */ |
#define FIXED_REGISTERS \ |
{ 1, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ \ |
0, 0, 0, 0, 0, 0, 0, 1, /* 8 - 15 */ \ |
1, 1, 1, 1, 1 } /* 16 - 20 */ |
|
/* XXX - MDL and MDH set as fixed for now - this is until I can get the |
mul patterns working. */ |
|
/* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in |
general) by function calls as well as for fixed registers. This macro |
therefore identifies the registers that are not available for general |
allocation of values that must live across function calls. |
|
If a register has 0 in `CALL_USED_REGISTERS', the compiler automatically |
saves it on function entry and restores it on function exit, if the register |
is used within the function. */ |
#define CALL_USED_REGISTERS \ |
{ 1, 1, 1, 1, 1, 1, 1, 1, /* 0 - 7 */ \ |
0, 0, 0, 0, 1, 1, 0, 1, /* 8 - 15 */ \ |
1, 1, 1, 1, 1 } /* 16 - 20 */ |
|
/* A C initializer containing the assembler's names for the machine registers, |
each one as a C string constant. This is what translates register numbers |
in the compiler into assembler language. */ |
#define REGISTER_NAMES \ |
{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ |
"r8", "r9", "r10", "r11", "r12", "ac", "fp", "sp", \ |
"cc", "rp", "mdh", "mdl", "ap" \ |
} |
|
/* If defined, a C initializer for an array of structures containing a name and |
a register number. This macro defines additional names for hard registers, |
thus allowing the `asm' option in declarations to refer to registers using |
alternate names. */ |
#define ADDITIONAL_REGISTER_NAMES \ |
{ \ |
{"r13", 13}, {"r14", 14}, {"r15", 15}, {"usp", 15}, {"ps", 16}\ |
} |
|
/*}}}*/ |
/*{{{ How Values Fit in Registers. */ |
|
/* A C expression for the number of consecutive hard registers, starting at |
register number REGNO, required to hold a value of mode MODE. */ |
|
#define HARD_REGNO_NREGS(REGNO, MODE) \ |
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) |
|
/* A C expression that is nonzero if it is permissible to store a value of mode |
MODE in hard register number REGNO (or in several registers starting with |
that one). */ |
|
#define HARD_REGNO_MODE_OK(REGNO, MODE) 1 |
|
/* A C expression that is nonzero if it is desirable to choose register |
allocation so as to avoid move instructions between a value of mode MODE1 |
and a value of mode MODE2. |
|
If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, MODE2)' are |
ever different for any R, then `MODES_TIEABLE_P (MODE1, MODE2)' must be |
zero. */ |
#define MODES_TIEABLE_P(MODE1, MODE2) 1 |
|
/*}}}*/ |
/*{{{ Register Classes. */ |
|
/* An enumeral type that must be defined with all the register class names as |
enumeral values. `NO_REGS' must be first. `ALL_REGS' must be the last |
register class, followed by one more enumeral value, `LIM_REG_CLASSES', |
which is not a register class but rather tells how many classes there are. |
|
Each register class has a number, which is the value of casting the class |
name to type `int'. The number serves as an index in many of the tables |
described below. */ |
enum reg_class |
{ |
NO_REGS, |
MULTIPLY_32_REG, /* the MDL register as used by the MULH, MULUH insns */ |
MULTIPLY_64_REG, /* the MDH,MDL register pair as used by MUL and MULU */ |
LOW_REGS, /* registers 0 through 7 */ |
HIGH_REGS, /* registers 8 through 15 */ |
REAL_REGS, /* i.e. all the general hardware registers on the FR30 */ |
ALL_REGS, |
LIM_REG_CLASSES |
}; |
|
#define GENERAL_REGS REAL_REGS |
#define N_REG_CLASSES ((int) LIM_REG_CLASSES) |
|
/* An initializer containing the names of the register classes as C string |
constants. These names are used in writing some of the debugging dumps. */ |
#define REG_CLASS_NAMES \ |
{ \ |
"NO_REGS", \ |
"MULTIPLY_32_REG", \ |
"MULTIPLY_64_REG", \ |
"LOW_REGS", \ |
"HIGH_REGS", \ |
"REAL_REGS", \ |
"ALL_REGS" \ |
} |
|
/* An initializer containing the contents of the register classes, as integers |
which are bit masks. The Nth integer specifies the contents of class N. |
The way the integer MASK is interpreted is that register R is in the class |
if `MASK & (1 << R)' is 1. |
|
When the machine has more than 32 registers, an integer does not suffice. |
Then the integers are replaced by sub-initializers, braced groupings |
containing several integers. Each sub-initializer must be suitable as an |
initializer for the type `HARD_REG_SET' which is defined in |
`hard-reg-set.h'. */ |
#define REG_CLASS_CONTENTS \ |
{ \ |
{ 0 }, \ |
{ 1 << MD_LOW_REGNUM }, \ |
{ (1 << MD_LOW_REGNUM) | (1 << MD_HIGH_REGNUM) }, \ |
{ (1 << 8) - 1 }, \ |
{ ((1 << 8) - 1) << 8 }, \ |
{ (1 << CONDITION_CODE_REGNUM) - 1 }, \ |
{ (1 << FIRST_PSEUDO_REGISTER) - 1 } \ |
} |
|
/* A C expression whose value is a register class containing hard register |
REGNO. In general there is more than one such class; choose a class which |
is "minimal", meaning that no smaller class also contains the register. */ |
#define REGNO_REG_CLASS(REGNO) \ |
( (REGNO) < 8 ? LOW_REGS \ |
: (REGNO) < CONDITION_CODE_REGNUM ? HIGH_REGS \ |
: (REGNO) == MD_LOW_REGNUM ? MULTIPLY_32_REG \ |
: (REGNO) == MD_HIGH_REGNUM ? MULTIPLY_64_REG \ |
: ALL_REGS) |
|
/* A macro whose definition is the name of the class to which a valid base |
register must belong. A base register is one used in an address which is |
the register value plus a displacement. */ |
#define BASE_REG_CLASS REAL_REGS |
|
/* A macro whose definition is the name of the class to which a valid index |
register must belong. An index register is one used in an address where its |
value is either multiplied by a scale factor or added to another register |
(as well as added to a displacement). */ |
#define INDEX_REG_CLASS REAL_REGS |
|
/* A C expression which defines the machine-dependent operand constraint |
letters for register classes. If CHAR is such a letter, the value should be |
the register class corresponding to it. Otherwise, the value should be |
`NO_REGS'. The register letter `r', corresponding to class `GENERAL_REGS', |
will not be passed to this macro; you do not need to handle it. |
|
The following letters are unavailable, due to being used as |
constraints: |
'0'..'9' |
'<', '>' |
'E', 'F', 'G', 'H' |
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P' |
'Q', 'R', 'S', 'T', 'U' |
'V', 'X' |
'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */ |
|
#define REG_CLASS_FROM_LETTER(CHAR) \ |
( (CHAR) == 'd' ? MULTIPLY_64_REG \ |
: (CHAR) == 'e' ? MULTIPLY_32_REG \ |
: (CHAR) == 'h' ? HIGH_REGS \ |
: (CHAR) == 'l' ? LOW_REGS \ |
: (CHAR) == 'a' ? ALL_REGS \ |
: NO_REGS) |
|
/* A C expression which is nonzero if register number NUM is suitable for use |
as a base register in operand addresses. It may be either a suitable hard |
register or a pseudo register that has been allocated such a hard register. */ |
#define REGNO_OK_FOR_BASE_P(NUM) 1 |
|
/* A C expression which is nonzero if register number NUM is suitable for use |
as an index register in operand addresses. It may be either a suitable hard |
register or a pseudo register that has been allocated such a hard register. |
|
The difference between an index register and a base register is that the |
index register may be scaled. If an address involves the sum of two |
registers, neither one of them scaled, then either one may be labeled the |
"base" and the other the "index"; but whichever labeling is used must fit |
the machine's constraints of which registers may serve in each capacity. |
The compiler will try both labelings, looking for one that is valid, and |
will reload one or both registers only if neither labeling works. */ |
#define REGNO_OK_FOR_INDEX_P(NUM) 1 |
|
/* A C expression that places additional restrictions on the register class to |
use when it is necessary to copy value X into a register in class CLASS. |
The value is a register class; perhaps CLASS, or perhaps another, smaller |
class. On many machines, the following definition is safe: |
|
#define PREFERRED_RELOAD_CLASS(X,CLASS) CLASS |
|
Sometimes returning a more restrictive class makes better code. For |
example, on the 68000, when X is an integer constant that is in range for a |
`moveq' instruction, the value of this macro is always `DATA_REGS' as long |
as CLASS includes the data registers. Requiring a data register guarantees |
that a `moveq' will be used. |
|
If X is a `const_double', by returning `NO_REGS' you can force X into a |
memory constant. This is useful on certain machines where immediate |
floating values cannot be loaded into certain kinds of registers. */ |
#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS |
|
/* A C expression for the maximum number of consecutive registers of |
class CLASS needed to hold a value of mode MODE. |
|
This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value |
of the macro `CLASS_MAX_NREGS (CLASS, MODE)' should be the maximum value of |
`HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class CLASS. |
|
This macro helps control the handling of multiple-word values in |
the reload pass. */ |
#define CLASS_MAX_NREGS(CLASS, MODE) HARD_REGNO_NREGS (0, MODE) |
|
/*}}}*/ |
/*{{{ CONSTANTS. */ |
|
/* A C expression that defines the machine-dependent operand constraint letters |
(`I', `J', `K', .. 'P') that specify particular ranges of integer values. |
If C is one of those letters, the expression should check that VALUE, an |
integer, is in the appropriate range and return 1 if so, 0 otherwise. If C |
is not one of those letters, the value should be 0 regardless of VALUE. */ |
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ |
( (C) == 'I' ? IN_RANGE (VALUE, 0, 15) \ |
: (C) == 'J' ? IN_RANGE (VALUE, -16, -1) \ |
: (C) == 'K' ? IN_RANGE (VALUE, 16, 31) \ |
: (C) == 'L' ? IN_RANGE (VALUE, 0, (1 << 8) - 1) \ |
: (C) == 'M' ? IN_RANGE (VALUE, 0, (1 << 20) - 1) \ |
: (C) == 'P' ? IN_RANGE (VALUE, -(1 << 8), (1 << 8) - 1) \ |
: 0) |
|
/* A C expression that defines the machine-dependent operand constraint letters |
(`G', `H') that specify particular ranges of `const_double' values. |
|
If C is one of those letters, the expression should check that VALUE, an RTX |
of code `const_double', is in the appropriate range and return 1 if so, 0 |
otherwise. If C is not one of those letters, the value should be 0 |
regardless of VALUE. |
|
`const_double' is used for all floating-point constants and for `DImode' |
fixed-point constants. A given letter can accept either or both kinds of |
values. It can use `GET_MODE' to distinguish between these kinds. */ |
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) 0 |
|
/* A C expression that defines the optional machine-dependent constraint |
letters (`Q', `R', `S', `T', `U') that can be used to segregate specific |
types of operands, usually memory references, for the target machine. |
Normally this macro will not be defined. If it is required for a particular |
target machine, it should return 1 if VALUE corresponds to the operand type |
represented by the constraint letter C. If C is not defined as an extra |
constraint, the value returned should be 0 regardless of VALUE. |
|
For example, on the ROMP, load instructions cannot have their output in r0 |
if the memory reference contains a symbolic address. Constraint letter `Q' |
is defined as representing a memory address that does *not* contain a |
symbolic address. An alternative is specified with a `Q' constraint on the |
input and `r' on the output. The next alternative specifies `m' on the |
input and a register class that does not include r0 on the output. */ |
#define EXTRA_CONSTRAINT(VALUE, C) \ |
((C) == 'Q' ? (GET_CODE (VALUE) == MEM && GET_CODE (XEXP (VALUE, 0)) == SYMBOL_REF) : 0) |
|
/*}}}*/ |
/*{{{ Basic Stack Layout. */ |
|
/* Define this macro if pushing a word onto the stack moves the stack pointer |
to a smaller address. */ |
#define STACK_GROWS_DOWNWARD 1 |
|
/* Define this to macro nonzero if the addresses of local variable slots |
are at negative offsets from the frame pointer. */ |
#define FRAME_GROWS_DOWNWARD 1 |
|
/* Offset from the frame pointer to the first local variable slot to be |
allocated. |
|
If `FRAME_GROWS_DOWNWARD', find the next slot's offset by subtracting the |
first slot's length from `STARTING_FRAME_OFFSET'. Otherwise, it is found by |
adding the length of the first slot to the value `STARTING_FRAME_OFFSET'. */ |
/* #define STARTING_FRAME_OFFSET -4 */ |
#define STARTING_FRAME_OFFSET 0 |
|
/* Offset from the stack pointer register to the first location at which |
outgoing arguments are placed. If not specified, the default value of zero |
is used. This is the proper value for most machines. |
|
If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first |
location at which outgoing arguments are placed. */ |
#define STACK_POINTER_OFFSET 0 |
|
/* Offset from the argument pointer register to the first argument's address. |
On some machines it may depend on the data type of the function. |
|
If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first |
argument's address. */ |
#define FIRST_PARM_OFFSET(FUNDECL) 0 |
|
/* A C expression whose value is RTL representing the location of the incoming |
return address at the beginning of any function, before the prologue. This |
RTL is either a `REG', indicating that the return value is saved in `REG', |
or a `MEM' representing a location in the stack. |
|
You only need to define this macro if you want to support call frame |
debugging information like that provided by DWARF 2. */ |
#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (SImode, RETURN_POINTER_REGNUM) |
|
/*}}}*/ |
/*{{{ Register That Address the Stack Frame. */ |
|
/* The register number of the arg pointer register, which is used to access the |
function's argument list. On some machines, this is the same as the frame |
pointer register. On some machines, the hardware determines which register |
this is. On other machines, you can choose any register you wish for this |
purpose. If this is not the same register as the frame pointer register, |
then you must mark it as a fixed register according to `FIXED_REGISTERS', or |
arrange to be able to eliminate it. */ |
#define ARG_POINTER_REGNUM 20 |
|
/*}}}*/ |
/*{{{ Eliminating the Frame Pointer and the Arg Pointer. */ |
|
/* A C expression which is nonzero if a function must have and use a frame |
pointer. This expression is evaluated in the reload pass. If its value is |
nonzero the function will have a frame pointer. |
|
The expression can in principle examine the current function and decide |
according to the facts, but on most machines the constant 0 or the constant |
1 suffices. Use 0 when the machine allows code to be generated with no |
frame pointer, and doing so saves some time or space. Use 1 when there is |
no possible advantage to avoiding a frame pointer. |
|
In certain cases, the compiler does not know how to produce valid code |
without a frame pointer. The compiler recognizes those cases and |
automatically gives the function a frame pointer regardless of what |
`FRAME_POINTER_REQUIRED' says. You don't need to worry about them. |
|
In a function that does not require a frame pointer, the frame pointer |
register can be allocated for ordinary usage, unless you mark it as a fixed |
register. See `FIXED_REGISTERS' for more information. */ |
/* #define FRAME_POINTER_REQUIRED 0 */ |
#define FRAME_POINTER_REQUIRED \ |
(flag_omit_frame_pointer == 0 || current_function_pretend_args_size > 0) |
|
/* If defined, this macro specifies a table of register pairs used to eliminate |
unneeded registers that point into the stack frame. If it is not defined, |
the only elimination attempted by the compiler is to replace references to |
the frame pointer with references to the stack pointer. |
|
The definition of this macro is a list of structure initializations, each of |
which specifies an original and replacement register. |
|
On some machines, the position of the argument pointer is not known until |
the compilation is completed. In such a case, a separate hard register must |
be used for the argument pointer. This register can be eliminated by |
replacing it with either the frame pointer or the argument pointer, |
depending on whether or not the frame pointer has been eliminated. |
|
In this case, you might specify: |
#define ELIMINABLE_REGS \ |
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ |
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ |
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} |
|
Note that the elimination of the argument pointer with the stack pointer is |
specified first since that is the preferred elimination. */ |
|
#define ELIMINABLE_REGS \ |
{ \ |
{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ |
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ |
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \ |
} |
|
/* A C expression that returns nonzero if the compiler is allowed to try to |
replace register number FROM with register number TO. This macro |
need only be defined if `ELIMINABLE_REGS' is defined, and will usually be |
the constant 1, since most of the cases preventing register elimination are |
things that the compiler already knows about. */ |
|
#define CAN_ELIMINATE(FROM, TO) \ |
((TO) == FRAME_POINTER_REGNUM || ! frame_pointer_needed) |
|
/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the |
initial difference between the specified pair of registers. This macro must |
be defined if `ELIMINABLE_REGS' is defined. */ |
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ |
(OFFSET) = fr30_compute_frame_size (FROM, TO) |
|
/*}}}*/ |
/*{{{ Passing Function Arguments on the Stack. */ |
|
/* If defined, the maximum amount of space required for outgoing arguments will |
be computed and placed into the variable |
`current_function_outgoing_args_size'. No space will be pushed onto the |
stack for each call; instead, the function prologue should increase the |
stack frame size by this amount. |
|
Defining both `PUSH_ROUNDING' and `ACCUMULATE_OUTGOING_ARGS' is not |
proper. */ |
#define ACCUMULATE_OUTGOING_ARGS 1 |
|
/* A C expression that should indicate the number of bytes of its own arguments |
that a function pops on returning, or 0 if the function pops no arguments |
and the caller must therefore pop them all after the function returns. |
|
FUNDECL is a C variable whose value is a tree node that describes the |
function in question. Normally it is a node of type `FUNCTION_DECL' that |
describes the declaration of the function. From this it is possible to |
obtain the DECL_ATTRIBUTES of the function. |
|
FUNTYPE is a C variable whose value is a tree node that describes the |
function in question. Normally it is a node of type `FUNCTION_TYPE' that |
describes the data type of the function. From this it is possible to obtain |
the data types of the value and arguments (if known). |
|
When a call to a library function is being considered, FUNTYPE will contain |
an identifier node for the library function. Thus, if you need to |
distinguish among various library functions, you can do so by their names. |
Note that "library function" in this context means a function used to |
perform arithmetic, whose name is known specially in the compiler and was |
not mentioned in the C code being compiled. |
|
STACK-SIZE is the number of bytes of arguments passed on the stack. If a |
variable number of bytes is passed, it is zero, and argument popping will |
always be the responsibility of the calling function. |
|
On the VAX, all functions always pop their arguments, so the definition of |
this macro is STACK-SIZE. On the 68000, using the standard calling |
convention, no functions pop their arguments, so the value of the macro is |
always 0 in this case. But an alternative calling convention is available |
in which functions that take a fixed number of arguments pop them but other |
functions (such as `printf') pop nothing (the caller pops all). When this |
convention is in use, FUNTYPE is examined to determine whether a function |
takes a fixed number of arguments. */ |
#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACK_SIZE) 0 |
|
/*}}}*/ |
/*{{{ Function Arguments in Registers. */ |
|
/* The number of register assigned to holding function arguments. */ |
|
#define FR30_NUM_ARG_REGS 4 |
|
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ |
( (NAMED) == 0 ? NULL_RTX \ |
: targetm.calls.must_pass_in_stack (MODE, TYPE) ? NULL_RTX \ |
: (CUM) >= FR30_NUM_ARG_REGS ? NULL_RTX \ |
: gen_rtx_REG (MODE, CUM + FIRST_ARG_REGNUM)) |
|
/* 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. |
|
There is no need to record in `CUMULATIVE_ARGS' anything about the arguments |
that have been passed on the stack. The compiler has other variables to |
keep track of that. For target machines on which all arguments are passed |
on the stack, there is no need to store anything in `CUMULATIVE_ARGS'; |
however, the data structure must exist and should not be empty, so use |
`int'. */ |
/* On the FR30 this value is an accumulating count of the number of argument |
registers that have been filled with argument values, as opposed to say, |
the number of bytes of argument accumulated so far. */ |
#define CUMULATIVE_ARGS int |
|
/* A C statement (sans semicolon) for initializing the variable CUM for the |
state at the beginning of the argument list. The variable has type |
`CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type |
of the function which will receive the args, or 0 if the args are to a |
compiler support library function. The value of INDIRECT is nonzero when |
processing an indirect call, for example a call through a function pointer. |
The value of INDIRECT is zero for a call to an explicitly named function, a |
library function call, or when `INIT_CUMULATIVE_ARGS' is used to find |
arguments for the function being compiled. |
|
When processing a call to a compiler support library function, LIBNAME |
identifies which one. It is a `symbol_ref' rtx which contains the name of |
the function, as a string. LIBNAME is 0 when an ordinary C function call is |
being processed. Thus, each time this macro is called, either LIBNAME or |
FNTYPE is nonzero, but never both of them at once. */ |
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ |
(CUM) = 0 |
|
/* A C statement (sans semicolon) to update the summarizer variable CUM to |
advance past an argument in the argument list. The values MODE, TYPE and |
NAMED describe that argument. Once this is done, the variable CUM is |
suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. |
|
This macro need not do anything if the argument in question was passed on |
the stack. The compiler knows how to track the amount of stack space used |
for arguments without any special help. */ |
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ |
(CUM) += (NAMED) * fr30_num_arg_regs (MODE, TYPE) |
|
/* A C expression that is nonzero if REGNO is the number of a hard register in |
which function arguments are sometimes passed. This does *not* include |
implicit arguments such as the static chain and the structure-value address. |
On many machines, no registers can be used for this purpose since all |
function arguments are pushed on the stack. */ |
#define FUNCTION_ARG_REGNO_P(REGNO) \ |
((REGNO) >= FIRST_ARG_REGNUM && ((REGNO) < FIRST_ARG_REGNUM + FR30_NUM_ARG_REGS)) |
|
/*}}}*/ |
/*{{{ How Scalar Function Values are Returned. */ |
|
#define FUNCTION_VALUE(VALTYPE, FUNC) \ |
gen_rtx_REG (TYPE_MODE (VALTYPE), RETURN_VALUE_REGNUM) |
|
/* A C expression to create an RTX representing the place where a library |
function returns a value of mode MODE. If the precise function being called |
is known, FUNC is a tree node (`FUNCTION_DECL') for it; otherwise, FUNC is a |
null pointer. This makes it possible to use a different value-returning |
convention for specific functions when all their calls are known. |
|
Note that "library function" in this context means a compiler support |
routine, used to perform arithmetic, whose name is known specially by the |
compiler and was not mentioned in the C code being compiled. |
|
The definition of `LIBRARY_VALUE' need not be concerned aggregate data |
types, because none of the library functions returns such types. */ |
#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, RETURN_VALUE_REGNUM) |
|
/* A C expression that is nonzero if REGNO is the number of a hard register in |
which the values of called function may come back. */ |
|
#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == RETURN_VALUE_REGNUM) |
|
/*}}}*/ |
/*{{{ How Large Values are Returned. */ |
|
/* Define this macro to be 1 if all structure and union return values must be |
in memory. Since this results in slower code, this should be defined only |
if needed for compatibility with other compilers or with an ABI. If you |
define this macro to be 0, then the conventions used for structure and union |
return values are decided by the `RETURN_IN_MEMORY' macro. |
|
If not defined, this defaults to the value 1. */ |
#define DEFAULT_PCC_STRUCT_RETURN 1 |
|
/*}}}*/ |
/*{{{ Generating Code for Profiling. */ |
|
/* A C statement or compound statement to output to FILE some assembler code to |
call the profiling subroutine `mcount'. Before calling, the assembler code |
must load the address of a counter variable into a register where `mcount' |
expects to find the address. The name of this variable is `LP' followed by |
the number LABELNO, so you would generate the name using `LP%d' in a |
`fprintf'. |
|
The details of how the address should be passed to `mcount' are determined |
by your operating system environment, not by GCC. To figure them out, |
compile a small program for profiling using the system's installed C |
compiler and look at the assembler code that results. */ |
#define FUNCTION_PROFILER(FILE, LABELNO) \ |
{ \ |
fprintf (FILE, "\t mov rp, r1\n" ); \ |
fprintf (FILE, "\t ldi:32 mcount, r0\n" ); \ |
fprintf (FILE, "\t call @r0\n" ); \ |
fprintf (FILE, ".word\tLP%d\n", LABELNO); \ |
} |
|
/*}}}*/ |
/*{{{ Trampolines for Nested Functions. */ |
|
/* On the FR30, the trampoline is: |
|
nop |
ldi:32 STATIC, r12 |
nop |
ldi:32 FUNCTION, r0 |
jmp @r0 |
|
The no-ops are to guarantee that the static chain and final |
target are 32 bit aligned within the trampoline. That allows us to |
initialize those locations with simple SImode stores. The alternative |
would be to use HImode stores. */ |
|
/* A C statement to output, on the stream FILE, assembler code for a block of |
data that contains the constant parts of a trampoline. This code should not |
include a label--the label is taken care of automatically. */ |
#define TRAMPOLINE_TEMPLATE(FILE) \ |
{ \ |
fprintf (FILE, "\tnop\n"); \ |
fprintf (FILE, "\tldi:32\t#0, %s\n", reg_names [STATIC_CHAIN_REGNUM]); \ |
fprintf (FILE, "\tnop\n"); \ |
fprintf (FILE, "\tldi:32\t#0, %s\n", reg_names [COMPILER_SCRATCH_REGISTER]); \ |
fprintf (FILE, "\tjmp\t@%s\n", reg_names [COMPILER_SCRATCH_REGISTER]); \ |
} |
|
/* A C expression for the size in bytes of the trampoline, as an integer. */ |
#define TRAMPOLINE_SIZE 18 |
|
/* We want the trampoline to be aligned on a 32bit boundary so that we can |
make sure the location of the static chain & target function within |
the trampoline is also aligned on a 32bit boundary. */ |
#define TRAMPOLINE_ALIGNMENT 32 |
|
/* A C statement to initialize the variable parts of a trampoline. ADDR is an |
RTX for the address of the trampoline; FNADDR is an RTX for the address of |
the nested function; STATIC_CHAIN is an RTX for the static chain value that |
should be passed to the function when it is called. */ |
#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, STATIC_CHAIN) \ |
do \ |
{ \ |
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (ADDR, 4)), STATIC_CHAIN);\ |
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (ADDR, 12)), FNADDR); \ |
} while (0); |
|
/*}}}*/ |
/*{{{ Addressing Modes. */ |
|
/* A C expression that is 1 if the RTX X is a constant which is a valid |
address. On most machines, this can be defined as `CONSTANT_P (X)', but a |
few machines are more restrictive in which constant addresses are supported. |
|
`CONSTANT_P' accepts integer-values expressions whose values are not |
explicitly known, such as `symbol_ref', `label_ref', and `high' expressions |
and `const' arithmetic expressions, in addition to `const_int' and |
`const_double' expressions. */ |
#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X) |
|
/* A number, the maximum number of registers that can appear in a valid memory |
address. Note that it is up to you to specify a value equal to the maximum |
number that `GO_IF_LEGITIMATE_ADDRESS' would ever accept. */ |
#define MAX_REGS_PER_ADDRESS 1 |
|
/* A C compound statement with a conditional `goto LABEL;' executed if X (an |
RTX) is a legitimate memory address on the target machine for a memory |
operand of mode MODE. */ |
|
/* On the FR30 we only have one real addressing mode - an address in a |
register. There are three special cases however: |
|
* indexed addressing using small positive offsets from the stack pointer |
|
* indexed addressing using small signed offsets from the frame pointer |
|
* register plus register addressing using R13 as the base register. |
|
At the moment we only support the first two of these special cases. */ |
|
#ifdef REG_OK_STRICT |
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ |
do \ |
{ \ |
if (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) \ |
goto LABEL; \ |
if (GET_CODE (X) == PLUS \ |
&& ((MODE) == SImode || (MODE) == SFmode) \ |
&& GET_CODE (XEXP (X, 0)) == REG \ |
&& REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \ |
&& GET_CODE (XEXP (X, 1)) == CONST_INT \ |
&& IN_RANGE (INTVAL (XEXP (X, 1)), 0, (1 << 6) - 4)) \ |
goto LABEL; \ |
if (GET_CODE (X) == PLUS \ |
&& ((MODE) == SImode || (MODE) == SFmode) \ |
&& GET_CODE (XEXP (X, 0)) == REG \ |
&& REGNO (XEXP (X, 0)) == FRAME_POINTER_REGNUM \ |
&& GET_CODE (XEXP (X, 1)) == CONST_INT \ |
&& IN_RANGE (INTVAL (XEXP (X, 1)), -(1 << 9), (1 << 9) - 4)) \ |
goto LABEL; \ |
} \ |
while (0) |
#else |
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ |
do \ |
{ \ |
if (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) \ |
goto LABEL; \ |
if (GET_CODE (X) == PLUS \ |
&& ((MODE) == SImode || (MODE) == SFmode) \ |
&& GET_CODE (XEXP (X, 0)) == REG \ |
&& REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \ |
&& GET_CODE (XEXP (X, 1)) == CONST_INT \ |
&& IN_RANGE (INTVAL (XEXP (X, 1)), 0, (1 << 6) - 4)) \ |
goto LABEL; \ |
if (GET_CODE (X) == PLUS \ |
&& ((MODE) == SImode || (MODE) == SFmode) \ |
&& GET_CODE (XEXP (X, 0)) == REG \ |
&& (REGNO (XEXP (X, 0)) == FRAME_POINTER_REGNUM \ |
|| REGNO (XEXP (X, 0)) == ARG_POINTER_REGNUM) \ |
&& GET_CODE (XEXP (X, 1)) == CONST_INT \ |
&& IN_RANGE (INTVAL (XEXP (X, 1)), -(1 << 9), (1 << 9) - 4)) \ |
goto LABEL; \ |
} \ |
while (0) |
#endif |
|
/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for |
use as a base register. For hard registers, it should always accept those |
which the hardware permits and reject the others. Whether the macro accepts |
or rejects pseudo registers must be controlled by `REG_OK_STRICT' as |
described above. This usually requires two variant definitions, of which |
`REG_OK_STRICT' controls the one actually used. */ |
#ifdef REG_OK_STRICT |
#define REG_OK_FOR_BASE_P(X) (((unsigned) REGNO (X)) <= STACK_POINTER_REGNUM) |
#else |
#define REG_OK_FOR_BASE_P(X) 1 |
#endif |
|
/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for |
use as an index register. |
|
The difference between an index register and a base register is that the |
index register may be scaled. If an address involves the sum of two |
registers, neither one of them scaled, then either one may be labeled the |
"base" and the other the "index"; but whichever labeling is used must fit |
the machine's constraints of which registers may serve in each capacity. |
The compiler will try both labelings, looking for one that is valid, and |
will reload one or both registers only if neither labeling works. */ |
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X) |
|
/* A C statement or compound statement with a conditional `goto LABEL;' |
executed if memory address X (an RTX) can have different meanings depending |
on the machine mode of the memory reference it is used for or if the address |
is valid for some modes but not others. |
|
Autoincrement and autodecrement addresses typically have mode-dependent |
effects because the amount of the increment or decrement is the size of the |
operand being addressed. Some machines have other mode-dependent addresses. |
Many RISC machines have no mode-dependent addresses. |
|
You may assume that ADDR is a valid address for the machine. */ |
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) |
|
/* A C expression that is nonzero if X is a legitimate constant for an |
immediate operand on the target machine. You can assume that X satisfies |
`CONSTANT_P', so you need not check this. In fact, `1' is a suitable |
definition for this macro on machines where anything `CONSTANT_P' is valid. */ |
#define LEGITIMATE_CONSTANT_P(X) 1 |
|
/*}}}*/ |
/*{{{ Describing Relative Costs of Operations */ |
|
/* Define this macro as a C expression which is nonzero if accessing less than |
a word of memory (i.e. a `char' or a `short') is no faster than accessing a |
word of memory, i.e., if such access require more than one instruction or if |
there is no difference in cost between byte and (aligned) word loads. |
|
When this macro is not defined, the compiler will access a field by finding |
the smallest containing object; when it is defined, a fullword load will be |
used if alignment permits. Unless bytes accesses are faster than word |
accesses, using word accesses is preferable since it may eliminate |
subsequent memory access if subsequent accesses occur to other fields in the |
same word of the structure, but to different bytes. */ |
#define SLOW_BYTE_ACCESS 1 |
|
/*}}}*/ |
/*{{{ Dividing the output into sections. */ |
|
/* A C expression whose value is a string containing the assembler operation |
that should precede instructions and read-only data. Normally `".text"' is |
right. */ |
#define TEXT_SECTION_ASM_OP "\t.text" |
|
/* A C expression whose value is a string containing the assembler operation to |
identify the following data as writable initialized data. Normally |
`".data"' is right. */ |
#define DATA_SECTION_ASM_OP "\t.data" |
|
/* If defined, a C expression whose value is a string containing the |
assembler operation to identify the following data as |
uninitialized global data. If not defined, and neither |
`ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined, |
uninitialized global data will be output in the data section if |
`-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be |
used. */ |
#define BSS_SECTION_ASM_OP "\t.section .bss" |
|
/*}}}*/ |
/*{{{ The Overall Framework of an Assembler File. */ |
|
/* A C string constant describing how to begin a comment in the target |
assembler language. The compiler assumes that the comment will end at the |
end of the line. */ |
#define ASM_COMMENT_START ";" |
|
/* A C string constant for text to be output before each `asm' statement or |
group of consecutive ones. Normally this is `"#APP"', which is a comment |
that has no effect on most assemblers but tells the GNU assembler that it |
must check the lines that follow for all valid assembler constructs. */ |
#define ASM_APP_ON "#APP\n" |
|
/* A C string constant for text to be output after each `asm' statement or |
group of consecutive ones. Normally this is `"#NO_APP"', which tells the |
GNU assembler to resume making the time-saving assumptions that are valid |
for ordinary compiler output. */ |
#define ASM_APP_OFF "#NO_APP\n" |
|
/*}}}*/ |
/*{{{ Output and Generation of Labels. */ |
|
/* Globalizing directive for a label. */ |
#define GLOBAL_ASM_OP "\t.globl " |
|
/*}}}*/ |
/*{{{ Output of Assembler Instructions. */ |
|
/* A C compound statement to output to stdio stream STREAM the assembler syntax |
for an instruction operand X. X is an RTL expression. |
|
CODE is a value that can be used to specify one of several ways of printing |
the operand. It is used when identical operands must be printed differently |
depending on the context. CODE comes from the `%' specification that was |
used to request printing of the operand. If the specification was just |
`%DIGIT' then CODE is 0; if the specification was `%LTR DIGIT' then CODE is |
the ASCII code for LTR. |
|
If X is a register, this macro should print the register's name. The names |
can be found in an array `reg_names' whose type is `char *[]'. `reg_names' |
is initialized from `REGISTER_NAMES'. |
|
When the machine description has a specification `%PUNCT' (a `%' followed by |
a punctuation character), this macro is called with a null pointer for X and |
the punctuation character for CODE. */ |
#define PRINT_OPERAND(STREAM, X, CODE) fr30_print_operand (STREAM, X, CODE) |
|
/* A C expression which evaluates to true if CODE is a valid punctuation |
character for use in the `PRINT_OPERAND' macro. If |
`PRINT_OPERAND_PUNCT_VALID_P' is not defined, it means that no punctuation |
characters (except for the standard one, `%') are used in this way. */ |
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) (CODE == '#') |
|
/* A C compound statement to output to stdio stream STREAM the assembler syntax |
for an instruction operand that is a memory reference whose address is X. X |
is an RTL expression. */ |
|
#define PRINT_OPERAND_ADDRESS(STREAM, X) fr30_print_operand_address (STREAM, X) |
|
/* If defined, C string expressions to be used for the `%R', `%L', `%U', and |
`%I' options of `asm_fprintf' (see `final.c'). These are useful when a |
single `md' file must support multiple assembler formats. In that case, the |
various `tm.h' files can define these macros differently. |
|
USER_LABEL_PREFIX is defined in svr4.h. */ |
#define REGISTER_PREFIX "%" |
#define LOCAL_LABEL_PREFIX "." |
#define USER_LABEL_PREFIX "" |
#define IMMEDIATE_PREFIX "" |
|
/*}}}*/ |
/*{{{ Output of Dispatch Tables. */ |
|
/* This macro should be provided on machines where the addresses in a dispatch |
table are relative to the table's own address. |
|
The definition should be a C statement to output to the stdio stream STREAM |
an assembler pseudo-instruction to generate a difference between two labels. |
VALUE and REL are the numbers of two internal labels. The definitions of |
these labels are output using `(*targetm.asm_out.internal_label)', and they must be |
printed in the same way here. For example, |
|
fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL) */ |
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ |
fprintf (STREAM, "\t.word .L%d-.L%d\n", VALUE, REL) |
|
/* This macro should be provided on machines where the addresses in a dispatch |
table are absolute. |
|
The definition should be a C statement to output to the stdio stream STREAM |
an assembler pseudo-instruction to generate a reference to a label. VALUE |
is the number of an internal label whose definition is output using |
`(*targetm.asm_out.internal_label)'. For example, |
|
fprintf (STREAM, "\t.word L%d\n", VALUE) */ |
#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ |
fprintf (STREAM, "\t.word .L%d\n", VALUE) |
|
/*}}}*/ |
/*{{{ Assembler Commands for Alignment. */ |
|
/* A C statement to output to the stdio stream STREAM an assembler command to |
advance the location counter to a multiple of 2 to the POWER bytes. POWER |
will be a C expression of type `int'. */ |
#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ |
fprintf ((STREAM), "\t.p2align %d\n", (POWER)) |
|
/*}}}*/ |
/*{{{ Miscellaneous Parameters. */ |
|
/* An alias for a machine mode name. This is the machine mode that elements of |
a jump-table should have. */ |
#define CASE_VECTOR_MODE SImode |
|
/* The maximum number of bytes that a single instruction can move quickly from |
memory to memory. */ |
#define MOVE_MAX 8 |
|
/* A C expression which is nonzero if on this machine it is safe to "convert" |
an integer of INPREC bits to one of OUTPREC bits (where OUTPREC is smaller |
than INPREC) by merely operating on it as if it had only OUTPREC bits. |
|
On many machines, this expression can be 1. |
|
When `TRULY_NOOP_TRUNCATION' returns 1 for a pair of sizes for modes for |
which `MODES_TIEABLE_P' is 0, suboptimal code can result. If this is the |
case, making `TRULY_NOOP_TRUNCATION' return 0 in such cases may improve |
things. */ |
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 |
|
/* An alias for the machine mode for pointers. On most machines, define this |
to be the integer mode corresponding to the width of a hardware pointer; |
`SImode' on 32-bit machine or `DImode' on 64-bit machines. On some machines |
you must define this to be one of the partial integer modes, such as |
`PSImode'. |
|
The width of `Pmode' must be at least as large as the value of |
`POINTER_SIZE'. If it is not equal, you must define the macro |
`POINTERS_EXTEND_UNSIGNED' to specify how pointers are extended to `Pmode'. */ |
#define Pmode SImode |
|
/* An alias for the machine mode used for memory references to functions being |
called, in `call' RTL expressions. On most machines this should be |
`QImode'. */ |
#define FUNCTION_MODE QImode |
|
/* If cross-compiling, don't require stdio.h etc to build libgcc.a. */ |
#if defined CROSS_COMPILE && ! defined inhibit_libc |
#define inhibit_libc |
#endif |
|
/*}}}*/ |
/*{{{ Exported variables */ |
|
/* Define the information needed to generate branch and scc 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 * fr30_compare_op0; |
extern struct rtx_def * fr30_compare_op1; |
|
/*}}}*/ |
|
/* Local Variables: */ |
/* folded-file: t */ |
/* End: */ |
/predicates.md
0,0 → 1,123
;; Predicate definitions for FR30. |
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
;; |
;; 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 3, 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 GCC; see the file COPYING3. If not see |
;; <http://www.gnu.org/licenses/>. |
|
;; Returns true if OP is an integer value suitable for use in an |
;; ADDSP instruction. |
|
(define_predicate "stack_add_operand" |
(match_code "const_int") |
{ |
return |
(GET_CODE (op) == CONST_INT |
&& INTVAL (op) >= -512 |
&& INTVAL (op) <= 508 |
&& ((INTVAL (op) & 3) == 0)); |
}) |
|
;; Returns true if OP is hard register in the range 8 - 15. |
|
(define_predicate "high_register_operand" |
(match_code "reg") |
{ |
return |
(GET_CODE (op) == REG |
&& REGNO (op) <= 15 |
&& REGNO (op) >= 8); |
}) |
|
;; Returns true if OP is hard register in the range 0 - 7. |
|
(define_predicate "low_register_operand" |
(match_code "reg") |
{ |
return |
(GET_CODE (op) == REG |
&& REGNO (op) <= 7); |
}) |
|
;; Returns true if OP is suitable for use in a CALL insn. |
|
(define_predicate "call_operand" |
(match_code "mem") |
{ |
return (GET_CODE (op) == MEM |
&& (GET_CODE (XEXP (op, 0)) == SYMBOL_REF |
|| GET_CODE (XEXP (op, 0)) == REG)); |
}) |
|
;; Returns TRUE if OP is a valid operand of a DImode operation. |
|
(define_predicate "di_operand" |
(match_code "const_int,const_double,reg,mem") |
{ |
if (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; |
} |
}) |
|
;; Returns TRUE if OP is a DImode register or MEM. |
|
(define_predicate "nonimmediate_di_operand" |
(match_code "reg,mem") |
{ |
if (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; |
}) |
|
;; Returns true if OP is an integer value suitable for use in an ADD |
;; or ADD2 instruction, or if it is a register. |
|
(define_predicate "add_immediate_operand" |
(match_code "reg,const_int") |
{ |
return |
(GET_CODE (op) == REG |
|| (GET_CODE (op) == CONST_INT |
&& INTVAL (op) >= -16 |
&& INTVAL (op) <= 15)); |
}) |
/fr30-protos.h
0,0 → 1,37
/* Prototypes for fr30.c functions used in the md file & elsewhere. |
Copyright (C) 1999, 2000, 2002, 2004, 2007 Free Software Foundation, Inc. |
|
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 3, 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 GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
extern void fr30_expand_prologue (void); |
extern void fr30_expand_epilogue (void); |
extern unsigned int fr30_compute_frame_size (int, int); |
|
#ifdef RTX_CODE |
extern int fr30_check_multiple_regs (rtx *, int, int); |
extern void fr30_print_operand (FILE *, rtx, int); |
extern void fr30_print_operand_address (FILE *, rtx); |
extern rtx fr30_move_double (rtx *); |
#ifdef TREE_CODE |
extern int fr30_num_arg_regs (enum machine_mode, tree); |
#endif /* TREE_CODE */ |
#ifdef HAVE_MACHINE_MODES |
#define Mmode enum machine_mode |
extern int fr30_const_double_is_zero (rtx); |
#undef Mmode |
#endif /* HAVE_MACHINE_MODES */ |
#endif /* RTX_CODE */ |
/crtn.asm
0,0 → 1,57
# crtn.asm for ELF |
|
# Copyright (C) 1992, 1999 Free Software Foundation, Inc. |
# Written By David Vinayak Henkel-Wallace, June 1992 |
# |
# 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, 51 Franklin Street, Fifth Floor, |
# Boston, MA 02110-1301, 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. |
|
.file "crtn.s" |
|
.section ".init" |
.align 4 |
|
leave |
ld @r15+,rp |
ret |
|
|
.section ".fini" |
.align 4 |
|
leave |
ld @r15+,rp |
ret |
|
# Th-th-th-that is all folks! |
/lib1funcs.asm
0,0 → 1,120
/* libgcc routines for the FR30. |
Copyright (C) 1998, 1999 Free Software Foundation, Inc. |
|
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. |
|
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, 51 Franklin Street, Fifth Floor, |
Boston, MA 02110-1301, USA. */ |
|
.macro FUNC_START name |
.text |
.globl __\name |
.type __\name, @function |
__\name: |
.endm |
|
.macro FUNC_END name |
.size __\name, . - __\name |
.endm |
|
.macro DIV_BODY reg number |
.if \number |
DIV_BODY \reg, "\number - 1" |
div1 \reg |
.endif |
.endm |
|
#ifdef L_udivsi3 |
FUNC_START udivsi3 |
;; Perform an unsiged division of r4 / r5 and place the result in r4. |
;; Does not handle overflow yet... |
mov r4, mdl |
div0u r5 |
DIV_BODY r5 32 |
mov mdl, r4 |
ret |
FUNC_END udivsi3 |
#endif /* L_udivsi3 */ |
|
#ifdef L_divsi3 |
FUNC_START divsi3 |
;; Perform a siged division of r4 / r5 and place the result in r4. |
;; Does not handle overflow yet... |
mov r4, mdl |
div0s r5 |
DIV_BODY r5 32 |
div2 r5 |
div3 |
div4s |
mov mdl, r4 |
ret |
FUNC_END divsi3 |
#endif /* L_divsi3 */ |
|
#ifdef L_umodsi3 |
FUNC_START umodsi3 |
;; Perform an unsiged division of r4 / r5 and places the remainder in r4. |
;; Does not handle overflow yet... |
mov r4, mdl |
div0u r5 |
DIV_BODY r5 32 |
mov mdh, r4 |
ret |
FUNC_END umodsi3 |
#endif /* L_umodsi3 */ |
|
#ifdef L_modsi3 |
FUNC_START modsi3 |
;; Perform a siged division of r4 / r5 and place the remainder in r4. |
;; Does not handle overflow yet... |
mov r4, mdl |
div0s r5 |
DIV_BODY r5 32 |
div2 r5 |
div3 |
div4s |
mov mdh, r4 |
ret |
FUNC_END modsi3 |
#endif /* L_modsi3 */ |
|
#ifdef L_negsi2 |
FUNC_START negsi2 |
ldi:8 #0, r0 |
sub r4, r0 |
mov r0, r4 |
ret |
FUNC_END negsi2 |
#endif /* L_negsi2 */ |
|
#ifdef L_one_cmplsi2 |
FUNC_START one_cmplsi2 |
ldi:8 #0xff, r0 |
extsb r0 |
eor r0, r4 |
ret |
FUNC_END one_cmplsi2 |
#endif /* L_one_cmplsi2 */ |
|
|
/fr30.md
0,0 → 1,1444
;; FR30 machine description. |
;; Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005, 2007 |
;; Free Software Foundation, Inc. |
;; Contributed by Cygnus Solutions. |
|
;; 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 3, 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 GCC; see the file COPYING3. If not see |
;; <http://www.gnu.org/licenses/>. |
|
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. |
|
;;{{{ Attributes |
|
(define_attr "length" "" (const_int 2)) |
|
;; Used to distinguish between small memory model targets and big mode targets. |
|
(define_attr "size" "small,big" |
(const (if_then_else (symbol_ref "TARGET_SMALL_MODEL") |
(const_string "small") |
(const_string "big")))) |
|
|
;; Define an attribute to be used by the delay slot code. |
;; An instruction by default is considered to be 'delayable' |
;; that is, it can be placed into a delay slot, but it is not |
;; itself a delayed branch type instruction. An instruction |
;; whose type is 'delayed' is one which has a delay slot, and |
;; an instruction whose delay_type is 'other' is one which does |
;; not have a delay slot, nor can it be placed into a delay slot. |
|
(define_attr "delay_type" "delayable,delayed,other" (const_string "delayable")) |
|
;;}}} |
;;{{{ Delay Slot Specifications |
|
(define_delay (eq_attr "delay_type" "delayed") |
[(and (eq_attr "delay_type" "delayable") |
(eq_attr "length" "2")) |
(nil) |
(nil)] |
) |
|
(include "predicates.md") |
|
;;}}} |
;;{{{ Moves |
|
;;{{{ Comment |
|
;; Wrap moves in define_expand to prevent memory->memory moves from being |
;; generated at the RTL level, which generates better code for most machines |
;; which can't do mem->mem moves. |
|
;; If operand 0 is a `subreg' with mode M of a register whose own mode is wider |
;; than M, the effect of this instruction is to store the specified value in |
;; the part of the register that corresponds to mode M. The effect on the rest |
;; of the register is undefined. |
|
;; This class of patterns is special in several ways. First of all, each of |
;; these names *must* be defined, because there is no other way to copy a datum |
;; from one place to another. |
|
;; Second, these patterns are not used solely in the RTL generation pass. Even |
;; the reload pass can generate move insns to copy values from stack slots into |
;; temporary registers. When it does so, one of the operands is a hard |
;; register and the other is an operand that can need to be reloaded into a |
;; register. |
|
;; Therefore, when given such a pair of operands, the pattern must |
;; generate RTL which needs no reloading and needs no temporary |
;; registers--no registers other than the operands. For example, if |
;; you support the pattern with a `define_expand', then in such a |
;; case the `define_expand' mustn't call `force_reg' or any other such |
;; function which might generate new pseudo registers. |
|
;; This requirement exists even for subword modes on a RISC machine |
;; where fetching those modes from memory normally requires several |
;; insns and some temporary registers. Look in `spur.md' to see how |
;; the requirement can be satisfied. |
|
;; During reload a memory reference with an invalid address may be passed as an |
;; operand. Such an address will be replaced with a valid address later in the |
;; reload pass. In this case, nothing may be done with the address except to |
;; use it as it stands. If it is copied, it will not be replaced with a valid |
;; address. No attempt should be made to make such an address into a valid |
;; address and no routine (such as `change_address') that will do so may be |
;; called. Note that `general_operand' will fail when applied to such an |
;; address. |
;; |
;; The global variable `reload_in_progress' (which must be explicitly declared |
;; if required) can be used to determine whether such special handling is |
;; required. |
;; |
;; The variety of operands that have reloads depends on the rest of |
;; the machine description, but typically on a RISC machine these can |
;; only be pseudo registers that did not get hard registers, while on |
;; other machines explicit memory references will get optional |
;; reloads. |
;; |
;; If a scratch register is required to move an object to or from memory, it |
;; can be allocated using `gen_reg_rtx' prior to reload. But this is |
;; impossible during and after reload. If there are cases needing scratch |
;; registers after reload, you must define `SECONDARY_INPUT_RELOAD_CLASS' and |
;; perhaps also `SECONDARY_OUTPUT_RELOAD_CLASS' to detect them, and provide |
;; patterns `reload_inM' or `reload_outM' to handle them. |
|
;; The constraints on a `moveM' must permit moving any hard register to any |
;; other hard register provided that `HARD_REGNO_MODE_OK' permits mode M in |
;; both registers and `REGISTER_MOVE_COST' applied to their classes returns a |
;; value of 2. |
|
;; It is obligatory to support floating point `moveM' instructions |
;; into and out of any registers that can hold fixed point values, |
;; because unions and structures (which have modes `SImode' or |
;; `DImode') can be in those registers and they may have floating |
;; point members. |
|
;; There may also be a need to support fixed point `moveM' instructions in and |
;; out of floating point registers. Unfortunately, I have forgotten why this |
;; was so, and I don't know whether it is still true. If `HARD_REGNO_MODE_OK' |
;; rejects fixed point values in floating point registers, then the constraints |
;; of the fixed point `moveM' instructions must be designed to avoid ever |
;; trying to reload into a floating point register. |
|
;;}}} |
;;{{{ Push and Pop |
|
;; Push a register onto the stack |
(define_insn "movsi_push" |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "register_operand" "a"))] |
"" |
"st %0, @-r15" |
) |
|
;; Pop a register off the stack |
(define_insn "movsi_pop" |
[(set:SI (match_operand:SI 0 "register_operand" "=a") |
(mem:SI (post_inc:SI (reg:SI 15))))] |
"" |
"ld @r15+, %0" |
) |
|
;;}}} |
;;{{{ 1 Byte Moves |
|
(define_expand "movqi" |
[(set (match_operand:QI 0 "general_operand" "") |
(match_operand:QI 1 "general_operand" ""))] |
"" |
" |
{ |
if (!reload_in_progress |
&& !reload_completed |
&& GET_CODE (operands[0]) == MEM |
&& (GET_CODE (operands[1]) == MEM |
|| immediate_operand (operands[1], QImode))) |
operands[1] = copy_to_mode_reg (QImode, operands[1]); |
}") |
|
(define_insn "movqi_unsigned_register_load" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))] |
"" |
"ldub %1, %0" |
) |
|
(define_expand "movqi_signed_register_load" |
[(set (match_operand:SI 0 "register_operand" "") |
(sign_extend:SI (match_operand:QI 1 "memory_operand" "")))] |
"" |
" |
emit_insn (gen_movqi_unsigned_register_load (operands[0], operands[1])); |
emit_insn (gen_extendqisi2 (operands[0], operands[0])); |
DONE; |
" |
) |
|
(define_insn "*movqi_internal" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,red,m,r") |
(match_operand:QI 1 "general_operand" "i,red,r,rm"))] |
"" |
"@ |
ldi:8\\t#%A1, %0 |
mov \\t%1, %0 |
stb \\t%1, %0 |
ldub \\t%1, %0" |
) |
|
;;}}} |
;;{{{ 2 Byte Moves |
|
(define_expand "movhi" |
[(set (match_operand:HI 0 "general_operand" "") |
(match_operand:HI 1 "general_operand" ""))] |
"" |
" |
{ |
if (!reload_in_progress |
&& !reload_completed |
&& GET_CODE (operands[0]) == MEM |
&& (GET_CODE (operands[1]) == MEM |
|| immediate_operand (operands[1], HImode))) |
operands[1] = copy_to_mode_reg (HImode, operands[1]); |
}") |
|
(define_insn "movhi_unsigned_register_load" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))] |
"" |
"lduh %1, %0" |
) |
|
(define_expand "movhi_signed_register_load" |
[(set (match_operand:SI 0 "register_operand" "") |
(sign_extend:SI (match_operand:HI 1 "memory_operand" "")))] |
"" |
" |
emit_insn (gen_movhi_unsigned_register_load (operands[0], operands[1])); |
emit_insn (gen_extendhisi2 (operands[0], operands[0])); |
DONE; |
" |
) |
|
(define_insn "*movhi_internal" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,red,m,r") |
(match_operand:HI 1 "general_operand" "L,M,n,red,r,rm"))] |
"" |
"@ |
ldi:8 \\t#%1, %0 |
ldi:20\\t#%1, %0 |
ldi:32\\t#%1, %0 |
mov \\t%1, %0 |
sth \\t%1, %0 |
lduh \\t%1, %0" |
[(set_attr "length" "*,4,6,*,*,*")] |
) |
|
;;}}} |
;;{{{ 4 Byte Moves |
|
;; If the destination is a MEM and the source is a |
;; MEM or an CONST_INT move the source into a register. |
(define_expand "movsi" |
[(set (match_operand:SI 0 "nonimmediate_operand" "") |
(match_operand:SI 1 "general_operand" ""))] |
"" |
"{ |
if (!reload_in_progress |
&& !reload_completed |
&& GET_CODE(operands[0]) == MEM |
&& (GET_CODE (operands[1]) == MEM |
|| immediate_operand (operands[1], SImode))) |
operands[1] = copy_to_mode_reg (SImode, operands[1]); |
}" |
) |
|
;; We can do some clever tricks when loading certain immediate |
;; values. We implement these tricks as define_splits, rather |
;; than putting the code into the define_expand "movsi" above, |
;; because if we put them there, they will be evaluated at RTL |
;; generation time and then the combiner pass will come along |
;; and replace the multiple insns that have been generated with |
;; the original, slower, load insns. (The combiner pass only |
;; cares about reducing the number of instructions, it does not |
;; care about instruction lengths or speeds). Splits are |
;; evaluated after the combine pass and before the scheduling |
;; passes, so that they are the perfect place to put this |
;; intelligence. |
;; |
;; XXX we probably ought to implement these for QI and HI mode |
;; loads as well. |
|
;; If we are loading a small negative constant we can save space |
;; and time by loading the positive value and then sign extending it. |
(define_split |
[(set (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "const_int_operand" ""))] |
"INTVAL (operands[1]) <= -1 && INTVAL (operands[1]) >= -128 |
&& (GET_CODE (operands[0]) != SUBREG |
|| SCALAR_INT_MODE_P (GET_MODE (XEXP (operands[0], 0))))" |
[(set:SI (match_dup 0) (match_dup 1)) |
(set:SI (match_dup 0) (sign_extend:SI (match_dup 2)))] |
"{ |
operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff); |
operands[2] = gen_lowpart (QImode, operands[0]); |
}" |
) |
|
;; If we are loading a large negative constant, one which does |
;; not have any of its bottom 24 bit set, then we can save time |
;; and space by loading the byte value and shifting it into place. |
(define_split |
[(set (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "const_int_operand" ""))] |
"(INTVAL (operands[1]) < 0) && ((INTVAL (operands[1]) & 0x00ffffff) == 0)" |
[(set:SI (match_dup 0) (match_dup 2)) |
(parallel [(set:SI (match_dup 0) (ashift:SI (match_dup 0) (const_int 24))) |
(clobber (reg:CC 16))])] |
"{ |
HOST_WIDE_INT val = INTVAL (operands[1]); |
operands[2] = GEN_INT (val >> 24); |
}" |
) |
|
;; If we are loading a large positive constant, one which has bits |
;; in the top byte set, but whose set bits all lie within an 8 bit |
;; range, then we can save time and space by loading the byte value |
;; and shifting it into place. |
(define_split |
[(set (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "const_int_operand" ""))] |
"(INTVAL (operands[1]) > 0x00ffffff) |
&& ((INTVAL (operands[1]) >> exact_log2 (INTVAL (operands[1]) & (- INTVAL (operands[1])))) < 0x100)" |
[(set:SI (match_dup 0) (match_dup 2)) |
(parallel [(set:SI (match_dup 0) (ashift:SI (match_dup 0) (match_dup 3))) |
(clobber (reg:CC 16))])] |
"{ |
HOST_WIDE_INT val = INTVAL (operands[1]); |
int shift = exact_log2 (val & ( - val)); |
operands[2] = GEN_INT (val >> shift); |
operands[3] = GEN_INT (shift); |
}" |
) |
|
;; When TARGET_SMALL_MODEL is defined we assume that all symbolic |
;; values are addresses which will fit in 20 bits. |
|
(define_insn "movsi_internal" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,red,m,r") |
(match_operand:SI 1 "general_operand" "L,M,n,i,rde,r,rm"))] |
"" |
"* |
{ |
switch (which_alternative) |
{ |
case 0: return \"ldi:8 \\t#%1, %0\"; |
case 1: return \"ldi:20\\t#%1, %0\"; |
case 2: return \"ldi:32\\t#%1, %0\"; |
case 3: if (TARGET_SMALL_MODEL) |
return \"ldi:20\\t%1, %0\"; |
else |
return \"ldi:32\\t%1, %0\"; |
case 4: return \"mov \\t%1, %0\"; |
case 5: return \"st \\t%1, %0\"; |
case 6: return \"ld \\t%1, %0\"; |
default: gcc_unreachable (); |
} |
}" |
[(set (attr "length") (cond [(eq_attr "alternative" "1") (const_int 4) |
(eq_attr "alternative" "2") (const_int 6) |
(eq_attr "alternative" "3") |
(if_then_else (eq_attr "size" "small") |
(const_int 4) |
(const_int 6))] |
(const_int 2)))] |
) |
|
;;}}} |
;;{{{ 8 Byte Moves |
|
;; Note - the FR30 does not have an 8 byte load/store instruction |
;; but we have to support this pattern because some other patterns |
;; (e.g. muldisi2) can produce a DImode result. |
;; (This code is stolen from the M32R port.) |
|
(define_expand "movdi" |
[(set (match_operand:DI 0 "general_operand" "") |
(match_operand:DI 1 "general_operand" ""))] |
"" |
" |
/* Everything except mem = const or mem = mem can be done easily. */ |
|
if (GET_CODE (operands[0]) == MEM) |
operands[1] = force_reg (DImode, operands[1]); |
") |
|
;; We use an insn and a split so that we can generate |
;; RTL rather than text from fr30_move_double(). |
|
(define_insn "*movdi_insn" |
[(set (match_operand:DI 0 "nonimmediate_di_operand" "=r,r,m,r") |
(match_operand:DI 1 "di_operand" "r,m,r,nF"))] |
"register_operand (operands[0], DImode) || register_operand (operands[1], DImode)" |
"#" |
[(set_attr "length" "4,8,12,12")] |
) |
|
(define_split |
[(set (match_operand:DI 0 "nonimmediate_di_operand" "") |
(match_operand:DI 1 "di_operand" ""))] |
"reload_completed" |
[(match_dup 2)] |
"operands[2] = fr30_move_double (operands);") |
|
;;}}} |
;;{{{ Load & Store Multiple Registers |
|
;; The load multiple and store multiple patterns are implemented |
;; as peepholes because the only time they are expected to occur |
;; is during function prologues and epilogues. |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 2 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 3 "high_register_operand" "h"))] |
"fr30_check_multiple_regs (operands, 4, 1)" |
"stm1 (%0, %1, %2, %3)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 2 "high_register_operand" "h"))] |
"fr30_check_multiple_regs (operands, 3, 1)" |
"stm1 (%0, %1, %2)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "high_register_operand" "h")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "high_register_operand" "h"))] |
"fr30_check_multiple_regs (operands, 2, 1)" |
"stm1 (%0, %1)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (match_operand:SI 0 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 1 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 2 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 3 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15))))] |
"fr30_check_multiple_regs (operands, 4, 0)" |
"ldm1 (%0, %1, %2, %3)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (match_operand:SI 0 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 1 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 2 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15))))] |
"fr30_check_multiple_regs (operands, 3, 0)" |
"ldm1 (%0, %1, %2)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (match_operand:SI 0 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15)))) |
(set:SI (match_operand:SI 1 "high_register_operand" "h") |
(mem:SI (post_inc:SI (reg:SI 15))))] |
"fr30_check_multiple_regs (operands, 2, 0)" |
"ldm1 (%0, %1)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 2 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 3 "low_register_operand" "l"))] |
"fr30_check_multiple_regs (operands, 4, 1)" |
"stm0 (%0, %1, %2, %3)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 2 "low_register_operand" "l"))] |
"fr30_check_multiple_regs (operands, 3, 1)" |
"stm0 (%0, %1, %2)" |
[(set_attr "delay_type" "other")] |
) |
|
(define_peephole |
[(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 0 "low_register_operand" "l")) |
(set:SI (mem:SI (pre_dec:SI (reg:SI 15))) |
(match_operand:SI 1 "low_register_operand" "l"))] |
"fr30_check_multiple_regs (operands, 2, 1)" |
"stm0 (%0, %1)" |
[(set_attr "delay_type" "other")] |
) |
|
;;}}} |
;;{{{ Floating Point Moves |
|
;; Note - Patterns for SF mode moves are compulsory, but |
;; patterns for DF are optional, as GCC can synthesize them. |
|
(define_expand "movsf" |
[(set (match_operand:SF 0 "general_operand" "") |
(match_operand:SF 1 "general_operand" ""))] |
"" |
"{ |
if (!reload_in_progress && !reload_completed |
&& memory_operand (operands[0], SFmode) |
&& memory_operand (operands[1], SFmode)) |
operands[1] = copy_to_mode_reg (SFmode, operands[1]); |
}" |
) |
|
(define_insn "*movsf_internal" |
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,red,m,r") |
(match_operand:SF 1 "general_operand" "Fn,i,rde,r,rm"))] |
"" |
"* |
{ |
switch (which_alternative) |
{ |
case 0: return \"ldi:32\\t%1, %0\"; |
case 1: if (TARGET_SMALL_MODEL) |
return \"ldi:20\\t%1, %0\"; |
else |
return \"ldi:32\\t%1, %0\"; |
case 2: return \"mov \\t%1, %0\"; |
case 3: return \"st \\t%1, %0\"; |
case 4: return \"ld \\t%1, %0\"; |
default: gcc_unreachable (); |
} |
}" |
[(set (attr "length") (cond [(eq_attr "alternative" "0") (const_int 6) |
(eq_attr "alternative" "1") |
(if_then_else (eq_attr "size" "small") |
(const_int 4) |
(const_int 6))] |
(const_int 2)))] |
) |
|
(define_insn "*movsf_constant_store" |
[(set (match_operand:SF 0 "memory_operand" "=m") |
(match_operand:SF 1 "immediate_operand" "F"))] |
"" |
"* |
{ |
const char * ldi_instr; |
const char * tmp_reg; |
static char buffer[100]; |
|
ldi_instr = fr30_const_double_is_zero (operands[1]) |
? ldi_instr = \"ldi:8\" : \"ldi:32\"; |
|
tmp_reg = reg_names [COMPILER_SCRATCH_REGISTER]; |
|
sprintf (buffer, \"%s\\t#%%1, %s\\t;\\n\\tst\\t%s, %%0\\t; Created by movsf_constant_store\", |
ldi_instr, tmp_reg, tmp_reg); |
|
return buffer; |
}" |
[(set_attr "length" "8")] |
) |
|
;;}}} |
|
;;}}} |
;;{{{ Conversions |
|
;; Signed conversions from a smaller integer to a larger integer |
|
(define_insn "extendqisi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(sign_extend:SI (match_operand:QI 1 "register_operand" "0")))] |
"" |
"extsb %0" |
) |
|
(define_insn "extendhisi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(sign_extend:SI (match_operand:HI 1 "register_operand" "0")))] |
"" |
"extsh %0" |
) |
|
;; Unsigned conversions from a smaller integer to a larger integer |
|
(define_insn "zero_extendqisi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(zero_extend:SI (match_operand:QI 1 "register_operand" "0")))] |
"" |
"extub %0" |
) |
|
(define_insn "zero_extendhisi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(zero_extend:SI (match_operand:HI 1 "register_operand" "0")))] |
"" |
"extuh %0" |
) |
|
;;}}} |
;;{{{ Arithmetic |
|
;;{{{ Addition |
|
;; This is a special pattern just for adjusting the stack size. |
(define_insn "add_to_stack" |
[(set (reg:SI 15) |
(plus:SI (reg:SI 15) |
(match_operand:SI 0 "stack_add_operand" "i")))] |
"" |
"addsp %0" |
) |
|
;; We need some trickery to be able to handle the addition of |
;; large (i.e. outside +/- 16) constants. We need to be able to |
;; handle this because reload assumes that it can generate add |
;; instructions with arbitrary sized constants. |
(define_expand "addsi3" |
[(set (match_operand:SI 0 "register_operand" "") |
(plus:SI (match_operand:SI 1 "register_operand" "") |
(match_operand:SI 2 "nonmemory_operand" "")))] |
"" |
"{ |
if ( GET_CODE (operands[2]) == REG |
|| GET_CODE (operands[2]) == SUBREG) |
emit_insn (gen_addsi_regs (operands[0], operands[1], operands[2])); |
else if (GET_CODE (operands[2]) != CONST_INT) |
emit_insn (gen_addsi_big_int (operands[0], operands[1], operands[2])); |
else if (INTVAL (operands[2]) >= -16 |
&& INTVAL (operands[2]) <= 15 |
&& (!REGNO_PTR_FRAME_P (REGNO (operands[1])) |
|| REGNO (operands[1]) == STACK_POINTER_REGNUM)) |
emit_insn (gen_addsi_small_int (operands[0], operands[1], operands[2])); |
else |
emit_insn (gen_addsi_big_int (operands[0], operands[1], operands[2])); |
DONE; |
}" |
) |
|
(define_insn "addsi_regs" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(plus:SI (match_operand:SI 1 "register_operand" "%0") |
(match_operand:SI 2 "register_operand" "r")))] |
"" |
"addn %2, %0" |
) |
|
;; Do not allow an eliminable register in the source register. It |
;; might be eliminated in favor of the stack pointer, probably |
;; increasing the offset, and so rendering the instruction illegal. |
(define_insn "addsi_small_int" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(plus:SI (match_operand:SI 1 "register_operand" "0,0") |
(match_operand:SI 2 "add_immediate_operand" "I,J")))] |
"! REGNO_PTR_FRAME_P (REGNO (operands[1])) |
|| REGNO (operands[1]) == STACK_POINTER_REGNUM" |
"@ |
addn %2, %0 |
addn2 %2, %0" |
) |
|
(define_expand "addsi_big_int" |
[(set (match_operand:SI 0 "register_operand" "") |
(plus:SI (match_operand:SI 1 "register_operand" "") |
(match_operand:SI 2 "immediate_operand" "")))] |
"" |
"{ |
/* Cope with the possibility that ops 0 and 1 are the same register. */ |
if (REGNO (operands[0]) == REGNO (operands[1])) |
{ |
if (reload_in_progress || reload_completed) |
{ |
rtx reg = gen_rtx_REG (SImode, 0/*COMPILER_SCRATCH_REGISTER*/); |
|
emit_insn (gen_movsi (reg, operands[2])); |
emit_insn (gen_addsi_regs (operands[0], operands[0], reg)); |
} |
else |
{ |
operands[2] = force_reg (SImode, operands[2]); |
emit_insn (gen_addsi_regs (operands[0], operands[0], operands[2])); |
} |
} |
else |
{ |
emit_insn (gen_movsi (operands[0], operands[2])); |
emit_insn (gen_addsi_regs (operands[0], operands[0], operands[1])); |
} |
DONE; |
}" |
) |
|
(define_insn "*addsi_for_reload" |
[(set (match_operand:SI 0 "register_operand" "=&r,r,r") |
(plus:SI (match_operand:SI 1 "register_operand" "r,r,r") |
(match_operand:SI 2 "immediate_operand" "L,M,n")))] |
"reload_in_progress || reload_completed" |
"@ |
ldi:8\\t#%2, %0 \\n\\taddn\\t%1, %0 |
ldi:20\\t#%2, %0 \\n\\taddn\\t%1, %0 |
ldi:32\\t#%2, %0 \\n\\taddn\\t%1, %0" |
[(set_attr "length" "4,6,8")] |
) |
|
;;}}} |
;;{{{ Subtraction |
|
(define_insn "subsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(minus:SI (match_operand:SI 1 "register_operand" "0") |
(match_operand:SI 2 "register_operand" "r")))] |
"" |
"subn %2, %0" |
) |
|
;;}}} |
;;{{{ Multiplication |
|
;; Signed multiplication producing 64 bit results from 32 bit inputs |
(define_insn "mulsidi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%r")) |
(sign_extend:DI (match_operand:SI 2 "register_operand" "r")))) |
(clobber (reg:CC 16))] |
"" |
"mul %2, %1\\n\\tmov\\tmdh, %0\\n\\tmov\\tmdl, %p0" |
[(set_attr "length" "6")] |
) |
|
;; Unsigned multiplication producing 64 bit results from 32 bit inputs |
(define_insn "umulsidi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%r")) |
(zero_extend:DI (match_operand:SI 2 "register_operand" "r")))) |
(clobber (reg:CC 16))] |
"" |
"mulu %2, %1\\n\\tmov\\tmdh, %0\\n\\tmov\\tmdl, %p0" |
[(set_attr "length" "6")] |
) |
|
;; Signed multiplication producing 32 bit result from 16 bit inputs |
(define_insn "mulhisi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%r")) |
(sign_extend:SI (match_operand:HI 2 "register_operand" "r")))) |
(clobber (reg:CC 16))] |
"" |
"mulh %2, %1\\n\\tmov\\tmdl, %0" |
[(set_attr "length" "4")] |
) |
|
;; Unsigned multiplication producing 32 bit result from 16 bit inputs |
(define_insn "umulhisi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%r")) |
(zero_extend:SI (match_operand:HI 2 "register_operand" "r")))) |
(clobber (reg:CC 16))] |
"" |
"muluh %2, %1\\n\\tmov\\tmdl, %0" |
[(set_attr "length" "4")] |
) |
|
;; Signed multiplication producing 32 bit result from 32 bit inputs |
(define_insn "mulsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(mult:SI (match_operand:SI 1 "register_operand" "%r") |
(match_operand:SI 2 "register_operand" "r"))) |
(clobber (reg:CC 16))] |
"" |
"mul %2, %1\\n\\tmov\\tmdl, %0" |
[(set_attr "length" "4")] |
) |
|
;;}}} |
;;{{{ Negation |
|
(define_expand "negsi2" |
[(set (match_operand:SI 0 "register_operand" "") |
(neg:SI (match_operand:SI 1 "register_operand" "")))] |
"" |
"{ |
if (REGNO (operands[0]) == REGNO (operands[1])) |
{ |
if (reload_in_progress || reload_completed) |
{ |
rtx reg = gen_rtx_REG (SImode, 0/*COMPILER_SCRATCH_REGISTER*/); |
|
emit_insn (gen_movsi (reg, const0_rtx)); |
emit_insn (gen_subsi3 (reg, reg, operands[0])); |
emit_insn (gen_movsi (operands[0], reg)); |
} |
else |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_movsi (reg, const0_rtx)); |
emit_insn (gen_subsi3 (reg, reg, operands[0])); |
emit_insn (gen_movsi (operands[0], reg)); |
} |
} |
else |
{ |
emit_insn (gen_movsi_internal (operands[0], const0_rtx)); |
emit_insn (gen_subsi3 (operands[0], operands[0], operands[1])); |
} |
DONE; |
}" |
) |
|
;;}}} |
|
;;}}} |
;;{{{ Shifts |
|
;; Arithmetic Shift Left |
(define_insn "ashlsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r,r") |
(ashift:SI (match_operand:SI 1 "register_operand" "0,0,0") |
(match_operand:SI 2 "nonmemory_operand" "r,I,K"))) |
(clobber (reg:CC 16))] |
"" |
"@ |
lsl %2, %0 |
lsl %2, %0 |
lsl2 %x2, %0" |
) |
|
;; Arithmetic Shift Right |
(define_insn "ashrsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r,r") |
(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0") |
(match_operand:SI 2 "nonmemory_operand" "r,I,K"))) |
(clobber (reg:CC 16))] |
"" |
"@ |
asr %2, %0 |
asr %2, %0 |
asr2 %x2, %0" |
) |
|
;; Logical Shift Right |
(define_insn "lshrsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r,r") |
(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0") |
(match_operand:SI 2 "nonmemory_operand" "r,I,K"))) |
(clobber (reg:CC 16))] |
"" |
"@ |
lsr %2, %0 |
lsr %2, %0 |
lsr2 %x2, %0" |
) |
|
;;}}} |
;;{{{ Logical Operations |
|
;; Logical AND, 32 bit integers |
(define_insn "andsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(and:SI (match_operand:SI 1 "register_operand" "%r") |
(match_operand:SI 2 "register_operand" "0"))) |
(clobber (reg:CC 16))] |
"" |
"and %1, %0" |
) |
|
;; Inclusive OR, 32 bit integers |
(define_insn "iorsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(ior:SI (match_operand:SI 1 "register_operand" "%r") |
(match_operand:SI 2 "register_operand" "0"))) |
(clobber (reg:CC 16))] |
"" |
"or %1, %0" |
) |
|
;; Exclusive OR, 32 bit integers |
(define_insn "xorsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(xor:SI (match_operand:SI 1 "register_operand" "%r") |
(match_operand:SI 2 "register_operand" "0"))) |
(clobber (reg:CC 16))] |
"" |
"eor %1, %0" |
) |
|
;; One's complement, 32 bit integers |
(define_expand "one_cmplsi2" |
[(set (match_operand:SI 0 "register_operand" "") |
(not:SI (match_operand:SI 1 "register_operand" "")))] |
"" |
"{ |
if (REGNO (operands[0]) == REGNO (operands[1])) |
{ |
if (reload_in_progress || reload_completed) |
{ |
rtx reg = gen_rtx_REG (SImode, 0/*COMPILER_SCRATCH_REGISTER*/); |
|
emit_insn (gen_movsi (reg, constm1_rtx)); |
emit_insn (gen_xorsi3 (operands[0], operands[0], reg)); |
} |
else |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_movsi (reg, constm1_rtx)); |
emit_insn (gen_xorsi3 (operands[0], operands[0], reg)); |
} |
} |
else |
{ |
emit_insn (gen_movsi_internal (operands[0], constm1_rtx)); |
emit_insn (gen_xorsi3 (operands[0], operands[1], operands[0])); |
} |
DONE; |
}" |
) |
|
;;}}} |
;;{{{ Comparisons |
|
;; Note, we store the operands in the comparison insns, and use them later |
;; when generating the branch or scc operation. |
|
;; First the routines called by the machine independent part of the compiler |
(define_expand "cmpsi" |
[(set (reg:CC 16) |
(compare:CC (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "nonmemory_operand" "")))] |
"" |
"{ |
fr30_compare_op0 = operands[0]; |
fr30_compare_op1 = operands[1]; |
DONE; |
}" |
) |
|
;; Now, the actual comparisons, generated by the branch and/or scc operations |
|
(define_insn "*cmpsi_internal" |
[(set (reg:CC 16) |
(compare:CC (match_operand:SI 0 "register_operand" "r,r,r") |
(match_operand:SI 1 "nonmemory_operand" "r,I,J")))] |
"" |
"@ |
cmp %1, %0 |
cmp %1, %0 |
cmp2 %1, %0" |
) |
|
;;}}} |
;;{{{ Branches |
|
;; Define_expands called by the machine independent part of the compiler |
;; to allocate a new comparison register |
|
(define_expand "beq" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (eq:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bne" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (ne:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "blt" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (lt:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "ble" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (le:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bgt" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (gt:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bge" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (ge:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bltu" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (ltu:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bleu" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (leu:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bgtu" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (gtu:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
(define_expand "bgeu" |
[(set (reg:CC 16) |
(compare:CC (match_dup 1) |
(match_dup 2))) |
(set (pc) |
(if_then_else (geu:CC (reg:CC 16) |
(const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"{ |
operands[1] = fr30_compare_op0; |
operands[2] = fr30_compare_op1; |
}" |
) |
|
;; Actual branches. We must allow for the (label_ref) and the (pc) to be |
;; swapped. If they are swapped, it reverses the sense of the branch. |
|
;; This pattern matches the (branch-if-true) branches generated above. |
;; It generates two different instruction sequences depending upon how |
;; far away the destination is. |
|
;; The calculation for the instruction length is derived as follows: |
;; The branch instruction has a 9 bit signed displacement so we have |
;; this inequality for the displacement: |
;; |
;; -256 <= pc < 256 |
;; or |
;; -256 + 256 <= pc + 256 < 256 + 256 |
;; i.e. |
;; 0 <= pc + 256 < 512 |
;; |
;; if we consider the displacement as an unsigned value, then negative |
;; displacements become very large positive displacements, and the |
;; inequality becomes: |
;; |
;; pc + 256 < 512 |
;; |
;; In order to allow for the fact that the real branch instruction works |
;; from pc + 2, we increase the offset to 258. |
;; |
;; Note - we do not have to worry about whether the branch is delayed or |
;; not, as branch shortening happens after delay slot reorganization. |
|
(define_insn "*branch_true" |
[(set (pc) |
(if_then_else (match_operator:CC 0 "comparison_operator" |
[(reg:CC 16) |
(const_int 0)]) |
(label_ref (match_operand 1 "" "")) |
(pc)))] |
"" |
"* |
{ |
if (get_attr_length (insn) == 2) |
return \"b%b0%#\\t%l1\"; |
else |
{ |
static char buffer [100]; |
const char * tmp_reg; |
const char * ldi_insn; |
|
tmp_reg = reg_names [COMPILER_SCRATCH_REGISTER]; |
|
ldi_insn = TARGET_SMALL_MODEL ? \"ldi:20\" : \"ldi:32\"; |
|
/* The code produced here is, for say the EQ case: |
|
Bne 1f |
LDI <label>, r0 |
JMP r0 |
1: */ |
|
sprintf (buffer, |
\"b%%B0\\t1f\\t;\\n\\t%s\\t%%l1, %s\\t;\\n\\tjmp%%#\\t@%s\\t;\\n1:\", |
ldi_insn, tmp_reg, tmp_reg); |
|
return buffer; |
} |
}" |
[(set (attr "length") (if_then_else |
(ltu |
(plus |
(minus |
(match_dup 1) |
(pc)) |
(const_int 254)) |
(const_int 506)) |
(const_int 2) |
(if_then_else (eq_attr "size" "small") |
(const_int 8) |
(const_int 10)))) |
(set_attr "delay_type" "delayed")] |
) |
|
|
;; This pattern is a duplicate of the previous one, except that the |
;; branch occurs if the test is false, so the %B operator is used. |
(define_insn "*branch_false" |
[(set (pc) |
(if_then_else (match_operator:CC 0 "comparison_operator" |
[(reg:CC 16) |
(const_int 0)]) |
(pc) |
(label_ref (match_operand 1 "" ""))))] |
"" |
"* |
{ |
if (get_attr_length (insn) == 2) |
return \"b%B0%#\\t%l1 \"; |
else |
{ |
static char buffer [100]; |
const char * tmp_reg; |
const char * ldi_insn; |
|
tmp_reg = reg_names [COMPILER_SCRATCH_REGISTER]; |
|
ldi_insn = TARGET_SMALL_MODEL ? \"ldi:20\" : \"ldi:32\"; |
|
sprintf (buffer, |
\"b%%b0\\t1f\\t;\\n\\t%s\\t%%l1, %s\\t;\\n\\tjmp%%#\\t@%s\\t;\\n1:\", |
ldi_insn, tmp_reg, tmp_reg); |
|
return buffer; |
} |
}" |
[(set (attr "length") (if_then_else (ltu (plus (minus (match_dup 1) (pc)) |
(const_int 254)) |
(const_int 506)) |
(const_int 2) |
(if_then_else (eq_attr "size" "small") |
(const_int 8) |
(const_int 10)))) |
(set_attr "delay_type" "delayed")] |
) |
|
;;}}} |
;;{{{ Calls & Jumps |
|
;; Subroutine call instruction returning no value. Operand 0 is the function |
;; to call; operand 1 is the number of bytes of arguments pushed (in mode |
;; `SImode', except it is normally a `const_int'); operand 2 is the number of |
;; registers used as operands. |
|
(define_insn "call" |
[(call (match_operand 0 "call_operand" "Qm") |
(match_operand 1 "" "g")) |
(clobber (reg:SI 17))] |
"" |
"call%#\\t%0" |
[(set_attr "delay_type" "delayed")] |
) |
|
;; Subroutine call instruction returning a value. Operand 0 is the hard |
;; register in which the value is returned. There are three more operands, the |
;; same as the three operands of the `call' instruction (but with numbers |
;; increased by one). |
|
;; Subroutines that return `BLKmode' objects use the `call' insn. |
|
(define_insn "call_value" |
[(set (match_operand 0 "register_operand" "=r") |
(call (match_operand 1 "call_operand" "Qm") |
(match_operand 2 "" "g"))) |
(clobber (reg:SI 17))] |
"" |
"call%#\\t%1" |
[(set_attr "delay_type" "delayed")] |
) |
|
;; Normal unconditional jump. |
;; For a description of the computation of the length |
;; attribute see the branch patterns above. |
;; |
;; Although this instruction really clobbers r0, flow |
;; relies on jump being simplejump_p in several places |
;; and as r0 is fixed, this doesn't change anything |
(define_insn "jump" |
[(set (pc) (label_ref (match_operand 0 "" "")))] |
"" |
"* |
{ |
if (get_attr_length (insn) == 2) |
return \"bra%#\\t%0\"; |
else |
{ |
static char buffer [100]; |
const char * tmp_reg; |
const char * ldi_insn; |
|
tmp_reg = reg_names [COMPILER_SCRATCH_REGISTER]; |
|
ldi_insn = TARGET_SMALL_MODEL ? \"ldi:20\" : \"ldi:32\"; |
|
sprintf (buffer, \"%s\\t%%0, %s\\t;\\n\\tjmp%%#\\t@%s\\t;\", |
ldi_insn, tmp_reg, tmp_reg); |
|
return buffer; |
} |
}" |
[(set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) |
(const_int 254)) |
(const_int 506)) |
(const_int 2) |
(if_then_else (eq_attr "size" "small") |
(const_int 6) |
(const_int 8)))) |
(set_attr "delay_type" "delayed")] |
) |
|
;; Indirect jump through a register |
(define_insn "indirect_jump" |
[(set (pc) (match_operand:SI 0 "nonimmediate_operand" "r"))] |
"GET_CODE (operands[0]) != MEM || GET_CODE (XEXP (operands[0], 0)) != PLUS" |
"jmp%#\\t@%0" |
[(set_attr "delay_type" "delayed")] |
) |
|
(define_insn "tablejump" |
[(set (pc) (match_operand:SI 0 "register_operand" "r")) |
(use (label_ref (match_operand 1 "" "")))] |
"" |
"jmp%#\\t@%0" |
[(set_attr "delay_type" "delayed")] |
) |
|
;;}}} |
;;{{{ Function Prologues and Epilogues |
|
;; Called after register allocation to add any instructions needed for the |
;; prologue. Using a prologue insn is favored compared to putting all of the |
;; instructions in output_function_prologue(), since it allows the scheduler |
;; to intermix instructions with the saves of the caller saved registers. In |
;; some cases, it might be necessary to emit a barrier instruction as the last |
;; insn to prevent such scheduling. |
(define_expand "prologue" |
[(clobber (const_int 0))] |
"" |
"{ |
fr30_expand_prologue (); |
DONE; |
}" |
) |
|
;; Called after register allocation to add any instructions needed for the |
;; epilogue. Using an epilogue insn is favored compared to putting all of the |
;; instructions in output_function_epilogue(), since it allows the scheduler |
;; to intermix instructions with the restores of the caller saved registers. |
;; In some cases, it might be necessary to emit a barrier instruction as the |
;; first insn to prevent such scheduling. |
(define_expand "epilogue" |
[(return)] |
"" |
"{ |
fr30_expand_epilogue (); |
DONE; |
}" |
) |
|
(define_insn "return_from_func" |
[(return) |
(use (reg:SI 17))] |
"reload_completed" |
"ret%#" |
[(set_attr "delay_type" "delayed")] |
) |
|
(define_insn "leave_func" |
[(set (reg:SI 15) (reg:SI 14)) |
(set (reg:SI 14) (mem:SI (post_inc:SI (reg:SI 15))))] |
"reload_completed" |
"leave" |
) |
|
(define_insn "enter_func" |
[(set:SI (mem:SI (minus:SI (reg:SI 15) |
(const_int 4))) |
(reg:SI 14)) |
(set:SI (reg:SI 14) |
(minus:SI (reg:SI 15) |
(const_int 4))) |
(set:SI (reg:SI 15) |
(minus:SI (reg:SI 15) |
(match_operand 0 "immediate_operand" "i")))] |
"reload_completed" |
"enter #%0" |
[(set_attr "delay_type" "other")] |
) |
|
;;}}} |
;;{{{ Miscellaneous |
|
;; No operation, needed in case the user uses -g but not -O. |
(define_insn "nop" |
[(const_int 0)] |
"" |
"nop" |
) |
|
;; Pseudo instruction that prevents the scheduler from moving code above this |
;; point. |
(define_insn "blockage" |
[(unspec_volatile [(const_int 0)] 0)] |
"" |
"" |
[(set_attr "length" "0")] |
) |
;;}}} |
|
;; Local Variables: |
;; mode: md |
;; folded-file: t |
;; End: |
/fr30.c
0,0 → 1,934
/* FR30 specific functions. |
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2007 |
Free Software Foundation, Inc. |
Contributed by Cygnus Solutions. |
|
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 3, 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 GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
/*{{{ Includes */ |
|
#include "config.h" |
#include "system.h" |
#include "coretypes.h" |
#include "tm.h" |
#include "rtl.h" |
#include "regs.h" |
#include "hard-reg-set.h" |
#include "real.h" |
#include "insn-config.h" |
#include "conditions.h" |
#include "insn-attr.h" |
#include "flags.h" |
#include "recog.h" |
#include "tree.h" |
#include "output.h" |
#include "expr.h" |
#include "obstack.h" |
#include "except.h" |
#include "function.h" |
#include "toplev.h" |
#include "tm_p.h" |
#include "target.h" |
#include "target-def.h" |
|
/*}}}*/ |
/*{{{ Function Prologues & Epilogues */ |
|
/* Define the information needed to generate branch and scc insns. This is |
stored from the compare operation. */ |
|
struct rtx_def * fr30_compare_op0; |
struct rtx_def * fr30_compare_op1; |
|
/* The FR30 stack looks like this: |
|
Before call After call |
FP ->| | | | |
+-----------------------+ +-----------------------+ high |
| | | | memory |
| local variables, | | local variables, | |
| reg save area, etc. | | reg save area, etc. | |
| | | | |
+-----------------------+ +-----------------------+ |
| | | | |
| args to the func that | | args to this func. | |
| is being called that | | | |
SP ->| do not fit in regs | | | |
+-----------------------+ +-----------------------+ |
| args that used to be | \ |
| in regs; only created | | pretend_size |
AP-> | for vararg funcs | / |
+-----------------------+ |
| | \ |
| register save area | | |
| | | |
+-----------------------+ | reg_size |
| return address | | |
+-----------------------+ | |
FP ->| previous frame ptr | / |
+-----------------------+ |
| | \ |
| local variables | | var_size |
| | / |
+-----------------------+ |
| | \ |
low | room for args to | | |
memory | other funcs called | | args_size |
| from this one | | |
SP ->| | / |
+-----------------------+ |
|
Note, AP is a fake hard register. It will be eliminated in favor of |
SP or FP as appropriate. |
|
Note, Some or all of the stack sections above may be omitted if they |
are not needed. */ |
|
/* Structure to be filled in by fr30_compute_frame_size() with register |
save masks, and offsets for the current function. */ |
struct fr30_frame_info |
{ |
unsigned int total_size; /* # Bytes that the entire frame takes up. */ |
unsigned int pretend_size; /* # Bytes we push and pretend caller did. */ |
unsigned int args_size; /* # Bytes that outgoing arguments take up. */ |
unsigned int reg_size; /* # Bytes needed to store regs. */ |
unsigned int var_size; /* # Bytes that variables take up. */ |
unsigned int frame_size; /* # Bytes in current frame. */ |
unsigned int gmask; /* Mask of saved registers. */ |
unsigned int save_fp; /* Nonzero if frame pointer must be saved. */ |
unsigned int save_rp; /* Nonzero if return pointer must be saved. */ |
int initialised; /* Nonzero if frame size already calculated. */ |
}; |
|
/* Current frame information calculated by fr30_compute_frame_size(). */ |
static struct fr30_frame_info current_frame_info; |
|
/* Zero structure to initialize current_frame_info. */ |
static struct fr30_frame_info zero_frame_info; |
|
static void fr30_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, |
tree, int *, int); |
static bool fr30_must_pass_in_stack (enum machine_mode, tree); |
static int fr30_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, |
tree, bool); |
|
|
#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) |
#define RETURN_POINTER_MASK (1 << (RETURN_POINTER_REGNUM)) |
|
/* Tell prologue and epilogue if register REGNO should be saved / restored. |
The return address and frame pointer are treated separately. |
Don't consider them here. */ |
#define MUST_SAVE_REGISTER(regno) \ |
( (regno) != RETURN_POINTER_REGNUM \ |
&& (regno) != FRAME_POINTER_REGNUM \ |
&& regs_ever_live [regno] \ |
&& ! call_used_regs [regno] ) |
|
#define MUST_SAVE_FRAME_POINTER (regs_ever_live [FRAME_POINTER_REGNUM] || frame_pointer_needed) |
#define MUST_SAVE_RETURN_POINTER (regs_ever_live [RETURN_POINTER_REGNUM] || current_function_profile) |
|
#if UNITS_PER_WORD == 4 |
#define WORD_ALIGN(SIZE) (((SIZE) + 3) & ~3) |
#endif |
|
/* Initialize the GCC target structure. */ |
#undef TARGET_ASM_ALIGNED_HI_OP |
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" |
#undef TARGET_ASM_ALIGNED_SI_OP |
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" |
|
#undef TARGET_PROMOTE_PROTOTYPES |
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true |
#undef TARGET_PASS_BY_REFERENCE |
#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack |
#undef TARGET_ARG_PARTIAL_BYTES |
#define TARGET_ARG_PARTIAL_BYTES fr30_arg_partial_bytes |
|
#undef TARGET_SETUP_INCOMING_VARARGS |
#define TARGET_SETUP_INCOMING_VARARGS fr30_setup_incoming_varargs |
#undef TARGET_MUST_PASS_IN_STACK |
#define TARGET_MUST_PASS_IN_STACK fr30_must_pass_in_stack |
|
struct gcc_target targetm = TARGET_INITIALIZER; |
|
/* Returns the number of bytes offset between FROM_REG and TO_REG |
for the current function. As a side effect it fills in the |
current_frame_info structure, if the data is available. */ |
unsigned int |
fr30_compute_frame_size (int from_reg, int to_reg) |
{ |
int regno; |
unsigned int return_value; |
unsigned int var_size; |
unsigned int args_size; |
unsigned int pretend_size; |
unsigned int reg_size; |
unsigned int gmask; |
|
var_size = WORD_ALIGN (get_frame_size ()); |
args_size = WORD_ALIGN (current_function_outgoing_args_size); |
pretend_size = current_function_pretend_args_size; |
|
reg_size = 0; |
gmask = 0; |
|
/* Calculate space needed for registers. */ |
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++) |
{ |
if (MUST_SAVE_REGISTER (regno)) |
{ |
reg_size += UNITS_PER_WORD; |
gmask |= 1 << regno; |
} |
} |
|
current_frame_info.save_fp = MUST_SAVE_FRAME_POINTER; |
current_frame_info.save_rp = MUST_SAVE_RETURN_POINTER; |
|
reg_size += (current_frame_info.save_fp + current_frame_info.save_rp) |
* UNITS_PER_WORD; |
|
/* Save computed information. */ |
current_frame_info.pretend_size = pretend_size; |
current_frame_info.var_size = var_size; |
current_frame_info.args_size = args_size; |
current_frame_info.reg_size = reg_size; |
current_frame_info.frame_size = args_size + var_size; |
current_frame_info.total_size = args_size + var_size + reg_size + pretend_size; |
current_frame_info.gmask = gmask; |
current_frame_info.initialised = reload_completed; |
|
/* Calculate the required distance. */ |
return_value = 0; |
|
if (to_reg == STACK_POINTER_REGNUM) |
return_value += args_size + var_size; |
|
if (from_reg == ARG_POINTER_REGNUM) |
return_value += reg_size; |
|
return return_value; |
} |
|
/* Called after register allocation to add any instructions needed for the |
prologue. Using a prologue insn is favored compared to putting all of the |
instructions in output_function_prologue(), since it allows the scheduler |
to intermix instructions with the saves of the caller saved registers. In |
some cases, it might be necessary to emit a barrier instruction as the last |
insn to prevent such scheduling. */ |
|
void |
fr30_expand_prologue (void) |
{ |
int regno; |
rtx insn; |
|
if (! current_frame_info.initialised) |
fr30_compute_frame_size (0, 0); |
|
/* This cases shouldn't happen. Catch it now. */ |
gcc_assert (current_frame_info.total_size || !current_frame_info.gmask); |
|
/* Allocate space for register arguments if this is a variadic function. */ |
if (current_frame_info.pretend_size) |
{ |
int regs_to_save = current_frame_info.pretend_size / UNITS_PER_WORD; |
|
/* Push argument registers into the pretend arg area. */ |
for (regno = FIRST_ARG_REGNUM + FR30_NUM_ARG_REGS; regno --, regs_to_save --;) |
{ |
insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
} |
|
if (current_frame_info.gmask) |
{ |
/* Save any needed call-saved regs. */ |
for (regno = STACK_POINTER_REGNUM; regno--;) |
{ |
if ((current_frame_info.gmask & (1 << regno)) != 0) |
{ |
insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
} |
} |
|
/* Save return address if necessary. */ |
if (current_frame_info.save_rp) |
{ |
insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, |
RETURN_POINTER_REGNUM))); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
|
/* Save old frame pointer and create new one, if necessary. */ |
if (current_frame_info.save_fp) |
{ |
if (current_frame_info.frame_size < ((1 << 10) - UNITS_PER_WORD)) |
{ |
int enter_size = current_frame_info.frame_size + UNITS_PER_WORD; |
rtx pattern; |
|
insn = emit_insn (gen_enter_func (GEN_INT (enter_size))); |
RTX_FRAME_RELATED_P (insn) = 1; |
|
pattern = PATTERN (insn); |
|
/* Also mark all 3 subexpressions as RTX_FRAME_RELATED_P. */ |
if (GET_CODE (pattern) == PARALLEL) |
{ |
int x; |
for (x = XVECLEN (pattern, 0); x--;) |
{ |
rtx part = XVECEXP (pattern, 0, x); |
|
/* One of the insns in the ENTER pattern updates the |
frame pointer. If we do not actually need the frame |
pointer in this function then this is a side effect |
rather than a desired effect, so we do not mark that |
insn as being related to the frame set up. Doing this |
allows us to compile the crash66.C test file in the |
G++ testsuite. */ |
if (! frame_pointer_needed |
&& GET_CODE (part) == SET |
&& REGNO (SET_DEST (part)) == HARD_FRAME_POINTER_REGNUM) |
RTX_FRAME_RELATED_P (part) = 0; |
else |
RTX_FRAME_RELATED_P (part) = 1; |
} |
} |
} |
else |
{ |
insn = emit_insn (gen_movsi_push (frame_pointer_rtx)); |
RTX_FRAME_RELATED_P (insn) = 1; |
|
if (frame_pointer_needed) |
{ |
insn = emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
} |
} |
|
/* Allocate the stack frame. */ |
if (current_frame_info.frame_size == 0) |
; /* Nothing to do. */ |
else if (current_frame_info.save_fp |
&& current_frame_info.frame_size < ((1 << 10) - UNITS_PER_WORD)) |
; /* Nothing to do. */ |
else if (current_frame_info.frame_size <= 512) |
{ |
insn = emit_insn (gen_add_to_stack (GEN_INT (- current_frame_info.frame_size))); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
else |
{ |
rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); |
insn = emit_insn (gen_movsi (tmp, GEN_INT (current_frame_info.frame_size))); |
RTX_FRAME_RELATED_P (insn) = 1; |
insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
|
if (current_function_profile) |
emit_insn (gen_blockage ()); |
} |
|
/* Called after register allocation to add any instructions needed for the |
epilogue. Using an epilogue insn is favored compared to putting all of the |
instructions in output_function_epilogue(), since it allows the scheduler |
to intermix instructions with the restores of the caller saved registers. |
In some cases, it might be necessary to emit a barrier instruction as the |
first insn to prevent such scheduling. */ |
void |
fr30_expand_epilogue (void) |
{ |
int regno; |
|
/* Perform the inversion operations of the prologue. */ |
gcc_assert (current_frame_info.initialised); |
|
/* Pop local variables and arguments off the stack. |
If frame_pointer_needed is TRUE then the frame pointer register |
has actually been used as a frame pointer, and we can recover |
the stack pointer from it, otherwise we must unwind the stack |
manually. */ |
if (current_frame_info.frame_size > 0) |
{ |
if (current_frame_info.save_fp && frame_pointer_needed) |
{ |
emit_insn (gen_leave_func ()); |
current_frame_info.save_fp = 0; |
} |
else if (current_frame_info.frame_size <= 508) |
emit_insn (gen_add_to_stack |
(GEN_INT (current_frame_info.frame_size))); |
else |
{ |
rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); |
emit_insn (gen_movsi (tmp, GEN_INT (current_frame_info.frame_size))); |
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); |
} |
} |
|
if (current_frame_info.save_fp) |
emit_insn (gen_movsi_pop (frame_pointer_rtx)); |
|
/* Pop all the registers that were pushed. */ |
if (current_frame_info.save_rp) |
emit_insn (gen_movsi_pop (gen_rtx_REG (Pmode, RETURN_POINTER_REGNUM))); |
|
for (regno = 0; regno < STACK_POINTER_REGNUM; regno ++) |
if (current_frame_info.gmask & (1 << regno)) |
emit_insn (gen_movsi_pop (gen_rtx_REG (Pmode, regno))); |
|
if (current_frame_info.pretend_size) |
emit_insn (gen_add_to_stack (GEN_INT (current_frame_info.pretend_size))); |
|
/* Reset state info for each function. */ |
current_frame_info = zero_frame_info; |
|
emit_jump_insn (gen_return_from_func ()); |
} |
|
/* Do any needed setup for a variadic function. We must create a register |
parameter block, and then copy any anonymous arguments, plus the last |
named argument, from registers into memory. * copying actually done in |
fr30_expand_prologue(). |
|
ARG_REGS_USED_SO_FAR has *not* been updated for the last named argument |
which has type TYPE and mode MODE, and we rely on this fact. */ |
void |
fr30_setup_incoming_varargs (CUMULATIVE_ARGS *arg_regs_used_so_far, |
enum machine_mode mode, |
tree type ATTRIBUTE_UNUSED, |
int *pretend_size, |
int second_time ATTRIBUTE_UNUSED) |
{ |
int size; |
|
/* All BLKmode values are passed by reference. */ |
gcc_assert (mode != BLKmode); |
|
/* ??? This run-time test as well as the code inside the if |
statement is probably unnecessary. */ |
if (targetm.calls.strict_argument_naming (arg_regs_used_so_far)) |
/* If TARGET_STRICT_ARGUMENT_NAMING returns true, then the last named |
arg must not be treated as an anonymous arg. */ |
arg_regs_used_so_far += fr30_num_arg_regs (mode, type); |
|
size = FR30_NUM_ARG_REGS - (* arg_regs_used_so_far); |
|
if (size <= 0) |
return; |
|
* pretend_size = (size * UNITS_PER_WORD); |
} |
|
/*}}}*/ |
/*{{{ Printing operands */ |
|
/* Print a memory address as an operand to reference that memory location. */ |
|
void |
fr30_print_operand_address (FILE *stream, rtx address) |
{ |
switch (GET_CODE (address)) |
{ |
case SYMBOL_REF: |
output_addr_const (stream, address); |
break; |
|
default: |
fprintf (stderr, "code = %x\n", GET_CODE (address)); |
debug_rtx (address); |
output_operand_lossage ("fr30_print_operand_address: unhandled address"); |
break; |
} |
} |
|
/* Print an operand. */ |
|
void |
fr30_print_operand (FILE *file, rtx x, int code) |
{ |
rtx x0; |
|
switch (code) |
{ |
case '#': |
/* Output a :D if this instruction is delayed. */ |
if (dbr_sequence_length () != 0) |
fputs (":D", file); |
return; |
|
case 'p': |
/* Compute the register name of the second register in a hi/lo |
register pair. */ |
if (GET_CODE (x) != REG) |
output_operand_lossage ("fr30_print_operand: unrecognized %%p code"); |
else |
fprintf (file, "r%d", REGNO (x) + 1); |
return; |
|
case 'b': |
/* Convert GCC's comparison operators into FR30 comparison codes. */ |
switch (GET_CODE (x)) |
{ |
case EQ: fprintf (file, "eq"); break; |
case NE: fprintf (file, "ne"); break; |
case LT: fprintf (file, "lt"); break; |
case LE: fprintf (file, "le"); break; |
case GT: fprintf (file, "gt"); break; |
case GE: fprintf (file, "ge"); break; |
case LTU: fprintf (file, "c"); break; |
case LEU: fprintf (file, "ls"); break; |
case GTU: fprintf (file, "hi"); break; |
case GEU: fprintf (file, "nc"); break; |
default: |
output_operand_lossage ("fr30_print_operand: unrecognized %%b code"); |
break; |
} |
return; |
|
case 'B': |
/* Convert GCC's comparison operators into the complimentary FR30 |
comparison codes. */ |
switch (GET_CODE (x)) |
{ |
case EQ: fprintf (file, "ne"); break; |
case NE: fprintf (file, "eq"); break; |
case LT: fprintf (file, "ge"); break; |
case LE: fprintf (file, "gt"); break; |
case GT: fprintf (file, "le"); break; |
case GE: fprintf (file, "lt"); break; |
case LTU: fprintf (file, "nc"); break; |
case LEU: fprintf (file, "hi"); break; |
case GTU: fprintf (file, "ls"); break; |
case GEU: fprintf (file, "c"); break; |
default: |
output_operand_lossage ("fr30_print_operand: unrecognized %%B code"); |
break; |
} |
return; |
|
case 'A': |
/* Print a signed byte value as an unsigned value. */ |
if (GET_CODE (x) != CONST_INT) |
output_operand_lossage ("fr30_print_operand: invalid operand to %%A code"); |
else |
{ |
HOST_WIDE_INT val; |
|
val = INTVAL (x); |
|
val &= 0xff; |
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, val); |
} |
return; |
|
case 'x': |
if (GET_CODE (x) != CONST_INT |
|| INTVAL (x) < 16 |
|| INTVAL (x) > 32) |
output_operand_lossage ("fr30_print_operand: invalid %%x code"); |
else |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) - 16); |
return; |
|
case 'F': |
if (GET_CODE (x) != CONST_DOUBLE) |
output_operand_lossage ("fr30_print_operand: invalid %%F code"); |
else |
{ |
char str[30]; |
|
real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), |
sizeof (str), 0, 1); |
fputs (str, file); |
} |
return; |
|
case 0: |
/* Handled below. */ |
break; |
|
default: |
fprintf (stderr, "unknown code = %x\n", code); |
output_operand_lossage ("fr30_print_operand: unknown code"); |
return; |
} |
|
switch (GET_CODE (x)) |
{ |
case REG: |
fputs (reg_names [REGNO (x)], file); |
break; |
|
case MEM: |
x0 = XEXP (x,0); |
|
switch (GET_CODE (x0)) |
{ |
case REG: |
gcc_assert ((unsigned) REGNO (x0) < ARRAY_SIZE (reg_names)); |
fprintf (file, "@%s", reg_names [REGNO (x0)]); |
break; |
|
case PLUS: |
if (GET_CODE (XEXP (x0, 0)) != REG |
|| REGNO (XEXP (x0, 0)) < FRAME_POINTER_REGNUM |
|| REGNO (XEXP (x0, 0)) > STACK_POINTER_REGNUM |
|| GET_CODE (XEXP (x0, 1)) != CONST_INT) |
{ |
fprintf (stderr, "bad INDEXed address:"); |
debug_rtx (x); |
output_operand_lossage ("fr30_print_operand: unhandled MEM"); |
} |
else if (REGNO (XEXP (x0, 0)) == FRAME_POINTER_REGNUM) |
{ |
HOST_WIDE_INT val = INTVAL (XEXP (x0, 1)); |
if (val < -(1 << 9) || val > ((1 << 9) - 4)) |
{ |
fprintf (stderr, "frame INDEX out of range:"); |
debug_rtx (x); |
output_operand_lossage ("fr30_print_operand: unhandled MEM"); |
} |
fprintf (file, "@(r14, #" HOST_WIDE_INT_PRINT_DEC ")", val); |
} |
else |
{ |
HOST_WIDE_INT val = INTVAL (XEXP (x0, 1)); |
if (val < 0 || val > ((1 << 6) - 4)) |
{ |
fprintf (stderr, "stack INDEX out of range:"); |
debug_rtx (x); |
output_operand_lossage ("fr30_print_operand: unhandled MEM"); |
} |
fprintf (file, "@(r15, #" HOST_WIDE_INT_PRINT_DEC ")", val); |
} |
break; |
|
case SYMBOL_REF: |
output_address (x0); |
break; |
|
default: |
fprintf (stderr, "bad MEM code = %x\n", GET_CODE (x0)); |
debug_rtx (x); |
output_operand_lossage ("fr30_print_operand: unhandled MEM"); |
break; |
} |
break; |
|
case CONST_DOUBLE : |
/* We handle SFmode constants here as output_addr_const doesn't. */ |
if (GET_MODE (x) == SFmode) |
{ |
REAL_VALUE_TYPE d; |
long l; |
|
REAL_VALUE_FROM_CONST_DOUBLE (d, x); |
REAL_VALUE_TO_TARGET_SINGLE (d, l); |
fprintf (file, "0x%08lx", l); |
break; |
} |
|
/* Fall through. Let output_addr_const deal with it. */ |
default: |
output_addr_const (file, x); |
break; |
} |
|
return; |
} |
|
/*}}}*/ |
/*{{{ Function arguments */ |
|
/* Return true if we should pass an argument on the stack rather than |
in registers. */ |
|
static bool |
fr30_must_pass_in_stack (enum machine_mode mode, tree type) |
{ |
if (mode == BLKmode) |
return true; |
if (type == NULL) |
return false; |
return AGGREGATE_TYPE_P (type); |
} |
|
/* Compute the number of word sized registers needed to hold a |
function argument of mode INT_MODE and tree type TYPE. */ |
int |
fr30_num_arg_regs (enum machine_mode mode, tree type) |
{ |
int size; |
|
if (targetm.calls.must_pass_in_stack (mode, type)) |
return 0; |
|
if (type && mode == BLKmode) |
size = int_size_in_bytes (type); |
else |
size = GET_MODE_SIZE (mode); |
|
return (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; |
} |
|
/* Returns the number of bytes in which *part* of a parameter of machine |
mode MODE and tree type TYPE (which may be NULL if the type is not known). |
If the argument fits entirely in the argument registers, or entirely on |
the stack, then 0 is returned. |
CUM is the number of argument registers already used by earlier |
parameters to the function. */ |
|
static int |
fr30_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
tree type, bool named) |
{ |
/* Unnamed arguments, i.e. those that are prototyped as ... |
are always passed on the stack. |
Also check here to see if all the argument registers are full. */ |
if (named == 0 || *cum >= FR30_NUM_ARG_REGS) |
return 0; |
|
/* Work out how many argument registers would be needed if this |
parameter were to be passed entirely in registers. If there |
are sufficient argument registers available (or if no registers |
are needed because the parameter must be passed on the stack) |
then return zero, as this parameter does not require partial |
register, partial stack stack space. */ |
if (*cum + fr30_num_arg_regs (mode, type) <= FR30_NUM_ARG_REGS) |
return 0; |
|
return (FR30_NUM_ARG_REGS - *cum) * UNITS_PER_WORD; |
} |
|
/*}}}*/ |
/*{{{ Operand predicates */ |
|
#ifndef Mmode |
#define Mmode enum machine_mode |
#endif |
|
/* Returns true iff all the registers in the operands array |
are in descending or ascending order. */ |
int |
fr30_check_multiple_regs (rtx *operands, int num_operands, int descending) |
{ |
if (descending) |
{ |
unsigned int prev_regno = 0; |
|
while (num_operands --) |
{ |
if (GET_CODE (operands [num_operands]) != REG) |
return 0; |
|
if (REGNO (operands [num_operands]) < prev_regno) |
return 0; |
|
prev_regno = REGNO (operands [num_operands]); |
} |
} |
else |
{ |
unsigned int prev_regno = CONDITION_CODE_REGNUM; |
|
while (num_operands --) |
{ |
if (GET_CODE (operands [num_operands]) != REG) |
return 0; |
|
if (REGNO (operands [num_operands]) > prev_regno) |
return 0; |
|
prev_regno = REGNO (operands [num_operands]); |
} |
} |
|
return 1; |
} |
|
int |
fr30_const_double_is_zero (rtx operand) |
{ |
REAL_VALUE_TYPE d; |
|
if (operand == NULL || GET_CODE (operand) != CONST_DOUBLE) |
return 0; |
|
REAL_VALUE_FROM_CONST_DOUBLE (d, operand); |
|
return REAL_VALUES_EQUAL (d, dconst0); |
} |
|
/*}}}*/ |
/*{{{ Instruction Output Routines */ |
|
/* Output a double word move. |
It must be REG<-REG, REG<-MEM, MEM<-REG or REG<-CONST. |
On the FR30 we are constrained by the fact that it does not |
support offsetable addresses, and so we have to load the |
address of the secnd word into the second destination register |
before we can use it. */ |
|
rtx |
fr30_move_double (rtx * operands) |
{ |
rtx src = operands[1]; |
rtx dest = operands[0]; |
enum rtx_code src_code = GET_CODE (src); |
enum rtx_code dest_code = GET_CODE (dest); |
enum machine_mode mode = GET_MODE (dest); |
rtx val; |
|
start_sequence (); |
|
if (dest_code == REG) |
{ |
if (src_code == REG) |
{ |
int reverse = (REGNO (dest) == REGNO (src) + 1); |
|
/* We normally copy the low-numbered register first. However, if |
the first register of operand 0 is the same as the second register |
of operand 1, we must copy in the opposite order. */ |
emit_insn (gen_rtx_SET (VOIDmode, |
operand_subword (dest, reverse, TRUE, mode), |
operand_subword (src, reverse, TRUE, mode))); |
|
emit_insn (gen_rtx_SET (VOIDmode, |
operand_subword (dest, !reverse, TRUE, mode), |
operand_subword (src, !reverse, TRUE, mode))); |
} |
else if (src_code == MEM) |
{ |
rtx addr = XEXP (src, 0); |
int dregno = REGNO (dest); |
rtx dest0; |
rtx dest1; |
rtx new_mem; |
|
/* If the high-address word is used in the address, we |
must load it last. Otherwise, load it first. */ |
int reverse = (refers_to_regno_p (dregno, dregno + 1, addr, 0) != 0); |
|
gcc_assert (GET_CODE (addr) == REG); |
|
dest0 = operand_subword (dest, reverse, TRUE, mode); |
dest1 = operand_subword (dest, !reverse, TRUE, mode); |
|
if (reverse) |
{ |
emit_insn (gen_rtx_SET (VOIDmode, dest1, |
adjust_address (src, SImode, 0))); |
emit_insn (gen_rtx_SET (SImode, dest0, |
gen_rtx_REG (SImode, REGNO (addr)))); |
emit_insn (gen_rtx_SET (SImode, dest0, |
plus_constant (dest0, UNITS_PER_WORD))); |
|
new_mem = gen_rtx_MEM (SImode, dest0); |
MEM_COPY_ATTRIBUTES (new_mem, src); |
|
emit_insn (gen_rtx_SET (VOIDmode, dest0, new_mem)); |
} |
else |
{ |
emit_insn (gen_rtx_SET (VOIDmode, dest0, |
adjust_address (src, SImode, 0))); |
emit_insn (gen_rtx_SET (SImode, dest1, |
gen_rtx_REG (SImode, REGNO (addr)))); |
emit_insn (gen_rtx_SET (SImode, dest1, |
plus_constant (dest1, UNITS_PER_WORD))); |
|
new_mem = gen_rtx_MEM (SImode, dest1); |
MEM_COPY_ATTRIBUTES (new_mem, src); |
|
emit_insn (gen_rtx_SET (VOIDmode, dest1, new_mem)); |
} |
} |
else if (src_code == CONST_INT || src_code == CONST_DOUBLE) |
{ |
rtx words[2]; |
split_double (src, &words[0], &words[1]); |
emit_insn (gen_rtx_SET (VOIDmode, |
operand_subword (dest, 0, TRUE, mode), |
words[0])); |
|
emit_insn (gen_rtx_SET (VOIDmode, |
operand_subword (dest, 1, TRUE, mode), |
words[1])); |
} |
} |
else if (src_code == REG && dest_code == MEM) |
{ |
rtx addr = XEXP (dest, 0); |
rtx src0; |
rtx src1; |
|
gcc_assert (GET_CODE (addr) == REG); |
|
src0 = operand_subword (src, 0, TRUE, mode); |
src1 = operand_subword (src, 1, TRUE, mode); |
|
emit_insn (gen_rtx_SET (VOIDmode, adjust_address (dest, SImode, 0), |
src0)); |
|
if (REGNO (addr) == STACK_POINTER_REGNUM |
|| REGNO (addr) == FRAME_POINTER_REGNUM) |
emit_insn (gen_rtx_SET (VOIDmode, |
adjust_address (dest, SImode, UNITS_PER_WORD), |
src1)); |
else |
{ |
rtx new_mem; |
|
/* We need a scratch register to hold the value of 'address + 4'. |
We ought to allow gcc to find one for us, but for now, just |
push one of the source registers. */ |
emit_insn (gen_movsi_push (src0)); |
emit_insn (gen_movsi_internal (src0, addr)); |
emit_insn (gen_addsi_small_int (src0, src0, GEN_INT (UNITS_PER_WORD))); |
|
new_mem = gen_rtx_MEM (SImode, src0); |
MEM_COPY_ATTRIBUTES (new_mem, dest); |
|
emit_insn (gen_rtx_SET (VOIDmode, new_mem, src1)); |
emit_insn (gen_movsi_pop (src0)); |
} |
} |
else |
/* This should have been prevented by the constraints on movdi_insn. */ |
gcc_unreachable (); |
|
val = get_insns (); |
end_sequence (); |
|
return val; |
} |
/*}}}*/ |
/* Local Variables: */ |
/* folded-file: t */ |
/* End: */ |
/fr30.opt
0,0 → 1,26
; Options for the FR30 port of the compiler. |
|
; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
; |
; 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 3, 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 GCC; see the file COPYING3. If not see |
; <http://www.gnu.org/licenses/>. |
|
msmall-model |
Target Report Mask(SMALL_MODEL) |
Assume small address space |
|
mno-lsim |
Target RejectNegative Undocumented |
/t-fr30
0,0 → 1,38
LIB1ASMSRC = fr30/lib1funcs.asm |
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 |
|
# Assemble startup files. |
crti.o: $(srcdir)/config/fr30/crti.asm $(GCC_PASSES) |
$(GCC_FOR_TARGET) -c -o crti.o -x assembler $(srcdir)/config/fr30/crti.asm |
|
crtn.o: $(srcdir)/config/fr30/crtn.asm $(GCC_PASSES) |
$(GCC_FOR_TARGET) -c -o crtn.o -x assembler $(srcdir)/config/fr30/crtn.asm |
|
# 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 |
|
# If any special flags are necessary when building libgcc2 put them here. |
# |
# TARGET_LIBGCC2_CFLAGS |
|
fp-bit.c: $(srcdir)/config/fp-bit.c |
echo '#define FLOAT' > fp-bit.c |
cat $(srcdir)/config/fp-bit.c >> fp-bit.c |
|
dp-bit.c: $(srcdir)/config/fp-bit.c |
cat $(srcdir)/config/fp-bit.c > dp-bit.c |
|
# Enable the following if multilibs are needed. |
# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a |
# description of the options and their values. |
# |
# MULTILIB_OPTIONS = |
# MULTILIB_DIRNAMES = |
# MULTILIB_MATCHES = |
# MULTILIB_EXCEPTIONS = |
# MULTILIB_EXTRA_OPTS = |
# |
# LIBGCC = stmp-multilib |
# INSTALL_LIBGCC = install-multilib |