URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/gcc/gcc-3.4.4/gcc
- from Rev 1613 to Rev 1765
- ↔ Reverse comparison
Rev 1613 → Rev 1765
/config/or32/or32.h
0,0 → 1,1361
/* Definitions of target machine for GNU compiler. OpenRISC 1000 version. |
Copyright (C) 1987, 1988, 1992, 1995, 1996, 1999, 2000, 2001, 2002, |
2003, 2004, 2005 Free Software Foundation, Inc. |
Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999. |
Major optimizations by Matjaz Breskvar <matjazb@bsemi.com> in 2005. |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 1, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
#ifndef _OR32_H_ |
#define _OR32_H_ |
|
/* Target CPU builtins */ |
#define TARGET_CPU_CPP_BUILTINS() \ |
do \ |
{ \ |
builtin_define_std ("OR1K"); \ |
builtin_define_std ("or1k"); \ |
builtin_assert ("cpu=or1k"); \ |
builtin_assert ("machine=or1k"); \ |
} \ |
while (0) |
|
#if 0 |
|
/* Which library to get. The only difference from the default is to get |
libsc.a if -sim is given to the driver. Repeat -lc -lsysX |
{X=sim,linux}, because libsysX needs (at least) errno from libc, and |
then we want to resolve new unknowns in libc against libsysX, not |
libnosys. */ |
/* Override previous definitions (linux.h). */ |
#undef LIB_SPEC |
#define LIB_SPEC \ |
"%{sim*:-lc -lsyssim -lc -lsyssim}\ |
%{!sim*:%{g*:-lg}\ |
%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p} -lbsp}\ |
-lnosys" |
#endif |
|
#define TARGET_VERSION fprintf (stderr, " (OpenRISC 1000)"); |
|
/* Run-time compilation parameters selecting different hardware subsets. */ |
|
extern int target_flags; |
|
/* Enable hardware fp support. */ |
#define MASK_HARD_FLOAT 0x00000001 |
|
/* Enable hardware div instruction. */ |
#define MASK_HARD_DIV 0x00000002 |
|
/* Enable hardware mul instruction. */ |
#define MASK_HARD_MUL 0x00000004 |
|
/* Use aligned jumps. */ |
#define MASK_ALIGNED_JUMPS 0x00000008 |
|
/* Use l.extbs and l.exths instructions. */ |
#define MASK_SEXT 0x00000010 |
|
/* Use l.cmov instruction. */ |
#define MASK_CMOV 0x00000020 |
|
/* Emit prologue and epilogue. */ |
#define MASK_SCHED_LOGUE 0x00000040 |
|
/* Emit prologue and epilogue. */ |
#define MASK_ROR 0x00000080 |
|
/* Emit prologue and epilogue. */ |
#define MASK_SIBCALL 0x00000100 |
|
|
/* Macros used in the machine description to test the flags. */ |
|
#define TARGET_HARD_FLOAT (target_flags & MASK_HARD_FLOAT) |
#define TARGET_HARD_DIV (target_flags & MASK_HARD_DIV) |
#define TARGET_HARD_MUL (target_flags & MASK_HARD_MUL) |
#define TARGET_ALIGNED_JUMPS (target_flags & MASK_ALIGNED_JUMPS) |
#define TARGET_SEXT (target_flags & MASK_SEXT) |
#define TARGET_CMOV (target_flags & MASK_CMOV) |
#define TARGET_SCHED_LOGUE (target_flags & MASK_SCHED_LOGUE) |
#define TARGET_ROR (target_flags & MASK_ROR) |
#define TARGET_SIBCALL (target_flags & MASK_SIBCALL) |
|
/* Macro to define tables used to set the flags. |
This is a list in braces of pairs in braces, |
each pair being { "NAME", VALUE } |
where VALUE is the bits to set or minus the bits to clear. |
An empty string NAME is used to identify the default VALUE. */ |
|
#define TARGET_SWITCHES \ |
{ {"hard-float", MASK_HARD_FLOAT, N_("Use hardware floating point.")}, \ |
{"soft-float", - MASK_HARD_FLOAT, N_("Use software floating point.")}, \ |
{"hard-div", MASK_HARD_DIV, N_("Use hardware divison.")}, \ |
{"soft-div", - MASK_HARD_DIV, N_("Use software divison.")}, \ |
{"hard-mul", MASK_HARD_MUL, N_("Use hardware multiplication.")}, \ |
{"soft-mul", - MASK_HARD_MUL, N_("Use software multiplication.")}, \ |
{"aj", MASK_ALIGNED_JUMPS, N_("Use aligned jumps.")}, \ |
{"no-aj", - MASK_ALIGNED_JUMPS, N_("Do not use aligned jumps.")}, \ |
{"sext", MASK_SEXT, N_("Use sign extending instructions.")}, \ |
{"no-sext", - MASK_SEXT, N_("Do not use sign extending insn.")}, \ |
{"cmov", MASK_CMOV, N_("Use conditional move insn.")}, \ |
{"no-cmov", - MASK_CMOV, N_("Do not use conditional move insn.")},\ |
{"logue", MASK_SCHED_LOGUE, N_("Schedule pro/epi-logue.")}, \ |
{"no-logue", - MASK_SCHED_LOGUE, N_("Do not schedule pro/epi-logue.")}, \ |
{"ror", MASK_ROR, N_("Emit ror insns.")}, \ |
{"no-ror", - MASK_ROR, N_("Do not emit ror insns.")}, \ |
{"sibcall", MASK_SIBCALL, N_("Enable sibcall optimization.")}, \ |
{"no-sibcall", - MASK_SIBCALL, N_("Disable sibcall optimization.")}, \ |
{ "", TARGET_DEFAULT, 0}} |
|
/* Default target_flags if no switches specified. */ |
|
#ifndef TARGET_DEFAULT |
#define TARGET_DEFAULT (MASK_HARD_MUL) |
#endif |
|
#undef TARGET_ASM_NAMED_SECTION |
#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section |
|
/* Target machine storage layout */ |
|
/* Define this if most significant bit is lowest numbered |
in instructions that operate on numbered bit-fields. |
This is not true on the or1k. */ |
#define BITS_BIG_ENDIAN 0 |
|
/* Define this if most significant byte of a word is the lowest numbered. */ |
#define BYTES_BIG_ENDIAN 1 |
|
/* Define this if most significant word of a multiword number is numbered. */ |
#define WORDS_BIG_ENDIAN 1 |
|
/* Number of bits in an addressable storage unit */ |
#define BITS_PER_UNIT 8 |
|
#define BITS_PER_WORD 32 |
#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 |
|
/* Width of a word, in units (bytes). */ |
#define UNITS_PER_WORD 4 |
|
/* Width in bits of a pointer. |
See also the macro `Pmode' defined below. */ |
#define POINTER_SIZE 32 |
|
/* Allocation boundary (in *bits*) for storing pointers in memory. */ |
#define POINTER_BOUNDARY 32 |
|
/* Allocation boundary (in *bits*) for storing arguments in argument list. */ |
#define PARM_BOUNDARY 32 |
|
/* Boundary (in *bits*) on which stack pointer should be aligned. */ |
#define STACK_BOUNDARY 32 |
|
/* Allocation boundary (in *bits*) for the code of a function. */ |
#define FUNCTION_BOUNDARY 32 |
|
/* Alignment of field after `int : 0' in a structure. */ |
#define EMPTY_FIELD_BOUNDARY 8 |
|
/* Every structure's size must be a multiple of this. */ |
#define STRUCTURE_SIZE_BOUNDARY 32 |
|
/* A bitfield declared as `int' forces `int' alignment for the struct. */ |
#define PCC_BITFIELD_TYPE_MATTERS 1 |
|
/* No data type wants to be aligned rounder than this. */ |
#define BIGGEST_ALIGNMENT 32 |
|
/* The best alignment to use in cases where we have a choice. */ |
#define FASTEST_ALIGNMENT 32 |
|
/* Make strings word-aligned so strcpy from constants will be faster. */ |
/* |
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ |
((TREE_CODE (EXP) == STRING_CST || TREE_CODE (EXP) == CONSTRUCTOR) \ |
&& (ALIGN) < FASTEST_ALIGNMENT \ |
? FASTEST_ALIGNMENT : (ALIGN)) |
*/ |
|
/* One use of this macro is to increase alignment of medium-size |
data to make it all fit in fewer cache lines. Another is to |
cause character arrays to be word-aligned so that `strcpy' calls |
that copy constants to character arrays can be done inline. */ |
/* |
#define DATA_ALIGNMENT(TYPE, ALIGN) \ |
((((ALIGN) < FASTEST_ALIGNMENT) \ |
&& (TREE_CODE (TYPE) == ARRAY_TYPE \ |
|| TREE_CODE (TYPE) == UNION_TYPE \ |
|| TREE_CODE (TYPE) == RECORD_TYPE)) ? FASTEST_ALIGNMENT : (ALIGN)) |
*/ /* CHECK - btw code gets bigger with this one */ |
|
/* Define this if move instructions will actually fail to work |
when given unaligned data. */ |
#define STRICT_ALIGNMENT 1 /* CHECK */ |
|
/* Align an address */ |
#define OR32_ALIGN(n,a) (((n) + (a) - 1) & ~((a) - 1)) |
|
/* Define this macro if an argument declared as `char' or `short' in a |
prototype should actually be passed as an `int'. In addition to |
avoiding errors in certain cases of mismatch, it also makes for |
better code on certain machines. */ |
#define PROMOTE_PROTOTYPES 1 |
|
/* Define if operations between registers always perform the operation |
on the full register even if a narrower mode is specified. */ |
#define WORD_REGISTER_OPERATIONS /* CHECK */ |
|
|
/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD |
will either zero-extend or sign-extend. The value of this macro should |
be the code that says which one of the two operations is implicitly |
done, NIL if none. */ |
#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND |
|
/* Define this macro if it is advisable to hold scalars in registers |
in a wider mode than that declared by the program. In such cases, |
the value is constrained to be within the bounds of the declared |
type, but kept valid in the wider mode. The signedness of the |
extension may differ from that of the type. */ |
|
#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ |
if (GET_MODE_CLASS (MODE) == MODE_INT \ |
&& GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ |
(MODE) = SImode; |
/* CHECK */ |
|
/* Define this if function arguments should also be promoted using the above |
procedure. */ |
|
#define PROMOTE_FUNCTION_ARGS |
|
/* Likewise, if the function return value is promoted. */ |
|
#define PROMOTE_FUNCTION_RETURN |
|
/* |
* brings 0.4% improvment in static size for linux |
* |
#define PROMOTE_FOR_CALL_ONLY |
*/ |
|
/* Define this macro if it is as good or better to call a constant |
function address than to call an address kept in a register. */ |
#define NO_FUNCTION_CSE 1 /* check */ |
|
/* Define this macro if it is as good or better for a function to |
call itself with an explicit address than to call an address |
kept in a register. */ |
#define NO_RECURSIVE_FUNCTION_CSE 1 /*check*/ |
|
/* Standard register usage. */ |
|
/* Number of actual hardware registers. |
The hardware registers are assigned numbers for the compiler |
from 0 to just below FIRST_PSEUDO_REGISTER. |
All registers that the compiler knows about must be given numbers, |
even those that are not normally considered general registers. */ |
#define FIRST_PSEUDO_REGISTER 33 |
#define LAST_INT_REG (FIRST_PSEUDO_REGISTER - 1) |
|
/* 1 for registers that have pervasive standard uses |
and are not available for the register allocator. |
On the or1k, these are r1 as stack pointer and |
r2 as frame/arg pointer. */ |
#define FIXED_REGISTERS {1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, \ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} |
/* 1 for registers not available across function calls. |
These must include the FIXED_REGISTERS and also any |
registers that can be used without being saved. |
The latter must include the registers where values are returned |
and the register where structure-value addresses are passed. |
Aside from that, you can include as many other registers as you like. */ |
#define CALL_USED_REGISTERS {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, \ |
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1} |
|
/* stack pointer: must be FIXED and CALL_USED */ |
/* frame pointer: must be FIXED and CALL_USED */ |
|
/* Return number of consecutive hard regs needed starting at reg REGNO |
to hold something of mode MODE. |
This is ordinarily the length in words of a value of mode MODE |
but can be less for certain modes in special long registers. |
On the or1k, all registers are one word long. */ |
#define HARD_REGNO_NREGS(REGNO, MODE) \ |
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) |
|
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ |
#define HARD_REGNO_MODE_OK(REGNO, MODE) 1 |
|
/* Value is 1 if it is a good idea to tie two pseudo registers |
when one has mode MODE1 and one has mode MODE2. |
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, |
for any hard reg, then this must be 0 for correct output. */ |
#define MODES_TIEABLE_P(MODE1, MODE2) 1 |
|
/* A C expression returning the cost of moving data from a register of class |
CLASS1 to one of CLASS2. */ |
#define REGISTER_MOVE_COST or32_register_move_cost |
|
/* A C expressions returning the cost of moving data of MODE from a register to |
or from memory. */ |
#define MEMORY_MOVE_COST or32_memory_move_cost |
|
/* Specify the cost of a branch insn; roughly the number of extra insns that |
should be added to avoid a branch. */ |
#define BRANCH_COST or32_branch_cost() |
|
/* Specify the registers used for certain standard purposes. |
The values of these macros are register numbers. */ |
|
/* Register to use for pushing function arguments. */ |
#define STACK_POINTER_REGNUM 1 |
|
/* Base register for access to local variables of the function. */ |
#define FRAME_POINTER_REGNUM 2 |
|
/* Link register. */ |
#define LINK_REGNUM 9 |
|
/* Value should be nonzero if functions must have frame pointers. |
Zero means the frame pointer need not be set up (and parms |
may be accessed via the stack pointer) in functions that seem suitable. |
This is computed in `reload', in reload1.c. */ |
#define FRAME_POINTER_REQUIRED 0 |
|
/* De ne this macro if debugging can be performed even without a frame pointer. |
If this macro is de ned, GCC will turn on the `-fomit-frame-pointer' option |
whenever `-O' is specifed. |
*/ |
/* |
#define CAN_DEBUG_WITHOUT_FP |
*/ |
|
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \ |
{ int regno; \ |
int offset = 0; \ |
for( regno=0; regno < FIRST_PSEUDO_REGISTER; regno++ ) \ |
if( regs_ever_live[regno] && !call_used_regs[regno] ) \ |
offset += 4; \ |
(DEPTH) = (!current_function_is_leaf || regs_ever_live[LINK_REGNUM] ? 4 : 0) + \ |
(frame_pointer_needed ? 4 : 0) + \ |
offset + \ |
OR32_ALIGN(current_function_outgoing_args_size,4) + \ |
OR32_ALIGN(get_frame_size(),4); \ |
} |
|
/* Base register for access to arguments of the function. */ |
#define ARG_POINTER_REGNUM FRAME_POINTER_REGNUM |
|
/* Register in which static-chain is passed to a function. */ |
#define STATIC_CHAIN_REGNUM 0 |
|
/* Register in which address to store a structure value |
is passed to a function. */ |
/*#define STRUCT_VALUE_REGNUM 0*/ |
|
/* Pass address of result struct to callee as "invisible" first argument */ |
#define STRUCT_VALUE 0 |
|
/* -----------------------[ PHX start ]-------------------------------- */ |
|
/* Define the classes of registers for register constraints in the |
machine description. Also define ranges of constants. |
|
One of the classes must always be named ALL_REGS and include all hard regs. |
If there is more than one class, another class must be named NO_REGS |
and contain no registers. |
|
The name GENERAL_REGS must be the name of a class (or an alias for |
another name such as ALL_REGS). This is the class of registers |
that is allowed by "g" or "r" in a register constraint. |
Also, registers outside this class are allocated only when |
instructions express preferences for them. |
|
GENERAL_REGS and BASE_REGS classess are the same on or32. |
|
The classes must be numbered in nondecreasing order; that is, |
a larger-numbered class must never be contained completely |
in a smaller-numbered class. |
|
For any two classes, it is very desirable that there be another |
class that represents their union. */ |
|
/* The or1k has only one kind of registers, so NO_REGS, GENERAL_REGS |
and ALL_REGS are the only classes. */ |
|
enum reg_class |
{ |
NO_REGS, |
GENERAL_REGS, |
CR_REGS, |
ALL_REGS, |
LIM_REG_CLASSES |
}; |
|
#define N_REG_CLASSES (int) LIM_REG_CLASSES |
|
/* Give names of register classes as strings for dump file. */ |
|
#define REG_CLASS_NAMES \ |
{ \ |
"NO_REGS", \ |
"GENERAL_REGS", \ |
"ALL_REGS" \ |
} |
|
|
/* Define which registers fit in which classes. |
This is an initializer for a vector of HARD_REG_SET |
of length N_REG_CLASSES. */ |
|
/* 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 \ |
{ \ |
{ 0x00000000, 0x00000000 }, /* NO_REGS */ \ |
{ 0xffffffff, 0x00000001 }, /* GENERAL_REGS */ \ |
{ 0xffffffff, 0x00000000 } /* ALL_REGS */ \ |
} |
|
/* The same information, inverted: |
Return the class number of the smallest class containing |
reg number REGNO. This could be a conditional expression |
or could index an array. */ |
|
#define REGNO_REG_CLASS(REGNO) \ |
((REGNO) < 32 ? GENERAL_REGS \ |
: NO_REGS) |
|
/* The class value for index registers, and the one for base regs. */ |
#define INDEX_REG_CLASS GENERAL_REGS |
#define BASE_REG_CLASS GENERAL_REGS |
|
/* Get reg_class from a letter such as appears in the machine description. */ |
|
#define REG_CLASS_FROM_LETTER(C) NO_REGS |
|
#if 1 |
/* The letters I, J, K, L and M in a register constraint string |
can be used to stand for particular ranges of immediate operands. |
This macro defines what the ranges are. |
C is the letter, and VALUE is a constant value. |
Return 1 if VALUE is in the range specified by C. */ |
|
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ |
( (C) == 'I' ? ((VALUE) >=-32768 && (VALUE) <=32767) \ |
: (C) == 'J' ? ((VALUE) >=0 && (VALUE) <=0) \ |
: (C) == 'K' ? ((VALUE) >=0 && (VALUE) <=65535) \ |
: (C) == 'L' ? ((VALUE) >=0 && (VALUE) <=31) \ |
: (C) == 'M' ? (((VALUE) & 0xffff) == 0 ) \ |
: (C) == 'N' ? ((VALUE) >=-33554432 && (VALUE) <=33554431) \ |
: (C) == 'O' ? ((VALUE) >=0 && (VALUE) <=0) \ |
: 0 ) |
#else |
|
/* The letters I, J, K, L, M, N, and P in a register constraint string |
can be used to stand for particular ranges of immediate operands. |
This macro defines what the ranges are. |
C is the letter, and VALUE is a constant value. |
Return 1 if VALUE is in the range specified by C. |
|
`I' is a signed 16-bit constant |
`J' is a constant with only the high-order 16 bits nonzero |
`K' is a constant with only the low-order 16 bits nonzero |
`L' is a signed 16-bit constant shifted left 16 bits |
`M' is a constant that is greater than 31 |
`N' is a positive constant that is an exact power of two |
`O' is the constant zero |
`P' is a constant whose negation is a signed 16-bit constant */ |
|
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ |
( (C) == 'I' ? (unsigned HOST_WIDE_INT) ((VALUE) + 0x8000) < 0x10000 \ |
: (C) == 'J' ? ((VALUE) & (~ (unsigned HOST_WIDE_INT) 0xffff0000)) == 0 \ |
: (C) == 'K' ? ((VALUE) & (~ (HOST_WIDE_INT) 0xffff)) == 0 \ |
: (C) == 'L' ? (((VALUE) & 0xffff) == 0 \ |
&& ((VALUE) >> 31 == -1 || (VALUE) >> 31 == 0)) \ |
: (C) == 'M' ? (VALUE) > 31 \ |
: (C) == 'N' ? (VALUE) > 0 && exact_log2 (VALUE) >= 0 \ |
: (C) == 'O' ? (VALUE) == 0 \ |
: (C) == 'P' ? (unsigned HOST_WIDE_INT) ((- (VALUE)) + 0x8000) < 0x10000 \ |
: 0) |
#endif |
|
/* -----------------------[ PHX stop ]-------------------------------- */ |
|
/* Similar, but for floating constants, and defining letters G and H. |
Here VALUE is the CONST_DOUBLE rtx itself. */ |
|
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) 1 |
|
/* Given an rtx X being reloaded into a reg required to be |
in class CLASS, return the class of reg to actually use. |
In general this is just CLASS; but on some machines |
in some cases it is preferable to use a more restrictive class. */ |
|
#define PREFERRED_RELOAD_CLASS(X,CLASS) (CLASS) |
|
/* Define the codes that are matched by predicates in or32.c. */ |
|
#define PREDICATE_CODES \ |
{"cc_reg_operand", {SUBREG, REG}}, \ |
{"branch_comparison_operator", {EQ, NE, LE, LT, GE, \ |
GT, LEU, LTU, GEU, GTU, \ |
UNORDERED, ORDERED, \ |
UNGE, UNLE }}, \ |
{"sibcall_insn_operand", {REG, SUBREG, SYMBOL_REF}}, \ |
{"input_operand", {SUBREG, REG, CONST_INT, MEM, CONST}}, \ |
{"sym_ref_mem_operand", {MEM} }, |
|
/* Return the maximum number of consecutive registers |
needed to represent mode MODE in a register of class CLASS. */ |
/* On the or1k, this is always the size of MODE in words, |
since all registers are the same size. */ |
#define CLASS_MAX_NREGS(CLASS, MODE) \ |
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) |
|
/* Stack layout; function entry, exit and calling. */ |
|
/* Define this if pushing a word on the stack |
makes the stack pointer a smaller address. */ |
#define STACK_GROWS_DOWNWARD |
|
/* Define this if the nominal address of the stack frame |
is at the high-address end of the local variables; |
that is, each additional local variable allocated |
goes at a more negative offset in the frame. */ |
#define FRAME_GROWS_DOWNWARD |
|
/* Offset within stack frame to start allocating local variables at. |
If FRAME_GROWS_DOWNWARD, this is the offset to the END of the |
first local allocated. Otherwise, it is the offset to the BEGINNING |
of the first local allocated. */ |
#define STARTING_FRAME_OFFSET 0 |
|
/* Offset of first parameter from the argument pointer register value. */ |
#define FIRST_PARM_OFFSET(FNDECL) 0 |
|
/* Define this if stack space is still allocated for a parameter passed |
in a register. The value is the number of bytes allocated to this |
area. */ |
/* |
#define REG_PARM_STACK_SPACE(FNDECL) (UNITS_PER_WORD * GP_ARG_NUM_REG) |
*/ |
/* Define this if the above stack space is to be considered part of the |
space allocated by the caller. */ |
/* |
#define OUTGOING_REG_PARM_STACK_SPACE |
*/ |
/* Define this macro if `REG_PARM_STACK_SPACE' is defined, but the |
stack parameters don't skip the area specified by it. */ |
/* |
#define STACK_PARMS_IN_REG_PARM_AREA |
*/ |
/* Define this if the maximum size of all the outgoing args is to be |
accumulated and pushed during the prologue. The amount can be |
found in the variable current_function_outgoing_args_size. */ |
#define ACCUMULATE_OUTGOING_ARGS 1 |
|
/* Value is 1 if returning from a function call automatically |
pops the arguments described by the number-of-args field in the call. |
FUNDECL is the declaration node of the function (as a tree), |
FUNTYPE is the data type of the function (as a tree), |
or for a library call it is an identifier node for the subroutine name. |
|
On the Vax, the RET insn always pops all the args for any function. */ |
/* SIMON */ |
/*#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) (SIZE)*/ |
#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 |
|
/* Minimum and maximum general purpose registers used to hold arguments. */ |
#define GP_ARG_MIN_REG 3 |
#define GP_ARG_MAX_REG 8 |
#define GP_ARG_NUM_REG (GP_ARG_MAX_REG - GP_ARG_MIN_REG + 1) |
|
/* Return registers */ |
#define GP_ARG_RETURN 11 |
|
/* Define how to find the value returned by a function. |
VALTYPE is the data type of the value (as a tree). |
If the precise function being called is known, FUNC is its FUNCTION_DECL; |
otherwise, FUNC is 0. */ |
|
/* Return value is in R11. */ |
#define FUNCTION_VALUE(VALTYPE, FUNC) LIBCALL_VALUE (TYPE_MODE (VALTYPE)) |
|
/* Define how to find the value returned by a library function |
assuming the value has mode MODE. */ |
|
/* Return value is in R11. */ |
|
#define LIBCALL_VALUE(MODE) \ |
gen_rtx (REG, \ |
((GET_MODE_CLASS (MODE) != MODE_INT \ |
|| GET_MODE_SIZE (MODE) >= 4) \ |
? (MODE) \ |
: SImode), \ |
GP_ARG_RETURN) |
|
/* Define this if PCC uses the nonreentrant convention for returning |
structure and union values. */ |
|
/*#define PCC_STATIC_STRUCT_RETURN */ |
|
/* 1 if N is a possible register number for a function value. |
R3 to R8 are possible (set to 1 in CALL_USED_REGISTERS) */ |
|
#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_ARG_RETURN) |
|
/* 1 if N is a possible register number for function argument passing. */ |
|
#define FUNCTION_ARG_REGNO_P(N) \ |
((N) >= GP_ARG_MIN_REG && (N) <= GP_ARG_MAX_REG) |
|
/* A code distinguishing the floating point format of the target |
machine. There are three defined values: IEEE_FLOAT_FORMAT, |
VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */ |
|
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT |
#define FLOAT_WORDS_BIG_ENDIAN 1 |
|
/* Define a data type for recording info about an argument list |
during the scan of that argument list. This data type should |
hold all necessary information about the function itself |
and about the args processed so far, enough to enable macros |
such as FUNCTION_ARG to determine where the next arg should go. |
|
On the vax, this is a single integer, which is a number of bytes |
of arguments scanned so far. */ |
|
#define CUMULATIVE_ARGS int |
|
/* Initialize a variable CUM of type CUMULATIVE_ARGS |
for a call to a function whose data type is FNTYPE. |
For a library call, FNTYPE is 0. |
|
On the vax, the offset starts at 0. */ |
|
/* The regs member is an integer, the number of arguments got into |
registers so far. */ |
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ |
(CUM = 0) |
|
/* Define intermediate macro to compute the size (in registers) of an argument |
for the or1k. */ |
|
/* The ROUND_ADVANCE* macros are local to this file. */ |
/* Round SIZE up to a word boundary. */ |
#define ROUND_ADVANCE(SIZE) \ |
(((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) |
|
/* Round arg MODE/TYPE up to the next word boundary. */ |
#define ROUND_ADVANCE_ARG(MODE, TYPE) \ |
((MODE) == BLKmode \ |
? ROUND_ADVANCE (int_size_in_bytes (TYPE)) \ |
: ROUND_ADVANCE (GET_MODE_SIZE (MODE))) |
|
/* Round CUM up to the necessary point for argument MODE/TYPE. */ |
/* This is either rounded to nearest reg or nearest double-reg boundary */ |
#define ROUND_ADVANCE_CUM(CUM, MODE, TYPE) \ |
((((MODE) == BLKmode ? TYPE_ALIGN (TYPE) : GET_MODE_BITSIZE (MODE)) \ |
> BITS_PER_WORD) \ |
? (((CUM) + 1) & ~1) \ |
: (CUM)) |
|
/* A C expression that indicates when an argument must be passed by |
reference. If nonzero for an argument, a copy of that argument is |
made in memory and a pointer to the argument is passed instead of |
the argument itself. The pointer is passed in whatever way is |
appropriate for passing a pointer to that type. */ |
/* All aggregates and arguments greater than 8 bytes are passed this way. */ |
#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ |
(TYPE \ |
&& (AGGREGATE_TYPE_P (TYPE) \ |
|| int_size_in_bytes (TYPE) > 8)) |
|
/* Update the data in CUM to advance over an argument |
of mode MODE and data type TYPE. |
(TYPE is null for libcalls where that information may not be available.) */ |
|
/* Update the data in CUM to advance over an argument |
of mode MODE and data type TYPE. |
(TYPE is null for libcalls where that information may not be available.) */ |
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ |
((CUM) = (ROUND_ADVANCE_CUM ((CUM), (MODE), (TYPE)) \ |
+ ROUND_ADVANCE_ARG ((MODE), (TYPE)))) |
|
/* Return boolean indicating arg of type TYPE and mode MODE will be passed in |
a reg. This includes arguments that have to be passed by reference as the |
pointer to them is passed in a reg if one is available (and that is what |
we're given). |
When passing arguments NAMED is always 1. When receiving arguments NAMED |
is 1 for each argument except the last in a stdarg/varargs function. In |
a stdarg function we want to treat the last named arg as named. In a |
varargs function we want to treat the last named arg (which is |
`__builtin_va_alist') as unnamed. |
This macro is only used in this file. */ |
#define PASS_IN_REG_P(CUM, MODE, TYPE, NAMED) \ |
((NAMED) \ |
&& ((ROUND_ADVANCE_CUM ((CUM), (MODE), (TYPE)) \ |
+ ROUND_ADVANCE_ARG ((MODE), (TYPE)) \ |
<= GP_ARG_NUM_REG))) |
|
/* Determine where to put an argument to a function. |
Value is zero to push the argument on the stack, |
or a hard register in which to store the argument. |
|
MODE is the argument's machine mode. |
TYPE is the data type of the argument (as a tree). |
This is null for libcalls where that information may |
not be available. |
CUM is a variable of type CUMULATIVE_ARGS which gives info about |
the preceding args and about the function being called. |
NAMED is nonzero if this argument is a named parameter |
(otherwise it is an extra parameter matching an ellipsis). */ |
/* On the ARC the first MAX_ARC_PARM_REGS args are normally in registers |
and the rest are pushed. */ |
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ |
(PASS_IN_REG_P ((CUM), (MODE), (TYPE), (NAMED)) \ |
? gen_rtx_REG ((MODE), ROUND_ADVANCE_CUM ((CUM), (MODE), (TYPE)) + GP_ARG_MIN_REG) \ |
: 0) |
|
/* For an arg passed partly in registers and partly in memory, |
this is the number of registers used. |
For args passed entirely in registers or entirely in memory, zero. */ |
|
#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 |
|
/* Output assembler code to FILE to increment profiler label # LABELNO |
for profiling a function entry. */ |
|
#define FUNCTION_PROFILER(FILE, LABELNO) \ |
fprintf (FILE, "\tl.load32u\tr0,LP%d\n\tcall\tmcount\n", (LABELNO)); |
|
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, |
the stack pointer does not matter. The value is tested only in |
functions that have frame pointers. |
No definition is equivalent to always zero. */ |
|
#define EXIT_IGNORE_STACK 0 |
|
/* If the memory address ADDR is relative to the frame pointer, |
correct it to be relative to the stack pointer instead. |
This is for when we don't use a frame pointer. |
ADDR should be a variable name. */ |
|
#define FIX_FRAME_POINTER_ADDRESS(ADDR,DEPTH) \ |
{ int offset = -1; \ |
rtx regs = stack_pointer_rtx; \ |
if (ADDR == frame_pointer_rtx) \ |
offset = 0; \ |
else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 1) == frame_pointer_rtx \ |
&& GET_CODE (XEXP (ADDR, 0)) == CONST_INT) \ |
offset = INTVAL (XEXP (ADDR, 0)); \ |
else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 0) == frame_pointer_rtx \ |
&& GET_CODE (XEXP (ADDR, 1)) == CONST_INT) \ |
offset = INTVAL (XEXP (ADDR, 1)); \ |
else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 0) == frame_pointer_rtx) \ |
{ rtx other_reg = XEXP (ADDR, 1); \ |
offset = 0; \ |
regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ |
else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 1) == frame_pointer_rtx) \ |
{ rtx other_reg = XEXP (ADDR, 0); \ |
offset = 0; \ |
regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ |
if (offset >= 0) \ |
{ int regno; \ |
extern char call_used_regs[]; \ |
offset += 4; /* I don't know why??? */ \ |
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \ |
if (regs_ever_live[regno] && ! call_used_regs[regno]) \ |
offset += 4; \ |
ADDR = plus_constant (regs, offset + (DEPTH)); } } |
|
|
/* Addressing modes, and classification of registers for them. */ |
|
/* #define HAVE_POST_INCREMENT */ |
/* #define HAVE_POST_DECREMENT */ |
|
/* #define HAVE_PRE_DECREMENT */ |
/* #define HAVE_PRE_INCREMENT */ |
|
/* Macros to check register numbers against specific register classes. */ |
|
#define MAX_REGS_PER_ADDRESS 1 |
|
/* True if X is an rtx for a constant that is a valid address. */ |
#define CONSTANT_ADDRESS_P(X) \ |
(GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ |
|| GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \ |
|| GET_CODE (X) == HIGH) |
|
#define REGNO_OK_FOR_BASE_P(REGNO) \ |
((REGNO) < FIRST_PSEUDO_REGISTER ? ((REGNO) > 0 && (REGNO) <= LAST_INT_REG) \ |
: (reg_renumber[REGNO] > 0 && (reg_renumber[REGNO] <= LAST_INT_REG ))) |
|
#ifdef REG_OK_STRICT |
/* Strict version, used in reload pass. This should not |
* accept pseudo registers. |
*/ |
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P(REGNO(X)) |
#else |
/* Accept an int register or a pseudo reg. */ |
#define REG_OK_FOR_BASE_P(X) (REGNO(X) <= LAST_INT_REG || \ |
REGNO(X) >= FIRST_PSEUDO_REGISTER) |
#endif |
|
/* |
* OR1K doesn't have any indexed addressing. |
*/ |
#define REG_OK_FOR_INDEX_P(X) 0 |
#define REGNO_OK_FOR_INDEX_P(X) 0 |
|
#define LEGITIMATE_ADDRESS_INTEGER_P(X,OFFSET) \ |
(GET_CODE (X) == CONST_INT && SMALL_INT(X)) |
|
#define LEGITIMATE_OFFSET_ADDRESS_P(MODE,X) \ |
(GET_CODE (X) == PLUS \ |
&& GET_CODE (XEXP (X, 0)) == REG \ |
&& REG_OK_FOR_BASE_P (XEXP (X, 0)) \ |
&& LEGITIMATE_ADDRESS_INTEGER_P (XEXP (X, 1), 0) \ |
&& (((MODE) != DFmode && (MODE) != DImode) \ |
|| LEGITIMATE_ADDRESS_INTEGER_P (XEXP (X, 1), 4))) |
|
#define LEGITIMATE_NONOFFSET_ADDRESS_P(MODE,X) \ |
(GET_CODE(X) == REG && REG_OK_FOR_BASE_P(X)) |
/* |
* OR1K only has one addressing mode: |
* register + 16 bit signed offset. |
*/ |
#define GO_IF_LEGITIMATE_ADDRESS(MODE,X,ADDR) \ |
if(LEGITIMATE_OFFSET_ADDRESS_P(MODE,X)) goto ADDR; \ |
if(LEGITIMATE_NONOFFSET_ADDRESS_P(MODE,X)) goto ADDR; |
|
/* |
if(GET_CODE(X) == SYMBOL_REF) goto ADDR; */ /* If used, smaller code */ |
|
/* Alternative */ |
#if 0 |
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ |
{ \ |
if (GET_CODE (X) == REG) goto ADDR; \ |
if (GET_CODE (X) == SYMBOL_REF) goto ADDR; \ |
if (CONSTANT_ADDRESS_P (X)) goto ADDR; \ |
if (GET_CODE (X) == PLUS) \ |
{ \ |
/* Handle [index]<address> represented with index-sum outermost */\ |
if (GET_CODE (XEXP (X, 0)) == REG \ |
&& REG_OK_FOR_BASE_P (XEXP (X, 0)) \ |
&& GET_CODE (XEXP (X, 1)) == CONST_INT) \ |
goto ADDR; \ |
if (GET_CODE (XEXP (X, 1)) == REG \ |
&& REG_OK_FOR_BASE_P (XEXP (X, 0)) \ |
&& GET_CODE (XEXP (X, 0)) == CONST_INT) \ |
goto ADDR; \ |
} \ |
} |
#endif |
/* |
* We have to force symbol_ref's into registers here |
* because nobody else seems to want to do that! |
*/ |
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) {} |
/* |
{ if (GET_CODE (x) == SYMBOL_REF) \ |
(X) = copy_to_reg (X); \ |
if (memory_address_p (MODE, X)) \ |
goto WIN; \ |
} |
*/ |
|
/* |
* OR1K addresses do not depend on the machine mode they are |
* being used in. |
*/ |
#define GO_IF_MODE_DEPENDENT_ADDRESS(addr,label) |
|
/* OR1K has 16 bit immediates. |
*/ |
#define SMALL_INT(X) (INTVAL(X) >= -32768 && INTVAL(X) <= 32767) |
|
#define LEGITIMATE_CONSTANT_P(x) (GET_CODE(x) != CONST_DOUBLE) |
|
/* Specify the machine mode that this machine uses |
for the index in the tablejump instruction. */ |
#define CASE_VECTOR_MODE SImode |
|
/* Define as C expression which evaluates to nonzero if the tablejump |
instruction expects the table to contain offsets from the address of the |
table. |
Do not define this if the table should contain absolute addresses. */ |
/* #define CASE_VECTOR_PC_RELATIVE 1 */ |
|
/* Define this as 1 if `char' should by default be signed; else as 0. */ |
#define DEFAULT_SIGNED_CHAR 1 |
|
/* This flag, if defined, says the same insns that convert to a signed fixnum |
also convert validly to an unsigned one. */ |
#define FIXUNS_TRUNC_LIKE_FIX_TRUNC |
|
/* Max number of bytes we can move from memory to memory |
in one reasonably fast instruction. */ |
#define MOVE_MAX 4 |
|
/* Define this if zero-extension is slow (more than one real instruction). */ |
/* #define SLOW_ZERO_EXTEND */ |
|
/* Nonzero if access to memory by bytes is slow and undesirable. |
For RISC chips, it means that access to memory by bytes is no |
better than access by words when possible, so grab a whole word |
and maybe make use of that. */ |
#define SLOW_BYTE_ACCESS 1 |
|
/* Define if shifts truncate the shift count |
which implies one can omit a sign-extension or zero-extension |
of a shift count. */ |
/* #define SHIFT_COUNT_TRUNCATED */ |
|
/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits |
is done just by pretending it is already truncated. */ |
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 |
|
/* Specify the machine mode that pointers have. |
After generation of rtl, the compiler makes no further distinction |
between pointers and any other objects of this machine mode. */ |
#define Pmode SImode |
|
/* A function address in a call instruction |
is a byte address (for indexing purposes) |
so give the MEM rtx a byte's mode. */ |
#define FUNCTION_MODE SImode |
|
/* Compute the cost of computing a constant rtl expression RTX |
whose rtx-code is CODE. The body of this macro is a portion |
of a switch statement. If the code is computed here, |
return it with a return statement. Otherwise, break from the switch. */ |
#if 0 |
__PHX__ cleanup |
#define CONST_COSTS(RTX,CODE,OUTER_CODE) \ |
case CONST_INT: \ |
/* Constant zero is super cheap due to clr instruction. */ \ |
if (RTX == const0_rtx) return 0; \ |
if ((unsigned) INTVAL (RTX) < 077) return 1; \ |
case CONST: \ |
case LABEL_REF: \ |
case SYMBOL_REF: \ |
return 3; \ |
case CONST_DOUBLE: \ |
return 5; |
#endif |
|
|
/* Given a comparison code (EQ, NE, etc.) and the first operand of a |
COMPARE, return the mode to be used for the comparison. |
*/ |
|
#define SELECT_CC_MODE(OP, X, Y) or32_cc_mode ((OP), (X), (Y)) |
|
/* Can the condition code MODE be safely reversed? This is safe in |
all cases on this port, because at present it doesn't use the |
trapping FP comparisons (fcmpo). */ |
#define REVERSIBLE_CC_MODE(MODE) 1 |
|
/* Given a condition code and a mode, return the inverse condition. */ |
#define REVERSE_CONDITION(CODE, MODE) or32_reverse_condition (MODE, CODE) |
|
|
/* Control the assembler format that we output. */ |
|
/* 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 "#" |
|
/* Output at beginning of assembler file. */ |
/* |
__PHX__ clenup |
#ifndef ASM_FILE_START |
#define ASM_FILE_START(FILE) do {\ |
fprintf (FILE, "%s file %s\n", ASM_COMMENT_START, main_input_filename);\ |
fprintf (FILE, ".file\t"); \ |
output_quoted_string (FILE, main_input_filename);\ |
fputc ('\n', FILE);} while (0) |
#endif |
*/ |
/* Output to assembler file text saying following lines |
may contain character constants, extra white space, comments, etc. */ |
|
#define ASM_APP_ON "" |
|
/* Output to assembler file text saying following lines |
no longer contain unusual constructs. */ |
|
#define ASM_APP_OFF "" |
|
/* Switch to the text or data segment. */ |
|
/* Output before read-only data. */ |
#define TEXT_SECTION_ASM_OP ".section .text" |
|
/* Output before writable data. */ |
#define DATA_SECTION_ASM_OP ".section .data" |
|
/* Output before uninitialized data. */ |
#define BSS_SECTION_ASM_OP ".section .bss" |
|
/* How to refer to registers in assembler output. |
This sequence is indexed by compiler's hard-register-number (see above). */ |
|
#define REGISTER_NAMES \ |
{"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" \ |
, "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "cc-flag"} |
|
|
/* Define this to be the delimiter between SDB sub-sections. The default |
is ";". */ |
#define SDB_DELIM "\n" |
|
/* Do not break .stabs pseudos into continuations. */ |
#define DBX_CONTIN_LENGTH 0 |
|
/* Don't try to use the type-cross-reference character in DBX data. |
Also has the consequence of putting each struct, union or enum |
into a separate .stabs, containing only cross-refs to the others. */ |
#define DBX_NO_XREFS |
|
/* How to renumber registers for dbx and gdb. |
Vax needs no change in the numeration. */ |
|
#define DBX_REGISTER_NUMBER(REGNO) (REGNO) |
|
/* This is the char to use for continuation (in case we need to turn |
continuation back on). */ |
|
#define DBX_CONTIN_CHAR '?' |
|
|
|
|
|
/* Node: Label Output */ |
|
/* Globalizing directive for a label. */ |
#define GLOBAL_ASM_OP "\t.global " |
|
#define SUPPORTS_WEAK 1 |
|
/* This is how to output the definition of a user-level label named NAME, |
such as the label on a static function or variable NAME. */ |
|
#define ASM_OUTPUT_LABEL(FILE,NAME) \ |
{ assemble_name (FILE, NAME); fputs (":\n", FILE); } |
#if 0 |
/* This is how to output a command to make the user-level label named NAME |
defined for reference from other files. */ |
/* |
__PHX__ CLEANUP |
#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ |
{ fputs ("\t.global ", FILE); assemble_name (FILE, NAME); fputs ("\n", FILE); } |
*/ |
|
/* SIMON */ |
/*#define ASM_OUTPUT_LABELREF(stream,name) \ |
{ fputc('_',stream); fputs(name,stream); } |
*/ |
#define ASM_OUTPUT_LABELREF(stream,name) \ |
{if(name[0] == '*') \ |
fputs(name,stream); \ |
else { \ |
fputc('_',stream); fputs(name,stream); \ |
}} |
#endif |
|
/* The prefix to add to user-visible assembler symbols. */ |
|
/* Remove any previous definition (elfos.h). */ |
/* We use -fno-leading-underscore to remove it, when necessary. */ |
#undef USER_LABEL_PREFIX |
#define USER_LABEL_PREFIX "_" |
|
/* Remove any previous definition (elfos.h). */ |
#ifndef ASM_GENERATE_INTERNAL_LABEL |
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \ |
sprintf (LABEL, "*%s%d", PREFIX, NUM) |
#endif |
|
/* This is how to output an assembler line defining an `int' constant. */ |
|
#define ASM_OUTPUT_INT(FILE,VALUE) \ |
( \ |
fprintf (FILE, "\t.word "), \ |
output_addr_const (FILE, (VALUE)), \ |
fprintf (FILE, "\n")) |
|
#define ASM_OUTPUT_FLOAT(stream,value) \ |
{ long l; \ |
REAL_VALUE_TO_TARGET_SINGLE(value,l); \ |
fprintf(stream,"\t.word 0x%08x\t\n# float %26.7e\n",l,value); } |
|
#define ASM_OUTPUT_DOUBLE(stream,value) \ |
{ long l[2]; \ |
REAL_VALUE_TO_TARGET_DOUBLE(value,&l[0]); \ |
fprintf(stream,"\t.word 0x%08x,0x%08x\t\n# float %26.16le\n", \ |
l[0],l[1],value); } |
|
#define ASM_OUTPUT_LONG_DOUBLE(stream,value) \ |
{ long l[4]; \ |
REAL_VALUE_TO_TARGET_DOUBLE(value,&l[0]); \ |
fprintf(stream,"\t.word 0x%08x,0x%08x,0x%08x,0x%08x\t\n# float %26.18lle\n", \ |
l[0],l[1],l[2],l[3],value); } |
|
/* Likewise for `char' and `short' constants. */ |
|
#define ASM_OUTPUT_SHORT(FILE,VALUE) \ |
( fprintf (FILE, "\t.half "), \ |
output_addr_const (FILE, (VALUE)), \ |
fprintf (FILE, "\n")) |
|
#define ASM_OUTPUT_CHAR(FILE,VALUE) \ |
( fprintf (FILE, "\t.byte "), \ |
output_addr_const (FILE, (VALUE)), \ |
fprintf (FILE, "\n")) |
|
/* This is how to output an assembler line for a numeric constant byte. */ |
|
#define ASM_OUTPUT_BYTE(FILE,VALUE) \ |
fprintf (FILE, "\t.byte 0x%02x\n", (VALUE)) |
|
/* This is how to output an insn to push a register on the stack. |
It need not be very fast code. */ |
|
#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \ |
fprintf (FILE, "\tl.sub \tr1,4\n\tl.sw \t0(r1),%s\n", reg_names[REGNO]) |
|
/* This is how to output an insn to pop a register from the stack. |
It need not be very fast code. */ |
|
#define ASM_OUTPUT_REG_POP(FILE,REGNO) \ |
fprintf (FILE, "\tl.lwz \t%s,0(r1)\n\tl.addi \tr1,4\n", reg_names[REGNO]) |
|
/* This is how to output an element of a case-vector that is absolute. |
(The Vax does not use such vectors, |
but we must define this macro anyway.) */ |
|
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ |
fprintf (FILE, "\t.word .L%d\n", VALUE) |
|
/* This is how to output an element of a case-vector that is relative. */ |
|
#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ |
fprintf (FILE, "\t.word .L%d-.L%d\n", VALUE, REL) |
|
/* This is how to output an assembler line |
that says to advance the location counter |
to a multiple of 2**LOG bytes. */ |
|
#define ASM_OUTPUT_ALIGN(FILE,LOG) \ |
if ((LOG) != 0) fprintf (FILE, "\t.align %d\n", 1 << (LOG)) |
|
/* This is how to output an assembler line |
that says to advance the location counter by SIZE bytes. */ |
|
#ifndef ASM_OUTPUT_SKIP |
#define ASM_OUTPUT_SKIP(FILE,SIZE) \ |
fprintf (FILE, "\t.space %d\n", (SIZE)) |
#endif |
|
/* Need to split up .ascii directives to avoid breaking |
the linker. */ |
|
/* This is how to output a string. */ |
#ifndef ASM_OUTPUT_ASCII |
#define ASM_OUTPUT_ASCII(STREAM, STRING, LEN) \ |
do { \ |
register int i, c, len = (LEN), cur_pos = 17; \ |
register unsigned char *string = (unsigned char *)(STRING); \ |
fprintf ((STREAM), "\t.ascii\t\""); \ |
for (i = 0; i < len; i++) \ |
{ \ |
register int c = string[i]; \ |
\ |
switch (c) \ |
{ \ |
case '\"': \ |
case '\\': \ |
putc ('\\', (STREAM)); \ |
putc (c, (STREAM)); \ |
cur_pos += 2; \ |
break; \ |
\ |
case TARGET_NEWLINE: \ |
fputs ("\\n", (STREAM)); \ |
if (i+1 < len \ |
&& (((c = string[i+1]) >= '\040' && c <= '~') \ |
|| c == TARGET_TAB)) \ |
cur_pos = 32767; /* break right here */ \ |
else \ |
cur_pos += 2; \ |
break; \ |
\ |
case TARGET_TAB: \ |
fputs ("\\t", (STREAM)); \ |
cur_pos += 2; \ |
break; \ |
\ |
case TARGET_FF: \ |
fputs ("\\f", (STREAM)); \ |
cur_pos += 2; \ |
break; \ |
\ |
case TARGET_BS: \ |
fputs ("\\b", (STREAM)); \ |
cur_pos += 2; \ |
break; \ |
\ |
case TARGET_CR: \ |
fputs ("\\r", (STREAM)); \ |
cur_pos += 2; \ |
break; \ |
\ |
default: \ |
if (c >= ' ' && c < 0177) \ |
{ \ |
putc (c, (STREAM)); \ |
cur_pos++; \ |
} \ |
else \ |
{ \ |
fprintf ((STREAM), "\\%03o", c); \ |
cur_pos += 4; \ |
} \ |
} \ |
\ |
if (cur_pos > 72 && i+1 < len) \ |
{ \ |
cur_pos = 17; \ |
fprintf ((STREAM), "\"\n\t.ascii\t\""); \ |
} \ |
} \ |
fprintf ((STREAM), "\"\n"); \ |
} while (0) |
#endif /* ASM_OUTPUT_ASCII */ |
|
/* Invoked just before function output. */ |
|
#define ASM_OUTPUT_FUNCTION_PREFIX(stream, fnname) \ |
fputs(".proc ",stream); assemble_name(stream,fnname); \ |
fputs("\n",stream); |
|
/* This says how to output an assembler line |
to define a global common symbol. */ |
|
#define ASM_OUTPUT_COMMON(stream,name,size,rounded) \ |
{ data_section(); \ |
fputs(".global\t",stream); assemble_name(stream,name); \ |
fputs("\n",stream); assemble_name(stream,name); \ |
fprintf(stream,":\n\t.space %d\n",rounded); } |
|
/* This says how to output an assembler line |
to define a local common symbol. */ |
|
#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ |
( fputs (".bss ", (FILE)), \ |
assemble_name ((FILE), (NAME)), \ |
fprintf ((FILE), ",%d,%d\n", (SIZE),(ROUNDED))) |
|
/* This says how to output an assembler line to define a global common symbol |
with size SIZE (in bytes) and alignment ALIGN (in bits). */ |
#ifndef ASM_OUTPUT_ALIGNED_COMMON |
#define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \ |
{ data_section(); \ |
if ((ALIGN) > 8) \ |
fprintf(FILE, "\t.align %d\n", ((ALIGN) / BITS_PER_UNIT)); \ |
fputs(".global\t", FILE); assemble_name(FILE, NAME); \ |
fputs("\n", FILE); \ |
assemble_name(FILE, NAME); \ |
fprintf(FILE, ":\n\t.space %d\n", SIZE); \ |
} |
#endif /* ASM_OUTPUT_ALIGNED_COMMON */ |
|
/* This says how to output an assembler line to define a local common symbol |
with size SIZE (in bytes) and alignment ALIGN (in bits). */ |
|
#ifndef ASM_OUTPUT_ALIGNED_LOCAL |
#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \ |
{ data_section(); \ |
if ((ALIGN) > 8) \ |
fprintf(FILE, "\t.align %d\n", ((ALIGN) / BITS_PER_UNIT)); \ |
assemble_name(FILE, NAME); \ |
fprintf(FILE, ":\n\t.space %d\n", SIZE); \ |
} |
#endif /* ASM_OUTPUT_ALIGNED_LOCAL */ |
|
/* Store in OUTPUT a string (made with alloca) containing |
an assembler-name for a local static variable named NAME. |
LABELNO is an integer which is different for each call. */ |
|
#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ |
( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ |
sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) |
|
/* Macro for %code validation. Returns nonzero if valid. */ |
#define PRINT_OPERAND_PUNCT_VALID_P(code) print_operand_punct_valid_p(code) |
|
/* Print an instruction operand X on file FILE. |
CODE is the code from the %-spec that requested printing this operand; |
if `%z3' was used to print operand 3, then CODE is 'z'. */ |
|
#define PRINT_OPERAND(FILE, X, CODE) print_operand(FILE, X, CODE) |
|
/* Print a memory operand whose address is X, on file FILE. |
This uses a function in output-vax.c. */ |
|
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) |
|
/* These are stubs, and have yet to bee written. */ |
|
#define TRAMPOLINE_SIZE 26 |
#define TRAMPOLINE_TEMPLATE(FILE) |
#define INITIALIZE_TRAMPOLINE(TRAMP,FNADDR,CXT) |
|
extern GTY(()) rtx or32_compare_op0; |
extern GTY(()) rtx or32_compare_op1; |
|
/* We don't use libg.a */ |
#undef LIB_SPEC |
#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}" |
#endif /* _OR32_H_ */ |
/config/or32/or32-protos.h
0,0 → 1,37
/* Definitions of target machine for GNU compiler, Argonaut ARC cpu. |
Copyright (C) 2000 Free Software Foundation, Inc. |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
#ifdef RTX_CODE |
#ifdef TREE_CODE |
#endif /* TREE_CODE */ |
|
extern void print_operand PARAMS ((FILE *, rtx, int)); |
extern void print_operand_address PARAMS ((FILE *, register rtx)); |
extern const char *or1k_output_move_double PARAMS ((rtx *operands)); |
|
extern rtx or1k_cmp_op[]; |
|
#endif /* RTX_CODE */ |
|
#ifdef TREE_CODE |
|
#endif /* TREE_CODE */ |
|
extern int print_operand_punct_valid_p PARAMS ((int)); |
/config/or32/rtems.h
0,0 → 1,37
/* Definitions for rtems targeting an OpenRisc OR32 using COFF |
Copyright (C) 1996, 1997 Free Software Foundation, Inc. |
Contributed by Joel Sherrill (joel@OARcorp.com). |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
/* Specify predefined symbols in preprocessor. */ |
|
#undef CPP_PREDEFINES |
#define CPP_PREDEFINES "-DOR1K -Dor1k -D__or1k__ -D__OR1K__ \ |
-Drtems -D__rtems__ -Asystem(rtems) -Acpu(or1k) -Amachine(or1k)" |
|
/* Generate calls to memcpy, memcmp and memset. */ |
#ifndef TARGET_MEM_FUNCTIONS |
#define TARGET_MEM_FUNCTIONS |
#endif |
|
/* We use COFF, so we need SDB format. */ |
#define SDB_DEBUGGING_INFO |
|
/* Get machine-independent configuration parameters for RTEMS. */ |
#include <rtems.h> |
/config/or32/or32.md
0,0 → 1,1281
;; Machine description for GNU compiler, OpenRISC 1000 family, OR32 ISA |
;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. |
;; Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999. |
;; Major optimizations by Matjaz Breskvar <matjazb@bsemi.com> in 2005. |
|
;; This file is part of GNU CC. |
|
;; GNU CC is free software; you can redistribute it and/or modify |
;; it under the terms of the GNU General Public License as published by |
;; the Free Software Foundation; either version 1, or (at your option) |
;; any later version. |
|
;; GNU CC is distributed in the hope that it will be useful, |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
;; GNU General Public License for more details. |
|
;; You should have received a copy of the GNU General Public License |
;; along with GNU CC; see the file COPYING. If not, write to |
;; the Free Software Foundation, 59 Temple Place - Suite 330, |
;; Boston, MA 02111-1307, USA. |
|
(define_attr "type" |
"unknown,load,store,move,extend,logic,add,mul,shift,compare,branch,jump,fp" |
(const_string "unknown")) |
|
;; Number of instructions |
(define_attr "length" "" (const_int 1)) |
|
(define_delay (eq_attr "type" "branch,jump") |
[(and (eq_attr "type" "!branch,jump") |
(eq_attr "length" "1")) (nil) (nil)]) |
|
;; (define_function_unit NAME MULTIPLICITY SIMULTANEITY |
;; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST]) |
;; MULTIPLICITY - Number of functional units of this type |
;; SIMULTANEITY - Zero for pipelined functional unit |
;; READY-DELAY - Number of cycles before result is available |
;; ISSUE-DELAY - Number of cycles before unit can accept new instruction |
;; |
(define_function_unit "bit_unit" 1 0 (eq_attr "type" "shift") 3 1) |
(define_function_unit "lsu" 1 0 (eq_attr "type" "load") 3 3) |
(define_function_unit "lsu" 1 0 (eq_attr "type" "store") 2 1) |
(define_function_unit "alu" 1 0 (eq_attr "type" "add,logic,extend,move,compare") 2 1) |
(define_function_unit "mul_unit" 1 0 (eq_attr "type" "mul") 16 16) |
|
|
;; 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" |
[(use (const_int 1))] |
"TARGET_SCHED_LOGUE" |
{ |
or32_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" |
[(use (const_int 2))] |
"TARGET_SCHED_LOGUE" |
{ |
or32_expand_epilogue (false); |
DONE; |
}) |
|
(define_expand "sibcall_epilogue" |
[(use (const_int 2))] |
"TARGET_SCHED_LOGUE" |
{ |
or32_expand_epilogue (true); |
DONE; |
}) |
|
(define_insn "return_internal" |
[(return) |
(use (match_operand 0 "pmode_register_operand" ""))] |
"TARGET_SCHED_LOGUE" |
"l.jr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; |
;; Sibcalls |
;; |
|
(define_expand "sibcall" |
[(parallel [(call (match_operand 0 "" "") |
(match_operand 1 "" "")) |
(use (match_operand 2 "" "")) ;; next_arg_reg |
(use (match_operand 3 "" ""))])] ;; struct_value_size_rtx |
"TARGET_SIBCALL" |
" |
{ |
or32_expand_sibcall (0, XEXP (operands[0], 0), operands[1]); |
DONE; |
}") |
|
(define_expand "sibcall_value" |
[(set (match_operand 0 "" "") |
(call (match_operand:SI 1 "" "") |
(match_operand 2 "" "")))] |
"TARGET_SIBCALL" |
" |
{ |
or32_expand_sibcall (operands[0], XEXP (operands[1], 0), operands[2]); |
DONE; |
}") |
|
(define_insn "sibcall_internal" |
[(call (mem:SI (match_operand:SI 0 "sibcall_insn_operand" "s,r")) |
(match_operand 1 "" "")) |
(use (reg:SI 9))] |
"TARGET_SIBCALL" |
"@ |
l.j \t%S0%(\t # sibcall s |
l.jr \t%0%(\t # sibcall r" |
[(set_attr "type" "jump,jump")]) |
|
|
|
;; |
;; movQI |
;; |
|
(define_expand "movqi" |
[(set (match_operand:QI 0 "general_operand" "") |
(match_operand:QI 1 "general_operand" ""))] |
"" |
" |
if (!no_new_pseudos) |
{ |
if (GET_CODE (operands[1]) == CONST_INT) |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_movsi (reg, operands[1])); |
operands[1] = gen_lowpart (QImode, reg); |
} |
if (GET_CODE (operands[1]) == MEM && optimize > 0) |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_rtx_SET (SImode, reg, |
gen_rtx_ZERO_EXTEND (SImode, |
operands[1]))); |
|
operands[1] = gen_lowpart (QImode, reg); |
} |
if (GET_CODE (operands[0]) != REG) |
operands[1] = force_reg (QImode, operands[1]); |
} |
") |
|
(define_insn "*movqi_internal" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=m,r,r,r,r") |
(match_operand:QI 1 "general_operand" "r,r,I,K,m"))] |
"" |
"@ |
l.sb \t%0,%1\t # movqi |
l.ori \t%0,%1,0\t # movqi: move reg to reg |
l.addi \t%0,r0,%1\t # movqi: move immediate |
l.ori \t%0,r0,%1\t # movqi: move immediate |
l.lbz \t%0,%1\t # movqi" |
[(set_attr "type" "store,add,add,logic,load")]) |
|
|
;; |
;; movHI |
;; |
|
(define_expand "movhi" |
[(set (match_operand:HI 0 "general_operand" "") |
(match_operand:HI 1 "general_operand" ""))] |
"" |
" |
if (!no_new_pseudos) |
{ |
if (GET_CODE (operands[1]) == CONST_INT) |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_movsi (reg, operands[1])); |
operands[1] = gen_lowpart (HImode, reg); |
} |
if (GET_CODE (operands[1]) == MEM && optimize > 0) |
{ |
rtx reg = gen_reg_rtx (SImode); |
|
emit_insn (gen_rtx_SET (SImode, reg, |
gen_rtx_ZERO_EXTEND (SImode, |
operands[1]))); |
operands[1] = gen_lowpart (HImode, reg); |
} |
if (GET_CODE (operands[0]) != REG) |
operands[1] = force_reg (HImode, operands[1]); |
} |
") |
|
(define_insn "*movhi_internal" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=m,r,r,r,r") |
(match_operand:HI 1 "general_operand" "r,r,I,K,m"))] |
"" |
"@ |
l.sh \t%0,%1\t # movhi |
l.ori \t%0,%1,0\t # movhi: move reg to reg |
l.addi \t%0,r0,%1\t # movhi: move immediate |
l.ori \t%0,r0,%1\t # movhi: move immediate |
l.lhz \t%0,%1\t # movhi" |
[(set_attr "type" "store,add,add,logic,load")]) |
|
(define_expand "movsi" |
[(set (match_operand:SI 0 "general_operand" "") |
(match_operand:SI 1 "general_operand" ""))] |
"" |
{ |
/* Working with CONST_INTs is easier, so convert |
a double if needed. */ |
|
if (GET_CODE (operands[1]) == CONST_DOUBLE) { |
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); |
} |
|
/* Handle sets of MEM first. */ |
if (GET_CODE (operands[0]) == MEM) |
{ |
if (register_operand(operands[1], SImode) |
|| (operands[1] == const0_rtx)) |
goto movsi_is_ok; |
|
if (! reload_in_progress) |
{ |
operands[0] = validize_mem (operands[0]); |
operands[1] = force_reg (SImode, operands[1]); |
} |
} |
|
/* This makes sure we will not get rematched due to splittage. */ |
if (! CONSTANT_P (operands[1]) || input_operand (operands[1], SImode)) |
; |
else if (CONSTANT_P (operands[1]) |
&& GET_CODE (operands[1]) != HIGH |
&& GET_CODE (operands[1]) != LO_SUM) |
{ |
or32_emit_set_const32 (operands[0], operands[1]); |
DONE; |
} |
movsi_is_ok: |
; |
}) |
|
;; |
;; movSI |
;; |
|
(define_insn "*movsi_insn" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,r,m") |
(match_operand:SI 1 "input_operand" "I,K,M,r,m,r"))] |
"(register_operand (operands[0], SImode) |
|| register_operand (operands[1], SImode) |
|| (operands[1] == const0_rtx))" |
"@ |
l.addi \t%0,r0,%1\t # move immediate I |
l.ori \t%0,r0,%1\t # move immediate K |
l.movhi \t%0,hi(%1)\t # move immediate M |
l.ori \t%0,%1,0\t # move reg to reg |
l.lwz \t%0,%1\t # SI load |
l.sw \t%0,%1\t # SI store" |
[(set_attr "type" "add,load,store,add,logic,move") |
(set_attr "length" "1,1,1,1,1,1")]) |
|
(define_insn "*movsi_lo_sum" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(lo_sum:SI (match_operand:SI 1 "register_operand" "r") |
(match_operand:SI 2 "immediate_operand" "i")))] |
"" |
"l.ori \t%0,%1,lo(%2)" |
[(set_attr "type" "logic") |
(set_attr "length" "1")]) |
|
(define_insn "*movsi_high" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(high:SI (match_operand:SI 1 "immediate_operand" "i")))] |
"" |
"l.movhi \t%0,hi(%1)" |
[(set_attr "type" "move") |
(set_attr "length" "1")]) |
|
(define_insn "movsi_insn_big" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=r") |
(match_operand:SI 1 "immediate_operand" "i"))] |
"GET_CODE(operands[1]) != CONST_INT" |
"l.movhi \t%0,hi(%1)\;l.ori \t%0,%0,lo(%1)" |
[(set_attr "type" "move") |
(set_attr "length" "2")]) |
|
|
;; |
;; Conditional Branches & Moves |
;; |
|
(define_expand "addsicc" |
[(match_operand:SI 0 "register_operand" "") |
(match_operand 1 "comparison_operator" "") |
(match_operand:SI 2 "register_operand" "") |
(match_operand:SI 3 "register_operand" "")] |
"" |
"FAIL;") |
|
(define_expand "addhicc" |
[(match_operand:HI 0 "register_operand" "") |
(match_operand 1 "comparison_operator" "") |
(match_operand:HI 2 "register_operand" "") |
(match_operand:HI 3 "register_operand" "")] |
"" |
"FAIL;") |
|
(define_expand "addqicc" |
[(match_operand:QI 0 "register_operand" "") |
(match_operand 1 "comparison_operator" "") |
(match_operand:QI 2 "register_operand" "") |
(match_operand:QI 3 "register_operand" "")] |
"" |
"FAIL;") |
|
|
;; |
;; conditional moves |
;; |
|
(define_expand "movsicc" |
[(set (match_operand:SI 0 "register_operand" "") |
(if_then_else:SI (match_operand 1 "comparison_operator" "") |
(match_operand:SI 2 "register_operand" "") |
(match_operand:SI 3 "register_operand" "")))] |
"TARGET_CMOV" |
" |
{ |
if (or32_emit_cmove (operands[0], operands[1], operands[2], operands[3])) |
DONE; |
}") |
|
(define_expand "movhicc" |
[(set (match_operand:HI 0 "register_operand" "") |
(if_then_else:SI (match_operand 1 "comparison_operator" "") |
(match_operand:HI 2 "register_operand" "") |
(match_operand:HI 3 "register_operand" "")))] |
"" |
" |
{ |
FAIL; |
}") |
|
(define_expand "movqicc" |
[(set (match_operand:QI 0 "register_operand" "") |
(if_then_else:SI (match_operand 1 "comparison_operator" "") |
(match_operand:QI 2 "register_operand" "") |
(match_operand:QI 3 "register_operand" "")))] |
"" |
" |
{ |
FAIL; |
}") |
|
|
;; We use the BASE_REGS for the cmov input operands because, if rA is |
;; 0, the value of 0 is placed in rD upon truth. Similarly for rB |
;; because we may switch the operands and rB may end up being rA. |
|
(define_insn "cmov" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(if_then_else:SI |
(match_operator 1 "comparison_operator" |
[(match_operand 4 "cc_reg_operand" "") |
(const_int 0)]) |
(match_operand:SI 2 "register_operand" "r") |
(match_operand:SI 3 "register_operand" "r")))] |
"TARGET_CMOV" |
"* |
{ output_cmov(operands); }") |
|
;; |
;; .................... |
;; |
;; COMPARISONS |
;; |
;; .................... |
|
;; Flow here is rather complex: |
;; |
;; 1) The cmp{si,di,sf,df} routine is called. It deposits the |
;; arguments into the branch_cmp array, and the type into |
;; branch_type. No RTL is generated. |
;; |
;; 2) The appropriate branch define_expand is called, which then |
;; creates the appropriate RTL for the comparison and branch. |
;; Different CC modes are used, based on what type of branch is |
;; done, so that we can constrain things appropriately. There |
;; are assumptions in the rest of GCC that break if we fold the |
;; operands into the branches for integer operations, and use cc0 |
;; for floating point, so we use the fp status register instead. |
;; If needed, an appropriate temporary is created to hold the |
;; of the integer compare. |
|
;; Compare insns are next. Note that the RS/6000 has two types of compares, |
;; signed & unsigned, and one type of branch. |
;; |
;; Start with the DEFINE_EXPANDs to generate the rtl for compares, scc |
;; insns, and branches. We store the operands of compares until we see |
;; how it is used. |
|
(define_expand "cmpsi" |
[(set (reg:CC 32) |
(compare:CC (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "nonmemory_operand" "")))] |
"" |
{ |
if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) |
operands[0] = force_reg (SImode, operands[0]); |
or32_compare_op0 = operands[0]; |
or32_compare_op1 = operands[1]; |
DONE; |
}) |
|
|
|
;; |
;; Conditional branches |
;; |
|
(define_expand "beq" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (EQ, operands[0]); DONE;") |
|
(define_expand "bne" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (NE, operands[0]); DONE;") |
|
(define_expand "bgt" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (GT, operands[0]); DONE;") |
|
(define_expand "bgtu" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (GTU, operands[0]); DONE;") |
|
(define_expand "blt" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (LT, operands[0]); DONE;") |
|
(define_expand "bltu" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (LTU, operands[0]); DONE;") |
|
(define_expand "bge" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (GE, operands[0]); DONE;") |
|
(define_expand "bgeu" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (GEU, operands[0]); DONE;") |
|
(define_expand "ble" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (LE, operands[0]); DONE;") |
|
(define_expand "bleu" |
[(use (match_operand 0 "" ""))] |
"" |
"or32_expand_branch (LEU, operands[0]); DONE;") |
|
|
|
;; |
;; Setting a CCxx registers from comparision |
;; |
|
|
|
;; Here are the actual compare insns. |
(define_insn "*cmpsi_eq" |
[(set (reg:CCEQ 32) |
(compare:CCEQ (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfeqi\t%0,%1 |
l.sfeq \t%0,%1") |
|
(define_insn "*cmpsi_ne" |
[(set (reg:CCNE 32) |
(compare:CCNE (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfnei\t%0,%1 |
l.sfne \t%0,%1") |
|
(define_insn "*cmpsi_gt" |
[(set (reg:CCGT 32) |
(compare:CCGT (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfgtsi\t%0,%1 |
l.sfgts \t%0,%1") |
|
(define_insn "*cmpsi_gtu" |
[(set (reg:CCGTU 32) |
(compare:CCGTU (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfgtui\t%0,%1 |
l.sfgtu \t%0,%1") |
|
(define_insn "*cmpsi_lt" |
[(set (reg:CCLT 32) |
(compare:CCLT (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfltsi\t%0,%1 |
l.sflts \t%0,%1") |
|
(define_insn "*cmpsi_ltu" |
[(set (reg:CCLTU 32) |
(compare:CCLTU (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfltui\t%0,%1 |
l.sfltu \t%0,%1") |
|
(define_insn "*cmpsi_ge" |
[(set (reg:CCGE 32) |
(compare:CCGE (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfgesi\t%0,%1 |
l.sfges \t%0,%1") |
|
|
(define_insn "*cmpsi_geu" |
[(set (reg:CCGEU 32) |
(compare:CCGEU (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfgeui\t%0,%1 |
l.sfgeu \t%0,%1") |
|
|
(define_insn "*cmpsi_le" |
[(set (reg:CCLE 32) |
(compare:CCLE (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sflesi\t%0,%1 |
l.sfles \t%0,%1") |
|
(define_insn "*cmpsi_leu" |
[(set (reg:CCLEU 32) |
(compare:CCLEU (match_operand:SI 0 "register_operand" "r,r") |
(match_operand:SI 1 "nonmemory_operand" "I,r")))] |
"" |
"@ |
l.sfleui\t%0,%1 |
l.sfleu \t%0,%1") |
|
(define_insn "*bf" |
[(set (pc) |
(if_then_else (match_operator 1 "comparison_operator" |
[(match_operand 2 |
"cc_reg_operand" "") |
(const_int 0)]) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"* |
{ output_bf(operands); }" |
[(set_attr "type" "branch") |
(set_attr "length" "1")]) |
|
;; |
;; |
;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; |
;; |
|
|
(define_insn "movdi" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=r, r, m, r") |
(match_operand:DI 1 "general_operand" " r, m, r, i"))] |
"" |
"* |
return or32_output_move_double (operands); |
" |
[(set_attr "length" "2,2,2,3")]) |
|
(define_insn "movdf" |
[(set (match_operand:DF 0 "nonimmediate_operand" "=r, r, m, r") |
(match_operand:DF 1 "general_operand" " r, m, r, i"))] |
"" |
"* |
return or32_output_move_double (operands); |
" |
[(set_attr "length" "2,2,2,3")]) |
|
|
(define_insn "movsf" |
[(set (match_operand:SF 0 "general_operand" "=r,r,m") |
(match_operand:SF 1 "general_operand" "r,m,r"))] |
"" |
"@ |
l.ori \t%0,%1,0\t # movsf |
l.lwz \t%0,%1\t # movsf |
l.sw \t%0,%1\t # movsf" |
[(set_attr "type" "move,load,store") |
(set_attr "length" "1,1,1")]) |
|
;; |
;; extendqisi2 |
;; |
|
(define_expand "extendqisi2" |
[(use (match_operand:SI 0 "register_operand" "")) |
(use (match_operand:QI 1 "nonimmediate_operand" ""))] |
"" |
" |
{ |
if (TARGET_SEXT) |
emit_insn (gen_extendqisi2_sext(operands[0], operands[1])); |
else { |
if ( GET_CODE(operands[1]) == MEM ) { |
emit_insn (gen_extendqisi2_no_sext_mem(operands[0], operands[1])); |
} |
else { |
emit_insn (gen_extendqisi2_no_sext_reg(operands[0], operands[1])); |
} |
} |
DONE; |
}") |
|
(define_insn "extendqisi2_sext" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] |
"TARGET_SEXT" |
"@ |
l.extbs \t%0,%1\t # extendqisi2_has_signed_extend |
l.lbs \t%0,%1\t # extendqisi2_has_signed_extend" |
[(set_attr "length" "1,1") |
(set_attr "type" "extend,load")]) |
|
(define_insn "extendqisi2_no_sext_mem" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))] |
"!TARGET_SEXT" |
"l.lbs \t%0,%1\t # extendqisi2_no_sext_mem" |
[(set_attr "length" "1") |
(set_attr "type" "load")]) |
|
(define_expand "extendqisi2_no_sext_reg" |
[(set (match_dup 2) |
(ashift:SI (match_operand:QI 1 "register_operand" "") |
(const_int 24))) |
(set (match_operand:SI 0 "register_operand" "") |
(ashiftrt:SI (match_dup 2) |
(const_int 24)))] |
"!TARGET_SEXT" |
" |
{ |
operands[1] = gen_lowpart (SImode, operands[1]); |
operands[2] = gen_reg_rtx (SImode); }") |
|
;; |
;; extendhisi2 |
;; |
|
(define_expand "extendhisi2" |
[(use (match_operand:SI 0 "register_operand" "")) |
(use (match_operand:HI 1 "nonimmediate_operand" ""))] |
"" |
" |
{ |
if (TARGET_SEXT) |
emit_insn (gen_extendhisi2_sext(operands[0], operands[1])); |
else { |
if ( GET_CODE(operands[1]) == MEM ) { |
emit_insn (gen_extendhisi2_no_sext_mem(operands[0], operands[1])); |
} |
else { |
emit_insn (gen_extendhisi2_no_sext_reg(operands[0], operands[1])); |
} |
} |
DONE; |
}") |
|
(define_insn "extendhisi2_sext" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))] |
"TARGET_SEXT" |
"@ |
l.exths \t%0,%1\t # extendhisi2_has_signed_extend |
l.lhs \t%0,%1\t # extendhisi2_has_signed_extend" |
[(set_attr "length" "1,1") |
(set_attr "type" "extend,load")]) |
|
(define_insn "extendhisi2_no_sext_mem" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))] |
"!TARGET_SEXT" |
"l.lhs \t%0,%1\t # extendhisi2_no_sext_mem" |
[(set_attr "length" "1") |
(set_attr "type" "load")]) |
|
(define_expand "extendhisi2_no_sext_reg" |
[(set (match_dup 2) |
(ashift:SI (match_operand:HI 1 "register_operand" "") |
(const_int 16))) |
(set (match_operand:SI 0 "register_operand" "") |
(ashiftrt:SI (match_dup 2) |
(const_int 16)))] |
"!TARGET_SEXT" |
" |
{ |
operands[1] = gen_lowpart (SImode, operands[1]); |
operands[2] = gen_reg_rtx (SImode); }") |
|
|
;; |
;; zero_extend<m><n>2 |
;; |
|
(define_insn "zero_extendqisi2" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] |
"" |
"@ |
l.andi \t%0,%1,0xff\t # zero_extendqisi2 |
l.lbz \t%0,%1\t # zero_extendqisi2" |
[(set_attr "type" "logic,load") |
(set_attr "length" "1,1")]) |
|
|
(define_insn "zero_extendhisi2" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))] |
"" |
"@ |
l.andi \t%0,%1,0xffff\t # zero_extendqisi2 |
l.lhz \t%0,%1\t # zero_extendqisi2" |
[(set_attr "type" "logic,load") |
(set_attr "length" "1,1")]) |
|
;; |
;; Shift/rotate operations |
;; |
|
(define_insn "ashlsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(ashift:SI (match_operand:SI 1 "register_operand" "r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,L")))] |
"" |
"@ |
l.sll \t%0,%1,%2 |
l.slli \t%0,%1,%2" |
[(set_attr "type" "shift,shift") |
(set_attr "length" "1,1")]) |
|
(define_insn "ashrsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,L")))] |
"" |
"@ |
l.sra \t%0,%1,%2 |
l.srai \t%0,%1,%2" |
[(set_attr "type" "shift,shift") |
(set_attr "length" "1,1")]) |
|
(define_insn "lshrsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,L")))] |
"" |
"@ |
l.srl \t%0,%1,%2 |
l.srli \t%0,%1,%2" |
[(set_attr "type" "shift,shift") |
(set_attr "length" "1,1")]) |
|
(define_insn "rotrsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(rotatert:SI (match_operand:SI 1 "register_operand" "r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,L")))] |
"TARGET_ROR" |
"@ |
l.ror \t%0,%1,%2 |
l.rori \t%0,%1,%2" |
[(set_attr "type" "shift,shift") |
(set_attr "length" "1,1")]) |
|
;; |
;; Logical bitwise operations |
;; |
|
(define_insn "andsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(and:SI (match_operand:SI 1 "register_operand" "%r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,K")))] |
"" |
"@ |
l.and \t%0,%1,%2 |
l.andi \t%0,%1,%2" |
[(set_attr "type" "logic,logic") |
(set_attr "length" "1,1")]) |
|
(define_insn "iorsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(ior:SI (match_operand:SI 1 "register_operand" "%r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,K")))] |
"" |
"@ |
l.or \t%0,%1,%2 |
l.ori \t%0,%1,%2" |
[(set_attr "type" "logic,logic") |
(set_attr "length" "1,1")]) |
|
(define_insn "xorsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(xor:SI (match_operand:SI 1 "register_operand" "%r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,I")))] |
"" |
"@ |
l.xor \t%0,%1,%2 |
l.xori \t%0,%1,%2" |
[(set_attr "type" "logic,logic") |
(set_attr "length" "1,1")]) |
|
(define_insn "one_cmplqi2" |
[(set (match_operand:QI 0 "register_operand" "=r") |
(not:QI (match_operand:QI 1 "register_operand" "r")))] |
"" |
"l.xori \t%0,%1,0x00ff" |
[(set_attr "type" "logic") |
(set_attr "length" "1")]) |
|
(define_insn "one_cmplsi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(not:SI (match_operand:SI 1 "register_operand" "r")))] |
"" |
"l.xori \t%0,%1,0xffff" |
[(set_attr "type" "logic") |
(set_attr "length" "1")]) |
|
;; |
;; Arithmetic operations |
;; |
|
(define_insn "negsi2" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(neg:SI (match_operand:SI 1 "register_operand" "r")))] |
"" |
"l.sub \t%0,r0,%1" |
[(set_attr "type" "add") |
(set_attr "length" "1")]) |
|
(define_insn "addsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(plus:SI (match_operand:SI 1 "register_operand" "%r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,I")))] |
"" |
"@ |
l.add \t%0,%1,%2 |
l.addi \t%0,%1,%2" |
[(set_attr "type" "add,add") |
(set_attr "length" "1,1")]) |
|
(define_insn "subsi3" |
[(set (match_operand:SI 0 "register_operand" "=r,r") |
(minus:SI (match_operand:SI 1 "register_operand" "r,r") |
(match_operand:SI 2 "nonmemory_operand" "r,I")))] |
"" |
"@ |
l.sub \t%0,%1,%2 |
l.addi \t%0,%1,%n2" |
[(set_attr "type" "add,add")] |
) |
|
;; |
;; mul and div |
;; |
|
(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")))] |
"TARGET_HARD_MUL" |
"l.mul \t%0,%1,%2" |
[(set_attr "type" "mul") |
(set_attr "length" "1")]) |
|
(define_insn "divsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(div:SI (match_operand:SI 1 "register_operand" "r") |
(match_operand:SI 2 "register_operand" "r")))] |
"TARGET_HARD_DIV" |
"l.div \t%0,%1,%2" |
[(set_attr "type" "mul") |
(set_attr "length" "1")]) |
|
(define_insn "udivsi3" |
[(set (match_operand:SI 0 "register_operand" "=r") |
(udiv:SI (match_operand:SI 1 "register_operand" "r") |
(match_operand:SI 2 "register_operand" "r")))] |
"TARGET_HARD_DIV" |
"l.divu \t%0,%1,%2" |
[(set_attr "type" "mul") |
(set_attr "length" "1")]) |
|
;; |
;; jumps |
;; |
|
;; jump |
|
(define_expand "jump" |
[(set (pc) |
(label_ref (match_operand 0 "" "")))] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_jump_insn (gen_jump_internal (operands[0])); |
else |
emit_jump_insn (gen_jump_aligned (operands[0])); |
DONE; |
}") |
|
(define_insn "jump_internal" |
[(set (pc) |
(label_ref (match_operand 0 "" "")))] |
"!TARGET_ALIGNED_JUMPS" |
"l.j \t%l0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "jump_aligned" |
[(set (pc) |
(label_ref (match_operand 0 "" "")))] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.j \t%l0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; indirect jump |
|
(define_expand "indirect_jump" |
[(set (pc) (match_operand:SI 0 "register_operand" "r"))] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_jump_insn (gen_indirect_jump_internal (operands[0])); |
else |
emit_jump_insn (gen_indirect_jump_aligned (operands[0])); |
DONE; |
|
}") |
|
(define_insn "indirect_jump_internal" |
[(set (pc) (match_operand:SI 0 "register_operand" "r"))] |
"!TARGET_ALIGNED_JUMPS" |
"l.jr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "indirect_jump_aligned" |
[(set (pc) (match_operand:SI 0 "register_operand" "r"))] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; |
;; calls |
;; |
|
;; call |
|
(define_expand "call" |
[(parallel [(call (match_operand:SI 0 "sym_ref_mem_operand" "") |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_call_insn (gen_call_internal (operands[0], operands[1])); |
else |
emit_call_insn (gen_call_aligned (operands[0], operands[1])); |
DONE; |
}") |
|
(define_insn "call_internal" |
[(parallel [(call (match_operand:SI 0 "sym_ref_mem_operand" "") |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"!TARGET_ALIGNED_JUMPS" |
"l.jal \t%S0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "call_aligned" |
[(parallel [(call (match_operand:SI 0 "sym_ref_mem_operand" "") |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jal \t%S0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; call value |
|
(define_expand "call_value" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (match_operand:SI 1 "sym_ref_mem_operand" "") |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_call_insn (gen_call_value_internal (operands[0], operands[1], operands[2])); |
else |
emit_call_insn (gen_call_value_aligned (operands[0], operands[1], operands[2])); |
DONE; |
}") |
|
(define_insn "call_value_internal" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (match_operand:SI 1 "sym_ref_mem_operand" "") |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"!TARGET_ALIGNED_JUMPS" |
"l.jal \t%S1%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "call_value_aligned" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (match_operand:SI 1 "sym_ref_mem_operand" "") |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jal \t%S1%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; indirect call value |
|
(define_expand "call_value_indirect" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (mem:SI (match_operand:SI 1 "register_operand" "r")) |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_call_insn (gen_call_value_indirect_internal (operands[0], operands[1], operands[2])); |
else |
emit_call_insn (gen_call_value_indirect_aligned (operands[0], operands[1], operands[2])); |
DONE; |
}") |
|
(define_insn "call_value_indirect_internal" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (mem:SI (match_operand:SI 1 "register_operand" "r")) |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"!TARGET_ALIGNED_JUMPS" |
"l.jalr \t%1%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "call_value_indirect_aligned" |
[(parallel [(set (match_operand 0 "register_operand" "=r") |
(call (mem:SI (match_operand:SI 1 "register_operand" "r")) |
(match_operand 2 "" "i"))) |
(clobber (reg:SI 9))])] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jalr \t%1%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; indirect call |
|
(define_expand "call_indirect" |
[(parallel [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_call_insn (gen_call_indirect_internal (operands[0], operands[1])); |
else |
emit_call_insn (gen_call_indirect_aligned (operands[0], operands[1])); |
DONE; |
}") |
|
(define_insn "call_indirect_internal" |
[(parallel [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"!TARGET_ALIGNED_JUMPS" |
"l.jalr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "call_indirect_aligned" |
[(parallel [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) |
(match_operand 1 "" "i")) |
(clobber (reg:SI 9))])] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jalr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
;; table jump |
|
(define_expand "tablejump" |
[(set (pc) (match_operand:SI 0 "register_operand" "r")) |
(use (label_ref (match_operand 1 "" "")))] |
"" |
" |
{ |
if (!TARGET_ALIGNED_JUMPS) |
emit_jump_insn (gen_tablejump_internal (operands[0], operands[1])); |
else |
emit_jump_insn (gen_tablejump_aligned (operands[0], operands[1])); |
DONE; |
}") |
|
(define_insn "tablejump_internal" |
[(set (pc) (match_operand:SI 0 "register_operand" "r")) |
(use (label_ref (match_operand 1 "" "")))] |
"!TARGET_ALIGNED_JUMPS" |
"l.jr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
(define_insn "tablejump_aligned" |
[(set (pc) (match_operand:SI 0 "register_operand" "r")) |
(use (label_ref (match_operand 1 "" "")))] |
"TARGET_ALIGNED_JUMPS" |
".balignl 0x8,0x15000015,0x4\;l.jr \t%0%(" |
[(set_attr "type" "jump") |
(set_attr "length" "1")]) |
|
|
;; no-op |
|
(define_insn "nop" |
[(const_int 0)] |
"" |
"l.nop" |
[(set_attr "type" "logic") |
(set_attr "length" "1")]) |
|
;; |
;; floating point |
;; |
|
(define_insn "addsf3" |
[(set (match_operand:SF 0 "register_operand" "=r") |
(plus:SF (match_operand:SF 1 "register_operand" "r") |
(match_operand:SF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.add.s\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "adddf3" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(plus:DF (match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.add.d\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "subsf3" |
[(set (match_operand:SF 0 "register_operand" "=r") |
(minus:SF (match_operand:SF 1 "register_operand" "r") |
(match_operand:SF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.sub.s\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "subdf3" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(minus:DF (match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.sub.d\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "mulsf3" |
[(set (match_operand:SF 0 "register_operand" "=r") |
(mult:SF (match_operand:SF 1 "register_operand" "r") |
(match_operand:SF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.mul.s\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "muldf3" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(mult:DF (match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.mul.d\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "divsf3" |
[(set (match_operand:SF 0 "register_operand" "=r") |
(div:SF (match_operand:SF 1 "register_operand" "r") |
(match_operand:SF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.div.s\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
(define_insn "divdf3" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(div:DF (match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"TARGET_HARD_FLOAT" |
"lf.div.d\t%0,%1,%2" |
[(set_attr "type" "fp") |
(set_attr "length" "1")]) |
|
|
;; Local variables: |
;; mode:emacs-lisp |
;; comment-start: ";; " |
;; eval: (set-syntax-table (copy-sequence (syntax-table))) |
;; eval: (modify-syntax-entry ?[ "(]") |
;; eval: (modify-syntax-entry ?] ")[") |
;; eval: (modify-syntax-entry ?{ "(}") |
;; eval: (modify-syntax-entry ?} "){") |
;; eval: (setq indent-tabs-mode t) |
;; End: |
/config/or32/t-default
0,0 → 1,34
# |
# t-default is Makefile fragment to be included when |
# building gcc for or32 target |
# |
|
# we don't support -g so don't use it |
LIBGCC2_DEBUG_CFLAGS = |
|
TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer |
|
LIB1ASMSRC = or32/or32.S |
LIB1ASMFUNCS = __mulsi3 __udivsi3 __divsi3 __umodsi3 __modsi3 |
|
# These are really part of libgcc1, but this will cause them to be |
# built correctly, so... [taken from t-sparclite] |
LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c |
|
dp-bit.c: $(srcdir)/config/fp-bit.c |
cat $(srcdir)/config/fp-bit.c > dp-bit.c |
|
fp-bit.c: $(srcdir)/config/fp-bit.c |
echo '#define FLOAT' > fp-bit.c |
cat $(srcdir)/config/fp-bit.c >> fp-bit.c |
|
# Build the libraries for both hard and soft floating point |
|
#MULTILIB_OPTIONS = msoft-float |
#MULTILIB_DIRNAMES = soft-float |
|
#LIBGCC = stmp-multilib |
#INSTALL_LIBGCC = install-multilib |
|
#LIBGCC = |
#INSTALL_LIBGCC = |
/config/or32/or32.S
0,0 → 1,182
/* |
* Assembly functions for software multiplication and devision. |
*/ |
|
#define ENTRY(symbol) \ |
.align 4 ;\ |
.global symbol ;\ |
symbol: |
|
#ifdef L__mulsi3 |
ENTRY(___mulsi3) |
l.addi r11,r0,0x0 |
l.sfne r3,r11 |
l.bnf 3f |
l.ori r5,r3,0x0 |
l.addi r6,r0,0x0 |
1: |
l.andi r3,r5,0x1 |
l.sfeq r3,r6 |
l.bf 2f |
l.srli r5,r5,0x1 |
l.add r11,r11,r4 |
2: |
l.sfne r5,r6 |
l.bf 1b |
l.slli r4,r4,0x1 |
3: |
l.jr r9 |
l.nop 0x0 |
#endif |
|
#ifdef L__udivsi3 |
ENTRY(___udivsi3) |
l.addi r1,r1,-4 |
l.sw 0(r1),r9 |
l.addi r11,r0,0 |
l.addi r8,r4,0 |
l.addi r5,r3,0 |
l.sfne r8,r11 |
l.bnf 4f |
l.addi r7,r0,0 |
l.sfgtu r8,r5 |
l.bf 5f |
l.sfeq r8,r5 |
l.bf 6f |
l.sfltu r11,r8 |
l.bnf 2f |
l.addi r13,r0,32 |
l.movhi r9,hi(0x80000000) |
l.addi r6,r0,-1 |
1: |
l.and r3,r5,r9 |
l.slli r4,r7,1 |
l.addi r15,r5,0 |
l.srli r3,r3,31 |
l.add r13,r13,r6 |
l.or r7,r4,r3 |
l.sfltu r7,r8 |
l.bf 1b |
l.slli r5,r5,1 |
2: |
l.srli r7,r7,1 |
l.addi r13,r13,1 |
l.addi r9,r0,0 |
l.sfltu r9,r13 |
l.bnf 4f |
l.addi r5,r15,0 |
l.movhi r15,hi(0x80000000) |
l.addi r17,r0,0 |
3: |
l.and r3,r5,r15 |
l.slli r4,r7,1 |
l.srli r3,r3,31 |
l.or r7,r4,r3 |
l.sub r6,r7,r8 |
l.and r3,r6,r15 |
l.srli r3,r3,31 |
l.addi r4,r0,0 |
l.sfne r3,r4 |
l.bf 1f |
l.slli r3,r11,1 |
l.addi r4,r0,1 |
1: |
l.slli r5,r5,1 |
l.sfne r4,r17 |
l.bnf 2f |
l.or r11,r3,r4 |
l.addi r7,r6,0 |
2: |
l.addi r9,r9,1 |
l.sfltu r9,r13 |
l.bf 3b |
l.nop 0 |
l.j 4f |
l.nop 0 |
6: |
l.j 4f |
l.addi r11,r0,1 |
5: |
l.addi r7,r5,0 |
4: |
l.lwz r9,0(r1) |
l.jr r9 |
l.addi r1,r1,4 |
#endif |
|
|
#ifdef L__divsi3 |
ENTRY(___divsi3) |
l.addi r1,r1,-8 |
l.sw 0(r1),r9 |
l.sw 4(r1),r10 |
l.addi r5,r3,0 |
l.addi r10,r0,0 |
l.sflts r5,r0 |
l.bnf 1f |
l.addi r3,r0,0 |
l.addi r10,r0,1 |
l.sub r5,r0,r5 |
1: |
l.sflts r4,r0 |
l.bnf 1f |
l.nop 0 |
l.addi r10,r10,1 |
l.sub r4,r0,r4 |
1: |
l.jal ___udivsi3 |
l.addi r3,r5,0 |
l.sfeqi r10,1 |
l.bnf 1f |
l.nop 0 |
l.sub r11,r0,r11 |
1: |
l.lwz r9,0(r1) |
l.lwz r10,4(r1) |
l.jr r9 |
l.addi r1,r1,8 |
#endif |
|
|
#ifdef L__umodsi3 |
ENTRY(___umodsi3) |
l.addi r1,r1,-4 |
l.sw 0(r1),r9 |
l.jal ___udivsi3 |
l.nop 0 |
l.addi r11,r7,0 |
l.lwz r9,0(r1) |
l.jr r9 |
l.addi r1,r1,4 |
#endif |
|
|
#ifdef L__modsi3 |
ENTRY(___modsi3) |
l.addi r1,r1,-8 |
l.sw 0(r1),r9 |
l.sw 4(r1),r10 |
l.addi r10,r0,0 |
l.sflts r3,r0 |
l.bnf 1f |
l.nop 0 |
l.addi r10,r0,1 |
l.sub r3,r0,r3 |
1: |
l.sflts r4,r0 |
l.bnf 1f |
l.nop 0 |
l.sub r4,r0,r4 |
1: |
l.jal ___udivsi3 |
l.nop 0 |
l.sfeqi r10,1 |
l.bnf 1f |
l.addi r11,r7,0 |
l.sub r11,r0,r11 |
1: |
l.lwz r9,0(r1) |
l.lwz r10,4(r1) |
l.jr r9 |
l.addi r1,r1,8 |
#endif |
/config/or32/or32.c
0,0 → 1,1260
/* Subroutines for insn-output.c for GNU compiler. OpenRISC 1000 version. |
Copyright (C) 1987, 1992, 1997, 1999, 2000, 2001, 2002, 2003, 2004, |
2005 Free Software Foundation, Inc |
Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999. |
Major optimizations by Matjaz Breskvar <matjazb@bsemi.com> in 2005. |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 1, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
#include "config.h" |
#include "system.h" |
#include "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 "tree.h" |
#include "expr.h" |
#include "except.h" |
#include "function.h" |
#include "toplev.h" |
#include "recog.h" |
#include "tm_p.h" |
#include "debug.h" |
#include "output.h" |
#include "target.h" |
#include "target-def.h" |
#include "ggc.h" |
#include "optabs.h" |
|
/* Set thist to nonzero if you want l.nop instruction in delay slot |
of l.jr instruction in epilogue. */ |
#define NOP_DELAY_SLOT_FILL 0 |
|
/* This is the pseudo register number that holds the comparison flags */ |
|
#define FLAGS_REG 32 |
|
/* Save information from a "cmpxx" operation until the branch or scc is |
emitted. */ |
rtx or32_compare_op0, or32_compare_op1; |
|
/* used in function prologue/epilogue generation */ |
extern int leaf_function; |
|
/* Local function prototypes */ |
static void or32_output_function_prologue (FILE * file, HOST_WIDE_INT vars); |
static void or32_output_function_epilogue (FILE * file, HOST_WIDE_INT vars); |
static bool or32_save_reg_p (int regno); |
HOST_WIDE_INT or32_compute_frame_size (HOST_WIDE_INT size); |
static rtx emit_frame_insn (rtx insn); |
static rtx indexed_memory (rtx base, HOST_WIDE_INT disp); |
void or32_expand_prologue (void); |
void or32_expand_epilogue (int sibcall); |
const char *or32_output_move_double (rtx * operands); |
enum rtx_code or32_reverse_condition (enum machine_mode mode, |
enum rtx_code code); |
enum machine_mode or32_cc_mode (enum rtx_code code, rtx op0, rtx op1); |
rtx or32_expand_compare (enum rtx_code code, rtx op0, rtx op1); |
void or32_expand_branch (enum rtx_code code, rtx label); |
static int or32_emit_int_cmove (rtx dest, rtx op, rtx true_cond, |
rtx false_cond); |
int or32_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond); |
const char *output_cmov (rtx * operands); |
const char *output_bf (rtx * operands); |
void or32_emit_set_const32 (rtx op0, rtx op1); |
int or32_register_move_cost (enum machine_mode mode, enum reg_class from, |
enum reg_class to); |
int or32_memory_move_cost (enum machine_mode mode, enum reg_class class, |
int in); |
int or32_branch_cost (); |
|
|
#undef TARGET_ASM_FUNCTION_PROLOGUE |
#define TARGET_ASM_FUNCTION_PROLOGUE or32_output_function_prologue |
#undef TARGET_ASM_FUNCTION_EPILOGUE |
#define TARGET_ASM_FUNCTION_EPILOGUE or32_output_function_epilogue |
|
static bool or32_function_ok_for_sibcall (tree decl, tree exp); |
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL |
#define TARGET_FUNCTION_OK_FOR_SIBCALL or32_function_ok_for_sibcall |
|
|
/* Initialize the GCC target structure. */ |
struct gcc_target targetm = TARGET_INITIALIZER; |
|
/* Stack layout we use for pushing and poping saved registers */ |
struct or32_frame_info |
{ |
bool save_lr_p; |
int lr_save_offset; |
bool save_fp_p; |
int fp_save_offset; |
int gpr_size; |
int gpr_offset; |
int total_size; |
int vars_size; |
int args_size; |
HOST_WIDE_INT mask; |
}; |
|
static struct or32_frame_info frame_info; |
|
/* Returns 1 if OP is either a pseudo-register or a register denoting a |
CR field. */ |
|
int |
cc_reg_operand (rtx op, enum machine_mode mode) |
{ |
register_operand (op, mode); |
|
if (GET_CODE (op) == REG && REGNO (op) == 32) |
return 1; |
|
return 0; |
} |
|
int |
sym_ref_mem_operand (op, mode) |
register rtx op; |
enum machine_mode mode ATTRIBUTE_UNUSED; |
{ |
if (GET_CODE (op) == MEM) |
{ |
rtx t1 = XEXP (op, 0); |
if (GET_CODE (t1) == SYMBOL_REF) |
return 1; |
} |
return 0; |
} |
|
/* Return 1 if OP is a valid operand for the source of a move insn. */ |
|
int |
input_operand (rtx op, enum machine_mode mode) |
{ |
|
/* If both modes are non-void they must be the same. */ |
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) |
return 0; |
|
/* Accept CONSTANT_P_RTX, since it will be gone by CSE1 and result in 0/1. */ |
if (GET_CODE (op) == CONSTANT_P_RTX) |
return 1; |
|
/* Allow any one instruction integer constant, and all CONST_INT |
variants when we are working in DImode and !arch64. */ |
if (GET_MODE_CLASS (mode) == MODE_INT |
&& ((GET_CODE (op) == CONST_INT) |
&& (CONST_OK_FOR_LETTER_P (INTVAL (op), 'K') |
|| CONST_OK_FOR_LETTER_P (INTVAL (op), 'M') |
|| CONST_OK_FOR_LETTER_P (INTVAL (op), 'I')))) |
return 1; |
|
if (register_operand (op, mode)) |
return 1; |
|
/* If this is a SUBREG, look inside so that we handle |
paradoxical ones. */ |
if (GET_CODE (op) == SUBREG) |
op = SUBREG_REG (op); |
|
|
/* Check for valid MEM forms. */ |
if (GET_CODE (op) == MEM) |
return memory_address_p (mode, XEXP (op, 0)); |
|
return 0; |
} |
|
/* Test for a valid operand for a call instruction. Don't allow the |
arg pointer register or virtual regs since they may decay into |
reg + const, which the patterns can't handle. */ |
|
int |
sibcall_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) |
{ |
|
|
/* Disallow indirect through a virtual register. This leads to |
compiler aborts when trying to eliminate them. */ |
if (GET_CODE (op) == REG |
&& (op == arg_pointer_rtx |
|| op == frame_pointer_rtx |
|| (REGNO (op) >= FIRST_PSEUDO_REGISTER |
&& REGNO (op) <= LAST_VIRTUAL_REGISTER))) |
{ |
|
fprintf (stderr, "0\n"); |
return 0; |
} |
|
/* Explicitly allow SYMBOL_REF even if pic. */ |
if (GET_CODE (op) == SYMBOL_REF) |
return 1; |
|
/* Otherwise we can only allow register operands. */ |
return register_operand (op, Pmode); |
} |
|
|
/* Add a REG_MAYBE_DEAD note to the insn. */ |
static void |
or32_maybe_dead (rtx insn) |
{ |
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, |
const0_rtx, REG_NOTES (insn)); |
} |
|
int |
print_operand_punct_valid_p (code) |
int code; |
{ |
switch (code) |
{ |
case '(': /* idea taken from sparc; output nop for %( if |
not optimizing or the slot is not filled. */ |
case '%': |
return 1; |
} |
return 0; |
} |
|
void |
print_operand_address (file, addr) |
FILE *file; |
register rtx addr; |
{ |
register rtx reg1, reg2, breg, ireg; |
rtx offset; |
|
switch (GET_CODE (addr)) |
{ |
case MEM: |
if (GET_CODE (XEXP (addr, 0)) == REG) |
fprintf (file, "%s", reg_names[REGNO (addr)]); |
else |
abort (); |
break; |
|
case REG: |
fprintf (file, "0(%s)", reg_names[REGNO (addr)]); |
break; |
|
case PLUS: |
reg1 = 0; |
reg2 = 0; |
ireg = 0; |
breg = 0; |
offset = 0; |
if (GET_CODE (XEXP (addr, 0)) == REG) |
{ |
offset = XEXP (addr, 1); |
addr = XEXP (addr, 0); |
} |
else if (GET_CODE (XEXP (addr, 1)) == REG) |
{ |
offset = XEXP (addr, 0); |
addr = XEXP (addr, 1); |
} |
output_address (offset); |
fprintf (file, "(%s)", reg_names[REGNO (addr)]); |
break; |
|
default: |
/* fprintf(file, "{%d}", GET_CODE (addr)); */ |
output_addr_const (file, addr); |
} |
} |
|
/* Calulcate and return stack size for current function. */ |
static int |
calculate_stack_size (int vars, int *lr_save_area, |
int *fp_save_area, int *gpr_save_area, int *save_area) |
{ |
int regno; |
|
*gpr_save_area = 0; |
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
{ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
*gpr_save_area += 4; |
} |
|
*lr_save_area = (!current_function_is_leaf |
|| regs_ever_live[LINK_REGNUM]) ? 4 : 0; |
*fp_save_area = frame_pointer_needed ? 4 : 0; |
|
*save_area = (OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ *lr_save_area + *fp_save_area); |
|
return |
(OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ *lr_save_area + *fp_save_area + *gpr_save_area + OR32_ALIGN (vars, 4)); |
} |
|
/* Set up the stack and frame pointer (if desired) for the |
function. */ |
static void |
or32_output_function_prologue (FILE * file, HOST_WIDE_INT vars) |
{ |
int save_area; |
int gpr_save_area; |
int lr_save_area; |
int fp_save_area; |
int stack_size; |
int regno; |
|
if (TARGET_SCHED_LOGUE) |
return; |
|
#if 0 |
save_area = 0; |
|
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
{ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
{ |
save_area += 1; |
} |
} |
|
if (save_area != 0) |
fprintf (file, "\tl.nop \t0x%x\n", 0x100 + save_area); |
|
return; |
#endif |
|
if (vars < 0) |
abort (); |
|
stack_size = calculate_stack_size |
(vars, &lr_save_area, &fp_save_area, &gpr_save_area, &save_area); |
|
fprintf (file, |
"\n\t# gpr_save_area %d vars %ld current_function_outgoing_args_size %d\n", |
gpr_save_area, vars, current_function_outgoing_args_size); |
|
if (stack_size >= 0x8000) |
{ |
fprintf (file, "\tl.movhi \tr%d,hi(%d)\n", GP_ARG_RETURN, stack_size); |
fprintf (file, "\tl.ori \tr%d,r%d,lo(%d)\n", GP_ARG_RETURN, |
GP_ARG_RETURN, stack_size); |
fprintf (file, "\tl.sub \tr%d,r%d,r%d\n", STACK_POINTER_REGNUM, |
STACK_POINTER_REGNUM, GP_ARG_RETURN); |
} |
else if (stack_size > 0) |
{ |
fprintf (file, "\tl.addi \tr%d,r%d,%d\n", STACK_POINTER_REGNUM, |
STACK_POINTER_REGNUM, -stack_size); |
} |
|
if (fp_save_area) |
{ |
fprintf (file, "\tl.sw \t%d(r%d),r%d\n", |
OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ lr_save_area, STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM); |
if (stack_size >= 0x8000) |
fprintf (file, "\tl.add \tr%d,r%d,r%d\n", FRAME_POINTER_REGNUM, |
STACK_POINTER_REGNUM, GP_ARG_RETURN); |
else |
fprintf (file, "\tl.addi \tr%d,r%d,%d\n", FRAME_POINTER_REGNUM, |
STACK_POINTER_REGNUM, stack_size); |
} |
|
if (lr_save_area) |
{ |
fprintf (file, "\tl.sw \t%d(r%d),r%d\n", |
OR32_ALIGN (current_function_outgoing_args_size, 4), |
STACK_POINTER_REGNUM, LINK_REGNUM); |
} |
|
save_area = (OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ lr_save_area + fp_save_area); |
|
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
{ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
{ |
fprintf (file, "\tl.sw \t%d(r%d),r%d\n", save_area, |
STACK_POINTER_REGNUM, regno); |
save_area += 4; |
} |
} |
} |
|
/* Do any necessary cleanup after a function to restore stack, frame, |
and regs. */ |
static void |
or32_output_function_epilogue (FILE * file, HOST_WIDE_INT vars) |
{ |
int save_area; |
int gpr_save_area; |
int lr_save_area; |
int fp_save_area; |
int stack_size; |
int regno; |
|
if (TARGET_SCHED_LOGUE) |
return; |
|
#if 0 |
save_area = 0; |
|
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
{ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
{ |
save_area += 1; |
} |
} |
|
fprintf (file, "\tl.nop \t0x%x\n", 0x200 + save_area); |
return; |
#endif |
|
stack_size = calculate_stack_size |
(vars, &lr_save_area, &fp_save_area, &gpr_save_area, &save_area); |
|
if (lr_save_area) |
{ |
fprintf (file, "\tl.lwz \tr%d,%d(r%d)\n", LINK_REGNUM, |
OR32_ALIGN (current_function_outgoing_args_size, 4), |
STACK_POINTER_REGNUM); |
} |
if (fp_save_area) |
{ |
fprintf (file, "\tl.lwz \tr%d,%d(r%d)\n", FRAME_POINTER_REGNUM, |
OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ lr_save_area, STACK_POINTER_REGNUM); |
} |
save_area = (OR32_ALIGN (current_function_outgoing_args_size, 4) |
+ lr_save_area + fp_save_area); |
|
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
{ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
{ |
fprintf (file, "\tl.lwz \tr%d,%d(r%d)\n", regno, save_area, |
STACK_POINTER_REGNUM); |
save_area += 4; |
} |
} |
|
if (stack_size >= 0x8000) |
{ |
fprintf (file, "\tl.movhi \tr3,hi(%d)\n", stack_size); |
fprintf (file, "\tl.ori \tr3,r3,lo(%d)\n", stack_size); |
|
if (!TARGET_ALIGNED_JUMPS) |
fprintf (file, "\tl.jr \tr%d\n", LINK_REGNUM); |
else |
fprintf (file, "\t.balignl 0x8,0x15000015,0x4;l.jr \tr%d\n", |
LINK_REGNUM); |
|
fprintf (file, "\tl.add \tr%d,r%d,r3\n", STACK_POINTER_REGNUM, |
STACK_POINTER_REGNUM); |
} |
else if (stack_size > 0) |
{ |
if (!TARGET_ALIGNED_JUMPS) |
fprintf (file, "\tl.jr \tr%d\n", LINK_REGNUM); |
else |
fprintf (file, "\t.balignl 0x8,0x15000015,0x4;l.jr \tr%d\n", |
LINK_REGNUM); |
|
fprintf (file, "\tl.addi \tr%d,r%d,%d\n", STACK_POINTER_REGNUM, |
STACK_POINTER_REGNUM, stack_size); |
} |
else |
{ |
if (!TARGET_ALIGNED_JUMPS) |
fprintf (file, "\tl.jr \tr%d\n", LINK_REGNUM); |
else |
fprintf (file, "\t.balignl 0x8,0x15000015,0x4;l.jr \tr%d\n", |
LINK_REGNUM); |
|
fprintf (file, "\tl.nop\n"); |
} |
|
#if 0 |
fprintf (file, ".endproc _%s\n", get_function_name ()); |
#endif |
} |
|
/* Compuate full frame size and layout. SIZE is the size of the |
functions local variables. Store information in FRAME_INFO and |
return total size of stack frame. */ |
|
HOST_WIDE_INT |
or32_compute_frame_size (HOST_WIDE_INT size) |
{ |
HOST_WIDE_INT args_size; |
HOST_WIDE_INT vars_size; |
HOST_WIDE_INT stack_offset; |
int regno; |
|
args_size = current_function_outgoing_args_size; |
vars_size = OR32_ALIGN (size, 4); |
|
frame_info.args_size = args_size; |
frame_info.vars_size = vars_size; |
|
/* If the function has local variables, we're committed to |
allocating it anyway. Otherwise reclaim it here. */ |
/* FIXME: Verify this. Got if from the MIPS port. */ |
if (vars_size == 0 && current_function_is_leaf) |
args_size = 0; |
|
stack_offset = args_size; |
|
/* Save link register right after possible outgoing arguments. */ |
if (or32_save_reg_p (LINK_REGNUM)) |
{ |
frame_info.lr_save_offset = stack_offset; |
frame_info.save_lr_p = true; |
stack_offset = stack_offset + UNITS_PER_WORD; |
} |
else |
frame_info.save_lr_p = false; |
|
/* Save frame pointer right after possible link register. */ |
if (or32_save_reg_p (FRAME_POINTER_REGNUM)) |
{ |
frame_info.fp_save_offset = stack_offset; |
frame_info.save_fp_p = true; |
stack_offset = stack_offset + UNITS_PER_WORD; |
} |
else |
frame_info.save_fp_p = false; |
|
frame_info.gpr_size = 0; |
frame_info.mask = 0; |
frame_info.gpr_offset = stack_offset; |
|
for (regno = 0; regno <= LAST_INT_REG; regno++) |
{ |
if (regno == LINK_REGNUM || regno == FRAME_POINTER_REGNUM) |
/* These has already been saved if so needed. */ |
continue; |
|
if (or32_save_reg_p (regno)) |
{ |
frame_info.gpr_size += UNITS_PER_WORD; |
frame_info.mask |= (1 << regno); |
} |
} |
|
frame_info.total_size = ((frame_info.save_fp_p ? UNITS_PER_WORD : 0) |
+ (frame_info.save_lr_p ? UNITS_PER_WORD : 0) |
+ args_size + frame_info.gpr_size + vars_size); |
|
return frame_info.total_size; |
} |
|
|
/* Return true if current function must save REGNO. */ |
static bool |
or32_save_reg_p (int regno) |
{ |
/* No need to save the faked cc0 register. */ |
if (regno == FLAGS_REG) |
return false; |
|
/* Check call-saved registers. */ |
if (regs_ever_live[regno] && !call_used_regs[regno]) |
return true; |
|
/* We need to save the old frame pointer before setting up a new |
one. */ |
if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) |
return true; |
|
/* We need to save the incoming return address if it is ever clobbered |
within the function. */ |
if (regno == LINK_REGNUM && regs_ever_live[regno]) |
return true; |
|
return false; |
} |
|
/* Emit a frame related insn. Same as emit_insn, but sets |
RTX_FRAME_RELATED_P to one. */ |
|
static rtx |
emit_frame_insn (rtx insn) |
{ |
insn = emit_insn (insn); |
RTX_FRAME_RELATED_P (insn) = 1; |
return (insn); |
} |
|
static rtx |
indexed_memory (rtx base, HOST_WIDE_INT disp) |
{ |
return gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, base, GEN_INT (disp))); |
} |
|
/* 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 |
or32_expand_prologue () |
{ |
int total_size = or32_compute_frame_size (get_frame_size ()); |
rtx sp_rtx; |
rtx value_rtx; |
|
if (!total_size) |
/* No frame needed. */ |
return; |
|
sp_rtx = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); |
|
if (total_size > 32767) |
{ |
value_rtx = gen_rtx_REG (Pmode, GP_ARG_RETURN); |
emit_frame_insn (gen_rtx_SET (Pmode, value_rtx, GEN_INT (total_size))); |
} |
else |
value_rtx = GEN_INT (total_size); |
|
/* Update the stack pointer to reflect frame size. */ |
emit_frame_insn |
(gen_rtx_SET (Pmode, stack_pointer_rtx, |
gen_rtx_MINUS (Pmode, stack_pointer_rtx, value_rtx))); |
|
if (frame_info.save_fp_p) |
{ |
emit_frame_insn |
(gen_rtx_SET (Pmode, |
indexed_memory (stack_pointer_rtx, |
frame_info.fp_save_offset), |
frame_pointer_rtx)); |
|
emit_frame_insn |
(gen_rtx_SET (Pmode, frame_pointer_rtx, |
gen_rtx_PLUS (Pmode, frame_pointer_rtx, value_rtx))); |
} |
if (frame_info.save_lr_p) |
{ |
|
emit_frame_insn |
(gen_rtx_SET (Pmode, |
indexed_memory (stack_pointer_rtx, |
frame_info.lr_save_offset), |
gen_rtx_REG (Pmode, LINK_REGNUM))); |
} |
if (frame_info.gpr_size) |
{ |
int offset = 0; |
int regno; |
|
for (regno = 0; regno <= LAST_INT_REG; regno++) |
{ |
HOST_WIDE_INT disp = frame_info.gpr_offset + offset; |
|
if (!(frame_info.mask & (1 << regno))) |
continue; |
|
emit_frame_insn |
(gen_rtx_SET (Pmode, |
indexed_memory (stack_pointer_rtx, disp), |
gen_rtx_REG (Pmode, regno))); |
offset = offset + UNITS_PER_WORD; |
} |
} |
} |
|
/* 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 |
or32_expand_epilogue (int sibcall) |
{ |
int total_size = or32_compute_frame_size (get_frame_size ()); |
rtx value_rtx; |
|
if (total_size > 32767) |
{ |
value_rtx = gen_rtx_REG (Pmode, 3); |
|
emit_insn (gen_rtx_SET (Pmode, value_rtx, GEN_INT (total_size))); |
} |
else |
value_rtx = GEN_INT (total_size); |
|
if (frame_info.save_lr_p) |
{ |
or32_maybe_dead |
(emit_insn |
(gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, LINK_REGNUM), |
indexed_memory (stack_pointer_rtx, |
frame_info.lr_save_offset)))); |
} |
if (frame_info.save_fp_p) |
{ |
emit_insn |
(gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, FRAME_POINTER_REGNUM), |
indexed_memory (stack_pointer_rtx, |
frame_info.fp_save_offset))); |
} |
|
if (frame_info.gpr_size) |
{ |
int offset = 0; |
int regno; |
|
for (regno = 0; regno <= LAST_INT_REG; regno++) |
{ |
HOST_WIDE_INT disp = frame_info.gpr_offset + offset; |
|
if (!(frame_info.mask & (1 << regno))) |
continue; |
|
emit_insn |
(gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, regno), |
indexed_memory (stack_pointer_rtx, disp))); |
offset = offset + UNITS_PER_WORD; |
} |
} |
|
if (total_size) |
{ |
emit_insn (gen_rtx_SET (Pmode, stack_pointer_rtx, |
gen_rtx_PLUS (Pmode, |
stack_pointer_rtx, value_rtx))); |
} |
|
if (!sibcall) |
emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, 9))); |
} |
|
|
void |
print_operand (FILE * file, rtx x, int code) |
{ |
if (code == 'r' && GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == REG) |
fprintf (file, "%s", reg_names[REGNO (XEXP (x, 0))]); |
else if (code == '(') |
{ |
if (dbr_sequence_length ()) |
fprintf (file, "\t# delay slot filled"); |
else |
fprintf (file, "\n\tl.nop\t\t\t# nop delay slot"); |
} |
else if (code == 'C') |
{ |
switch (GET_CODE (x)) |
{ |
case EQ: |
fputs ("eq", file); |
break; |
case NE: |
fputs ("ne", file); |
break; |
case GT: |
fputs ("gts", file); |
break; |
case GE: |
fputs ("ges", file); |
break; |
case LT: |
fputs ("lts", file); |
break; |
case LE: |
fputs ("les", file); |
break; |
case GTU: |
fputs ("gtu", file); |
break; |
case GEU: |
fputs ("geu", file); |
break; |
case LTU: |
fputs ("ltu", file); |
break; |
case LEU: |
fputs ("leu", file); |
break; |
default: |
abort (); |
} |
} |
else if (code == 'H') |
{ |
if (GET_CODE (x) == REG) |
fprintf (file, "%s", reg_names[REGNO (x) + 1]); |
else |
abort (); |
} |
else if (GET_CODE (x) == REG) |
fprintf (file, "%s", reg_names[REGNO (x)]); |
else if (GET_CODE (x) == MEM) |
output_address (XEXP (x, 0)); |
else |
output_addr_const (file, x); |
} |
|
/* Generate assembler code for a movdi/movdf */ |
|
const char * |
or32_output_move_double (rtx * operands) |
{ |
rtx xoperands[3]; |
|
switch (GET_CODE (operands[0])) |
{ |
case REG: |
if (GET_CODE (operands[1]) == REG) |
{ |
if (REGNO (operands[0]) == REGNO (operands[1]) + 1) |
{ |
output_asm_insn ("\tl.or \t%H0, %H1, r0", operands); |
output_asm_insn ("\tl.or \t%0, %1, r0", operands); |
return ""; |
} |
else |
{ |
output_asm_insn ("\tl.or \t%0, %1, r0", operands); |
output_asm_insn ("\tl.or \t%H0, %H1, r0", operands); |
return ""; |
} |
} |
else if (GET_CODE (operands[1]) == MEM) |
{ |
xoperands[1] = XEXP (operands[1], 0); |
if (GET_CODE (xoperands[1]) == REG) |
{ |
xoperands[0] = operands[0]; |
if (REGNO (xoperands[0]) == REGNO (xoperands[1])) |
{ |
output_asm_insn ("\tl.lwz \t%H0, 4(%1)", xoperands); |
output_asm_insn ("\tl.lwz \t%0, 0(%1)", xoperands); |
return ""; |
} |
else |
{ |
output_asm_insn ("\tl.lwz \t%0, 0(%1)", xoperands); |
output_asm_insn ("\tl.lwz \t%H0, 4(%1)", xoperands); |
return ""; |
} |
} |
else if (GET_CODE (xoperands[1]) == PLUS) |
{ |
if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 1)) == REG) |
{ |
xoperands[0] = operands[0]; |
xoperands[1] = XEXP (xoperands[1], 0); |
if (REGNO (xoperands[0]) == REGNO (xoperands[2])) |
{ |
output_asm_insn ("\tl.lwz \t%H0, %1+4(%2)", |
xoperands); |
output_asm_insn ("\tl.lwz \t%0, %1(%2)", xoperands); |
return ""; |
} |
else |
{ |
output_asm_insn ("\tl.lwz \t%0, %1(%2)", xoperands); |
output_asm_insn ("\tl.lwz \t%H0, %1+4(%2)", |
xoperands); |
return ""; |
} |
} |
else if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 0)) == |
REG) |
{ |
xoperands[0] = operands[0]; |
xoperands[1] = XEXP (xoperands[1], 1); |
if (REGNO (xoperands[0]) == REGNO (xoperands[2])) |
{ |
output_asm_insn ("\tl.lwz \t%H0, %1+4(%2)", |
xoperands); |
output_asm_insn ("\tl.lwz \t%0, %1(%2)", xoperands); |
return ""; |
} |
else |
{ |
output_asm_insn ("\tl.lwz \t%0, %1(%2)", xoperands); |
output_asm_insn ("\tl.lwz \t%H0, %1+4(%2)", |
xoperands); |
return ""; |
} |
} |
else |
abort (); |
} |
else |
abort (); |
} |
else if (GET_CODE (operands[1]) == CONST_INT) |
{ |
if (INTVAL (operands[1]) < 0) |
output_asm_insn ("\tl.addi \t%0, r0, -1", operands); |
else |
output_asm_insn ("\tl.or \t%0, r0, r0", operands); |
output_asm_insn ("\tl.movhi \t%H0, hi(%1)", operands); |
output_asm_insn ("\tl.ori \t%H0, %H0, lo(%1)", operands); |
return ""; |
} |
else |
abort (); |
case MEM: |
xoperands[0] = XEXP (operands[0], 0); |
if (GET_CODE (xoperands[0]) == REG) |
{ |
xoperands[1] = operands[1]; |
output_asm_insn ("\tl.sw \t0(%0), %1", xoperands); |
output_asm_insn ("\tl.sw \t4(%0), %H1", xoperands); |
return ""; |
} |
else if (GET_CODE (xoperands[0]) == PLUS) |
{ |
if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 1)) == REG) |
{ |
xoperands[0] = XEXP (xoperands[0], 0); |
xoperands[2] = operands[1]; |
output_asm_insn ("\tl.sw \t%0(%1), %2", xoperands); |
output_asm_insn ("\tl.sw \t%0+4(%1), %H2", xoperands); |
return ""; |
} |
else if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 0)) == REG) |
{ |
xoperands[0] = XEXP (xoperands[0], 1); |
xoperands[2] = operands[1]; |
output_asm_insn ("\tl.sw \t%0(%1), %2", xoperands); |
output_asm_insn ("\tl.sw \t%0+4(%1), %H2", xoperands); |
return ""; |
} |
else |
abort (); |
} |
else |
abort (); |
default: |
abort (); |
} |
} |
|
enum rtx_code |
or32_reverse_condition (enum machine_mode mode ATTRIBUTE_UNUSED, |
enum rtx_code code) |
{ |
return reverse_condition (code); |
} |
|
enum machine_mode |
or32_cc_mode (enum rtx_code code, |
rtx op0 ATTRIBUTE_UNUSED, rtx op1 ATTRIBUTE_UNUSED) |
{ |
|
switch (code) |
{ |
case EQ: |
return CCEQmode; |
case NE: |
return CCNEmode; |
case GEU: |
return CCGEUmode; |
case GTU: |
return CCGTUmode; |
case LTU: |
return CCLTUmode; |
case LEU: |
return CCLEUmode; |
case GE: |
return CCGEmode; |
case LT: |
return CCLTmode; |
case GT: |
return CCGTmode; |
case LE: |
return CCLEmode; |
|
default: |
abort (); |
} |
} |
|
/* Generate insn patterns to do an integer compare of OPERANDS. */ |
|
static rtx |
or32_expand_int_compare (enum rtx_code code, rtx op0, rtx op1) |
{ |
enum machine_mode cmpmode; |
rtx tmp, flags; |
|
cmpmode = SELECT_CC_MODE (code, op0, op1); |
flags = gen_rtx_REG (cmpmode, FLAGS_REG); |
|
/* This is very simple, but making the interface the same as in the |
FP case makes the rest of the code easier. */ |
tmp = gen_rtx_COMPARE (cmpmode, op0, op1); |
emit_insn (gen_rtx_SET (VOIDmode, flags, tmp)); |
|
/* Return the test that should be put into the flags user, i.e. |
the bcc, scc, or cmov instruction. */ |
return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx); |
} |
|
rtx |
or32_expand_compare (enum rtx_code code, rtx op0, rtx op1) |
{ |
return or32_expand_int_compare (code, op0, op1); |
} |
|
void |
or32_expand_branch (enum rtx_code code, rtx label) |
{ |
rtx tmp; |
|
switch (GET_MODE (or32_compare_op0)) |
{ |
case SImode: |
tmp = or32_expand_compare (code, or32_compare_op0, or32_compare_op1); |
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, |
tmp, |
gen_rtx_LABEL_REF (VOIDmode, label), |
pc_rtx); |
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp)); |
return; |
|
|
default: |
abort (); |
} |
|
} |
|
static int |
or32_emit_int_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond) |
{ |
rtx condition_rtx, cr; |
|
if ((GET_MODE (or32_compare_op0) != SImode) && |
(GET_MODE (or32_compare_op0) != HImode) && |
(GET_MODE (or32_compare_op0) != QImode)) |
return 0; |
|
/* We still have to do the compare, because cmov doesn't do a |
compare, it just looks at the FLAG bit set by a previous compare |
instruction. */ |
|
condition_rtx = or32_expand_compare (GET_CODE (op), |
or32_compare_op0, or32_compare_op1); |
|
cr = XEXP (condition_rtx, 0); |
|
emit_insn (gen_cmov (dest, condition_rtx, true_cond, false_cond, cr)); |
|
return 1; |
} |
|
|
/* Emit a conditional move: move TRUE_COND to DEST if OP of the |
operands of the last comparison is nonzero/true, FALSE_COND if it |
is zero/false. Return 0 if the hardware has no such operation. */ |
|
int |
or32_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond) |
{ |
enum machine_mode result_mode = GET_MODE (dest); |
|
if (GET_MODE (true_cond) != result_mode) |
return 0; |
|
if (GET_MODE (false_cond) != result_mode) |
return 0; |
|
/* First, work out if the hardware can do this at all */ |
return or32_emit_int_cmove (dest, op, true_cond, false_cond); |
} |
|
const char * |
output_bf (rtx * operands) |
{ |
enum rtx_code code; |
enum machine_mode mode_calc, mode_got; |
|
code = GET_CODE (operands[1]); |
mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1); |
mode_got = GET_MODE (operands[2]); |
|
if (!TARGET_ALIGNED_JUMPS) |
{ |
if (mode_calc != mode_got) |
return "l.bnf \t%l0%("; |
else |
return "l.bf \t%l0%("; |
} |
else |
{ |
if (mode_calc != mode_got) |
return "\t.balignl 0x8,0x15000015,0x4;l.bnf \t%l0%("; |
else |
return "\t.balignl 0x8,0x15000015,0x4;l.bf \t%l0%("; |
} |
} |
|
const char * |
output_cmov (rtx * operands) |
{ |
enum rtx_code code; |
enum machine_mode mode_calc, mode_got; |
|
code = GET_CODE (operands[1]); |
mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1); |
mode_got = GET_MODE (operands[4]); |
|
if (mode_calc != mode_got) |
return "l.cmov \t%0,%3,%2"; /* reversed */ |
else |
return "l.cmov \t%0,%2,%3"; |
} |
|
/* Any function is ok for sibcall optimization if we allow this optimization |
*/ |
static bool |
or32_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, |
tree exp ATTRIBUTE_UNUSED) |
{ |
return TARGET_SIBCALL; |
} |
|
/* For now this is very simple way for sibcall support */ |
|
void |
or32_expand_sibcall (rtx result, rtx addr, rtx args_size) |
{ |
emit_call_insn (gen_sibcall_internal (addr, args_size)); |
} |
|
|
/* We know it can't be done in one insn when we get here, |
the movsi expander guarantees this. */ |
void |
or32_emit_set_const32 (rtx op0, rtx op1) |
{ |
enum machine_mode mode = GET_MODE (op0); |
rtx temp; |
|
|
if (GET_CODE (op1) == CONST_INT) |
{ |
HOST_WIDE_INT value = INTVAL (op1); |
|
if (CONST_OK_FOR_LETTER_P (value & GET_MODE_MASK (mode), 'K') |
|| CONST_OK_FOR_LETTER_P (value, 'M') |
|| CONST_OK_FOR_LETTER_P (value, 'I')) |
abort (); |
} |
|
|
/* Full 2-insn decomposition is needed. */ |
if (reload_in_progress || reload_completed) |
{ |
temp = op0; |
} |
else |
temp = gen_reg_rtx (mode); |
|
if (GET_CODE (op1) == CONST_INT) |
{ |
/* Emit them as real moves instead of a HIGH/LO_SUM, |
this way CSE can see everything and reuse intermediate |
values if it wants. */ |
emit_insn (gen_rtx_SET (VOIDmode, temp, |
GEN_INT (INTVAL (op1) |
& ~(HOST_WIDE_INT) 0xffff))); |
|
emit_insn (gen_rtx_SET (VOIDmode, |
op0, |
gen_rtx_IOR (mode, temp, |
GEN_INT (INTVAL (op1) & 0xffff)))); |
} |
else |
{ |
|
#if 0 |
/* A symbol, emit in the traditional way. */ |
|
emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_HIGH (mode, op1))); |
emit_insn (gen_rtx_SET (VOIDmode, |
op0, gen_rtx_LO_SUM (mode, temp, op1))); |
#else |
/* since or32 bfd can not deal with relocs that are not of type |
OR32_CONSTH_RELOC + OR32_CONST_RELOC (ie move high must be |
followed by exactly one lo_sum) |
*/ |
emit_insn (gen_movsi_insn_big (op0, op1)); |
#endif |
} |
} |
|
|
/* Functions returning costs and making code size/performance tradeoffs */ |
|
int |
or32_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, |
enum reg_class from ATTRIBUTE_UNUSED, |
enum reg_class to ATTRIBUTE_UNUSED) |
{ |
return 2; |
} |
|
/* A C expressions returning the cost of moving data of MODE from a register to |
or from memory. */ |
|
int |
or32_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, |
enum reg_class class ATTRIBUTE_UNUSED, |
int in ATTRIBUTE_UNUSED) |
{ |
return 2; |
} |
|
/* Specify the cost of a branch insn; roughly the number of extra insns that |
should be added to avoid a branch. |
|
Set this to 3 on the or32 since that is roughly the average cost of an |
unscheduled conditional branch. |
|
Cost of 2 and 3 give equal and ~0.7% bigger binaries |
|
*/ |
int |
or32_branch_cost () |
{ |
return 1; |
} |
/config/or32/or32-modes.def
0,0 → 1,39
/* Definitions of target machine for GNU compiler, for IBM RS/6000. |
Copyright (C) 2002, 2003 Free Software Foundation, Inc. |
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify it |
under the terms of the GNU General Public License as published |
by the Free Software Foundation; either version 2, or (at your |
option) any later version. |
|
GCC is distributed in the hope that it will be useful, but WITHOUT |
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING. If not, write to the |
Free Software Foundation, 59 Temple Place - Suite 330, Boston, |
MA 02111-1307, USA. */ |
|
/* Add any extra modes needed to represent the condition code. |
*/ |
|
CC_MODE (CCEQ); |
CC_MODE (CCNE); |
|
CC_MODE (CCLE); |
CC_MODE (CCGE); |
CC_MODE (CCLT); |
CC_MODE (CCGT); |
|
CC_MODE (CCLEU); |
CC_MODE (CCGEU); |
CC_MODE (CCLTU); |
CC_MODE (CCGTU); |
|
CC_MODE(CCFP); |
CC_MODE(CCUNS); |
/config/or32/linux-elf.h
0,0 → 1,65
/* Definitions for or32 running Linux-based GNU systems using ELF |
Copyright (C) 2002, 2005 |
Free Software Foundation, Inc. |
Contributed by Marko Mlinar <markom@opencores.org> |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
/* elfos.h should have already been included. Now just override |
any conflicting definitions and add any extras. */ |
|
/* Run-time Target Specification. */ |
#undef TARGET_VERSION |
#define TARGET_VERSION fputs (" (OR32 GNU/Linux with ELF)", stderr); |
|
/* Do not assume anything about header files. */ |
#define NO_IMPLICIT_EXTERN_C |
|
#undef USER_LABEL_PREFIX |
#define USER_LABEL_PREFIX "_" |
|
|
/* This is how we tell the assembler that two symbols have the same value. */ |
#define ASM_OUTPUT_DEF(FILE, NAME1, NAME2) \ |
do \ |
{ \ |
assemble_name (FILE, NAME1); \ |
fputs (" = ", FILE); \ |
assemble_name (FILE, NAME2); \ |
fputc ('\n', FILE); \ |
} \ |
while (0) |
|
|
#if 0 |
/* Node: Label Output */ |
|
#define SET_ASM_OP "\t.set\t" |
|
#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \ |
(*targetm.asm_out.globalize_label) (FILE, XSTR (FUN, 0)) |
|
#define ASM_WEAKEN_LABEL(FILE, NAME) \ |
do \ |
{ \ |
fputs ("\t.weak\t", (FILE)); \ |
assemble_name ((FILE), (NAME)); \ |
fputc ('\n', (FILE)); \ |
} \ |
while (0) |
#endif |
/config/or32/default.h
0,0 → 1,74
/* Definitions of target machine for GNU compiler for OR1K running Linux. |
(Actually OR1K is not yet running Linux but eventually it will). |
Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc. |
Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999. |
Based upon the rs6000 port. |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#undef CPP_PREDEFINES |
#define CPP_PREDEFINES \ |
"-DOR1K -D__or1k__ -D__OR1K__ -D__ELF__ -Dunix -Dlinux -Dor1k -Asystem(unix) -Asystem(linux) -Acpu(or1k) -Amachine(or1k)" |
|
#undef CPP_OS_DEFAULT_SPEC |
#define CPP_OS_DEFAULT_SPEC "%(cpp_os_linux)" |
|
#undef LINK_SPEC |
#define LINK_SPEC "-m elf32or32 %{G*} %{shared:-shared} \ |
%{!shared: \ |
%{!static: \ |
%{rdynamic:-export-dynamic} \ |
%{!dynamic-linker:-dynamic-linker /lib/ld.so.1}} \ |
%{static:-static}}" |
|
#undef LIB_DEFAULT_SPEC |
#define LIB_DEFAULT_SPEC "%(lib_linux)" |
|
#undef STARTFILE_DEFAULT_SPEC |
#define STARTFILE_DEFAULT_SPEC "%(startfile_linux)" |
|
#undef ENDFILE_DEFAULT_SPEC |
#define ENDFILE_DEFAULT_SPEC "%(endfile_linux)" |
|
#undef LINK_START_DEFAULT_SPEC |
#define LINK_START_DEFAULT_SPEC "%(link_start_linux)" |
|
#undef LINK_OS_DEFAULT_SPEC |
#define LINK_OS_DEFAULT_SPEC "%(link_os_linux)" |
|
#undef TARGET_VERSION |
#define TARGET_VERSION fprintf (stderr, " (OpenRISC 1000 GNU/Linux)"); |
|
/* Define this macro as a C expression for the initializer of an |
array of string to tell the driver program which options are |
defaults for this target and thus do not need to be handled |
specially when using `MULTILIB_OPTIONS'. |
|
Do not define this macro if `MULTILIB_OPTIONS' is not defined in |
the target makefile fragment or if none of the options listed in |
`MULTILIB_OPTIONS' are set by default. *Note Target Fragment::. */ |
|
#undef MULTILIB_DEFAULTS |
#define MULTILIB_DEFAULTS { "mbig", "mcall-linux" } |
|
#undef DEFAULT_VTABLE_THUNKS |
#ifndef USE_GNULIBC_1 |
#define DEFAULT_VTABLE_THUNKS 1 |
#endif |
|
#undef JUMP_TABLES_IN_TEXT_SECTION |
#define JUMP_TABLES_IN_TEXT_SECTION 0 |
/config/or32/elf.h
0,0 → 1,45
/* Definitions for rtems targeting an OpenRisc OR32 using COFF |
Copyright (C) 1996, 1997, 2005 Free Software Foundation, Inc. |
Contributed by Joel Sherrill (joel@OARcorp.com). |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GNU CC; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
#undef TARGET_VERSION |
#define TARGET_VERSION fputs (" (OR32/ELF)", stderr); |
|
/* Use ELF */ |
#undef OBJECT_FORMAT_ELF |
#define OBJECT_FORMAT_ELF |
|
/* use SDB debugging info and make it default */ |
#undef DBX_DEBUGGING_INFO |
#define DBX_DEBUGGING_INFO |
|
#undef PREFERRED_DEBUGGING_TYPE |
#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG |
|
#undef PUT_SDB_DEF |
#define PUT_SDB_DEF |
|
/* Generate calls to memcpy, memcmp and memset. */ |
#ifndef TARGET_MEM_FUNCTIONS |
# define TARGET_MEM_FUNCTIONS |
#endif |
|
#undef USER_LABEL_PREFIX |
#define USER_LABEL_PREFIX "_" |
/config/or32/linux-gas.h
0,0 → 1,37
/* Definitions of target machine for GNU compiler. |
Or32 Linux-based GNU systems version. |
Copyright (C) 2002, 2005 Free Software Foundation, Inc. |
Contributed by Marko Mlinar <markom@opencores.org> |
|
This file is part of GNU CC. |
|
GNU CC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2, or (at your option) |
any later version. |
|
GNU CC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
/* Unsigned chars produces much better code than signed. */ |
#undef DEFAULT_SIGNED_CHAR |
#define DEFAULT_SIGNED_CHAR 1 |
|
/* Make gcc agree with <machine/ansi.h> */ |
|
#define SIZE_TYPE "unsigned int" |
#define PTRDIFF_TYPE "int" |
#define WCHAR_TYPE "unsigned int" |
#define WCHAR_TYPE_SIZE 32 |
|
|
/* Clear the instruction cache from `beg' to `end'. This makes an |
inline system call to SYS_cacheflush. */ |
#define CLEAR_INSN_CACHE(BEG, END) /* Do something here !!! */ |