URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/mmix
- from Rev 154 to Rev 816
- ↔ Reverse comparison
Rev 154 → Rev 816
/crti.asm
0,0 → 1,121
/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson <hp@bitrange.com> |
|
This file is free software; you can redistribute it and/or modify it |
under the terms of the GNU General Public License as published by the |
Free Software Foundation; either version 2, or (at your option) any |
later version. |
|
In addition to the permissions in the GNU General Public License, the |
Free Software Foundation gives you unlimited permission to link the |
compiled version of this file into combinations with other programs, |
and to distribute those combinations without any restriction coming |
from the use of this file. (The General Public License restrictions |
do apply in other respects; for example, they cover modification of |
the file, and distribution when not linked into a combine |
executable.) |
|
This file is distributed in the hope that it will be useful, but |
WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; see the file COPYING. If not, write to |
the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
Boston, MA 02110-1301, USA. */ |
|
% This is the crt0 equivalent for mmix-knuth-mmixware, for setting up |
% things for compiler-generated assembly-code and for setting up things |
% between where the simulator calls and main, and shutting things down on |
% the way back. There's an actual crt0.o elsewhere, but that's a dummy. |
|
% This file and the GCC output are supposed to be *reasonably* |
% mmixal-compatible to enable people to re-use output with Knuth's mmixal. |
% However, forward references are used more freely: we are using the |
% binutils tools. Users of mmixal beware; you will sometimes have to |
% re-order things or use temporary variables. |
|
% Users of mmixal will want to set up 8H and 9H to be .text and .data |
% respectively, so the compiler can switch between them pretending they're |
% segments. |
|
% This little treasure is here so the 32 lowest address bits of user data |
% will not be zero. Because of truncation, that would cause testcase |
% gcc.c-torture/execute/980701-1.c to incorrectly fail. |
|
.data ! mmixal:= 8H LOC Data_Segment |
.p2align 3 |
LOC @+(8-@)@7 |
OCTA 2009 |
|
.text ! mmixal:= 9H LOC 8B; LOC #100 |
.global Main |
|
% The __Stack_start symbol is provided by the link script. |
stackpp OCTA __Stack_start |
|
% "Main" is the magic symbol the simulator jumps to. We want to go |
% on to "main". |
% We need to set rG explicitly to avoid hard-to-debug situations. |
Main SETL $255,32 |
PUT rG,$255 |
|
% Initialize the stack pointer. It is supposedly made a global |
% zero-initialized (allowed to change) register in crtn.asm; we use the |
% explicit number. |
GETA $255,stackpp |
LDOU $254,$255,0 |
|
% Make sure we get more than one mem, to simplify counting cycles. |
LDBU $255,$1,0 |
LDBU $255,$1,1 |
|
PUSHJ $2,_init |
|
#ifdef __MMIX_ABI_GNU__ |
% Copy argc and argv from their initial position to argument registers |
% where necessary. |
SET $231,$0 |
SET $232,$1 |
#else |
% For the mmixware ABI, we need to move arguments. The return value will |
% appear in $0. |
SET $2,$1 |
SET $1,$0 |
#endif |
|
PUSHJ $0,main |
JMP exit |
|
% Provide the first part of _init and _fini. Save the return address on the |
% register stack. We eventually ignore the return address of these |
% PUSHJ:s, so it doesn't matter that whether .init and .fini code calls |
% functions or where they store rJ. We shouldn't get there, so die |
% (TRAP Halt) if that happens. |
|
.section .init,"ax",@progbits |
.global _init |
_init: |
GET $0,:rJ |
PUSHJ $1,0F |
SETL $255,255 |
TRAP 0,0,0 |
0H IS @ |
|
% Register _fini to be executed as the last atexit function. |
#ifdef __MMIX_ABI_GNU__ |
GETA $231,_fini |
#else |
GETA $1,_fini |
#endif |
PUSHJ $0,atexit |
|
.section .fini,"ax",@progbits |
.global _fini |
_fini: |
GET $0,:rJ |
PUSHJ $1,0F |
SETL $255,255 |
TRAP 0,0,0 |
0H IS @ |
/mmix.h
0,0 → 1,1004
/* Definitions of target machine for GNU compiler, for MMIX. |
Copyright (C) 2000, 2001, 2002, 2004, 2005, 2007 Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson (hp@bitrange.com) |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 3, or (at your option) |
any later version. |
|
GCC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
#ifndef GCC_MMIX_H |
#define GCC_MMIX_H |
|
/* First, some local helper macros. Note that the "default" value of |
FIXED_REGISTERS, CALL_USED_REGISTERS, REG_ALLOC_ORDER and |
REG_CLASS_CONTENTS depend on these values. */ |
#define MMIX_RESERVED_GNU_ARG_0_REGNUM 231 |
#define MMIX_FIRST_ARG_REGNUM \ |
(TARGET_ABI_GNU ? MMIX_RESERVED_GNU_ARG_0_REGNUM : 16) |
#define MMIX_FIRST_INCOMING_ARG_REGNUM \ |
(TARGET_ABI_GNU ? MMIX_RESERVED_GNU_ARG_0_REGNUM : 0) |
#define MMIX_MAX_ARGS_IN_REGS 16 |
|
/* FIXME: This one isn't fully implemented yet. Return values larger than |
one register are passed by reference in MMIX_STRUCT_VALUE_REGNUM by the |
caller, except for return values of type "complex". */ |
#define MMIX_MAX_REGS_FOR_VALUE 16 |
#define MMIX_RETURN_VALUE_REGNUM \ |
(TARGET_ABI_GNU ? MMIX_RESERVED_GNU_ARG_0_REGNUM : 15) |
#define MMIX_OUTGOING_RETURN_VALUE_REGNUM \ |
(TARGET_ABI_GNU ? MMIX_RESERVED_GNU_ARG_0_REGNUM : 0) |
#define MMIX_STRUCT_VALUE_REGNUM 251 |
#define MMIX_STATIC_CHAIN_REGNUM 252 |
#define MMIX_FRAME_POINTER_REGNUM 253 |
#define MMIX_STACK_POINTER_REGNUM 254 |
#define MMIX_LAST_GENERAL_REGISTER 255 |
#define MMIX_INCOMING_RETURN_ADDRESS_REGNUM MMIX_rJ_REGNUM |
#define MMIX_HIMULT_REGNUM 258 |
#define MMIX_REMAINDER_REGNUM MMIX_rR_REGNUM |
#define MMIX_ARG_POINTER_REGNUM 261 |
#define MMIX_rO_REGNUM 262 |
#define MMIX_LAST_STACK_REGISTER_REGNUM 31 |
|
/* Four registers; "ideally, these registers should be call-clobbered", so |
just grab a bunch of the common clobbered registers. FIXME: Last |
registers of return-value should be used, with an error if there's a |
return-value (that collides in size). */ |
#define MMIX_EH_RETURN_DATA_REGNO_START (MMIX_STRUCT_VALUE_REGNUM - 4) |
|
/* Try to keep the definitions from running away on their own. */ |
#if (MMIX_EH_RETURN_DATA_REGNO_START \ |
!= MMIX_RESERVED_GNU_ARG_0_REGNUM + MMIX_MAX_ARGS_IN_REGS) |
#error MMIX register definition inconsistency |
#endif |
|
#if (MMIX_MAX_REGS_FOR_VALUE + MMIX_MAX_ARGS_IN_REGS > 32) |
#error MMIX parameters and return values bad, more than 32 registers |
#endif |
|
/* This chosen as "a call-clobbered hard register that is otherwise |
untouched by the epilogue". */ |
#define MMIX_EH_RETURN_STACKADJ_REGNUM MMIX_STATIC_CHAIN_REGNUM |
|
#ifdef REG_OK_STRICT |
# define MMIX_REG_OK_STRICT 1 |
#else |
# define MMIX_REG_OK_STRICT 0 |
#endif |
|
#define MMIX_FUNCTION_ARG_SIZE(MODE, TYPE) \ |
((MODE) != BLKmode ? GET_MODE_SIZE (MODE) : int_size_in_bytes (TYPE)) |
|
/* Declarations for helper variables that are not tied to a particular |
target macro. */ |
extern GTY(()) rtx mmix_compare_op0; |
extern GTY(()) rtx mmix_compare_op1; |
|
/* Per-function machine data. This is normally an opaque type just |
defined and used in the tm.c file, but we need to see the definition in |
mmix.md too. */ |
struct machine_function GTY(()) |
{ |
int has_landing_pad; |
int highest_saved_stack_register; |
int in_prologue; |
}; |
|
/* For these target macros, there is no generic documentation here. You |
should read `Using and Porting GCC' for that. Only comments specific |
to the MMIX target are here. |
|
There are however references to the specific texinfo node (comments |
with "Node:"), so there should be little or nothing amiss. Probably |
the opposite, since we don't have to care about old littering and |
soon outdated generic comments. */ |
|
/* Node: Driver */ |
|
/* User symbols are in the same name-space as built-in symbols, but we |
don't need the built-in symbols, so remove those and instead apply |
stricter operand checking. Don't warn when expanding insns. */ |
#define ASM_SPEC "-no-predefined-syms -x" |
|
/* Pass on -mset-program-start=N and -mset-data-start=M to the linker. |
Provide default program start 0x100 unless -mno-set-program-start. |
Don't do this if linking relocatably, with -r. For a final link, |
produce mmo, unless ELF is requested or when linking relocatably. */ |
#define LINK_SPEC \ |
"%{mset-program-start=*:--defsym __.MMIX.start..text=%*}\ |
%{mset-data-start=*:--defsym __.MMIX.start..data=%*}\ |
%{!mset-program-start=*:\ |
%{!mno-set-program-start:\ |
%{!r:--defsym __.MMIX.start..text=0x100}}}\ |
%{!melf:%{!r:-m mmo}}%{melf|r:-m elf64mmix}" |
|
/* FIXME: There's no provision for profiling here. */ |
#define STARTFILE_SPEC \ |
"crti%O%s crtbegin%O%s" |
|
#define ENDFILE_SPEC "crtend%O%s crtn%O%s" |
|
/* Node: Run-time Target */ |
|
/* Define __LONG_MAX__, since we're advised not to change glimits.h. */ |
#define TARGET_CPU_CPP_BUILTINS() \ |
do \ |
{ \ |
builtin_define ("__mmix__"); \ |
builtin_define ("__MMIX__"); \ |
if (TARGET_ABI_GNU) \ |
builtin_define ("__MMIX_ABI_GNU__"); \ |
else \ |
builtin_define ("__MMIX_ABI_MMIXWARE__"); \ |
} \ |
while (0) |
|
extern int target_flags; |
|
#define TARGET_DEFAULT \ |
(MASK_BRANCH_PREDICT | MASK_BASE_ADDRESSES | MASK_USE_RETURN_INSN) |
|
/* Unfortunately, this must not reference anything in "mmix.c". */ |
#define TARGET_VERSION \ |
fprintf (stderr, " (MMIX)") |
|
#define OVERRIDE_OPTIONS mmix_override_options () |
|
#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) \ |
do \ |
{ \ |
if (LEVEL >= 1) \ |
flag_regmove = TRUE; \ |
\ |
if (SIZE || LEVEL > 1) \ |
{ \ |
flag_omit_frame_pointer = TRUE; \ |
} \ |
} \ |
while (0) |
|
/* This one will have to wait a little bit; right now we can't debug |
neither with or without a frame-pointer. */ |
/* #define CAN_DEBUG_WITHOUT_FP */ |
|
|
/* Node: Per-Function Data */ |
#define INIT_EXPANDERS mmix_init_expanders () |
|
|
/* Node: Storage Layout */ |
/* I see no bit-field instructions. Anyway, the common order is from low |
to high, as the power of two, hence little-endian. */ |
#define BITS_BIG_ENDIAN 0 |
#define BYTES_BIG_ENDIAN 1 |
#define WORDS_BIG_ENDIAN 1 |
#define FLOAT_WORDS_BIG_ENDIAN 1 |
#define UNITS_PER_WORD 8 |
|
/* FIXME: Promotion of modes currently generates slow code, extending |
before every operation. */ |
/* I'm a little bit undecided about this one. It might be beneficial to |
promote all operations. */ |
|
#define PROMOTE_FUNCTION_MODE(MODE, UNSIGNEDP, TYPE) \ |
do { \ |
if (GET_MODE_CLASS (MODE) == MODE_INT \ |
&& GET_MODE_SIZE (MODE) < 8) \ |
{ \ |
(MODE) = DImode; \ |
/* Do the following some time later, \ |
scrutinizing differences. */ \ |
if (0) (UNSIGNEDP) = 0; \ |
} \ |
} while (0) |
|
/* We need to align everything to 64 bits that can affect the alignment |
of other types. Since address N is interpreted in MMIX as (N modulo |
access_size), we must align. */ |
#define PARM_BOUNDARY 64 |
#define STACK_BOUNDARY 64 |
#define FUNCTION_BOUNDARY 32 |
#define BIGGEST_ALIGNMENT 64 |
|
/* This one is only used in the ADA front end. */ |
#define MINIMUM_ATOMIC_ALIGNMENT 8 |
|
/* Copied from elfos.h. */ |
#define MAX_OFILE_ALIGNMENT (32768 * 8) |
|
#define DATA_ALIGNMENT(TYPE, BASIC_ALIGN) \ |
mmix_data_alignment (TYPE, BASIC_ALIGN) |
|
#define CONSTANT_ALIGNMENT(CONSTANT, BASIC_ALIGN) \ |
mmix_constant_alignment (CONSTANT, BASIC_ALIGN) |
|
#define LOCAL_ALIGNMENT(TYPE, BASIC_ALIGN) \ |
mmix_local_alignment (TYPE, BASIC_ALIGN) |
|
/* Following other ports, this seems to most commonly be the word-size, |
so let's do that here too. */ |
#define EMPTY_FIELD_BOUNDARY 64 |
|
/* We chose to have this low solely for similarity with the alpha. It has |
nothing to do with passing the tests dg/c99-scope-2 and |
execute/align-1.c. Nothing. Though the tests seem wrong. Padding of |
the structure is automatically added to get alignment when needed if we |
set this to just byte-boundary. */ |
#define STRUCTURE_SIZE_BOUNDARY 8 |
|
/* The lower bits are ignored. */ |
#define STRICT_ALIGNMENT 1 |
|
|
/* Node: Type Layout */ |
|
/* It might seem more natural to have 64-bit ints on a 64-bit machine, |
but then an occasional MMIX programmer needs to know how to put a lot |
of __attribute__ stuff to get to the 8, 16 and 32-bit modes rather |
than the "intuitive" char, short and int types. */ |
#define INT_TYPE_SIZE 32 |
#define SHORT_TYPE_SIZE 16 |
#define LONG_LONG_TYPE_SIZE 64 |
|
#define FLOAT_TYPE_SIZE 32 |
#define DOUBLE_TYPE_SIZE 64 |
#define LONG_DOUBLE_TYPE_SIZE 64 |
|
#define DEFAULT_SIGNED_CHAR 1 |
|
|
/* Node: Register Basics */ |
/* We tell GCC about all 256 general registers, and we also include |
rD, rE, rH, rJ, rR and rO (in that order) so we can describe what insns |
clobber them. We use a faked register for the argument pointer. It is |
always eliminated towards the frame-pointer or the stack-pointer, never |
output in assembly. Any fixed register would do for this, like $255, |
but future debugging is easier when using a separate register. It |
counts as a global register for pseudorandom reasons. */ |
#define FIRST_PSEUDO_REGISTER 263 |
|
/* We treat general registers with no assigned purpose as fixed. The |
stack pointer, $254, is also fixed. Register $255 is referred to as a |
temporary register in the MMIX papers, and used as such in mmixal, so |
it should not be used as a stack pointer. We set it to fixed, and use |
it "manually" at times of despair. */ |
#define FIXED_REGISTERS \ |
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, \ |
1, 1, 0, 0, 0, 1, 1 \ |
} |
|
/* General registers are fixed and therefore "historically" marked |
call-used. (FIXME: This has changed). Registers $15..$31 are |
call-clobbered; we'll put arguments in $16 and up, and we need $15 for |
the MMIX register-stack "hole". */ |
#define CALL_USED_REGISTERS \ |
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, \ |
1, 1, 1, 1, 1, 1, 1 \ |
} |
|
#define CONDITIONAL_REGISTER_USAGE mmix_conditional_register_usage () |
|
/* No INCOMING_REGNO or OUTGOING_REGNO, since those macros are not usable |
for MMIX: it doesn't have a fixed register window size. FIXME: Perhaps |
we should say something about $0..$15 may sometimes be the incoming |
$16..$31. Those macros need better documentation; it looks like |
they're just bogus and that FUNCTION_INCOMING_ARG_REGNO_P and |
FUNCTION_OUTGOING_VALUE should be used where they're used. For the |
moment, do nothing; things seem to work anyway. */ |
|
/* Defining LOCAL_REGNO is necessary in presence of prologue/epilogue, |
else GCC will be confused that those registers aren't saved and |
restored. */ |
#define LOCAL_REGNO(REGNO) mmix_local_regno (REGNO) |
|
/* Node: Allocation Order */ |
|
/* We should allocate registers from 0 to 31 by increasing number, because |
I think that's what people expect. Beyond that, just use |
call-clobbered global registers first, then call-clobbered special |
registers. Last, the fixed registers. */ |
#define MMIX_MMIXWARE_ABI_REG_ALLOC_ORDER \ |
{ 0, 1, 2, 3, 4, 5, 6, 7, \ |
8, 9, 10, 11, 12, 13, 14, 15, \ |
16, 17, 18, 19, 20, 21, 22, 23, \ |
24, 25, 26, 27, 28, 29, 30, 31, \ |
\ |
252, 251, 250, 249, 248, 247, \ |
\ |
253, \ |
\ |
258, 260, 259, \ |
\ |
32, 33, 34, 35, 36, 37, 38, 39, \ |
40, 41, 42, 43, 44, 45, 46, 47, \ |
48, 49, 50, 51, 52, 53, 54, 55, \ |
56, 57, 58, 59, 60, 61, 62, 63, \ |
64, 65, 66, 67, 68, 69, 70, 71, \ |
72, 73, 74, 75, 76, 77, 78, 79, \ |
80, 81, 82, 83, 84, 85, 86, 87, \ |
88, 89, 90, 91, 92, 93, 94, 95, \ |
96, 97, 98, 99, 100, 101, 102, 103, \ |
104, 105, 106, 107, 108, 109, 110, 111, \ |
112, 113, 114, 115, 116, 117, 118, 119, \ |
120, 121, 122, 123, 124, 125, 126, 127, \ |
128, 129, 130, 131, 132, 133, 134, 135, \ |
136, 137, 138, 139, 140, 141, 142, 143, \ |
144, 145, 146, 147, 148, 149, 150, 151, \ |
152, 153, 154, 155, 156, 157, 158, 159, \ |
160, 161, 162, 163, 164, 165, 166, 167, \ |
168, 169, 170, 171, 172, 173, 174, 175, \ |
176, 177, 178, 179, 180, 181, 182, 183, \ |
184, 185, 186, 187, 188, 189, 190, 191, \ |
192, 193, 194, 195, 196, 197, 198, 199, \ |
200, 201, 202, 203, 204, 205, 206, 207, \ |
208, 209, 210, 211, 212, 213, 214, 215, \ |
216, 217, 218, 219, 220, 221, 222, 223, \ |
224, 225, 226, 227, 228, 229, 230, 231, \ |
232, 233, 234, 235, 236, 237, 238, 239, \ |
240, 241, 242, 243, 244, 245, 246, \ |
\ |
254, 255, 256, 257, 261, 262 \ |
} |
|
/* As a convenience, we put this nearby, for ease of comparison. |
First, call-clobbered registers in reverse order of assignment as |
parameters (also the top ones; not because they're parameters, but |
for continuity). |
|
Second, saved registers that go on the register-stack. |
|
Third, special registers rH, rR and rJ. They should not normally be |
allocated, but since they're call-clobbered, it is cheaper to use one |
of them than using a call-saved register for a call-clobbered use, |
assuming it is referenced a very limited number of times. Other global |
and fixed registers come next; they are never allocated. */ |
#define MMIX_GNU_ABI_REG_ALLOC_ORDER \ |
{ 252, 251, 250, 249, 248, 247, 246, \ |
245, 244, 243, 242, 241, 240, 239, 238, \ |
237, 236, 235, 234, 233, 232, 231, \ |
\ |
0, 1, 2, 3, 4, 5, 6, 7, \ |
8, 9, 10, 11, 12, 13, 14, 15, \ |
16, 17, 18, 19, 20, 21, 22, 23, \ |
24, 25, 26, 27, 28, 29, 30, 31, \ |
\ |
253, \ |
\ |
258, 260, 259, \ |
\ |
32, 33, 34, 35, 36, 37, 38, 39, \ |
40, 41, 42, 43, 44, 45, 46, 47, \ |
48, 49, 50, 51, 52, 53, 54, 55, \ |
56, 57, 58, 59, 60, 61, 62, 63, \ |
64, 65, 66, 67, 68, 69, 70, 71, \ |
72, 73, 74, 75, 76, 77, 78, 79, \ |
80, 81, 82, 83, 84, 85, 86, 87, \ |
88, 89, 90, 91, 92, 93, 94, 95, \ |
96, 97, 98, 99, 100, 101, 102, 103, \ |
104, 105, 106, 107, 108, 109, 110, 111, \ |
112, 113, 114, 115, 116, 117, 118, 119, \ |
120, 121, 122, 123, 124, 125, 126, 127, \ |
128, 129, 130, 131, 132, 133, 134, 135, \ |
136, 137, 138, 139, 140, 141, 142, 143, \ |
144, 145, 146, 147, 148, 149, 150, 151, \ |
152, 153, 154, 155, 156, 157, 158, 159, \ |
160, 161, 162, 163, 164, 165, 166, 167, \ |
168, 169, 170, 171, 172, 173, 174, 175, \ |
176, 177, 178, 179, 180, 181, 182, 183, \ |
184, 185, 186, 187, 188, 189, 190, 191, \ |
192, 193, 194, 195, 196, 197, 198, 199, \ |
200, 201, 202, 203, 204, 205, 206, 207, \ |
208, 209, 210, 211, 212, 213, 214, 215, \ |
216, 217, 218, 219, 220, 221, 222, 223, \ |
224, 225, 226, 227, 228, 229, 230, \ |
\ |
254, 255, 256, 257, 261, 262 \ |
} |
|
/* The default one. */ |
#define REG_ALLOC_ORDER MMIX_MMIXWARE_ABI_REG_ALLOC_ORDER |
|
/* Node: Values in Registers */ |
|
#define HARD_REGNO_NREGS(REGNO, MODE) \ |
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \ |
/ UNITS_PER_WORD) |
|
#define HARD_REGNO_MODE_OK(REGNO, MODE) 1 |
|
/* Note that no register can really be accessed in single-float mode, so |
we *can* say 1 here. FIXME: Will TRT happen for single-float, or do |
we have to punt to libgcc1.asm? */ |
#define MODES_TIEABLE_P(MODE1, MODE2) 1 |
|
|
/* Node: Leaf Functions */ |
/* (empty) */ |
|
|
/* Node: Register Classes */ |
|
enum reg_class |
{ |
NO_REGS, GENERAL_REGS, REMAINDER_REG, HIMULT_REG, |
SYSTEM_REGS, ALL_REGS, LIM_REG_CLASSES |
}; |
|
#define N_REG_CLASSES (int) LIM_REG_CLASSES |
|
#define REG_CLASS_NAMES \ |
{"NO_REGS", "GENERAL_REGS", "REMAINDER_REG", "HIMULT_REG", \ |
"SYSTEM_REGS", "ALL_REGS"} |
|
/* Note that the contents of each item is always 32 bits. */ |
#define REG_CLASS_CONTENTS \ |
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, \ |
{~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0x20}, \ |
{0, 0, 0, 0, 0, 0, 0, 0, 0x10}, \ |
{0, 0, 0, 0, 0, 0, 0, 0, 4}, \ |
{0, 0, 0, 0, 0, 0, 0, 0, 0x7f}, \ |
{~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0x7f}} |
|
#define REGNO_REG_CLASS(REGNO) \ |
((REGNO) <= MMIX_LAST_GENERAL_REGISTER \ |
|| (REGNO) == MMIX_ARG_POINTER_REGNUM \ |
? GENERAL_REGS \ |
: (REGNO) == MMIX_REMAINDER_REGNUM ? REMAINDER_REG \ |
: (REGNO) == MMIX_HIMULT_REGNUM ? HIMULT_REG : SYSTEM_REGS) |
|
#define BASE_REG_CLASS GENERAL_REGS |
|
#define INDEX_REG_CLASS GENERAL_REGS |
|
#define REG_CLASS_FROM_LETTER(CHAR) \ |
((CHAR) == 'x' ? SYSTEM_REGS \ |
: (CHAR) == 'y' ? REMAINDER_REG \ |
: (CHAR) == 'z' ? HIMULT_REG : NO_REGS) |
|
#define REGNO_OK_FOR_BASE_P(REGNO) \ |
((REGNO) <= MMIX_LAST_GENERAL_REGISTER \ |
|| (REGNO) == MMIX_ARG_POINTER_REGNUM \ |
|| (reg_renumber[REGNO] > 0 \ |
&& reg_renumber[REGNO] <= MMIX_LAST_GENERAL_REGISTER)) |
|
#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_BASE_P (REGNO) |
|
#define PREFERRED_RELOAD_CLASS(X, CLASS) \ |
mmix_preferred_reload_class (X, CLASS) |
|
#define PREFERRED_OUTPUT_RELOAD_CLASS(X, CLASS) \ |
mmix_preferred_output_reload_class (X, CLASS) |
|
#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ |
mmix_secondary_reload_class (CLASS, MODE, X, 1) |
|
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ |
mmix_secondary_reload_class (CLASS, MODE, X, 0) |
|
#define CLASS_MAX_NREGS(CLASS, MODE) HARD_REGNO_NREGS (CLASS, MODE) |
|
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ |
mmix_const_ok_for_letter_p (VALUE, C) |
|
#define EXTRA_CONSTRAINT(VALUE, C) \ |
mmix_extra_constraint (VALUE, C, MMIX_REG_OK_STRICT) |
|
/* Do we need anything serious here? Yes, any FLOT constant. */ |
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ |
mmix_const_double_ok_for_letter_p (VALUE, C) |
|
|
/* Node: Frame Layout */ |
|
#define STACK_GROWS_DOWNWARD |
#define FRAME_GROWS_DOWNWARD 1 |
|
#define STARTING_FRAME_OFFSET \ |
mmix_starting_frame_offset () |
|
#define FIRST_PARM_OFFSET(FUNDECL) 0 |
|
#define DYNAMIC_CHAIN_ADDRESS(FRAMEADDR) \ |
mmix_dynamic_chain_address (FRAMEADDR) |
|
/* FIXME: It seems RETURN_ADDR_OFFSET is undocumented. */ |
|
#define SETUP_FRAME_ADDRESSES() \ |
mmix_setup_frame_addresses () |
|
#define RETURN_ADDR_RTX(COUNT, FRAME) \ |
mmix_return_addr_rtx (COUNT, FRAME) |
|
/* It's in rJ before we store it somewhere. */ |
#define INCOMING_RETURN_ADDR_RTX \ |
gen_rtx_REG (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM) |
|
/* FIXME: This does not seem properly documented or cross-indexed. |
Nowhere except in the code does it say it *has* to be in the range |
0..255, or else it will be truncated. That goes for the default too. */ |
#define DWARF_FRAME_RETURN_COLUMN \ |
DWARF_FRAME_REGNUM (MMIX_INCOMING_RETURN_ADDRESS_REGNUM) |
|
/* No return address is stored there. */ |
#define INCOMING_FRAME_SP_OFFSET 0 |
|
/* Node: Stack Checking */ |
/* (empty) */ |
|
|
/* Node: Exception Handling */ |
|
#define EH_RETURN_DATA_REGNO(N) \ |
mmix_eh_return_data_regno (N) |
|
#define EH_RETURN_STACKADJ_RTX \ |
mmix_eh_return_stackadj_rtx () |
|
#define EH_RETURN_HANDLER_RTX \ |
mmix_eh_return_handler_rtx () |
|
#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \ |
mmix_asm_preferred_eh_data_format (CODE, GLOBAL) |
|
/* Node: Frame Registers */ |
#define STACK_POINTER_REGNUM MMIX_STACK_POINTER_REGNUM |
|
/* Perhaps we can use HARD_FRAME_POINTER_REGNUM and decide later on |
what register we want to use. */ |
#define FRAME_POINTER_REGNUM MMIX_FRAME_POINTER_REGNUM |
#define ARG_POINTER_REGNUM MMIX_ARG_POINTER_REGNUM |
|
#define STATIC_CHAIN_REGNUM MMIX_STATIC_CHAIN_REGNUM |
|
|
/* Node: Elimination */ |
/* FIXME: Is this requirement built-in? Anyway, we should try to get rid |
of it; we can deduce the value. */ |
#define FRAME_POINTER_REQUIRED current_function_has_nonlocal_label |
|
/* The frame-pointer is stored in a location that either counts to the |
offset of incoming parameters, or that counts to the offset of the |
frame, so we can't use a single offset. We therefore eliminate those |
two separately. */ |
#define ELIMINABLE_REGS \ |
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ |
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ |
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} |
|
/* We need not worry about when the frame-pointer is required for other |
reasons; GCC takes care of those cases. */ |
#define CAN_ELIMINATE(FROM, TO) 1 |
|
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ |
(OFFSET) = mmix_initial_elimination_offset (FROM, TO) |
|
|
/* Node: Stack Arguments */ |
|
#define ACCUMULATE_OUTGOING_ARGS 1 |
|
#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACKSIZE) 0 |
|
|
/* Node: Register Arguments */ |
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ |
mmix_function_arg (&(CUM), MODE, TYPE, NAMED, 0) |
|
#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \ |
mmix_function_arg (&(CUM), MODE, TYPE, NAMED, 1) |
|
typedef struct { int regs; int lib; } CUMULATIVE_ARGS; |
|
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ |
((CUM).regs = 0, (CUM).lib = ((LIBNAME) != 0)) |
|
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ |
((CUM).regs \ |
= ((targetm.calls.must_pass_in_stack (MODE, TYPE)) \ |
|| (MMIX_FUNCTION_ARG_SIZE (MODE, TYPE) > 8 \ |
&& !TARGET_LIBFUNC && !(CUM).lib)) \ |
? (MMIX_MAX_ARGS_IN_REGS) + 1 \ |
: (CUM).regs + (7 + (MMIX_FUNCTION_ARG_SIZE (MODE, TYPE))) / 8) |
|
#define FUNCTION_ARG_REGNO_P(REGNO) \ |
mmix_function_arg_regno_p (REGNO, 0) |
|
#define FUNCTION_INCOMING_ARG_REGNO_P(REGNO) \ |
mmix_function_arg_regno_p (REGNO, 1) |
|
|
/* Node: Register Arguments */ |
|
#define FUNCTION_VALUE(VALTYPE, FUNC) \ |
gen_rtx_REG (TYPE_MODE (VALTYPE), MMIX_RETURN_VALUE_REGNUM) |
|
/* This needs to take care of the register hole for complex return values. */ |
#define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) \ |
mmix_function_outgoing_value (VALTYPE, FUNC) |
|
#define LIBCALL_VALUE(MODE) \ |
gen_rtx_REG (MODE, MMIX_RETURN_VALUE_REGNUM) |
|
#define FUNCTION_VALUE_REGNO_P(REGNO) \ |
mmix_function_value_regno_p (REGNO) |
|
|
/* Node: Caller Saves */ |
/* (empty) */ |
|
|
/* Node: Function Entry */ |
|
/* See mmix.c for TARGET_ASM_FUNCTION_PROLOGUE and |
TARGET_ASM_FUNCTION_EPILOGUE. */ |
|
/* We need to say that the epilogue uses the return address, so the |
initial-value machinery restores it. FIXME: Some targets |
conditionalize on "reload_completed &&". Investigate difference. |
FIXME: Not needed if nonlocal_goto_stack_level. */ |
#define EPILOGUE_USES(REGNO) \ |
((REGNO) == MMIX_INCOMING_RETURN_ADDRESS_REGNUM) |
|
/* Node: Profiling */ |
#define FUNCTION_PROFILER(FILE, LABELNO) \ |
mmix_function_profiler (FILE, LABELNO) |
|
/* Node: Trampolines */ |
|
#define TRAMPOLINE_TEMPLATE(FILE) \ |
mmix_trampoline_template (FILE) |
|
#define TRAMPOLINE_SIZE mmix_trampoline_size |
#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, STATIC_CHAIN) \ |
mmix_initialize_trampoline (ADDR, FNADDR, STATIC_CHAIN) |
|
|
/* Node: Addressing Modes */ |
|
#define CONSTANT_ADDRESS_P(X) \ |
mmix_constant_address_p (X) |
|
#define MAX_REGS_PER_ADDRESS 2 |
|
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ |
if (mmix_legitimate_address (MODE, X, MMIX_REG_OK_STRICT)) \ |
goto LABEL |
|
#ifndef REG_OK_STRICT |
# define REG_OK_FOR_BASE_P(X) \ |
(REGNO (X) <= MMIX_LAST_GENERAL_REGISTER \ |
|| REGNO (X) == MMIX_ARG_POINTER_REGNUM \ |
|| REGNO (X) >= FIRST_PSEUDO_REGISTER) |
#else |
# define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) |
#endif /* REG_OK_STRICT */ |
|
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X) |
|
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) |
|
#define LEGITIMATE_CONSTANT_P(X) \ |
mmix_legitimate_constant_p (X) |
|
|
/* Node: Condition Code */ |
|
#define SELECT_CC_MODE(OP, X, Y) \ |
mmix_select_cc_mode (OP, X, Y) |
|
/* A definition of CANONICALIZE_COMPARISON that changed LE and GT |
comparisons with -1 to LT and GE respectively, and LT, LTU, GE or GEU |
comparisons with 256 to 255 and LE, LEU, GT and GTU has been |
ineffective; the code path for performing the changes did not trig for |
neither the GCC testsuite nor ghostscript-6.52 nor Knuth's mmix.tar.gz |
itself (core GCC functionality supposedly handling it) with sources |
from 2002-06-06. */ |
|
#define REVERSIBLE_CC_MODE(MODE) \ |
mmix_reversible_cc_mode (MODE) |
|
|
/* Node: Costs */ |
|
/* The special registers can only move to and from general regs, and we |
need to check that their constraints match, so say 3 for them. */ |
/* WARNING: gcc-2.7.2.2 i686-pc-linux-gnulibc1 (as shipped with RH 4.2) |
miscompiles reload1.c:reload_cse_simplify_set; a call to |
reload_cse_regno_equal_p is missing when checking if a substitution of |
a register setting is valid if this is defined to just the expression |
in mmix_register_move_cost. |
|
Symptom: a (all?) register setting is optimized away for e.g. |
"char *p1(char *p) { return p+1; }" and the value of register zero ($0) |
is returned. |
|
We can workaround by making this a function call - unknown if this |
causes dire speed effects. */ |
#define REGISTER_MOVE_COST(MODE, FROM, TO) \ |
mmix_register_move_cost (MODE, FROM, TO) |
|
#define SLOW_BYTE_ACCESS 0 |
|
|
/* Node: Sections */ |
|
/* This must be a constant string, since it's used in crtstuff.c. */ |
#define TEXT_SECTION_ASM_OP \ |
"\t.text ! mmixal:= 9H LOC 8B" |
|
/* FIXME: Not documented. */ |
#define DATA_SECTION_ASM_OP \ |
mmix_data_section_asm_op () |
|
#define READONLY_DATA_SECTION_ASM_OP "\t.section\t.rodata" |
|
/* Node: PIC */ |
/* (empty) */ |
|
|
/* Node: File Framework */ |
|
/* While any other punctuation character but ";" would do, we prefer "%" |
or "!"; "!" is an unary operator and so will not be mistakenly included |
in correctly formed expressions. The hash character adds mass; catches |
the eye. We can't have it as a comment char by itself, since it's a |
hex-number prefix. */ |
#define ASM_COMMENT_START "!#" |
|
/* These aren't currently functional. We just keep them as markers. */ |
#define ASM_APP_ON "%APP\n" |
#define ASM_APP_OFF "%NO_APP\n" |
|
#define ASM_OUTPUT_SOURCE_FILENAME(STREAM, NAME) \ |
mmix_asm_output_source_filename (STREAM, NAME) |
|
#define OUTPUT_QUOTED_STRING(STREAM, STRING) \ |
mmix_output_quoted_string (STREAM, STRING, strlen (STRING)) |
|
#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section |
|
/* Node: Data Output */ |
|
#define ASM_OUTPUT_ASCII(STREAM, PTR, LEN) \ |
mmix_asm_output_ascii (STREAM, PTR, LEN) |
|
/* Node: Uninitialized Data */ |
|
#define ASM_OUTPUT_ALIGNED_COMMON(ST, N, S, A) \ |
mmix_asm_output_aligned_common (ST, N, S, A) |
|
#define ASM_OUTPUT_ALIGNED_LOCAL(ST, N, S, A) \ |
mmix_asm_output_aligned_local (ST, N, S, A) |
|
|
/* Node: Label Output */ |
|
#define ASM_OUTPUT_LABEL(STREAM, NAME) \ |
mmix_asm_output_label (STREAM, NAME) |
|
#define ASM_OUTPUT_INTERNAL_LABEL(STREAM, NAME) \ |
mmix_asm_output_internal_label (STREAM, NAME) |
|
#define ASM_DECLARE_REGISTER_GLOBAL(STREAM, DECL, REGNO, NAME) \ |
mmix_asm_declare_register_global (STREAM, DECL, REGNO, NAME) |
|
#define GLOBAL_ASM_OP "\t.global " |
|
#define ASM_WEAKEN_LABEL(STREAM, NAME) \ |
mmix_asm_weaken_label (STREAM, NAME) |
|
#define MAKE_DECL_ONE_ONLY(DECL) \ |
mmix_make_decl_one_only (DECL) |
|
#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ |
mmix_asm_output_labelref (STREAM, NAME) |
|
/* We insert a ":" to disambiguate against user symbols like L5. */ |
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \ |
sprintf (LABEL, "*%s:%ld", PREFIX, (long)(NUM)) |
|
/* Insert "::"; these are rarer than internal labels. FIXME: Make sure no |
":" is seen in the object file; we don't really want that mmixal |
feature visible there. We don't want the default, which uses a dot; |
that'd be incompatible with mmixal. */ |
#define ASM_PN_FORMAT "%s::%lu" |
|
#define ASM_OUTPUT_DEF(STREAM, NAME, VALUE) \ |
mmix_asm_output_def (STREAM, NAME, VALUE) |
|
/* Node: Macros for Initialization */ |
/* We're compiling to ELF and linking to MMO; fundamental ELF features |
that GCC depend on are there. */ |
|
/* These must be constant strings, since they're used in crtstuff.c. */ |
#define INIT_SECTION_ASM_OP "\t.section .init,\"ax\" ! mmixal-incompatible" |
|
#define FINI_SECTION_ASM_OP "\t.section .fini,\"ax\" ! mmixal-incompatible" |
|
#define OBJECT_FORMAT_ELF |
|
|
/* Node: Instruction Output */ |
|
/* The non-$ register names must be prefixed with ":", since they're |
affected by PREFIX. We provide the non-colon names as additional |
names. */ |
#define REGISTER_NAMES \ |
{"$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", \ |
"$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", \ |
"$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", \ |
"$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31", \ |
"$32", "$33", "$34", "$35", "$36", "$37", "$38", "$39", \ |
"$40", "$41", "$42", "$43", "$44", "$45", "$46", "$47", \ |
"$48", "$49", "$50", "$51", "$52", "$53", "$54", "$55", \ |
"$56", "$57", "$58", "$59", "$60", "$61", "$62", "$63", \ |
"$64", "$65", "$66", "$67", "$68", "$69", "$70", "$71", \ |
"$72", "$73", "$74", "$75", "$76", "$77", "$78", "$79", \ |
"$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", \ |
"$88", "$89", "$90", "$91", "$92", "$93", "$94", "$95", \ |
"$96", "$97", "$98", "$99", "$100", "$101", "$102", "$103", \ |
"$104", "$105", "$106", "$107", "$108", "$109", "$110", "$111", \ |
"$112", "$113", "$114", "$115", "$116", "$117", "$118", "$119", \ |
"$120", "$121", "$122", "$123", "$124", "$125", "$126", "$127", \ |
"$128", "$129", "$130", "$131", "$132", "$133", "$134", "$135", \ |
"$136", "$137", "$138", "$139", "$140", "$141", "$142", "$143", \ |
"$144", "$145", "$146", "$147", "$148", "$149", "$150", "$151", \ |
"$152", "$153", "$154", "$155", "$156", "$157", "$158", "$159", \ |
"$160", "$161", "$162", "$163", "$164", "$165", "$166", "$167", \ |
"$168", "$169", "$170", "$171", "$172", "$173", "$174", "$175", \ |
"$176", "$177", "$178", "$179", "$180", "$181", "$182", "$183", \ |
"$184", "$185", "$186", "$187", "$188", "$189", "$190", "$191", \ |
"$192", "$193", "$194", "$195", "$196", "$197", "$198", "$199", \ |
"$200", "$201", "$202", "$203", "$204", "$205", "$206", "$207", \ |
"$208", "$209", "$210", "$211", "$212", "$213", "$214", "$215", \ |
"$216", "$217", "$218", "$219", "$220", "$221", "$222", "$223", \ |
"$224", "$225", "$226", "$227", "$228", "$229", "$230", "$231", \ |
"$232", "$233", "$234", "$235", "$236", "$237", "$238", "$239", \ |
"$240", "$241", "$242", "$243", "$244", "$245", "$246", "$247", \ |
"$248", "$249", "$250", "$251", "$252", "$253", "$254", "$255", \ |
":rD", ":rE", ":rH", ":rJ", ":rR", "ap_!BAD!", ":rO"} |
|
#define ADDITIONAL_REGISTER_NAMES \ |
{{"sp", 254}, {":sp", 254}, {"rD", 256}, {"rE", 257}, \ |
{"rH", 258}, {"rJ", MMIX_rJ_REGNUM}, {"rO", MMIX_rO_REGNUM}} |
|
#define PRINT_OPERAND(STREAM, X, CODE) \ |
mmix_print_operand (STREAM, X, CODE) |
|
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \ |
mmix_print_operand_punct_valid_p (CODE) |
|
#define PRINT_OPERAND_ADDRESS(STREAM, X) \ |
mmix_print_operand_address (STREAM, X) |
|
#define ASM_OUTPUT_REG_PUSH(STREAM, REGNO) \ |
mmix_asm_output_reg_push (STREAM, REGNO) |
|
#define ASM_OUTPUT_REG_POP(STREAM, REGNO) \ |
mmix_asm_output_reg_pop (STREAM, REGNO) |
|
|
/* Node: Dispatch Tables */ |
|
/* We define both types, since SImode is the better, but DImode the only |
possible for mmixal so that's the one actually used. */ |
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ |
mmix_asm_output_addr_diff_elt (STREAM, BODY, VALUE, REL) |
|
#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ |
mmix_asm_output_addr_vec_elt (STREAM, VALUE) |
|
|
/* Node: Exception Region Output */ |
/* (empty) */ |
|
/* Node: Alignment Output */ |
|
#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \ |
mmix_asm_output_skip (STREAM, NBYTES) |
|
#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ |
mmix_asm_output_align (STREAM, POWER) |
|
|
/* Node: All Debuggers */ |
|
#define DBX_REGISTER_NUMBER(REGNO) \ |
mmix_dbx_register_number (REGNO) |
|
|
/* Node: DBX Options */ |
/* (empty) */ |
/* Node: DBX Hooks */ |
/* (empty) */ |
/* Node: File Names and DBX */ |
/* (empty) */ |
|
|
/* Node: SDB and DWARF */ |
#define DWARF2_DEBUGGING_INFO 1 |
#define DWARF2_ASM_LINE_DEBUG_INFO 1 |
|
/* Node: Misc */ |
|
/* There's no way to get a PC-relative offset into tables for SImode, so |
for the moment we have absolute entries in DImode. |
When we're going ELF, these should be SImode and 1. */ |
#define CASE_VECTOR_MODE DImode |
#define CASE_VECTOR_PC_RELATIVE 0 |
|
#define WORD_REGISTER_OPERATIONS |
|
/* We have a choice, which makes this yet another parameter to tweak. The |
gut feeling is currently that SIGN_EXTEND wins; "int" is more frequent |
than "unsigned int", and we have signed characters. FIXME: measure. */ |
#define LOAD_EXTEND_OP(MODE) (TARGET_ZERO_EXTEND ? ZERO_EXTEND : SIGN_EXTEND) |
|
#define MOVE_MAX 8 |
|
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 |
|
/* ??? MMIX allows a choice of STORE_FLAG_VALUE. Revisit later, |
we don't have scc expanders yet. */ |
|
#define Pmode DImode |
|
#define FUNCTION_MODE QImode |
|
#define NO_IMPLICIT_EXTERN_C |
|
#define HANDLE_SYSV_PRAGMA 1 |
|
/* These are checked. */ |
#define DOLLARS_IN_IDENTIFIERS 0 |
#define NO_DOLLAR_IN_LABEL |
#define NO_DOT_IN_LABEL |
|
#endif /* GCC_MMIX_H */ |
/* |
* Local variables: |
* eval: (c-set-style "gnu") |
* indent-tabs-mode: t |
* End: |
*/ |
/predicates.md
0,0 → 1,150
;; Operand and operator predicates for the GCC MMIX port. |
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
|
;; This file is part of GCC. |
;; |
;; GCC is free software; you can redistribute it and/or modify |
;; it under the terms of the GNU General Public License as published by |
;; the Free Software Foundation; either version 3, or (at your option) |
;; any later version. |
;; |
;; GCC is distributed in the hope that it will be useful, |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
;; GNU General Public License for more details. |
;; |
;; You should have received a copy of the GNU General Public License |
;; along with GCC; see the file COPYING3. If not see |
;; <http://www.gnu.org/licenses/>. |
|
;; True if this is a foldable comparison operator |
;; - one where a the result of (compare:CC (reg) (const_int 0)) can be |
;; replaced by (reg). */ |
|
(define_predicate "mmix_foldable_comparison_operator" |
(match_code "ne, eq, ge, gt, le, lt, gtu, leu") |
{ |
RTX_CODE code = GET_CODE (op); |
|
if (mode == VOIDmode) |
mode = GET_MODE (op); |
|
/* This little bit is why the body of this predicate is kept as C. */ |
if (mode == VOIDmode) |
mode = GET_MODE (XEXP (op, 0)); |
|
return ((mode == CCmode || mode == DImode) |
&& (code == NE || code == EQ || code == GE || code == GT |
|| code == LE || code == LT)) |
/* FIXME: This may be a stupid trick. What happens when GCC wants to |
reverse the condition? Can it do that by itself? Maybe it can |
even reverse the condition to fit a foldable one in the first |
place? */ |
|| (mode == CC_UNSmode && (code == GTU || code == LEU)); |
}) |
|
;; Like comparison_operator, but only true if this comparison operator is |
;; applied to a valid mode. Needed to avoid jump.c generating invalid |
;; code with -ffast-math (gcc.dg/20001228-1.c). |
|
(define_predicate "mmix_comparison_operator" |
(match_operand 0 "comparison_operator") |
{ |
RTX_CODE code = GET_CODE (op); |
|
/* Comparison operators usually don't have a mode, but let's try and get |
one anyway for the day that changes. */ |
if (mode == VOIDmode) |
mode = GET_MODE (op); |
|
/* Get the mode from the first operand if we don't have one. |
Also the reason why we do this in C. */ |
if (mode == VOIDmode) |
mode = GET_MODE (XEXP (op, 0)); |
|
/* FIXME: This needs to be kept in sync with the tables in |
mmix_output_condition. */ |
return |
mode == VOIDmode |
|| (mode == CC_FUNmode |
&& (code == ORDERED || code == UNORDERED)) |
|| (mode == CC_FPmode |
&& (code == GT || code == LT)) |
|| (mode == CC_FPEQmode |
&& (code == NE || code == EQ)) |
|| (mode == CC_UNSmode |
&& (code == GEU || code == GTU || code == LEU || code == LTU)) |
|| (mode == CCmode |
&& (code == NE || code == EQ || code == GE || code == GT |
|| code == LE || code == LT)) |
|| (mode == DImode |
&& (code == NE || code == EQ || code == GE || code == GT |
|| code == LE || code == LT || code == LEU || code == GTU)); |
}) |
|
;; True if this is a register with a condition-code mode. |
|
(define_predicate "mmix_reg_cc_operand" |
(and (match_operand 0 "register_operand") |
(ior (match_test "GET_MODE (op) == CCmode") |
(ior (match_test "GET_MODE (op) == CC_UNSmode") |
(ior (match_test "GET_MODE (op) == CC_FPmode") |
(ior (match_test "GET_MODE (op) == CC_FPEQmode") |
(match_test "GET_MODE (op) == CC_FUNmode"))))))) |
|
;; True if this is an address_operand or a symbolic operand. |
|
(define_predicate "mmix_symbolic_or_address_operand" |
(match_code "symbol_ref, label_ref, const, subreg, reg, plus") |
{ |
switch (GET_CODE (op)) |
{ |
case SYMBOL_REF: |
case LABEL_REF: |
return 1; |
case CONST: |
/* The reason why this body still is C. */ |
op = XEXP (op, 0); |
if ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF |
|| GET_CODE (XEXP (op, 0)) == LABEL_REF) |
&& (GET_CODE (XEXP (op, 1)) == CONST_INT |
|| (GET_CODE (XEXP (op, 1)) == CONST_DOUBLE |
&& GET_MODE (XEXP (op, 1)) == VOIDmode))) |
return 1; |
/* Fall through. */ |
default: |
return address_operand (op, mode); |
} |
}) |
|
;; True if this is a register or CONST_INT (or CONST_DOUBLE for DImode). |
;; We could narrow the value down with a couple of predicates, but that |
;; doesn't seem to be worth it at the moment. |
|
(define_predicate "mmix_reg_or_constant_operand" |
(ior (match_operand 0 "register_operand") |
(ior (match_code "const_int") |
(and (match_code "const_double") |
(match_test "GET_MODE (op) == VOIDmode"))))) |
|
;; True if this is a register or 0 (int or float). |
|
(define_predicate "mmix_reg_or_0_operand" |
(ior |
(match_operand 0 "register_operand") |
(ior |
(and (match_code "const_int") |
(match_test "op == const0_rtx")) |
(and |
(match_code "const_double") |
;; FIXME: Is mode calculation necessary and correct? |
(match_test |
"op == CONST0_RTX (mode == VOIDmode ? GET_MODE (op) : mode)"))))) |
|
;; True if this is a register or an int 0..255. |
|
(define_predicate "mmix_reg_or_8bit_operand" |
(ior |
(match_operand 0 "register_operand") |
(and (match_code "const_int") |
(match_test "CONST_OK_FOR_LETTER_P (INTVAL (op), 'I')")))) |
/mmix-protos.h
0,0 → 1,107
/* Prototypes for exported functions defined in mmix.c |
Copyright (C) 2000, 2001, 2002, 2003, 2004, 2007 Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson (hp@bitrange.com) |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 3, or (at your option) |
any later version. |
|
GCC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
extern void mmix_override_options (void); |
extern void mmix_init_expanders (void); |
extern int mmix_eh_return_data_regno (int); |
extern int mmix_initial_elimination_offset (int, int); |
extern int mmix_starting_frame_offset (void); |
extern int mmix_function_arg_regno_p (int, int); |
extern void mmix_function_profiler (FILE *, int); |
extern void mmix_trampoline_template (FILE *); |
extern int mmix_trampoline_size; |
extern int mmix_reversible_cc_mode (enum machine_mode); |
extern int mmix_register_move_cost |
(enum machine_mode, enum reg_class, enum reg_class); |
extern const char *mmix_text_section_asm_op (void); |
extern const char *mmix_data_section_asm_op (void); |
extern void mmix_asm_output_source_filename (FILE *, const char *); |
extern void mmix_output_quoted_string (FILE *, const char *, int); |
extern void mmix_asm_output_source_line (FILE *, int); |
extern void mmix_asm_output_ascii (FILE *, const char *, int); |
extern void mmix_asm_output_label (FILE *, const char *); |
extern void mmix_asm_output_internal_label (FILE *, const char *); |
extern void mmix_asm_weaken_label (FILE *, const char *); |
extern void mmix_asm_output_labelref (FILE *, const char *); |
extern void mmix_asm_output_def (FILE *, const char *, const char *); |
extern int mmix_print_operand_punct_valid_p (int); |
extern void mmix_asm_output_reg_push (FILE *, int); |
extern void mmix_asm_output_reg_pop (FILE *, int); |
extern void mmix_asm_output_skip (FILE *, int); |
extern void mmix_asm_output_align (FILE *, int); |
extern int mmix_shiftable_wyde_value (unsigned HOST_WIDEST_INT); |
extern void mmix_output_register_setting (FILE *, int, HOST_WIDEST_INT, int); |
extern void mmix_conditional_register_usage (void); |
extern int mmix_local_regno (int); |
extern int mmix_dbx_register_number (int); |
extern int mmix_use_simple_return (void); |
extern void mmix_make_decl_one_only (tree); |
extern rtx mmix_function_outgoing_value (tree, tree); |
extern int mmix_function_value_regno_p (int); |
extern int mmix_data_alignment (tree, int); |
extern int mmix_constant_alignment (tree, int); |
extern int mmix_local_alignment (tree, int); |
extern void mmix_asm_output_pool_prologue (FILE *, const char *, tree, int); |
extern void mmix_asm_output_aligned_common (FILE *, const char *, int, int); |
extern void mmix_asm_output_aligned_local (FILE *, const char *, int, int); |
extern void mmix_asm_declare_register_global |
(FILE *, tree, int, const char *); |
extern rtx mmix_function_arg |
(const CUMULATIVE_ARGS *, enum machine_mode, tree, int, int); |
extern void mmix_asm_output_addr_diff_elt (FILE *, rtx, int, int); |
extern void mmix_asm_output_addr_vec_elt (FILE *, int); |
extern enum reg_class mmix_preferred_reload_class (rtx, enum reg_class); |
extern enum reg_class mmix_preferred_output_reload_class |
(rtx, enum reg_class); |
extern enum reg_class mmix_secondary_reload_class |
(enum reg_class, enum machine_mode, rtx, int); |
extern int mmix_const_ok_for_letter_p (HOST_WIDE_INT, int); |
extern int mmix_const_double_ok_for_letter_p (rtx, int); |
extern int mmix_extra_constraint (rtx, int, int); |
extern rtx mmix_dynamic_chain_address (rtx); |
extern rtx mmix_return_addr_rtx (int, rtx); |
extern rtx mmix_eh_return_stackadj_rtx (void); |
extern rtx mmix_eh_return_handler_rtx (void); |
extern void mmix_initialize_trampoline (rtx, rtx, rtx); |
extern int mmix_constant_address_p (rtx); |
extern int mmix_legitimate_address (enum machine_mode, rtx, int); |
extern int mmix_legitimate_constant_p (rtx); |
extern void mmix_print_operand (FILE *, rtx, int); |
extern void mmix_print_operand_address (FILE *, rtx); |
extern void mmix_expand_prologue (void); |
extern void mmix_expand_epilogue (void); |
extern rtx mmix_get_hard_reg_initial_val (enum machine_mode, int); |
extern int mmix_asm_preferred_eh_data_format (int, int); |
extern void mmix_setup_frame_addresses (void); |
|
#ifdef RTX_CODE |
/* Needs to be ifdef:d for sake of enum rtx_code. */ |
extern enum machine_mode mmix_select_cc_mode (enum rtx_code, rtx, rtx); |
extern void mmix_canonicalize_comparison (enum rtx_code *, rtx *, rtx *); |
extern int mmix_valid_comparison (enum rtx_code, enum machine_mode, rtx); |
extern rtx mmix_gen_compare_reg (enum rtx_code, rtx, rtx); |
#endif |
|
/* |
* Local variables: |
* eval: (c-set-style "gnu") |
* indent-tabs-mode: t |
* End: |
*/ |
/crtn.asm
0,0 → 1,92
/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson <hp@bitrange.com> |
|
This file is free software; you can redistribute it and/or modify it |
under the terms of the GNU General Public License as published by the |
Free Software Foundation; either version 2, or (at your option) any |
later version. |
|
In addition to the permissions in the GNU General Public License, the |
Free Software Foundation gives you unlimited permission to link the |
compiled version of this file into combinations with other programs, |
and to distribute those combinations without any restriction coming |
from the use of this file. (The General Public License restrictions |
do apply in other respects; for example, they cover modification of |
the file, and distribution when not linked into a combine |
executable.) |
|
This file is distributed in the hope that it will be useful, but |
WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; see the file COPYING. If not, write to |
the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
Boston, MA 02110-1301, USA. */ |
|
% This must be the last file on the link-line, allocating global registers |
% from the top. |
|
% Register $254 is the stack-pointer. |
sp GREG |
|
% Register $253 is frame-pointer. It's not supposed to be used in most |
% functions. |
fp GREG |
|
% $252 is the static chain register; nested functions receive the |
% context of the surrounding function through a pointer passed in this |
% register. |
static_chain GREG |
struct_value_reg GREG |
|
% These registers are used to pass state at an exceptional return (C++). |
eh_state_3 GREG |
eh_state_2 GREG |
eh_state_1 GREG |
eh_state_0 GREG |
|
#ifdef __MMIX_ABI_GNU__ |
|
% Allocate global registers used by the GNU ABI. |
gnu_parm_reg_16 GREG |
gnu_parm_reg_15 GREG |
gnu_parm_reg_14 GREG |
gnu_parm_reg_13 GREG |
gnu_parm_reg_12 GREG |
gnu_parm_reg_11 GREG |
gnu_parm_reg_10 GREG |
gnu_parm_reg_9 GREG |
gnu_parm_reg_8 GREG |
gnu_parm_reg_7 GREG |
gnu_parm_reg_6 GREG |
gnu_parm_reg_5 GREG |
gnu_parm_reg_4 GREG |
gnu_parm_reg_3 GREG |
gnu_parm_reg_2 GREG |
gnu_parm_reg_1 GREG |
|
#endif /* __MMIX_ABI_GNU__ */ |
|
% Provide last part of _init and _fini. |
|
% The return address is stored in the topmost stored register in the |
% register-stack. We ignore the current value in rJ. It is probably |
% garbage because each fragment of _init and _fini may have their own idea |
% of the current stack frame, if they're cut out from a "real" function |
% like in gcc/crtstuff.c. |
|
.section .init,"ax",@progbits |
GETA $255,0F |
PUT rJ,$255 |
POP 0,0 |
0H PUT rJ,$0 |
POP 0,0 |
|
.section .fini,"ax",@progbits |
GETA $255,0F |
PUT rJ,$255 |
POP 0,0 |
0H PUT rJ,$0 |
POP 0,0 |
/mmix.md
0,0 → 1,1363
;; GCC machine description for MMIX |
;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 |
;; Free Software Foundation, Inc. |
;; Contributed by Hans-Peter Nilsson (hp@bitrange.com) |
|
;; This file is part of GCC. |
|
;; GCC is free software; you can redistribute it and/or modify |
;; it under the terms of the GNU General Public License as published by |
;; the Free Software Foundation; either version 3, or (at your option) |
;; any later version. |
|
;; GCC is distributed in the hope that it will be useful, |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
;; GNU General Public License for more details. |
|
;; You should have received a copy of the GNU General Public License |
;; along with GCC; see the file COPYING3. If not see |
;; <http://www.gnu.org/licenses/>. |
|
;; The original PO technology requires these to be ordered by speed, |
;; so that assigner will pick the fastest. |
|
;; See file "rtl.def" for documentation on define_insn, match_*, et al. |
|
;; Uses of UNSPEC in this file: |
;; UNSPEC_VOLATILE: |
;; |
;; 0 sync_icache (sync icache before trampoline jump) |
;; 1 nonlocal_goto_receiver |
;; |
|
;; The order of insns is as in Node: Standard Names, with smaller modes |
;; before bigger modes. |
|
(define_constants |
[(MMIX_rJ_REGNUM 259) |
(MMIX_rR_REGNUM 260) |
(MMIX_fp_rO_OFFSET -24)] |
) |
|
;; Operand and operator predicates. |
|
(include "predicates.md") |
|
;; FIXME: Can we remove the reg-to-reg for smaller modes? Shouldn't they |
;; be synthesized ok? |
(define_insn "movqi" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,r ,r,x ,r,r,m,??r") |
(match_operand:QI 1 "general_operand" "r,LS,K,rI,x,m,r,n"))] |
"" |
"@ |
SET %0,%1 |
%s1 %0,%v1 |
NEGU %0,0,%n1 |
PUT %0,%1 |
GET %0,%1 |
LDB%U0 %0,%1 |
STBU %1,%0 |
%r0%I1") |
|
(define_insn "movhi" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=r,r ,r ,x,r,r,m,??r") |
(match_operand:HI 1 "general_operand" "r,LS,K,r,x,m,r,n"))] |
"" |
"@ |
SET %0,%1 |
%s1 %0,%v1 |
NEGU %0,0,%n1 |
PUT %0,%1 |
GET %0,%1 |
LDW%U0 %0,%1 |
STWU %1,%0 |
%r0%I1") |
|
;; gcc.c-torture/compile/920428-2.c fails if there's no "n". |
(define_insn "movsi" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r ,r,x,r,r,m,??r") |
(match_operand:SI 1 "general_operand" "r,LS,K,r,x,m,r,n"))] |
"" |
"@ |
SET %0,%1 |
%s1 %0,%v1 |
NEGU %0,0,%n1 |
PUT %0,%1 |
GET %0,%1 |
LDT%U0 %0,%1 |
STTU %1,%0 |
%r0%I1") |
|
;; We assume all "s" are addresses. Does that hold? |
(define_insn "movdi" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=r,r ,r,x,r,m,r,m,r,r,??r") |
(match_operand:DI 1 "general_operand" "r,LS,K,r,x,I,m,r,R,s,n"))] |
"" |
"@ |
SET %0,%1 |
%s1 %0,%v1 |
NEGU %0,0,%n1 |
PUT %0,%1 |
GET %0,%1 |
STCO %1,%0 |
LDO %0,%1 |
STOU %1,%0 |
GETA %0,%1 |
LDA %0,%1 |
%r0%I1") |
|
;; Note that we move around the float as a collection of bits; no |
;; conversion to double. |
(define_insn "movsf" |
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,x,r,r,m,??r") |
(match_operand:SF 1 "general_operand" "r,G,r,x,m,r,F"))] |
"" |
"@ |
SET %0,%1 |
SETL %0,0 |
PUT %0,%1 |
GET %0,%1 |
LDT %0,%1 |
STTU %1,%0 |
%r0%I1") |
|
(define_insn "movdf" |
[(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,x,r,r,m,??r") |
(match_operand:DF 1 "general_operand" "r,G,r,x,m,r,F"))] |
"" |
"@ |
SET %0,%1 |
SETL %0,0 |
PUT %0,%1 |
GET %0,%1 |
LDO %0,%1 |
STOU %1,%0 |
%r0%I1") |
|
;; We need to be able to move around the values used as condition codes. |
;; First spotted as reported in |
;; <URL:http://gcc.gnu.org/ml/gcc-bugs/2003-03/msg00008.html> due to |
;; changes in loop optimization. The file machmode.def says they're of |
;; size 4 QI. Valid bit-patterns correspond to integers -1, 0 and 1, so |
;; we treat them as signed entities; see mmix-modes.def. The following |
;; expanders should cover all MODE_CC modes, and expand for this pattern. |
(define_insn "*movcc_expanded" |
[(set (match_operand 0 "nonimmediate_operand" "=r,x,r,r,m") |
(match_operand 1 "nonimmediate_operand" "r,r,x,m,r"))] |
"GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_CC |
&& GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_CC" |
"@ |
SET %0,%1 |
PUT %0,%1 |
GET %0,%1 |
LDT %0,%1 |
STT %1,%0") |
|
(define_expand "movcc" |
[(set (match_operand:CC 0 "nonimmediate_operand" "") |
(match_operand:CC 1 "nonimmediate_operand" ""))] |
"" |
"") |
|
(define_expand "movcc_uns" |
[(set (match_operand:CC_UNS 0 "nonimmediate_operand" "") |
(match_operand:CC_UNS 1 "nonimmediate_operand" ""))] |
"" |
"") |
|
(define_expand "movcc_fp" |
[(set (match_operand:CC_FP 0 "nonimmediate_operand" "") |
(match_operand:CC_FP 1 "nonimmediate_operand" ""))] |
"" |
"") |
|
(define_expand "movcc_fpeq" |
[(set (match_operand:CC_FPEQ 0 "nonimmediate_operand" "") |
(match_operand:CC_FPEQ 1 "nonimmediate_operand" ""))] |
"" |
"") |
|
(define_expand "movcc_fun" |
[(set (match_operand:CC_FUN 0 "nonimmediate_operand" "") |
(match_operand:CC_FUN 1 "nonimmediate_operand" ""))] |
"" |
"") |
|
(define_insn "adddi3" |
[(set (match_operand:DI 0 "register_operand" "=r,r,r") |
(plus:DI |
(match_operand:DI 1 "register_operand" "%r,r,0") |
(match_operand:DI 2 "mmix_reg_or_constant_operand" "rI,K,LS")))] |
"" |
"@ |
ADDU %0,%1,%2 |
SUBU %0,%1,%n2 |
%i2 %0,%v2") |
|
(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")))] |
"" |
"FADD %0,%1,%2") |
|
;; Insn canonicalization *should* have removed the need for an integer |
;; in operand 2. |
(define_insn "subdi3" |
[(set (match_operand:DI 0 "register_operand" "=r,r") |
(minus:DI (match_operand:DI 1 "mmix_reg_or_8bit_operand" "r,I") |
(match_operand:DI 2 "register_operand" "r,r")))] |
"" |
"@ |
SUBU %0,%1,%2 |
NEGU %0,%1,%2") |
|
(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")))] |
"" |
"FSUB %0,%1,%2") |
|
;; FIXME: Should we define_expand and match 2, 4, 8 (etc) with shift (or |
;; %{something}2ADDU %0,%1,0)? Hopefully GCC should still handle it, so |
;; we don't have to taint the machine description. If results are bad |
;; enough, we may have to do it anyway. |
(define_insn "muldi3" |
[(set (match_operand:DI 0 "register_operand" "=r,r") |
(mult:DI (match_operand:DI 1 "register_operand" "%r,r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "O,rI"))) |
(clobber (match_scratch:DI 3 "=X,z"))] |
"" |
"@ |
%m2ADDU %0,%1,%1 |
MULU %0,%1,%2") |
|
(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")))] |
"" |
"FMUL %0,%1,%2") |
|
(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")))] |
"" |
"FDIV %0,%1,%2") |
|
;; FIXME: Is "frem" doing the right operation for moddf3? |
(define_insn "moddf3" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(mod:DF (match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"" |
"FREM %0,%1,%2") |
|
;; FIXME: Should we define_expand for smin, smax, umin, umax using a |
;; nifty conditional sequence? |
|
;; FIXME: The cuter andn combinations don't get here, presumably because |
;; they ended up in the constant pool. Check: still? |
(define_insn "anddi3" |
[(set (match_operand:DI 0 "register_operand" "=r,r") |
(and:DI |
(match_operand:DI 1 "register_operand" "%r,0") |
(match_operand:DI 2 "mmix_reg_or_constant_operand" "rI,NT")))] |
"" |
"@ |
AND %0,%1,%2 |
%A2 %0,%V2") |
|
(define_insn "iordi3" |
[(set (match_operand:DI 0 "register_operand" "=r,r") |
(ior:DI (match_operand:DI 1 "register_operand" "%r,0") |
(match_operand:DI 2 "mmix_reg_or_constant_operand" "rH,LS")))] |
"" |
"@ |
OR %0,%1,%2 |
%o2 %0,%v2") |
|
(define_insn "xordi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(xor:DI (match_operand:DI 1 "register_operand" "%r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"XOR %0,%1,%2") |
|
;; FIXME: When TImode works for other reasons (like cross-compiling from |
;; a 32-bit host), add back umulditi3 and umuldi3_highpart here. |
|
;; FIXME: Check what's really reasonable for the mod part. |
|
;; One day we might persuade GCC to expand divisions with constants the |
;; way MMIX does; giving the remainder the sign of the divisor. But even |
;; then, it might be good to have an option to divide the way "everybody |
;; else" does. Perhaps then, this option can be on by default. However, |
;; it's not likely to happen because major (C, C++, Fortran) language |
;; standards in effect at 2002-04-29 reportedly demand that the sign of |
;; the remainder must follow the sign of the dividend. |
|
(define_insn "divmoddi4" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(div:DI (match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI"))) |
(set (match_operand:DI 3 "register_operand" "=y") |
(mod:DI (match_dup 1) (match_dup 2)))] |
;; Do the library stuff later. |
"TARGET_KNUTH_DIVISION" |
"DIV %0,%1,%2") |
|
(define_insn "udivmoddi4" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(udiv:DI (match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI"))) |
(set (match_operand:DI 3 "register_operand" "=y") |
(umod:DI (match_dup 1) (match_dup 2)))] |
"" |
"DIVU %0,%1,%2") |
|
(define_expand "divdi3" |
[(parallel |
[(set (match_operand:DI 0 "register_operand" "=&r") |
(div:DI (match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "register_operand" "r"))) |
(clobber (scratch:DI)) |
(clobber (scratch:DI)) |
(clobber (reg:DI MMIX_rR_REGNUM))])] |
"! TARGET_KNUTH_DIVISION" |
"") |
|
;; The %2-is-%1-case is there just to make sure things don't fail. Could |
;; presumably happen with optimizations off; no evidence. |
(define_insn "*divdi3_nonknuth" |
[(set (match_operand:DI 0 "register_operand" "=&r,r") |
(div:DI (match_operand:DI 1 "register_operand" "r,r") |
(match_operand:DI 2 "register_operand" "1,r"))) |
(clobber (match_scratch:DI 3 "=1,1")) |
(clobber (match_scratch:DI 4 "=2,2")) |
(clobber (reg:DI MMIX_rR_REGNUM))] |
"! TARGET_KNUTH_DIVISION" |
"@ |
SETL %0,1 |
XOR $255,%1,%2\;NEGU %0,0,%2\;CSN %2,%2,%0\;NEGU %0,0,%1\;CSN %1,%1,%0\;\ |
DIVU %0,%1,%2\;NEGU %1,0,%0\;CSN %0,$255,%1") |
|
(define_expand "moddi3" |
[(parallel |
[(set (match_operand:DI 0 "register_operand" "=&r") |
(mod:DI (match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "register_operand" "r"))) |
(clobber (scratch:DI)) |
(clobber (scratch:DI)) |
(clobber (reg:DI MMIX_rR_REGNUM))])] |
"! TARGET_KNUTH_DIVISION" |
"") |
|
;; The %2-is-%1-case is there just to make sure things don't fail. Could |
;; presumably happen with optimizations off; no evidence. |
(define_insn "*moddi3_nonknuth" |
[(set (match_operand:DI 0 "register_operand" "=&r,r") |
(mod:DI (match_operand:DI 1 "register_operand" "r,r") |
(match_operand:DI 2 "register_operand" "1,r"))) |
(clobber (match_scratch:DI 3 "=1,1")) |
(clobber (match_scratch:DI 4 "=2,2")) |
(clobber (reg:DI MMIX_rR_REGNUM))] |
"! TARGET_KNUTH_DIVISION" |
"@ |
SETL %0,0 |
NEGU %0,0,%2\;CSN %2,%2,%0\;NEGU $255,0,%1\;CSN %1,%1,$255\;\ |
DIVU %1,%1,%2\;GET %0,:rR\;NEGU %2,0,%0\;CSNN %0,$255,%2") |
|
(define_insn "ashldi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(ashift:DI |
(match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"SLU %0,%1,%2") |
|
(define_insn "ashrdi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(ashiftrt:DI |
(match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"SR %0,%1,%2") |
|
(define_insn "lshrdi3" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(lshiftrt:DI |
(match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"SRU %0,%1,%2") |
|
(define_insn "negdi2" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(neg:DI (match_operand:DI 1 "register_operand" "r")))] |
"" |
"NEGU %0,0,%1") |
|
(define_expand "negdf2" |
[(parallel [(set (match_operand:DF 0 "register_operand" "=r") |
(neg:DF (match_operand:DF 1 "register_operand" "r"))) |
(use (match_dup 2))])] |
"" |
{ |
/* Emit bit-flipping sequence to be IEEE-safe wrt. -+0. */ |
operands[2] = force_reg (DImode, GEN_INT ((HOST_WIDE_INT) 1 << 63)); |
}) |
|
(define_insn "*expanded_negdf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(neg:DF (match_operand:DF 1 "register_operand" "r"))) |
(use (match_operand:DI 2 "register_operand" "r"))] |
"" |
"XOR %0,%1,%2") |
|
;; FIXME: define_expand for absdi2? |
|
(define_insn "absdf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(abs:DF (match_operand:DF 1 "register_operand" "0")))] |
"" |
"ANDNH %0,#8000") |
|
(define_insn "sqrtdf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(sqrt:DF (match_operand:DF 1 "register_operand" "r")))] |
"" |
"FSQRT %0,%1") |
|
;; FIXME: define_expand for ffssi2? (not ffsdi2 since int is SImode). |
|
(define_insn "one_cmpldi2" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(not:DI (match_operand:DI 1 "register_operand" "r")))] |
"" |
"NOR %0,%1,0") |
|
;; Since we don't have cc0, we do what is recommended in the manual; |
;; store away the operands for use in the branch, scc or movcc insn. |
(define_expand "cmpdi" |
[(match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "")] |
"" |
" |
{ |
mmix_compare_op0 = operands[0]; |
mmix_compare_op1 = operands[1]; |
DONE; |
}") |
|
(define_expand "cmpdf" |
[(match_operand:DF 0 "register_operand" "") |
(match_operand:DF 1 "register_operand" "")] |
"" |
" |
{ |
mmix_compare_op0 = operands[0]; |
mmix_compare_op1 = operands[1]; |
DONE; |
}") |
|
;; When the user-patterns expand, the resulting insns will match the |
;; patterns below. |
|
;; We can fold the signed-compare where the register value is |
;; already equal to (compare:CCTYPE (reg) (const_int 0)). |
;; We can't do that at all for floating-point, due to NaN, +0.0 |
;; and -0.0, and we can only do it for the non/zero test of |
;; unsigned, so that has to be done another way. |
;; FIXME: Perhaps a peep2 changing CCcode to a new code, that |
;; gets folded here. |
(define_insn "*cmpcc_folded" |
[(set (match_operand:CC 0 "register_operand" "=r") |
(compare:CC |
(match_operand:DI 1 "register_operand" "r") |
(const_int 0)))] |
;; FIXME: Can we test equivalence any other way? |
;; FIXME: Can we fold any other way? |
"REGNO (operands[1]) == REGNO (operands[0])" |
"%% folded: cmp %0,%1,0") |
|
(define_insn "*cmpcc" |
[(set (match_operand:CC 0 "register_operand" "=r") |
(compare:CC |
(match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"CMP %0,%1,%2") |
|
(define_insn "*cmpu" |
[(set (match_operand:CC_UNS 0 "register_operand" "=r") |
(compare:CC_UNS |
(match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"CMPU %0,%1,%2") |
|
(define_insn "*fcmp" |
[(set (match_operand:CC_FP 0 "register_operand" "=r") |
(compare:CC_FP |
(match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"" |
"FCMP%e0 %0,%1,%2") |
|
;; FIXME: for -mieee, add fsub %0,%1,%1\;fsub %0,%2,%2 before to |
;; make signalling compliant. |
(define_insn "*feql" |
[(set (match_operand:CC_FPEQ 0 "register_operand" "=r") |
(compare:CC_FPEQ |
(match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"" |
"FEQL%e0 %0,%1,%2") |
|
(define_insn "*fun" |
[(set (match_operand:CC_FUN 0 "register_operand" "=r") |
(compare:CC_FUN |
(match_operand:DF 1 "register_operand" "r") |
(match_operand:DF 2 "register_operand" "r")))] |
"" |
"FUN%e0 %0,%1,%2") |
|
;; In order to get correct rounding, we have to use SFLOT and SFLOTU for |
;; conversion. They do not convert to SFmode; they convert to DFmode, |
;; with rounding as of SFmode. They are not usable as is, but we pretend |
;; we have a single instruction but emit two. |
|
;; Note that this will (somewhat unexpectedly) create an inexact |
;; exception if rounding is necessary - has to be masked off in crt0? |
(define_expand "floatdisf2" |
[(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "=rm") |
(float:SF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI"))) |
;; Let's use a DI scratch, since SF don't generally get into |
;; registers. Dunno what's best; it's really a DF, but that |
;; doesn't logically follow from operands in the pattern. |
(clobber (match_scratch:DI 2 "=&r"))])] |
"" |
" |
{ |
if (GET_CODE (operands[0]) != MEM) |
{ |
rtx stack_slot; |
|
/* FIXME: This stack-slot remains even at -O3. There must be a |
better way. */ |
stack_slot |
= validize_mem (assign_stack_temp (SFmode, |
GET_MODE_SIZE (SFmode), 0)); |
emit_insn (gen_floatdisf2 (stack_slot, operands[1])); |
emit_move_insn (operands[0], stack_slot); |
DONE; |
} |
}") |
|
(define_insn "*floatdisf2_real" |
[(set (match_operand:SF 0 "memory_operand" "=m") |
(float:SF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI"))) |
(clobber (match_scratch:DI 2 "=&r"))] |
"" |
"SFLOT %2,%1\;STSF %2,%0") |
|
(define_expand "floatunsdisf2" |
[(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "=rm") |
(unsigned_float:SF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI"))) |
;; Let's use a DI scratch, since SF don't generally get into |
;; registers. Dunno what's best; it's really a DF, but that |
;; doesn't logically follow from operands in the pattern. |
(clobber (scratch:DI))])] |
"" |
" |
{ |
if (GET_CODE (operands[0]) != MEM) |
{ |
rtx stack_slot; |
|
/* FIXME: This stack-slot remains even at -O3. Must be a better |
way. */ |
stack_slot |
= validize_mem (assign_stack_temp (SFmode, |
GET_MODE_SIZE (SFmode), 0)); |
emit_insn (gen_floatunsdisf2 (stack_slot, operands[1])); |
emit_move_insn (operands[0], stack_slot); |
DONE; |
} |
}") |
|
(define_insn "*floatunsdisf2_real" |
[(set (match_operand:SF 0 "memory_operand" "=m") |
(unsigned_float:SF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI"))) |
(clobber (match_scratch:DI 2 "=&r"))] |
"" |
"SFLOTU %2,%1\;STSF %2,%0") |
|
;; Note that this will (somewhat unexpectedly) create an inexact |
;; exception if rounding is necessary - has to be masked off in crt0? |
(define_insn "floatdidf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(float:DF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"FLOT %0,%1") |
|
(define_insn "floatunsdidf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(unsigned_float:DF |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI")))] |
"" |
"FLOTU %0,%1") |
|
(define_insn "ftruncdf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(fix:DF (match_operand:DF 1 "register_operand" "r")))] |
"" |
;; ROUND_OFF |
"FINT %0,1,%1") |
|
;; Note that this will (somewhat unexpectedly) create an inexact |
;; exception if rounding is necessary - has to be masked off in crt0? |
(define_insn "fix_truncdfdi2" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "r"))))] |
"" |
;; ROUND_OFF |
"FIX %0,1,%1") |
|
(define_insn "fixuns_truncdfdi2" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(unsigned_fix:DI |
(fix:DF (match_operand:DF 1 "register_operand" "r"))))] |
"" |
;; ROUND_OFF |
"FIXU %0,1,%1") |
|
;; It doesn't seem like it's possible to have memory_operand as a |
;; predicate here (testcase: libgcc2 floathisf). FIXME: Shouldn't it be |
;; possible to do that? Bug in GCC? Anyway, this used to be a simple |
;; pattern with a memory_operand predicate, but was split up with a |
;; define_expand with the old pattern as "anonymous". |
;; FIXME: Perhaps with SECONDARY_MEMORY_NEEDED? |
(define_expand "truncdfsf2" |
[(set (match_operand:SF 0 "memory_operand" "") |
(float_truncate:SF (match_operand:DF 1 "register_operand" "")))] |
"" |
" |
{ |
if (GET_CODE (operands[0]) != MEM) |
{ |
/* FIXME: There should be a way to say: 'put this in operands[0] |
but *after* the expanded insn'. */ |
rtx stack_slot; |
|
/* There is no sane destination but a register here, if it wasn't |
already MEM. (It's too hard to get fatal_insn to work here.) */ |
if (! REG_P (operands[0])) |
internal_error (\"MMIX Internal: Bad truncdfsf2 expansion\"); |
|
/* FIXME: This stack-slot remains even at -O3. Must be a better |
way. */ |
stack_slot |
= validize_mem (assign_stack_temp (SFmode, |
GET_MODE_SIZE (SFmode), 0)); |
emit_insn (gen_truncdfsf2 (stack_slot, operands[1])); |
emit_move_insn (operands[0], stack_slot); |
DONE; |
} |
}") |
|
(define_insn "*truncdfsf2_real" |
[(set (match_operand:SF 0 "memory_operand" "=m") |
(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))] |
"" |
"STSF %1,%0") |
|
;; Same comment as for truncdfsf2. |
(define_expand "extendsfdf2" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(float_extend:DF (match_operand:SF 1 "memory_operand" "m")))] |
"" |
" |
{ |
if (GET_CODE (operands[1]) != MEM) |
{ |
rtx stack_slot; |
|
/* There is no sane destination but a register here, if it wasn't |
already MEM. (It's too hard to get fatal_insn to work here.) */ |
if (! REG_P (operands[0])) |
internal_error (\"MMIX Internal: Bad extendsfdf2 expansion\"); |
|
/* FIXME: This stack-slot remains even at -O3. There must be a |
better way. */ |
stack_slot |
= validize_mem (assign_stack_temp (SFmode, |
GET_MODE_SIZE (SFmode), 0)); |
emit_move_insn (stack_slot, operands[1]); |
emit_insn (gen_extendsfdf2 (operands[0], stack_slot)); |
DONE; |
} |
}") |
|
(define_insn "*extendsfdf2_real" |
[(set (match_operand:DF 0 "register_operand" "=r") |
(float_extend:DF (match_operand:SF 1 "memory_operand" "m")))] |
"" |
"LDSF %0,%1") |
|
;; Neither sign-extend nor zero-extend are necessary; gcc knows how to |
;; synthesize using shifts or and, except with a memory source and not |
;; completely optimal. FIXME: Actually, other bugs surface when those |
;; patterns are defined; fix later. |
|
;; There are no sane values with the bit-patterns of (int) 0..255 except |
;; 0 to use in movdfcc. |
|
(define_expand "movdfcc" |
[(set (match_operand:DF 0 "register_operand" "") |
(if_then_else:DF |
(match_operand 1 "comparison_operator" "") |
(match_operand:DF 2 "mmix_reg_or_0_operand" "") |
(match_operand:DF 3 "mmix_reg_or_0_operand" "")))] |
"" |
" |
{ |
enum rtx_code code = GET_CODE (operands[1]); |
rtx cc_reg = mmix_gen_compare_reg (code, mmix_compare_op0, |
mmix_compare_op1); |
if (cc_reg == NULL_RTX) |
FAIL; |
operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); |
}") |
|
(define_expand "movdicc" |
[(set (match_operand:DI 0 "register_operand" "") |
(if_then_else:DI |
(match_operand 1 "comparison_operator" "") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "") |
(match_operand:DI 3 "mmix_reg_or_8bit_operand" "")))] |
"" |
" |
{ |
enum rtx_code code = GET_CODE (operands[1]); |
rtx cc_reg = mmix_gen_compare_reg (code, mmix_compare_op0, |
mmix_compare_op1); |
if (cc_reg == NULL_RTX) |
FAIL; |
operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); |
}") |
|
;; FIXME: Is this the right way to do "folding" of CCmode -> DImode? |
(define_insn "*movdicc_real_foldable" |
[(set (match_operand:DI 0 "register_operand" "=r,r,r,r") |
(if_then_else:DI |
(match_operator 2 "mmix_foldable_comparison_operator" |
[(match_operand:DI 3 "register_operand" "r,r,r,r") |
(const_int 0)]) |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI,0 ,rI,GM") |
(match_operand:DI 4 "mmix_reg_or_8bit_operand" "0 ,rI,GM,rI")))] |
"" |
"@ |
CS%d2 %0,%3,%1 |
CS%D2 %0,%3,%4 |
ZS%d2 %0,%3,%1 |
ZS%D2 %0,%3,%4") |
|
(define_insn "*movdicc_real_reversible" |
[(set |
(match_operand:DI 0 "register_operand" "=r ,r ,r ,r") |
(if_then_else:DI |
(match_operator |
2 "mmix_comparison_operator" |
[(match_operand 3 "mmix_reg_cc_operand" "r ,r ,r ,r") |
(const_int 0)]) |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI,0 ,rI,GM") |
(match_operand:DI 4 "mmix_reg_or_8bit_operand" "0 ,rI,GM,rI")))] |
"REVERSIBLE_CC_MODE (GET_MODE (operands[3]))" |
"@ |
CS%d2 %0,%3,%1 |
CS%D2 %0,%3,%4 |
ZS%d2 %0,%3,%1 |
ZS%D2 %0,%3,%4") |
|
(define_insn "*movdicc_real_nonreversible" |
[(set |
(match_operand:DI 0 "register_operand" "=r ,r") |
(if_then_else:DI |
(match_operator |
2 "mmix_comparison_operator" |
[(match_operand 3 "mmix_reg_cc_operand" "r ,r") |
(const_int 0)]) |
(match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI,rI") |
(match_operand:DI 4 "mmix_reg_or_0_operand" "0 ,GM")))] |
"!REVERSIBLE_CC_MODE (GET_MODE (operands[3]))" |
"@ |
CS%d2 %0,%3,%1 |
ZS%d2 %0,%3,%1") |
|
(define_insn "*movdfcc_real_foldable" |
[(set |
(match_operand:DF 0 "register_operand" "=r ,r ,r ,r") |
(if_then_else:DF |
(match_operator |
2 "mmix_foldable_comparison_operator" |
[(match_operand:DI 3 "register_operand" "r ,r ,r ,r") |
(const_int 0)]) |
(match_operand:DF 1 "mmix_reg_or_0_operand" "rGM,0 ,rGM,GM") |
(match_operand:DF 4 "mmix_reg_or_0_operand" "0 ,rGM,GM ,rGM")))] |
"" |
"@ |
CS%d2 %0,%3,%1 |
CS%D2 %0,%3,%4 |
ZS%d2 %0,%3,%1 |
ZS%D2 %0,%3,%4") |
|
(define_insn "*movdfcc_real_reversible" |
[(set |
(match_operand:DF 0 "register_operand" "=r ,r ,r ,r") |
(if_then_else:DF |
(match_operator |
2 "mmix_comparison_operator" |
[(match_operand 3 "mmix_reg_cc_operand" "r ,r ,r ,r") |
(const_int 0)]) |
(match_operand:DF 1 "mmix_reg_or_0_operand" "rGM,0 ,rGM,GM") |
(match_operand:DF 4 "mmix_reg_or_0_operand" "0 ,rGM,GM ,rGM")))] |
"REVERSIBLE_CC_MODE (GET_MODE (operands[3]))" |
"@ |
CS%d2 %0,%3,%1 |
CS%D2 %0,%3,%4 |
ZS%d2 %0,%3,%1 |
ZS%D2 %0,%3,%4") |
|
(define_insn "*movdfcc_real_nonreversible" |
[(set |
(match_operand:DF 0 "register_operand" "=r ,r") |
(if_then_else:DF |
(match_operator |
2 "mmix_comparison_operator" |
[(match_operand 3 "mmix_reg_cc_operand" "r ,r") |
(const_int 0)]) |
(match_operand:DF 1 "mmix_reg_or_0_operand" "rGM,rGM") |
(match_operand:DF 4 "mmix_reg_or_0_operand" "0 ,GM")))] |
"!REVERSIBLE_CC_MODE (GET_MODE (operands[3]))" |
"@ |
CS%d2 %0,%3,%1 |
ZS%d2 %0,%3,%1") |
|
;; FIXME: scc patterns will probably help, I just skip them |
;; right now. Revisit. |
|
(define_expand "beq" |
[(set (pc) |
(if_then_else (eq (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (EQ, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bne" |
[(set (pc) |
(if_then_else (ne (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (NE, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bgt" |
[(set (pc) |
(if_then_else (gt (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (GT, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "ble" |
[(set (pc) |
(if_then_else (le (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (LE, mmix_compare_op0, mmix_compare_op1); |
|
/* The head comment of optabs.c:can_compare_p says we're required to |
implement this, so we have to clean up the mess here. */ |
if (operands[1] == NULL_RTX) |
{ |
/* FIXME: Watch out for sharing/unsharing of rtx:es. */ |
emit_jump_insn ((*bcc_gen_fctn[(int) LT]) (operands[0])); |
emit_jump_insn ((*bcc_gen_fctn[(int) EQ]) (operands[0])); |
DONE; |
} |
}") |
|
(define_expand "bge" |
[(set (pc) |
(if_then_else (ge (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (GE, mmix_compare_op0, mmix_compare_op1); |
|
/* The head comment of optabs.c:can_compare_p says we're required to |
implement this, so we have to clean up the mess here. */ |
if (operands[1] == NULL_RTX) |
{ |
/* FIXME: Watch out for sharing/unsharing of rtx:es. */ |
emit_jump_insn ((*bcc_gen_fctn[(int) GT]) (operands[0])); |
emit_jump_insn ((*bcc_gen_fctn[(int) EQ]) (operands[0])); |
DONE; |
} |
}") |
|
(define_expand "blt" |
[(set (pc) |
(if_then_else (lt (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (LT, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bgtu" |
[(set (pc) |
(if_then_else (gtu (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (GTU, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bleu" |
[(set (pc) |
(if_then_else (leu (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (LEU, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bgeu" |
[(set (pc) |
(if_then_else (geu (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (GEU, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bltu" |
[(set (pc) |
(if_then_else (ltu (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (LTU, mmix_compare_op0, mmix_compare_op1); |
}") |
|
(define_expand "bunordered" |
[(set (pc) |
(if_then_else (unordered (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (UNORDERED, mmix_compare_op0, mmix_compare_op1); |
|
if (operands[1] == NULL_RTX) |
FAIL; |
}") |
|
(define_expand "bordered" |
[(set (pc) |
(if_then_else (ordered (match_dup 1) (const_int 0)) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
" |
{ |
operands[1] |
= mmix_gen_compare_reg (ORDERED, mmix_compare_op0, mmix_compare_op1); |
}") |
|
;; FIXME: we can emit an unordered-or-*not*-equal compare in one insn, but |
;; there's no RTL code for it. Maybe revisit in future. |
|
;; FIXME: Odd/Even matchers? |
(define_insn "*bCC_foldable" |
[(set (pc) |
(if_then_else |
(match_operator 1 "mmix_foldable_comparison_operator" |
[(match_operand:DI 2 "register_operand" "r") |
(const_int 0)]) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"%+B%d1 %2,%0") |
|
(define_insn "*bCC" |
[(set (pc) |
(if_then_else |
(match_operator 1 "mmix_comparison_operator" |
[(match_operand 2 "mmix_reg_cc_operand" "r") |
(const_int 0)]) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"" |
"%+B%d1 %2,%0") |
|
(define_insn "*bCC_inverted_foldable" |
[(set (pc) |
(if_then_else |
(match_operator 1 "mmix_foldable_comparison_operator" |
[(match_operand:DI 2 "register_operand" "r") |
(const_int 0)]) |
(pc) |
(label_ref (match_operand 0 "" ""))))] |
;; REVERSIBLE_CC_MODE is checked by mmix_foldable_comparison_operator. |
"" |
"%+B%D1 %2,%0") |
|
(define_insn "*bCC_inverted" |
[(set (pc) |
(if_then_else |
(match_operator 1 "mmix_comparison_operator" |
[(match_operand 2 "mmix_reg_cc_operand" "r") |
(const_int 0)]) |
(pc) |
(label_ref (match_operand 0 "" ""))))] |
"REVERSIBLE_CC_MODE (GET_MODE (operands[2]))" |
"%+B%D1 %2,%0") |
|
(define_expand "call" |
[(parallel [(call (match_operand:QI 0 "memory_operand" "") |
(match_operand 1 "general_operand" "")) |
(use (match_operand 2 "general_operand" "")) |
(clobber (match_dup 4))]) |
(set (match_dup 4) (match_dup 3))] |
"" |
" |
{ |
/* The caller checks that the operand is generally valid as an |
address, but at -O0 nothing makes sure that it's also a valid |
call address for a *call*; a mmix_symbolic_or_address_operand. |
Force into a register if it isn't. */ |
if (!mmix_symbolic_or_address_operand (XEXP (operands[0], 0), |
GET_MODE (XEXP (operands[0], 0)))) |
operands[0] |
= replace_equiv_address (operands[0], |
force_reg (Pmode, XEXP (operands[0], 0))); |
|
/* Since the epilogue 'uses' the return address, and it is clobbered |
in the call, and we set it back after every call (all but one setting |
will be optimized away), integrity is maintained. */ |
operands[3] |
= mmix_get_hard_reg_initial_val (Pmode, |
MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
|
/* FIXME: There's a bug in gcc which causes NULL to be passed as |
operand[2] when we get out of registers, which later confuses gcc. |
Work around it by replacing it with const_int 0. Possibly documentation |
error too. */ |
if (operands[2] == NULL_RTX) |
operands[2] = const0_rtx; |
|
operands[4] = gen_rtx_REG (DImode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
}") |
|
(define_expand "call_value" |
[(parallel [(set (match_operand 0 "" "") |
(call (match_operand:QI 1 "memory_operand" "") |
(match_operand 2 "general_operand" ""))) |
(use (match_operand 3 "general_operand" "")) |
(clobber (match_dup 5))]) |
(set (match_dup 5) (match_dup 4))] |
"" |
" |
{ |
/* The caller checks that the operand is generally valid as an |
address, but at -O0 nothing makes sure that it's also a valid |
call address for a *call*; a mmix_symbolic_or_address_operand. |
Force into a register if it isn't. */ |
if (!mmix_symbolic_or_address_operand (XEXP (operands[1], 0), |
GET_MODE (XEXP (operands[1], 0)))) |
operands[1] |
= replace_equiv_address (operands[1], |
force_reg (Pmode, XEXP (operands[1], 0))); |
|
/* Since the epilogue 'uses' the return address, and it is clobbered |
in the call, and we set it back after every call (all but one setting |
will be optimized away), integrity is maintained. */ |
operands[4] |
= mmix_get_hard_reg_initial_val (Pmode, |
MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
|
/* FIXME: See 'call'. */ |
if (operands[3] == NULL_RTX) |
operands[3] = const0_rtx; |
|
/* FIXME: Documentation bug: operands[3] (operands[2] for 'call') is the |
*next* argument register, not the number of arguments in registers. |
(There used to be code here where that mattered.) */ |
|
operands[5] = gen_rtx_REG (DImode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
}") |
|
;; Don't use 'p' here. A 'p' must stand first in constraints, or reload |
;; messes up, not registering the address for reload. Several C++ |
;; testcases, including g++.brendan/crash40.C. FIXME: This is arguably a |
;; bug in gcc. Note line ~2612 in reload.c, that does things on the |
;; condition <<else if (constraints[i][0] == 'p')>> and the comment on |
;; ~3017 that says: |
;; << case 'p': |
;; /* All necessary reloads for an address_operand |
;; were handled in find_reloads_address. */>> |
;; Sorry, I have not dug deeper. If symbolic addresses are used |
;; rarely compared to addresses in registers, disparaging the |
;; first ("p") alternative by adding ? in the first operand |
;; might do the trick. We define 'U' as a synonym to 'p', but without the |
;; caveats (and very small advantages) of 'p'. |
(define_insn "*call_real" |
[(call (mem:QI |
(match_operand:DI 0 "mmix_symbolic_or_address_operand" "s,rU")) |
(match_operand 1 "" "")) |
(use (match_operand 2 "" "")) |
(clobber (reg:DI MMIX_rJ_REGNUM))] |
"" |
"@ |
PUSHJ $%p2,%0 |
PUSHGO $%p2,%a0") |
|
(define_insn "*call_value_real" |
[(set (match_operand 0 "register_operand" "=r,r") |
(call (mem:QI |
(match_operand:DI 1 "mmix_symbolic_or_address_operand" "s,rU")) |
(match_operand 2 "" ""))) |
(use (match_operand 3 "" "")) |
(clobber (reg:DI MMIX_rJ_REGNUM))] |
"" |
"@ |
PUSHJ $%p3,%1 |
PUSHGO $%p3,%a1") |
|
;; I hope untyped_call and untyped_return are not needed for MMIX. |
;; Users of Objective-C will notice. |
|
; Generated by GCC. |
(define_expand "return" |
[(return)] |
"mmix_use_simple_return ()" |
"") |
|
; Generated by the epilogue expander. |
(define_insn "*expanded_return" |
[(return)] |
"" |
"POP %.,0") |
|
(define_expand "prologue" |
[(const_int 0)] |
"" |
"mmix_expand_prologue (); DONE;") |
|
; Note that the (return) from the expander itself is always the last insn |
; in the epilogue. |
(define_expand "epilogue" |
[(return)] |
"" |
"mmix_expand_epilogue ();") |
|
(define_insn "nop" |
[(const_int 0)] |
"" |
"SWYM 0,0,0") |
|
(define_insn "jump" |
[(set (pc) (label_ref (match_operand 0 "" "")))] |
"" |
"JMP %0") |
|
(define_insn "indirect_jump" |
[(set (pc) (match_operand 0 "address_operand" "p"))] |
"" |
"GO $255,%a0") |
|
;; FIXME: This is just a jump, and should be expanded to one. |
(define_insn "tablejump" |
[(set (pc) (match_operand:DI 0 "address_operand" "p")) |
(use (label_ref (match_operand 1 "" "")))] |
"" |
"GO $255,%a0") |
|
;; The only peculiar thing is that the register stack has to be unwound at |
;; nonlocal_goto_receiver. At each function that has a nonlocal label, we |
;; save at function entry the location of the "alpha" register stack |
;; pointer, rO, in a stack slot known to that function (right below where |
;; the frame-pointer would be located). |
;; In the nonlocal goto receiver, we unwind the register stack by a series |
;; of "pop 0,0" until rO equals the saved value. (If it goes lower, we |
;; should die with a trap.) |
(define_expand "nonlocal_goto_receiver" |
[(parallel [(unspec_volatile [(const_int 0)] 1) |
(clobber (scratch:DI)) |
(clobber (reg:DI MMIX_rJ_REGNUM))]) |
(set (reg:DI MMIX_rJ_REGNUM) (match_dup 0))] |
"" |
" |
{ |
operands[0] |
= mmix_get_hard_reg_initial_val (Pmode, |
MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
|
/* Mark this function as containing a landing-pad. */ |
cfun->machine->has_landing_pad = 1; |
}") |
|
;; GCC can insist on using saved registers to keep the slot address in |
;; "across" the exception, or (perhaps) to use saved registers in the |
;; address and re-use them after the register stack unwind, so it's best |
;; to form the address ourselves. |
(define_insn "*nonlocal_goto_receiver_expanded" |
[(unspec_volatile [(const_int 0)] 1) |
(clobber (match_scratch:DI 0 "=&r")) |
(clobber (reg:DI MMIX_rJ_REGNUM))] |
"" |
{ |
rtx temp_reg = operands[0]; |
rtx my_operands[2]; |
HOST_WIDEST_INT offs; |
const char *my_template |
= "GETA $255,0f\;PUT rJ,$255\;LDOU $255,%a0\n\ |
0:\;GET %1,rO\;CMPU %1,%1,$255\;BNP %1,1f\;POP 0,0\n1:"; |
|
my_operands[1] = temp_reg; |
|
/* If we have a frame-pointer (hence unknown stack-pointer offset), |
just use the frame-pointer and the known offset. */ |
if (frame_pointer_needed) |
{ |
my_operands[0] = GEN_INT (-MMIX_fp_rO_OFFSET); |
|
output_asm_insn ("NEGU %1,0,%0", my_operands); |
my_operands[0] = gen_rtx_PLUS (Pmode, frame_pointer_rtx, temp_reg); |
} |
else |
{ |
/* We know the fp-based offset, so "eliminate" it to be sp-based. */ |
offs |
= (mmix_initial_elimination_offset (MMIX_FRAME_POINTER_REGNUM, |
MMIX_STACK_POINTER_REGNUM) |
+ MMIX_fp_rO_OFFSET); |
|
if (offs >= 0 && offs <= 255) |
my_operands[0] |
= gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offs)); |
else |
{ |
mmix_output_register_setting (asm_out_file, REGNO (temp_reg), |
offs, 1); |
my_operands[0] = gen_rtx_PLUS (Pmode, stack_pointer_rtx, temp_reg); |
} |
} |
|
output_asm_insn (my_template, my_operands); |
return ""; |
}) |
|
(define_insn "*Naddu" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(plus:DI (mult:DI (match_operand:DI 1 "register_operand" "r") |
(match_operand:DI 2 "const_int_operand" "n")) |
(match_operand:DI 3 "mmix_reg_or_8bit_operand" "rI")))] |
"GET_CODE (operands[2]) == CONST_INT |
&& (INTVAL (operands[2]) == 2 |
|| INTVAL (operands[2]) == 4 |
|| INTVAL (operands[2]) == 8 |
|| INTVAL (operands[2]) == 16)" |
"%2ADDU %0,%1,%3") |
|
(define_insn "*andn" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(and:DI |
(not:DI (match_operand:DI 1 "mmix_reg_or_8bit_operand" "rI")) |
(match_operand:DI 2 "register_operand" "r")))] |
"" |
"ANDN %0,%2,%1") |
|
(define_insn "*nand" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(ior:DI |
(not:DI (match_operand:DI 1 "register_operand" "%r")) |
(not:DI (match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI"))))] |
"" |
"NAND %0,%1,%2") |
|
(define_insn "*nor" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(and:DI |
(not:DI (match_operand:DI 1 "register_operand" "%r")) |
(not:DI (match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI"))))] |
"" |
"NOR %0,%1,%2") |
|
(define_insn "*nxor" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(not:DI |
(xor:DI (match_operand:DI 1 "register_operand" "%r") |
(match_operand:DI 2 "mmix_reg_or_8bit_operand" "rI"))))] |
"" |
"NXOR %0,%1,%2") |
|
(define_insn "sync_icache" |
[(unspec_volatile [(match_operand:DI 0 "memory_operand" "m") |
(match_operand:DI 1 "const_int_operand" "I")] 0)] |
"" |
"SYNCID %1,%0") |
|
;; Local Variables: |
;; mode: lisp |
;; indent-tabs-mode: t |
;; End: |
/mmix.c
0,0 → 1,2741
/* Definitions of target machine for GNU compiler, for MMIX. |
Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 |
Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson (hp@bitrange.com) |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 3, or (at your option) |
any later version. |
|
GCC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
#include "config.h" |
#include "system.h" |
#include "coretypes.h" |
#include "tm.h" |
#include "rtl.h" |
#include "regs.h" |
#include "hard-reg-set.h" |
#include "hashtab.h" |
#include "insn-config.h" |
#include "output.h" |
#include "flags.h" |
#include "tree.h" |
#include "function.h" |
#include "expr.h" |
#include "toplev.h" |
#include "recog.h" |
#include "ggc.h" |
#include "dwarf2.h" |
#include "debug.h" |
#include "tm_p.h" |
#include "integrate.h" |
#include "target.h" |
#include "target-def.h" |
#include "real.h" |
|
/* First some local helper definitions. */ |
#define MMIX_FIRST_GLOBAL_REGNUM 32 |
|
/* We'd need a current_function_has_landing_pad. It's marked as such when |
a nonlocal_goto_receiver is expanded. Not just a C++ thing, but |
mostly. */ |
#define MMIX_CFUN_HAS_LANDING_PAD (cfun->machine->has_landing_pad != 0) |
|
/* We have no means to tell DWARF 2 about the register stack, so we need |
to store the return address on the stack if an exception can get into |
this function. FIXME: Narrow condition. Before any whole-function |
analysis, regs_ever_live[] isn't initialized. We know it's up-to-date |
after reload_completed; it may contain incorrect information some time |
before that. Within a RTL sequence (after a call to start_sequence, |
such as in RTL expanders), leaf_function_p doesn't see all insns |
(perhaps any insn). But regs_ever_live is up-to-date when |
leaf_function_p () isn't, so we "or" them together to get accurate |
information. FIXME: Some tweak to leaf_function_p might be |
preferable. */ |
#define MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS \ |
(flag_exceptions \ |
&& ((reload_completed && regs_ever_live[MMIX_rJ_REGNUM]) \ |
|| !leaf_function_p ())) |
|
#define IS_MMIX_EH_RETURN_DATA_REG(REGNO) \ |
(current_function_calls_eh_return \ |
&& (EH_RETURN_DATA_REGNO (0) == REGNO \ |
|| EH_RETURN_DATA_REGNO (1) == REGNO \ |
|| EH_RETURN_DATA_REGNO (2) == REGNO \ |
|| EH_RETURN_DATA_REGNO (3) == REGNO)) |
|
/* For the default ABI, we rename registers at output-time to fill the gap |
between the (statically partitioned) saved registers and call-clobbered |
registers. In effect this makes unused call-saved registers to be used |
as call-clobbered registers. The benefit comes from keeping the number |
of local registers (value of rL) low, since there's a cost of |
increasing rL and clearing unused (unset) registers with lower numbers. |
Don't translate while outputting the prologue. */ |
#define MMIX_OUTPUT_REGNO(N) \ |
(TARGET_ABI_GNU \ |
|| (int) (N) < MMIX_RETURN_VALUE_REGNUM \ |
|| (int) (N) > MMIX_LAST_STACK_REGISTER_REGNUM \ |
|| cfun == NULL \ |
|| cfun->machine == NULL \ |
|| cfun->machine->in_prologue \ |
? (N) : ((N) - MMIX_RETURN_VALUE_REGNUM \ |
+ cfun->machine->highest_saved_stack_register + 1)) |
|
/* The %d in "POP %d,0". */ |
#define MMIX_POP_ARGUMENT() \ |
((! TARGET_ABI_GNU \ |
&& current_function_return_rtx != NULL \ |
&& ! current_function_returns_struct) \ |
? (GET_CODE (current_function_return_rtx) == PARALLEL \ |
? GET_NUM_ELEM (XVEC (current_function_return_rtx, 0)) : 1) \ |
: 0) |
|
/* The canonical saved comparison operands for non-cc0 machines, set in |
the compare expander. */ |
rtx mmix_compare_op0; |
rtx mmix_compare_op1; |
|
/* Declarations of locals. */ |
|
/* Intermediate for insn output. */ |
static int mmix_output_destination_register; |
|
static void mmix_output_shiftvalue_op_from_str |
(FILE *, const char *, HOST_WIDEST_INT); |
static void mmix_output_shifted_value (FILE *, HOST_WIDEST_INT); |
static void mmix_output_condition (FILE *, rtx, int); |
static HOST_WIDEST_INT mmix_intval (rtx); |
static void mmix_output_octa (FILE *, HOST_WIDEST_INT, int); |
static bool mmix_assemble_integer (rtx, unsigned int, int); |
static struct machine_function *mmix_init_machine_status (void); |
static void mmix_encode_section_info (tree, rtx, int); |
static const char *mmix_strip_name_encoding (const char *); |
static void mmix_emit_sp_add (HOST_WIDE_INT offset); |
static void mmix_target_asm_function_prologue (FILE *, HOST_WIDE_INT); |
static void mmix_target_asm_function_end_prologue (FILE *); |
static void mmix_target_asm_function_epilogue (FILE *, HOST_WIDE_INT); |
static void mmix_reorg (void); |
static void mmix_asm_output_mi_thunk |
(FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); |
static void mmix_setup_incoming_varargs |
(CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int); |
static void mmix_file_start (void); |
static void mmix_file_end (void); |
static bool mmix_rtx_costs (rtx, int, int, int *); |
static rtx mmix_struct_value_rtx (tree, int); |
static bool mmix_pass_by_reference (const CUMULATIVE_ARGS *, |
enum machine_mode, tree, bool); |
|
/* Target structure macros. Listed by node. See `Using and Porting GCC' |
for a general description. */ |
|
/* Node: Function Entry */ |
|
#undef TARGET_ASM_BYTE_OP |
#define TARGET_ASM_BYTE_OP NULL |
#undef TARGET_ASM_ALIGNED_HI_OP |
#define TARGET_ASM_ALIGNED_HI_OP NULL |
#undef TARGET_ASM_ALIGNED_SI_OP |
#define TARGET_ASM_ALIGNED_SI_OP NULL |
#undef TARGET_ASM_ALIGNED_DI_OP |
#define TARGET_ASM_ALIGNED_DI_OP NULL |
#undef TARGET_ASM_INTEGER |
#define TARGET_ASM_INTEGER mmix_assemble_integer |
|
#undef TARGET_ASM_FUNCTION_PROLOGUE |
#define TARGET_ASM_FUNCTION_PROLOGUE mmix_target_asm_function_prologue |
|
#undef TARGET_ASM_FUNCTION_END_PROLOGUE |
#define TARGET_ASM_FUNCTION_END_PROLOGUE mmix_target_asm_function_end_prologue |
|
#undef TARGET_ASM_FUNCTION_EPILOGUE |
#define TARGET_ASM_FUNCTION_EPILOGUE mmix_target_asm_function_epilogue |
|
#undef TARGET_ENCODE_SECTION_INFO |
#define TARGET_ENCODE_SECTION_INFO mmix_encode_section_info |
#undef TARGET_STRIP_NAME_ENCODING |
#define TARGET_STRIP_NAME_ENCODING mmix_strip_name_encoding |
|
#undef TARGET_ASM_OUTPUT_MI_THUNK |
#define TARGET_ASM_OUTPUT_MI_THUNK mmix_asm_output_mi_thunk |
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK |
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall |
#undef TARGET_ASM_FILE_START |
#define TARGET_ASM_FILE_START mmix_file_start |
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE |
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true |
#undef TARGET_ASM_FILE_END |
#define TARGET_ASM_FILE_END mmix_file_end |
|
#undef TARGET_RTX_COSTS |
#define TARGET_RTX_COSTS mmix_rtx_costs |
#undef TARGET_ADDRESS_COST |
#define TARGET_ADDRESS_COST hook_int_rtx_0 |
|
#undef TARGET_MACHINE_DEPENDENT_REORG |
#define TARGET_MACHINE_DEPENDENT_REORG mmix_reorg |
|
#undef TARGET_PROMOTE_FUNCTION_ARGS |
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true |
#if 0 |
/* Apparently not doing TRT if int < register-size. FIXME: Perhaps |
FUNCTION_VALUE and LIBCALL_VALUE needs tweaking as some ports say. */ |
#undef TARGET_PROMOTE_FUNCTION_RETURN |
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true |
#endif |
|
#undef TARGET_STRUCT_VALUE_RTX |
#define TARGET_STRUCT_VALUE_RTX mmix_struct_value_rtx |
#undef TARGET_SETUP_INCOMING_VARARGS |
#define TARGET_SETUP_INCOMING_VARARGS mmix_setup_incoming_varargs |
#undef TARGET_PASS_BY_REFERENCE |
#define TARGET_PASS_BY_REFERENCE mmix_pass_by_reference |
#undef TARGET_CALLEE_COPIES |
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true |
#undef TARGET_DEFAULT_TARGET_FLAGS |
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT |
|
struct gcc_target targetm = TARGET_INITIALIZER; |
|
/* Functions that are expansions for target macros. |
See Target Macros in `Using and Porting GCC'. */ |
|
/* OVERRIDE_OPTIONS. */ |
|
void |
mmix_override_options (void) |
{ |
/* Should we err or should we warn? Hmm. At least we must neutralize |
it. For example the wrong kind of case-tables will be generated with |
PIC; we use absolute address items for mmixal compatibility. FIXME: |
They could be relative if we just elide them to after all pertinent |
labels. */ |
if (flag_pic) |
{ |
warning (0, "-f%s not supported: ignored", (flag_pic > 1) ? "PIC" : "pic"); |
flag_pic = 0; |
} |
} |
|
/* INIT_EXPANDERS. */ |
|
void |
mmix_init_expanders (void) |
{ |
init_machine_status = mmix_init_machine_status; |
} |
|
/* Set the per-function data. */ |
|
static struct machine_function * |
mmix_init_machine_status (void) |
{ |
return ggc_alloc_cleared (sizeof (struct machine_function)); |
} |
|
/* DATA_ALIGNMENT. |
We have trouble getting the address of stuff that is located at other |
than 32-bit alignments (GETA requirements), so try to give everything |
at least 32-bit alignment. */ |
|
int |
mmix_data_alignment (tree type ATTRIBUTE_UNUSED, int basic_align) |
{ |
if (basic_align < 32) |
return 32; |
|
return basic_align; |
} |
|
/* CONSTANT_ALIGNMENT. */ |
|
int |
mmix_constant_alignment (tree constant ATTRIBUTE_UNUSED, int basic_align) |
{ |
if (basic_align < 32) |
return 32; |
|
return basic_align; |
} |
|
/* LOCAL_ALIGNMENT. */ |
|
int |
mmix_local_alignment (tree type ATTRIBUTE_UNUSED, int basic_align) |
{ |
if (basic_align < 32) |
return 32; |
|
return basic_align; |
} |
|
/* CONDITIONAL_REGISTER_USAGE. */ |
|
void |
mmix_conditional_register_usage (void) |
{ |
int i; |
|
if (TARGET_ABI_GNU) |
{ |
static const int gnu_abi_reg_alloc_order[] |
= MMIX_GNU_ABI_REG_ALLOC_ORDER; |
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
reg_alloc_order[i] = gnu_abi_reg_alloc_order[i]; |
|
/* Change the default from the mmixware ABI. For the GNU ABI, |
$15..$30 are call-saved just as $0..$14. There must be one |
call-clobbered local register for the "hole" that holds the |
number of saved local registers saved by PUSHJ/PUSHGO during the |
function call, receiving the return value at return. So best is |
to use the highest, $31. It's already marked call-clobbered for |
the mmixware ABI. */ |
for (i = 15; i <= 30; i++) |
call_used_regs[i] = 0; |
|
/* "Unfix" the parameter registers. */ |
for (i = MMIX_RESERVED_GNU_ARG_0_REGNUM; |
i < MMIX_RESERVED_GNU_ARG_0_REGNUM + MMIX_MAX_ARGS_IN_REGS; |
i++) |
fixed_regs[i] = 0; |
} |
|
/* Step over the ":" in special register names. */ |
if (! TARGET_TOPLEVEL_SYMBOLS) |
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
if (reg_names[i][0] == ':') |
reg_names[i]++; |
} |
|
/* LOCAL_REGNO. |
All registers that are part of the register stack and that will be |
saved are local. */ |
|
int |
mmix_local_regno (int regno) |
{ |
return regno <= MMIX_LAST_STACK_REGISTER_REGNUM && !call_used_regs[regno]; |
} |
|
/* PREFERRED_RELOAD_CLASS. |
We need to extend the reload class of REMAINDER_REG and HIMULT_REG. */ |
|
enum reg_class |
mmix_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class) |
{ |
/* FIXME: Revisit. */ |
return GET_CODE (x) == MOD && GET_MODE (x) == DImode |
? REMAINDER_REG : class; |
} |
|
/* PREFERRED_OUTPUT_RELOAD_CLASS. |
We need to extend the reload class of REMAINDER_REG and HIMULT_REG. */ |
|
enum reg_class |
mmix_preferred_output_reload_class (rtx x ATTRIBUTE_UNUSED, |
enum reg_class class) |
{ |
/* FIXME: Revisit. */ |
return GET_CODE (x) == MOD && GET_MODE (x) == DImode |
? REMAINDER_REG : class; |
} |
|
/* SECONDARY_RELOAD_CLASS. |
We need to reload regs of REMAINDER_REG and HIMULT_REG elsewhere. */ |
|
enum reg_class |
mmix_secondary_reload_class (enum reg_class class, |
enum machine_mode mode ATTRIBUTE_UNUSED, |
rtx x ATTRIBUTE_UNUSED, |
int in_p ATTRIBUTE_UNUSED) |
{ |
if (class == REMAINDER_REG |
|| class == HIMULT_REG |
|| class == SYSTEM_REGS) |
return GENERAL_REGS; |
|
return NO_REGS; |
} |
|
/* CONST_OK_FOR_LETTER_P. */ |
|
int |
mmix_const_ok_for_letter_p (HOST_WIDE_INT value, int c) |
{ |
return |
(c == 'I' ? value >= 0 && value <= 255 |
: c == 'J' ? value >= 0 && value <= 65535 |
: c == 'K' ? value <= 0 && value >= -255 |
: c == 'L' ? mmix_shiftable_wyde_value (value) |
: c == 'M' ? value == 0 |
: c == 'N' ? mmix_shiftable_wyde_value (~value) |
: c == 'O' ? (value == 3 || value == 5 || value == 9 |
|| value == 17) |
: 0); |
} |
|
/* CONST_DOUBLE_OK_FOR_LETTER_P. */ |
|
int |
mmix_const_double_ok_for_letter_p (rtx value, int c) |
{ |
return |
(c == 'G' ? value == CONST0_RTX (GET_MODE (value)) |
: 0); |
} |
|
/* EXTRA_CONSTRAINT. |
We need this since our constants are not always expressible as |
CONST_INT:s, but rather often as CONST_DOUBLE:s. */ |
|
int |
mmix_extra_constraint (rtx x, int c, int strict) |
{ |
HOST_WIDEST_INT value; |
|
/* When checking for an address, we need to handle strict vs. non-strict |
register checks. Don't use address_operand, but instead its |
equivalent (its callee, which it is just a wrapper for), |
memory_operand_p and the strict-equivalent strict_memory_address_p. */ |
if (c == 'U') |
return |
strict |
? strict_memory_address_p (Pmode, x) |
: memory_address_p (Pmode, x); |
|
/* R asks whether x is to be loaded with GETA or something else. Right |
now, only a SYMBOL_REF and LABEL_REF can fit for |
TARGET_BASE_ADDRESSES. |
|
Only constant symbolic addresses apply. With TARGET_BASE_ADDRESSES, |
we just allow straight LABEL_REF or SYMBOL_REFs with SYMBOL_REF_FLAG |
set right now; only function addresses and code labels. If we change |
to let SYMBOL_REF_FLAG be set on other symbols, we have to check |
inside CONST expressions. When TARGET_BASE_ADDRESSES is not in |
effect, a "raw" constant check together with mmix_constant_address_p |
is all that's needed; we want all constant addresses to be loaded |
with GETA then. */ |
if (c == 'R') |
return |
GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE |
&& mmix_constant_address_p (x) |
&& (! TARGET_BASE_ADDRESSES |
|| (GET_CODE (x) == LABEL_REF |
|| (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x)))); |
|
if (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode) |
return 0; |
|
value = mmix_intval (x); |
|
/* We used to map Q->J, R->K, S->L, T->N, U->O, but we don't have to any |
more ('U' taken for address_operand, 'R' similarly). Some letters map |
outside of CONST_INT, though; we still use 'S' and 'T'. */ |
if (c == 'S') |
return mmix_shiftable_wyde_value (value); |
else if (c == 'T') |
return mmix_shiftable_wyde_value (~value); |
return 0; |
} |
|
/* DYNAMIC_CHAIN_ADDRESS. */ |
|
rtx |
mmix_dynamic_chain_address (rtx frame) |
{ |
/* FIXME: the frame-pointer is stored at offset -8 from the current |
frame-pointer. Unfortunately, the caller assumes that a |
frame-pointer is present for *all* previous frames. There should be |
a way to say that that cannot be done, like for RETURN_ADDR_RTX. */ |
return plus_constant (frame, -8); |
} |
|
/* STARTING_FRAME_OFFSET. */ |
|
int |
mmix_starting_frame_offset (void) |
{ |
/* The old frame pointer is in the slot below the new one, so |
FIRST_PARM_OFFSET does not need to depend on whether the |
frame-pointer is needed or not. We have to adjust for the register |
stack pointer being located below the saved frame pointer. |
Similarly, we store the return address on the stack too, for |
exception handling, and always if we save the register stack pointer. */ |
return |
(-8 |
+ (MMIX_CFUN_HAS_LANDING_PAD |
? -16 : (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS ? -8 : 0))); |
} |
|
/* RETURN_ADDR_RTX. */ |
|
rtx |
mmix_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) |
{ |
return count == 0 |
? (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS |
/* FIXME: Set frame_alias_set on the following. (Why?) |
See mmix_initial_elimination_offset for the reason we can't use |
get_hard_reg_initial_val for both. Always using a stack slot |
and not a register would be suboptimal. */ |
? validize_mem (gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx, -16))) |
: get_hard_reg_initial_val (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM)) |
: NULL_RTX; |
} |
|
/* SETUP_FRAME_ADDRESSES. */ |
|
void |
mmix_setup_frame_addresses (void) |
{ |
/* Nothing needed at the moment. */ |
} |
|
/* The difference between the (imaginary) frame pointer and the stack |
pointer. Used to eliminate the frame pointer. */ |
|
int |
mmix_initial_elimination_offset (int fromreg, int toreg) |
{ |
int regno; |
int fp_sp_offset |
= (get_frame_size () + current_function_outgoing_args_size + 7) & ~7; |
|
/* There is no actual offset between these two virtual values, but for |
the frame-pointer, we have the old one in the stack position below |
it, so the offset for the frame-pointer to the stack-pointer is one |
octabyte larger. */ |
if (fromreg == MMIX_ARG_POINTER_REGNUM |
&& toreg == MMIX_FRAME_POINTER_REGNUM) |
return 0; |
|
/* The difference is the size of local variables plus the size of |
outgoing function arguments that would normally be passed as |
registers but must be passed on stack because we're out of |
function-argument registers. Only global saved registers are |
counted; the others go on the register stack. |
|
The frame-pointer is counted too if it is what is eliminated, as we |
need to balance the offset for it from STARTING_FRAME_OFFSET. |
|
Also add in the slot for the register stack pointer we save if we |
have a landing pad. |
|
Unfortunately, we can't access $0..$14, from unwinder code easily, so |
store the return address in a frame slot too. FIXME: Only for |
non-leaf functions. FIXME: Always with a landing pad, because it's |
hard to know whether we need the other at the time we know we need |
the offset for one (and have to state it). It's a kludge until we |
can express the register stack in the EH frame info. |
|
We have to do alignment here; get_frame_size will not return a |
multiple of STACK_BOUNDARY. FIXME: Add note in manual. */ |
|
for (regno = MMIX_FIRST_GLOBAL_REGNUM; |
regno <= 255; |
regno++) |
if ((regs_ever_live[regno] && ! call_used_regs[regno]) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
fp_sp_offset += 8; |
|
return fp_sp_offset |
+ (MMIX_CFUN_HAS_LANDING_PAD |
? 16 : (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS ? 8 : 0)) |
+ (fromreg == MMIX_ARG_POINTER_REGNUM ? 0 : 8); |
} |
|
/* Return an rtx for a function argument to go in a register, and 0 for |
one that must go on stack. */ |
|
rtx |
mmix_function_arg (const CUMULATIVE_ARGS *argsp, |
enum machine_mode mode, |
tree type, |
int named ATTRIBUTE_UNUSED, |
int incoming) |
{ |
/* Last-argument marker. */ |
if (type == void_type_node) |
return (argsp->regs < MMIX_MAX_ARGS_IN_REGS) |
? gen_rtx_REG (mode, |
(incoming |
? MMIX_FIRST_INCOMING_ARG_REGNUM |
: MMIX_FIRST_ARG_REGNUM) + argsp->regs) |
: NULL_RTX; |
|
return (argsp->regs < MMIX_MAX_ARGS_IN_REGS |
&& !targetm.calls.must_pass_in_stack (mode, type) |
&& (GET_MODE_BITSIZE (mode) <= 64 |
|| argsp->lib |
|| TARGET_LIBFUNC)) |
? gen_rtx_REG (mode, |
(incoming |
? MMIX_FIRST_INCOMING_ARG_REGNUM |
: MMIX_FIRST_ARG_REGNUM) |
+ argsp->regs) |
: NULL_RTX; |
} |
|
/* Returns nonzero for everything that goes by reference, 0 for |
everything that goes by value. */ |
|
static bool |
mmix_pass_by_reference (const CUMULATIVE_ARGS *argsp, enum machine_mode mode, |
tree type, bool named ATTRIBUTE_UNUSED) |
{ |
/* FIXME: Check: I'm not sure the must_pass_in_stack check is |
necessary. */ |
if (targetm.calls.must_pass_in_stack (mode, type)) |
return true; |
|
if (MMIX_FUNCTION_ARG_SIZE (mode, type) > 8 |
&& !TARGET_LIBFUNC |
&& (!argsp || !argsp->lib)) |
return true; |
|
return false; |
} |
|
/* Return nonzero if regno is a register number where a parameter is |
passed, and 0 otherwise. */ |
|
int |
mmix_function_arg_regno_p (int regno, int incoming) |
{ |
int first_arg_regnum |
= incoming ? MMIX_FIRST_INCOMING_ARG_REGNUM : MMIX_FIRST_ARG_REGNUM; |
|
return regno >= first_arg_regnum |
&& regno < first_arg_regnum + MMIX_MAX_ARGS_IN_REGS; |
} |
|
/* FUNCTION_OUTGOING_VALUE. */ |
|
rtx |
mmix_function_outgoing_value (tree valtype, tree func ATTRIBUTE_UNUSED) |
{ |
enum machine_mode mode = TYPE_MODE (valtype); |
enum machine_mode cmode; |
int first_val_regnum = MMIX_OUTGOING_RETURN_VALUE_REGNUM; |
rtx vec[MMIX_MAX_REGS_FOR_VALUE]; |
int i; |
int nregs; |
|
/* Return values that fit in a register need no special handling. |
There's no register hole when parameters are passed in global |
registers. */ |
if (TARGET_ABI_GNU |
|| GET_MODE_BITSIZE (mode) <= BITS_PER_WORD) |
return |
gen_rtx_REG (mode, MMIX_OUTGOING_RETURN_VALUE_REGNUM); |
|
if (COMPLEX_MODE_P (mode)) |
/* A complex type, made up of components. */ |
cmode = TYPE_MODE (TREE_TYPE (valtype)); |
else |
{ |
/* Of the other larger-than-register modes, we only support |
scalar mode TImode. (At least, that's the only one that's |
been rudimentally tested.) Make sure we're alerted for |
unexpected cases. */ |
if (mode != TImode) |
sorry ("support for mode %qs", GET_MODE_NAME (mode)); |
|
/* In any case, we will fill registers to the natural size. */ |
cmode = DImode; |
} |
|
nregs = ((GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD); |
|
/* We need to take care of the effect of the register hole on return |
values of large sizes; the last register will appear as the first |
register, with the rest shifted. (For complex modes, this is just |
swapped registers.) */ |
|
if (nregs > MMIX_MAX_REGS_FOR_VALUE) |
internal_error ("too large function value type, needs %d registers,\ |
have only %d registers for this", nregs, MMIX_MAX_REGS_FOR_VALUE); |
|
/* FIXME: Maybe we should handle structure values like this too |
(adjusted for BLKmode), perhaps for both ABI:s. */ |
for (i = 0; i < nregs - 1; i++) |
vec[i] |
= gen_rtx_EXPR_LIST (VOIDmode, |
gen_rtx_REG (cmode, first_val_regnum + i), |
GEN_INT ((i + 1) * BITS_PER_UNIT)); |
|
vec[nregs - 1] |
= gen_rtx_EXPR_LIST (VOIDmode, |
gen_rtx_REG (cmode, first_val_regnum + nregs - 1), |
const0_rtx); |
|
return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nregs, vec)); |
} |
|
/* FUNCTION_VALUE_REGNO_P. */ |
|
int |
mmix_function_value_regno_p (int regno) |
{ |
return regno == MMIX_RETURN_VALUE_REGNUM; |
} |
|
/* EH_RETURN_DATA_REGNO. */ |
|
int |
mmix_eh_return_data_regno (int n) |
{ |
if (n >= 0 && n < 4) |
return MMIX_EH_RETURN_DATA_REGNO_START + n; |
|
return INVALID_REGNUM; |
} |
|
/* EH_RETURN_STACKADJ_RTX. */ |
|
rtx |
mmix_eh_return_stackadj_rtx (void) |
{ |
return gen_rtx_REG (Pmode, MMIX_EH_RETURN_STACKADJ_REGNUM); |
} |
|
/* EH_RETURN_HANDLER_RTX. */ |
|
rtx |
mmix_eh_return_handler_rtx (void) |
{ |
return gen_rtx_REG (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM); |
} |
|
/* ASM_PREFERRED_EH_DATA_FORMAT. */ |
|
int |
mmix_asm_preferred_eh_data_format (int code ATTRIBUTE_UNUSED, |
int global ATTRIBUTE_UNUSED) |
{ |
/* This is the default (was at 2001-07-20). Revisit when needed. */ |
return DW_EH_PE_absptr; |
} |
|
/* Make a note that we've seen the beginning of the prologue. This |
matters to whether we'll translate register numbers as calculated by |
mmix_reorg. */ |
|
static void |
mmix_target_asm_function_prologue (FILE *stream ATTRIBUTE_UNUSED, |
HOST_WIDE_INT framesize ATTRIBUTE_UNUSED) |
{ |
cfun->machine->in_prologue = 1; |
} |
|
/* Make a note that we've seen the end of the prologue. */ |
|
static void |
mmix_target_asm_function_end_prologue (FILE *stream ATTRIBUTE_UNUSED) |
{ |
cfun->machine->in_prologue = 0; |
} |
|
/* Implement TARGET_MACHINE_DEPENDENT_REORG. No actual rearrangements |
done here; just virtually by calculating the highest saved stack |
register number used to modify the register numbers at output time. */ |
|
static void |
mmix_reorg (void) |
{ |
int regno; |
|
/* We put the number of the highest saved register-file register in a |
location convenient for the call-patterns to output. Note that we |
don't tell dwarf2 about these registers, since it can't restore them |
anyway. */ |
for (regno = MMIX_LAST_STACK_REGISTER_REGNUM; |
regno >= 0; |
regno--) |
if ((regs_ever_live[regno] && !call_used_regs[regno]) |
|| (regno == MMIX_FRAME_POINTER_REGNUM && frame_pointer_needed)) |
break; |
|
/* Regardless of whether they're saved (they might be just read), we |
mustn't include registers that carry parameters. We could scan the |
insns to see whether they're actually used (and indeed do other less |
trivial register usage analysis and transformations), but it seems |
wasteful to optimize for unused parameter registers. As of |
2002-04-30, regs_ever_live[n] seems to be set for only-reads too, but |
that might change. */ |
if (!TARGET_ABI_GNU && regno < current_function_args_info.regs - 1) |
{ |
regno = current_function_args_info.regs - 1; |
|
/* We don't want to let this cause us to go over the limit and make |
incoming parameter registers be misnumbered and treating the last |
parameter register and incoming return value register call-saved. |
Stop things at the unmodified scheme. */ |
if (regno > MMIX_RETURN_VALUE_REGNUM - 1) |
regno = MMIX_RETURN_VALUE_REGNUM - 1; |
} |
|
cfun->machine->highest_saved_stack_register = regno; |
} |
|
/* TARGET_ASM_FUNCTION_EPILOGUE. */ |
|
static void |
mmix_target_asm_function_epilogue (FILE *stream, |
HOST_WIDE_INT locals_size ATTRIBUTE_UNUSED) |
{ |
/* Emit an \n for readability of the generated assembly. */ |
fputc ('\n', stream); |
} |
|
/* TARGET_ASM_OUTPUT_MI_THUNK. */ |
|
static void |
mmix_asm_output_mi_thunk (FILE *stream, |
tree fndecl ATTRIBUTE_UNUSED, |
HOST_WIDE_INT delta, |
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, |
tree func) |
{ |
/* If you define TARGET_STRUCT_VALUE_RTX that returns 0 (i.e. pass |
location of structure to return as invisible first argument), you |
need to tweak this code too. */ |
const char *regname = reg_names[MMIX_FIRST_INCOMING_ARG_REGNUM]; |
|
if (delta >= 0 && delta < 65536) |
fprintf (stream, "\tINCL %s,%d\n", regname, (int)delta); |
else if (delta < 0 && delta >= -255) |
fprintf (stream, "\tSUBU %s,%s,%d\n", regname, regname, (int)-delta); |
else |
{ |
mmix_output_register_setting (stream, 255, delta, 1); |
fprintf (stream, "\tADDU %s,%s,$255\n", regname, regname); |
} |
|
fprintf (stream, "\tJMP "); |
assemble_name (stream, XSTR (XEXP (DECL_RTL (func), 0), 0)); |
fprintf (stream, "\n"); |
} |
|
/* FUNCTION_PROFILER. */ |
|
void |
mmix_function_profiler (FILE *stream ATTRIBUTE_UNUSED, |
int labelno ATTRIBUTE_UNUSED) |
{ |
sorry ("function_profiler support for MMIX"); |
} |
|
/* Worker function for TARGET_SETUP_INCOMING_VARARGS. For the moment, |
let's stick to pushing argument registers on the stack. Later, we |
can parse all arguments in registers, to improve performance. */ |
|
static void |
mmix_setup_incoming_varargs (CUMULATIVE_ARGS *args_so_farp, |
enum machine_mode mode, |
tree vartype, |
int *pretend_sizep, |
int second_time ATTRIBUTE_UNUSED) |
{ |
/* The last named variable has been handled, but |
args_so_farp has not been advanced for it. */ |
if (args_so_farp->regs + 1 < MMIX_MAX_ARGS_IN_REGS) |
*pretend_sizep = (MMIX_MAX_ARGS_IN_REGS - (args_so_farp->regs + 1)) * 8; |
|
/* We assume that one argument takes up one register here. That should |
be true until we start messing with multi-reg parameters. */ |
if ((7 + (MMIX_FUNCTION_ARG_SIZE (mode, vartype))) / 8 != 1) |
internal_error ("MMIX Internal: Last named vararg would not fit in a register"); |
} |
|
/* TRAMPOLINE_SIZE. */ |
/* Four 4-byte insns plus two 8-byte values. */ |
int mmix_trampoline_size = 32; |
|
|
/* TRAMPOLINE_TEMPLATE. */ |
|
void |
mmix_trampoline_template (FILE *stream) |
{ |
/* Read a value into the static-chain register and jump somewhere. The |
static chain is stored at offset 16, and the function address is |
stored at offset 24. */ |
/* FIXME: GCC copies this using *intsize* (tetra), when it should use |
register size (octa). */ |
fprintf (stream, "\tGETA $255,1F\n\t"); |
fprintf (stream, "LDOU %s,$255,0\n\t", |
reg_names[MMIX_STATIC_CHAIN_REGNUM]); |
fprintf (stream, "LDOU $255,$255,8\n\t"); |
fprintf (stream, "GO $255,$255,0\n"); |
fprintf (stream, "1H\tOCTA 0\n\t"); |
fprintf (stream, "OCTA 0\n"); |
} |
|
/* INITIALIZE_TRAMPOLINE. */ |
/* Set the static chain and function pointer field in the trampoline. |
We also SYNCID here to be sure (doesn't matter in the simulator, but |
some day it will). */ |
|
void |
mmix_initialize_trampoline (rtx trampaddr, rtx fnaddr, rtx static_chain) |
{ |
emit_move_insn (gen_rtx_MEM (DImode, plus_constant (trampaddr, 16)), |
static_chain); |
emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (trampaddr, 24)), |
fnaddr); |
emit_insn (gen_sync_icache (validize_mem (gen_rtx_MEM (DImode, |
trampaddr)), |
GEN_INT (mmix_trampoline_size - 1))); |
} |
|
/* We must exclude constant addresses that have an increment that is not a |
multiple of four bytes because of restrictions of the GETA |
instruction, unless TARGET_BASE_ADDRESSES. */ |
|
int |
mmix_constant_address_p (rtx x) |
{ |
RTX_CODE code = GET_CODE (x); |
int addend = 0; |
/* When using "base addresses", anything constant goes. */ |
int constant_ok = TARGET_BASE_ADDRESSES != 0; |
|
switch (code) |
{ |
case LABEL_REF: |
case SYMBOL_REF: |
return 1; |
|
case HIGH: |
/* FIXME: Don't know how to dissect these. Avoid them for now, |
except we know they're constants. */ |
return constant_ok; |
|
case CONST_INT: |
addend = INTVAL (x); |
break; |
|
case CONST_DOUBLE: |
if (GET_MODE (x) != VOIDmode) |
/* Strange that we got here. FIXME: Check if we do. */ |
return constant_ok; |
addend = CONST_DOUBLE_LOW (x); |
break; |
|
case CONST: |
/* Note that expressions with arithmetic on forward references don't |
work in mmixal. People using gcc assembly code with mmixal might |
need to move arrays and such to before the point of use. */ |
if (GET_CODE (XEXP (x, 0)) == PLUS) |
{ |
rtx x0 = XEXP (XEXP (x, 0), 0); |
rtx x1 = XEXP (XEXP (x, 0), 1); |
|
if ((GET_CODE (x0) == SYMBOL_REF |
|| GET_CODE (x0) == LABEL_REF) |
&& (GET_CODE (x1) == CONST_INT |
|| (GET_CODE (x1) == CONST_DOUBLE |
&& GET_MODE (x1) == VOIDmode))) |
addend = mmix_intval (x1); |
else |
return constant_ok; |
} |
else |
return constant_ok; |
break; |
|
default: |
return 0; |
} |
|
return constant_ok || (addend & 3) == 0; |
} |
|
/* Return 1 if the address is OK, otherwise 0. |
Used by GO_IF_LEGITIMATE_ADDRESS. */ |
|
int |
mmix_legitimate_address (enum machine_mode mode ATTRIBUTE_UNUSED, |
rtx x, |
int strict_checking) |
{ |
#define MMIX_REG_OK(X) \ |
((strict_checking \ |
&& (REGNO (X) <= MMIX_LAST_GENERAL_REGISTER \ |
|| (reg_renumber[REGNO (X)] > 0 \ |
&& reg_renumber[REGNO (X)] <= MMIX_LAST_GENERAL_REGISTER))) \ |
|| (!strict_checking \ |
&& (REGNO (X) <= MMIX_LAST_GENERAL_REGISTER \ |
|| REGNO (X) >= FIRST_PSEUDO_REGISTER \ |
|| REGNO (X) == ARG_POINTER_REGNUM))) |
|
/* We only accept: |
(mem reg) |
(mem (plus reg reg)) |
(mem (plus reg 0..255)). |
unless TARGET_BASE_ADDRESSES, in which case we accept all |
(mem constant_address) too. */ |
|
|
/* (mem reg) */ |
if (REG_P (x) && MMIX_REG_OK (x)) |
return 1; |
|
if (GET_CODE(x) == PLUS) |
{ |
rtx x1 = XEXP (x, 0); |
rtx x2 = XEXP (x, 1); |
|
/* Try swapping the order. FIXME: Do we need this? */ |
if (! REG_P (x1)) |
{ |
rtx tem = x1; |
x1 = x2; |
x2 = tem; |
} |
|
/* (mem (plus (reg?) (?))) */ |
if (!REG_P (x1) || !MMIX_REG_OK (x1)) |
return TARGET_BASE_ADDRESSES && mmix_constant_address_p (x); |
|
/* (mem (plus (reg) (reg?))) */ |
if (REG_P (x2) && MMIX_REG_OK (x2)) |
return 1; |
|
/* (mem (plus (reg) (0..255?))) */ |
if (GET_CODE (x2) == CONST_INT |
&& CONST_OK_FOR_LETTER_P (INTVAL (x2), 'I')) |
return 1; |
|
return 0; |
} |
|
return TARGET_BASE_ADDRESSES && mmix_constant_address_p (x); |
} |
|
/* LEGITIMATE_CONSTANT_P. */ |
|
int |
mmix_legitimate_constant_p (rtx x) |
{ |
RTX_CODE code = GET_CODE (x); |
|
/* We must allow any number due to the way the cse passes works; if we |
do not allow any number here, general_operand will fail, and insns |
will fatally fail recognition instead of "softly". */ |
if (code == CONST_INT || code == CONST_DOUBLE) |
return 1; |
|
return CONSTANT_ADDRESS_P (x); |
} |
|
/* SELECT_CC_MODE. */ |
|
enum machine_mode |
mmix_select_cc_mode (RTX_CODE op, rtx x, rtx y ATTRIBUTE_UNUSED) |
{ |
/* We use CCmode, CC_UNSmode, CC_FPmode, CC_FPEQmode and CC_FUNmode to |
output different compare insns. Note that we do not check the |
validity of the comparison here. */ |
|
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) |
{ |
if (op == ORDERED || op == UNORDERED || op == UNGE |
|| op == UNGT || op == UNLE || op == UNLT) |
return CC_FUNmode; |
|
if (op == EQ || op == NE) |
return CC_FPEQmode; |
|
return CC_FPmode; |
} |
|
if (op == GTU || op == LTU || op == GEU || op == LEU) |
return CC_UNSmode; |
|
return CCmode; |
} |
|
/* REVERSIBLE_CC_MODE. */ |
|
int |
mmix_reversible_cc_mode (enum machine_mode mode) |
{ |
/* That is, all integer and the EQ, NE, ORDERED and UNORDERED float |
compares. */ |
return mode != CC_FPmode; |
} |
|
/* TARGET_RTX_COSTS. */ |
|
static bool |
mmix_rtx_costs (rtx x ATTRIBUTE_UNUSED, |
int code ATTRIBUTE_UNUSED, |
int outer_code ATTRIBUTE_UNUSED, |
int *total ATTRIBUTE_UNUSED) |
{ |
/* For the time being, this is just a stub and we'll accept the |
generic calculations, until we can do measurements, at least. |
Say we did not modify any calculated costs. */ |
return false; |
} |
|
/* REGISTER_MOVE_COST. */ |
|
int |
mmix_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, |
enum reg_class from, |
enum reg_class to) |
{ |
return (from == GENERAL_REGS && from == to) ? 2 : 3; |
} |
|
/* Note that we don't have a TEXT_SECTION_ASM_OP, because it has to be a |
compile-time constant; it's used in an asm in crtstuff.c, compiled for |
the target. */ |
|
/* DATA_SECTION_ASM_OP. */ |
|
const char * |
mmix_data_section_asm_op (void) |
{ |
return "\t.data ! mmixal:= 8H LOC 9B"; |
} |
|
static void |
mmix_encode_section_info (tree decl, rtx rtl, int first) |
{ |
/* Test for an external declaration, and do nothing if it is one. */ |
if ((TREE_CODE (decl) == VAR_DECL |
&& (DECL_EXTERNAL (decl) || TREE_PUBLIC (decl))) |
|| (TREE_CODE (decl) == FUNCTION_DECL && TREE_PUBLIC (decl))) |
; |
else if (first && DECL_P (decl)) |
{ |
/* For non-visible declarations, add a "@" prefix, which we skip |
when the label is output. If the label does not have this |
prefix, a ":" is output if -mtoplevel-symbols. |
|
Note that this does not work for data that is declared extern and |
later defined as static. If there's code in between, that code |
will refer to the extern declaration, and vice versa. This just |
means that when -mtoplevel-symbols is in use, we can just handle |
well-behaved ISO-compliant code. */ |
|
const char *str = XSTR (XEXP (rtl, 0), 0); |
int len = strlen (str); |
char *newstr; |
|
/* Why is the return type of ggc_alloc_string const? */ |
newstr = (char *) ggc_alloc_string ("", len + 1); |
|
strcpy (newstr + 1, str); |
*newstr = '@'; |
XSTR (XEXP (rtl, 0), 0) = newstr; |
} |
|
/* Set SYMBOL_REF_FLAG for things that we want to access with GETA. We |
may need different options to reach for different things with GETA. |
For now, functions and things we know or have been told are constant. */ |
if (TREE_CODE (decl) == FUNCTION_DECL |
|| TREE_CONSTANT (decl) |
|| (TREE_CODE (decl) == VAR_DECL |
&& TREE_READONLY (decl) |
&& !TREE_SIDE_EFFECTS (decl) |
&& (!DECL_INITIAL (decl) |
|| TREE_CONSTANT (DECL_INITIAL (decl))))) |
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1; |
} |
|
static const char * |
mmix_strip_name_encoding (const char *name) |
{ |
for (; (*name == '@' || *name == '*'); name++) |
; |
|
return name; |
} |
|
/* TARGET_ASM_FILE_START. |
We just emit a little comment for the time being. */ |
|
static void |
mmix_file_start (void) |
{ |
default_file_start (); |
|
fputs ("! mmixal:= 8H LOC Data_Section\n", asm_out_file); |
|
/* Make sure each file starts with the text section. */ |
switch_to_section (text_section); |
} |
|
/* TARGET_ASM_FILE_END. */ |
|
static void |
mmix_file_end (void) |
{ |
/* Make sure each file ends with the data section. */ |
switch_to_section (data_section); |
} |
|
/* ASM_OUTPUT_SOURCE_FILENAME. */ |
|
void |
mmix_asm_output_source_filename (FILE *stream, const char *name) |
{ |
fprintf (stream, "# 1 "); |
OUTPUT_QUOTED_STRING (stream, name); |
fprintf (stream, "\n"); |
} |
|
/* OUTPUT_QUOTED_STRING. */ |
|
void |
mmix_output_quoted_string (FILE *stream, const char *string, int length) |
{ |
const char * string_end = string + length; |
static const char *const unwanted_chars = "\"[]\\"; |
|
/* Output "any character except newline and double quote character". We |
play it safe and avoid all control characters too. We also do not |
want [] as characters, should input be passed through m4 with [] as |
quotes. Further, we avoid "\", because the GAS port handles it as a |
quoting character. */ |
while (string < string_end) |
{ |
if (*string |
&& (unsigned char) *string < 128 |
&& !ISCNTRL (*string) |
&& strchr (unwanted_chars, *string) == NULL) |
{ |
fputc ('"', stream); |
while (*string |
&& (unsigned char) *string < 128 |
&& !ISCNTRL (*string) |
&& strchr (unwanted_chars, *string) == NULL |
&& string < string_end) |
{ |
fputc (*string, stream); |
string++; |
} |
fputc ('"', stream); |
if (string < string_end) |
fprintf (stream, ","); |
} |
if (string < string_end) |
{ |
fprintf (stream, "#%x", *string & 255); |
string++; |
if (string < string_end) |
fprintf (stream, ","); |
} |
} |
} |
|
/* Target hook for assembling integer objects. Use mmix_print_operand |
for WYDE and TETRA. Use mmix_output_octa to output 8-byte |
CONST_DOUBLEs. */ |
|
static bool |
mmix_assemble_integer (rtx x, unsigned int size, int aligned_p) |
{ |
if (aligned_p) |
switch (size) |
{ |
/* We handle a limited number of types of operands in here. But |
that's ok, because we can punt to generic functions. We then |
pretend that aligned data isn't needed, so the usual .<pseudo> |
syntax is used (which works for aligned data too). We actually |
*must* do that, since we say we don't have simple aligned |
pseudos, causing this function to be called. We just try and |
keep as much compatibility as possible with mmixal syntax for |
normal cases (i.e. without GNU extensions and C only). */ |
case 1: |
if (GET_CODE (x) != CONST_INT) |
{ |
aligned_p = 0; |
break; |
} |
fputs ("\tBYTE\t", asm_out_file); |
mmix_print_operand (asm_out_file, x, 'B'); |
fputc ('\n', asm_out_file); |
return true; |
|
case 2: |
if (GET_CODE (x) != CONST_INT) |
{ |
aligned_p = 0; |
break; |
} |
fputs ("\tWYDE\t", asm_out_file); |
mmix_print_operand (asm_out_file, x, 'W'); |
fputc ('\n', asm_out_file); |
return true; |
|
case 4: |
if (GET_CODE (x) != CONST_INT) |
{ |
aligned_p = 0; |
break; |
} |
fputs ("\tTETRA\t", asm_out_file); |
mmix_print_operand (asm_out_file, x, 'L'); |
fputc ('\n', asm_out_file); |
return true; |
|
case 8: |
/* We don't get here anymore for CONST_DOUBLE, because DImode |
isn't expressed as CONST_DOUBLE, and DFmode is handled |
elsewhere. */ |
gcc_assert (GET_CODE (x) != CONST_DOUBLE); |
assemble_integer_with_op ("\tOCTA\t", x); |
return true; |
} |
return default_assemble_integer (x, size, aligned_p); |
} |
|
/* ASM_OUTPUT_ASCII. */ |
|
void |
mmix_asm_output_ascii (FILE *stream, const char *string, int length) |
{ |
while (length > 0) |
{ |
int chunk_size = length > 60 ? 60 : length; |
fprintf (stream, "\tBYTE "); |
mmix_output_quoted_string (stream, string, chunk_size); |
string += chunk_size; |
length -= chunk_size; |
fprintf (stream, "\n"); |
} |
} |
|
/* ASM_OUTPUT_ALIGNED_COMMON. */ |
|
void |
mmix_asm_output_aligned_common (FILE *stream, |
const char *name, |
int size, |
int align) |
{ |
/* This is mostly the elfos.h one. There doesn't seem to be a way to |
express this in a mmixal-compatible way. */ |
fprintf (stream, "\t.comm\t"); |
assemble_name (stream, name); |
fprintf (stream, ",%u,%u ! mmixal-incompatible COMMON\n", |
size, align / BITS_PER_UNIT); |
} |
|
/* ASM_OUTPUT_ALIGNED_LOCAL. */ |
|
void |
mmix_asm_output_aligned_local (FILE *stream, |
const char *name, |
int size, |
int align) |
{ |
switch_to_section (data_section); |
|
ASM_OUTPUT_ALIGN (stream, exact_log2 (align/BITS_PER_UNIT)); |
assemble_name (stream, name); |
fprintf (stream, "\tLOC @+%d\n", size); |
} |
|
/* ASM_OUTPUT_LABEL. */ |
|
void |
mmix_asm_output_label (FILE *stream, const char *name) |
{ |
assemble_name (stream, name); |
fprintf (stream, "\tIS @\n"); |
} |
|
/* ASM_OUTPUT_INTERNAL_LABEL. */ |
|
void |
mmix_asm_output_internal_label (FILE *stream, const char *name) |
{ |
assemble_name_raw (stream, name); |
fprintf (stream, "\tIS @\n"); |
} |
|
/* ASM_DECLARE_REGISTER_GLOBAL. */ |
|
void |
mmix_asm_declare_register_global (FILE *stream ATTRIBUTE_UNUSED, |
tree decl ATTRIBUTE_UNUSED, |
int regno ATTRIBUTE_UNUSED, |
const char *name ATTRIBUTE_UNUSED) |
{ |
/* Nothing to do here, but there *will* be, therefore the framework is |
here. */ |
} |
|
/* ASM_WEAKEN_LABEL. */ |
|
void |
mmix_asm_weaken_label (FILE *stream ATTRIBUTE_UNUSED, |
const char *name ATTRIBUTE_UNUSED) |
{ |
fprintf (stream, "\t.weak "); |
assemble_name (stream, name); |
fprintf (stream, " ! mmixal-incompatible\n"); |
} |
|
/* MAKE_DECL_ONE_ONLY. */ |
|
void |
mmix_make_decl_one_only (tree decl) |
{ |
DECL_WEAK (decl) = 1; |
} |
|
/* ASM_OUTPUT_LABELREF. |
Strip GCC's '*' and our own '@'. No order is assumed. */ |
|
void |
mmix_asm_output_labelref (FILE *stream, const char *name) |
{ |
int is_extern = 1; |
|
for (; (*name == '@' || *name == '*'); name++) |
if (*name == '@') |
is_extern = 0; |
|
asm_fprintf (stream, "%s%U%s", |
is_extern && TARGET_TOPLEVEL_SYMBOLS ? ":" : "", |
name); |
} |
|
/* ASM_OUTPUT_DEF. */ |
|
void |
mmix_asm_output_def (FILE *stream, const char *name, const char *value) |
{ |
assemble_name (stream, name); |
fprintf (stream, "\tIS "); |
assemble_name (stream, value); |
fputc ('\n', stream); |
} |
|
/* PRINT_OPERAND. */ |
|
void |
mmix_print_operand (FILE *stream, rtx x, int code) |
{ |
/* When we add support for different codes later, we can, when needed, |
drop through to the main handler with a modified operand. */ |
rtx modified_x = x; |
int regno = x != NULL_RTX && REG_P (x) ? REGNO (x) : 0; |
|
switch (code) |
{ |
/* Unrelated codes are in alphabetic order. */ |
|
case '+': |
/* For conditional branches, output "P" for a probable branch. */ |
if (TARGET_BRANCH_PREDICT) |
{ |
x = find_reg_note (current_output_insn, REG_BR_PROB, 0); |
if (x && INTVAL (XEXP (x, 0)) > REG_BR_PROB_BASE / 2) |
putc ('P', stream); |
} |
return; |
|
case '.': |
/* For the %d in POP %d,0. */ |
fprintf (stream, "%d", MMIX_POP_ARGUMENT ()); |
return; |
|
case 'B': |
if (GET_CODE (x) != CONST_INT) |
fatal_insn ("MMIX Internal: Expected a CONST_INT, not this", x); |
fprintf (stream, "%d", (int) (INTVAL (x) & 0xff)); |
return; |
|
case 'H': |
/* Highpart. Must be general register, and not the last one, as |
that one cannot be part of a consecutive register pair. */ |
if (regno > MMIX_LAST_GENERAL_REGISTER - 1) |
internal_error ("MMIX Internal: Bad register: %d", regno); |
|
/* This is big-endian, so the high-part is the first one. */ |
fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno)]); |
return; |
|
case 'L': |
/* Lowpart. Must be CONST_INT or general register, and not the last |
one, as that one cannot be part of a consecutive register pair. */ |
if (GET_CODE (x) == CONST_INT) |
{ |
fprintf (stream, "#%lx", |
(unsigned long) (INTVAL (x) |
& ((unsigned int) 0x7fffffff * 2 + 1))); |
return; |
} |
|
if (GET_CODE (x) == SYMBOL_REF) |
{ |
output_addr_const (stream, x); |
return; |
} |
|
if (regno > MMIX_LAST_GENERAL_REGISTER - 1) |
internal_error ("MMIX Internal: Bad register: %d", regno); |
|
/* This is big-endian, so the low-part is + 1. */ |
fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno) + 1]); |
return; |
|
/* Can't use 'a' because that's a generic modifier for address |
output. */ |
case 'A': |
mmix_output_shiftvalue_op_from_str (stream, "ANDN", |
~(unsigned HOST_WIDEST_INT) |
mmix_intval (x)); |
return; |
|
case 'i': |
mmix_output_shiftvalue_op_from_str (stream, "INC", |
(unsigned HOST_WIDEST_INT) |
mmix_intval (x)); |
return; |
|
case 'o': |
mmix_output_shiftvalue_op_from_str (stream, "OR", |
(unsigned HOST_WIDEST_INT) |
mmix_intval (x)); |
return; |
|
case 's': |
mmix_output_shiftvalue_op_from_str (stream, "SET", |
(unsigned HOST_WIDEST_INT) |
mmix_intval (x)); |
return; |
|
case 'd': |
case 'D': |
mmix_output_condition (stream, x, (code == 'D')); |
return; |
|
case 'e': |
/* Output an extra "e" to make fcmpe, fune. */ |
if (TARGET_FCMP_EPSILON) |
fprintf (stream, "e"); |
return; |
|
case 'm': |
/* Output the number minus 1. */ |
if (GET_CODE (x) != CONST_INT) |
{ |
fatal_insn ("MMIX Internal: Bad value for 'm', not a CONST_INT", |
x); |
} |
fprintf (stream, HOST_WIDEST_INT_PRINT_DEC, |
(HOST_WIDEST_INT) (mmix_intval (x) - 1)); |
return; |
|
case 'p': |
/* Store the number of registers we want to save. This was setup |
by the prologue. The actual operand contains the number of |
registers to pass, but we don't use it currently. Anyway, we |
need to output the number of saved registers here. */ |
fprintf (stream, "%d", |
cfun->machine->highest_saved_stack_register + 1); |
return; |
|
case 'r': |
/* Store the register to output a constant to. */ |
if (! REG_P (x)) |
fatal_insn ("MMIX Internal: Expected a register, not this", x); |
mmix_output_destination_register = MMIX_OUTPUT_REGNO (regno); |
return; |
|
case 'I': |
/* Output the constant. Note that we use this for floats as well. */ |
if (GET_CODE (x) != CONST_INT |
&& (GET_CODE (x) != CONST_DOUBLE |
|| (GET_MODE (x) != VOIDmode && GET_MODE (x) != DFmode |
&& GET_MODE (x) != SFmode))) |
fatal_insn ("MMIX Internal: Expected a constant, not this", x); |
mmix_output_register_setting (stream, |
mmix_output_destination_register, |
mmix_intval (x), 0); |
return; |
|
case 'U': |
/* An U for unsigned, if TARGET_ZERO_EXTEND. Ignore the operand. */ |
if (TARGET_ZERO_EXTEND) |
putc ('U', stream); |
return; |
|
case 'v': |
mmix_output_shifted_value (stream, (HOST_WIDEST_INT) mmix_intval (x)); |
return; |
|
case 'V': |
mmix_output_shifted_value (stream, (HOST_WIDEST_INT) ~mmix_intval (x)); |
return; |
|
case 'W': |
if (GET_CODE (x) != CONST_INT) |
fatal_insn ("MMIX Internal: Expected a CONST_INT, not this", x); |
fprintf (stream, "#%x", (int) (INTVAL (x) & 0xffff)); |
return; |
|
case 0: |
/* Nothing to do. */ |
break; |
|
default: |
/* Presumably there's a missing case above if we get here. */ |
internal_error ("MMIX Internal: Missing %qc case in mmix_print_operand", code); |
} |
|
switch (GET_CODE (modified_x)) |
{ |
case REG: |
regno = REGNO (modified_x); |
if (regno >= FIRST_PSEUDO_REGISTER) |
internal_error ("MMIX Internal: Bad register: %d", regno); |
fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno)]); |
return; |
|
case MEM: |
output_address (XEXP (modified_x, 0)); |
return; |
|
case CONST_INT: |
/* For -2147483648, mmixal complains that the constant does not fit |
in 4 bytes, so let's output it as hex. Take care to handle hosts |
where HOST_WIDE_INT is longer than an int. |
|
Print small constants +-255 using decimal. */ |
|
if (INTVAL (modified_x) > -256 && INTVAL (modified_x) < 256) |
fprintf (stream, "%d", (int) (INTVAL (modified_x))); |
else |
fprintf (stream, "#%x", |
(int) (INTVAL (modified_x)) & (unsigned int) ~0); |
return; |
|
case CONST_DOUBLE: |
/* Do somewhat as CONST_INT. */ |
mmix_output_octa (stream, mmix_intval (modified_x), 0); |
return; |
|
case CONST: |
output_addr_const (stream, modified_x); |
return; |
|
default: |
/* No need to test for all strange things. Let output_addr_const do |
it for us. */ |
if (CONSTANT_P (modified_x) |
/* Strangely enough, this is not included in CONSTANT_P. |
FIXME: Ask/check about sanity here. */ |
|| GET_CODE (modified_x) == CODE_LABEL) |
{ |
output_addr_const (stream, modified_x); |
return; |
} |
|
/* We need the original here. */ |
fatal_insn ("MMIX Internal: Cannot decode this operand", x); |
} |
} |
|
/* PRINT_OPERAND_PUNCT_VALID_P. */ |
|
int |
mmix_print_operand_punct_valid_p (int code ATTRIBUTE_UNUSED) |
{ |
/* A '+' is used for branch prediction, similar to other ports. */ |
return code == '+' |
/* A '.' is used for the %d in the POP %d,0 return insn. */ |
|| code == '.'; |
} |
|
/* PRINT_OPERAND_ADDRESS. */ |
|
void |
mmix_print_operand_address (FILE *stream, rtx x) |
{ |
if (REG_P (x)) |
{ |
/* I find the generated assembly code harder to read without |
the ",0". */ |
fprintf (stream, "%s,0", reg_names[MMIX_OUTPUT_REGNO (REGNO (x))]); |
return; |
} |
else if (GET_CODE (x) == PLUS) |
{ |
rtx x1 = XEXP (x, 0); |
rtx x2 = XEXP (x, 1); |
|
if (REG_P (x1)) |
{ |
fprintf (stream, "%s,", reg_names[MMIX_OUTPUT_REGNO (REGNO (x1))]); |
|
if (REG_P (x2)) |
{ |
fprintf (stream, "%s", |
reg_names[MMIX_OUTPUT_REGNO (REGNO (x2))]); |
return; |
} |
else if (GET_CODE (x2) == CONST_INT |
&& CONST_OK_FOR_LETTER_P (INTVAL (x2), 'I')) |
{ |
output_addr_const (stream, x2); |
return; |
} |
} |
} |
|
if (TARGET_BASE_ADDRESSES && mmix_legitimate_constant_p (x)) |
{ |
output_addr_const (stream, x); |
return; |
} |
|
fatal_insn ("MMIX Internal: This is not a recognized address", x); |
} |
|
/* ASM_OUTPUT_REG_PUSH. */ |
|
void |
mmix_asm_output_reg_push (FILE *stream, int regno) |
{ |
fprintf (stream, "\tSUBU %s,%s,8\n\tSTOU %s,%s,0\n", |
reg_names[MMIX_STACK_POINTER_REGNUM], |
reg_names[MMIX_STACK_POINTER_REGNUM], |
reg_names[MMIX_OUTPUT_REGNO (regno)], |
reg_names[MMIX_STACK_POINTER_REGNUM]); |
} |
|
/* ASM_OUTPUT_REG_POP. */ |
|
void |
mmix_asm_output_reg_pop (FILE *stream, int regno) |
{ |
fprintf (stream, "\tLDOU %s,%s,0\n\tINCL %s,8\n", |
reg_names[MMIX_OUTPUT_REGNO (regno)], |
reg_names[MMIX_STACK_POINTER_REGNUM], |
reg_names[MMIX_STACK_POINTER_REGNUM]); |
} |
|
/* ASM_OUTPUT_ADDR_DIFF_ELT. */ |
|
void |
mmix_asm_output_addr_diff_elt (FILE *stream, |
rtx body ATTRIBUTE_UNUSED, |
int value, |
int rel) |
{ |
fprintf (stream, "\tTETRA L%d-L%d\n", value, rel); |
} |
|
/* ASM_OUTPUT_ADDR_VEC_ELT. */ |
|
void |
mmix_asm_output_addr_vec_elt (FILE *stream, int value) |
{ |
fprintf (stream, "\tOCTA L:%d\n", value); |
} |
|
/* ASM_OUTPUT_SKIP. */ |
|
void |
mmix_asm_output_skip (FILE *stream, int nbytes) |
{ |
fprintf (stream, "\tLOC @+%d\n", nbytes); |
} |
|
/* ASM_OUTPUT_ALIGN. */ |
|
void |
mmix_asm_output_align (FILE *stream, int power) |
{ |
/* We need to record the needed alignment of this section in the object, |
so we have to output an alignment directive. Use a .p2align (not |
.align) so people will never have to wonder about whether the |
argument is in number of bytes or the log2 thereof. We do it in |
addition to the LOC directive, so nothing needs tweaking when |
copy-pasting assembly into mmixal. */ |
fprintf (stream, "\t.p2align %d\n", power); |
fprintf (stream, "\tLOC @+(%d-@)&%d\n", 1 << power, (1 << power) - 1); |
} |
|
/* DBX_REGISTER_NUMBER. */ |
|
int |
mmix_dbx_register_number (int regno) |
{ |
/* Adjust the register number to the one it will be output as, dammit. |
It'd be nice if we could check the assumption that we're filling a |
gap, but every register between the last saved register and parameter |
registers might be a valid parameter register. */ |
regno = MMIX_OUTPUT_REGNO (regno); |
|
/* We need to renumber registers to get the number of the return address |
register in the range 0..255. It is also space-saving if registers |
mentioned in the call-frame information (which uses this function by |
defaulting DWARF_FRAME_REGNUM to DBX_REGISTER_NUMBER) are numbered |
0 .. 63. So map 224 .. 256+15 -> 0 .. 47 and 0 .. 223 -> 48..223+48. */ |
return regno >= 224 ? (regno - 224) : (regno + 48); |
} |
|
/* End of target macro support functions. |
|
Now the MMIX port's own functions. First the exported ones. */ |
|
/* Wrapper for get_hard_reg_initial_val since integrate.h isn't included |
from insn-emit.c. */ |
|
rtx |
mmix_get_hard_reg_initial_val (enum machine_mode mode, int regno) |
{ |
return get_hard_reg_initial_val (mode, regno); |
} |
|
/* Nonzero when the function epilogue is simple enough that a single |
"POP %d,0" should be used even within the function. */ |
|
int |
mmix_use_simple_return (void) |
{ |
int regno; |
|
int stack_space_to_allocate |
= (current_function_outgoing_args_size |
+ current_function_pretend_args_size |
+ get_frame_size () + 7) & ~7; |
|
if (!TARGET_USE_RETURN_INSN || !reload_completed) |
return 0; |
|
for (regno = 255; |
regno >= MMIX_FIRST_GLOBAL_REGNUM; |
regno--) |
/* Note that we assume that the frame-pointer-register is one of these |
registers, in which case we don't count it here. */ |
if ((((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) |
&& regs_ever_live[regno] && !call_used_regs[regno])) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
return 0; |
|
if (frame_pointer_needed) |
stack_space_to_allocate += 8; |
|
if (MMIX_CFUN_HAS_LANDING_PAD) |
stack_space_to_allocate += 16; |
else if (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS) |
stack_space_to_allocate += 8; |
|
return stack_space_to_allocate == 0; |
} |
|
|
/* Expands the function prologue into RTX. */ |
|
void |
mmix_expand_prologue (void) |
{ |
HOST_WIDE_INT locals_size = get_frame_size (); |
int regno; |
HOST_WIDE_INT stack_space_to_allocate |
= (current_function_outgoing_args_size |
+ current_function_pretend_args_size |
+ locals_size + 7) & ~7; |
HOST_WIDE_INT offset = -8; |
|
/* Add room needed to save global non-register-stack registers. */ |
for (regno = 255; |
regno >= MMIX_FIRST_GLOBAL_REGNUM; |
regno--) |
/* Note that we assume that the frame-pointer-register is one of these |
registers, in which case we don't count it here. */ |
if ((((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) |
&& regs_ever_live[regno] && !call_used_regs[regno])) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
stack_space_to_allocate += 8; |
|
/* If we do have a frame-pointer, add room for it. */ |
if (frame_pointer_needed) |
stack_space_to_allocate += 8; |
|
/* If we have a non-local label, we need to be able to unwind to it, so |
store the current register stack pointer. Also store the return |
address if we do that. */ |
if (MMIX_CFUN_HAS_LANDING_PAD) |
stack_space_to_allocate += 16; |
else if (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS) |
/* If we do have a saved return-address slot, add room for it. */ |
stack_space_to_allocate += 8; |
|
/* Make sure we don't get an unaligned stack. */ |
if ((stack_space_to_allocate % 8) != 0) |
internal_error ("stack frame not a multiple of 8 bytes: %wd", |
stack_space_to_allocate); |
|
if (current_function_pretend_args_size) |
{ |
int mmix_first_vararg_reg |
= (MMIX_FIRST_INCOMING_ARG_REGNUM |
+ (MMIX_MAX_ARGS_IN_REGS |
- current_function_pretend_args_size / 8)); |
|
for (regno |
= MMIX_FIRST_INCOMING_ARG_REGNUM + MMIX_MAX_ARGS_IN_REGS - 1; |
regno >= mmix_first_vararg_reg; |
regno--) |
{ |
if (offset < 0) |
{ |
HOST_WIDE_INT stack_chunk |
= stack_space_to_allocate > (256 - 8) |
? (256 - 8) : stack_space_to_allocate; |
|
mmix_emit_sp_add (-stack_chunk); |
offset += stack_chunk; |
stack_space_to_allocate -= stack_chunk; |
} |
|
/* These registers aren't actually saved (as in "will be |
restored"), so don't tell DWARF2 they're saved. */ |
emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset)), |
gen_rtx_REG (DImode, regno)); |
offset -= 8; |
} |
} |
|
/* Store the frame-pointer. */ |
|
if (frame_pointer_needed) |
{ |
rtx insn; |
|
if (offset < 0) |
{ |
/* Get 8 less than otherwise, since we need to reach offset + 8. */ |
HOST_WIDE_INT stack_chunk |
= stack_space_to_allocate > (256 - 8 - 8) |
? (256 - 8 - 8) : stack_space_to_allocate; |
|
mmix_emit_sp_add (-stack_chunk); |
|
offset += stack_chunk; |
stack_space_to_allocate -= stack_chunk; |
} |
|
insn = emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset)), |
hard_frame_pointer_rtx); |
RTX_FRAME_RELATED_P (insn) = 1; |
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx, |
stack_pointer_rtx, |
GEN_INT (offset + 8))); |
RTX_FRAME_RELATED_P (insn) = 1; |
offset -= 8; |
} |
|
if (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS) |
{ |
rtx tmpreg, retreg; |
rtx insn; |
|
/* Store the return-address, if one is needed on the stack. We |
usually store it in a register when needed, but that doesn't work |
with -fexceptions. */ |
|
if (offset < 0) |
{ |
/* Get 8 less than otherwise, since we need to reach offset + 8. */ |
HOST_WIDE_INT stack_chunk |
= stack_space_to_allocate > (256 - 8 - 8) |
? (256 - 8 - 8) : stack_space_to_allocate; |
|
mmix_emit_sp_add (-stack_chunk); |
|
offset += stack_chunk; |
stack_space_to_allocate -= stack_chunk; |
} |
|
tmpreg = gen_rtx_REG (DImode, 255); |
retreg = gen_rtx_REG (DImode, MMIX_rJ_REGNUM); |
|
/* Dwarf2 code is confused by the use of a temporary register for |
storing the return address, so we have to express it as a note, |
which we attach to the actual store insn. */ |
emit_move_insn (tmpreg, retreg); |
|
insn = emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset)), |
tmpreg); |
RTX_FRAME_RELATED_P (insn) = 1; |
REG_NOTES (insn) |
= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
gen_rtx_SET (VOIDmode, |
gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset)), |
retreg), |
REG_NOTES (insn)); |
|
offset -= 8; |
} |
else if (MMIX_CFUN_HAS_LANDING_PAD) |
offset -= 8; |
|
if (MMIX_CFUN_HAS_LANDING_PAD) |
{ |
/* Store the register defining the numbering of local registers, so |
we know how long to unwind the register stack. */ |
|
if (offset < 0) |
{ |
/* Get 8 less than otherwise, since we need to reach offset + 8. */ |
HOST_WIDE_INT stack_chunk |
= stack_space_to_allocate > (256 - 8 - 8) |
? (256 - 8 - 8) : stack_space_to_allocate; |
|
mmix_emit_sp_add (-stack_chunk); |
|
offset += stack_chunk; |
stack_space_to_allocate -= stack_chunk; |
} |
|
/* We don't tell dwarf2 about this one; we just have it to unwind |
the register stack at landing pads. FIXME: It's a kludge because |
we can't describe the effect of the PUSHJ and PUSHGO insns on the |
register stack at the moment. Best thing would be to handle it |
like stack-pointer offsets. Better: some hook into dwarf2out.c |
to produce DW_CFA_expression:s that specify the increment of rO, |
and unwind it at eh_return (preferred) or at the landing pad. |
Then saves to $0..$G-1 could be specified through that register. */ |
|
emit_move_insn (gen_rtx_REG (DImode, 255), |
gen_rtx_REG (DImode, |
MMIX_rO_REGNUM)); |
emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, offset)), |
gen_rtx_REG (DImode, 255)); |
offset -= 8; |
} |
|
/* After the return-address and the frame-pointer, we have the local |
variables. They're the ones that may have an "unaligned" size. */ |
offset -= (locals_size + 7) & ~7; |
|
/* Now store all registers that are global, i.e. not saved by the |
register file machinery. |
|
It is assumed that the frame-pointer is one of these registers, so it |
is explicitly excluded in the count. */ |
|
for (regno = 255; |
regno >= MMIX_FIRST_GLOBAL_REGNUM; |
regno--) |
if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) |
&& regs_ever_live[regno] && ! call_used_regs[regno]) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
{ |
rtx insn; |
|
if (offset < 0) |
{ |
HOST_WIDE_INT stack_chunk |
= (stack_space_to_allocate > (256 - offset - 8) |
? (256 - offset - 8) : stack_space_to_allocate); |
|
mmix_emit_sp_add (-stack_chunk); |
offset += stack_chunk; |
stack_space_to_allocate -= stack_chunk; |
} |
|
insn = emit_move_insn (gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset)), |
gen_rtx_REG (DImode, regno)); |
RTX_FRAME_RELATED_P (insn) = 1; |
offset -= 8; |
} |
|
/* Finally, allocate room for outgoing args and local vars if room |
wasn't allocated above. */ |
if (stack_space_to_allocate) |
mmix_emit_sp_add (-stack_space_to_allocate); |
} |
|
/* Expands the function epilogue into RTX. */ |
|
void |
mmix_expand_epilogue (void) |
{ |
HOST_WIDE_INT locals_size = get_frame_size (); |
int regno; |
HOST_WIDE_INT stack_space_to_deallocate |
= (current_function_outgoing_args_size |
+ current_function_pretend_args_size |
+ locals_size + 7) & ~7; |
|
/* The first address to access is beyond the outgoing_args area. */ |
HOST_WIDE_INT offset = current_function_outgoing_args_size; |
|
/* Add the space for global non-register-stack registers. |
It is assumed that the frame-pointer register can be one of these |
registers, in which case it is excluded from the count when needed. */ |
for (regno = 255; |
regno >= MMIX_FIRST_GLOBAL_REGNUM; |
regno--) |
if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) |
&& regs_ever_live[regno] && !call_used_regs[regno]) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
stack_space_to_deallocate += 8; |
|
/* Add in the space for register stack-pointer. If so, always add room |
for the saved PC. */ |
if (MMIX_CFUN_HAS_LANDING_PAD) |
stack_space_to_deallocate += 16; |
else if (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS) |
/* If we have a saved return-address slot, add it in. */ |
stack_space_to_deallocate += 8; |
|
/* Add in the frame-pointer. */ |
if (frame_pointer_needed) |
stack_space_to_deallocate += 8; |
|
/* Make sure we don't get an unaligned stack. */ |
if ((stack_space_to_deallocate % 8) != 0) |
internal_error ("stack frame not a multiple of octabyte: %wd", |
stack_space_to_deallocate); |
|
/* We will add back small offsets to the stack pointer as we go. |
First, we restore all registers that are global, i.e. not saved by |
the register file machinery. */ |
|
for (regno = MMIX_FIRST_GLOBAL_REGNUM; |
regno <= 255; |
regno++) |
if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) |
&& regs_ever_live[regno] && !call_used_regs[regno]) |
|| IS_MMIX_EH_RETURN_DATA_REG (regno)) |
{ |
if (offset > 255) |
{ |
mmix_emit_sp_add (offset); |
stack_space_to_deallocate -= offset; |
offset = 0; |
} |
|
emit_move_insn (gen_rtx_REG (DImode, regno), |
gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset))); |
offset += 8; |
} |
|
/* Here is where the local variables were. As in the prologue, they |
might be of an unaligned size. */ |
offset += (locals_size + 7) & ~7; |
|
/* The saved register stack pointer is just below the frame-pointer |
register. We don't need to restore it "manually"; the POP |
instruction does that. */ |
if (MMIX_CFUN_HAS_LANDING_PAD) |
offset += 16; |
else if (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS) |
/* The return-address slot is just below the frame-pointer register. |
We don't need to restore it because we don't really use it. */ |
offset += 8; |
|
/* Get back the old frame-pointer-value. */ |
if (frame_pointer_needed) |
{ |
if (offset > 255) |
{ |
mmix_emit_sp_add (offset); |
|
stack_space_to_deallocate -= offset; |
offset = 0; |
} |
|
emit_move_insn (hard_frame_pointer_rtx, |
gen_rtx_MEM (DImode, |
plus_constant (stack_pointer_rtx, |
offset))); |
offset += 8; |
} |
|
/* We do not need to restore pretended incoming args, just add back |
offset to sp. */ |
if (stack_space_to_deallocate != 0) |
mmix_emit_sp_add (stack_space_to_deallocate); |
|
if (current_function_calls_eh_return) |
/* Adjust the (normal) stack-pointer to that of the receiver. |
FIXME: It would be nice if we could also adjust the register stack |
here, but we need to express it through DWARF 2 too. */ |
emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, |
gen_rtx_REG (DImode, |
MMIX_EH_RETURN_STACKADJ_REGNUM))); |
} |
|
/* Output an optimal sequence for setting a register to a specific |
constant. Used in an alternative for const_ints in movdi, and when |
using large stack-frame offsets. |
|
Use do_begin_end to say if a line-starting TAB and newline before the |
first insn and after the last insn is wanted. */ |
|
void |
mmix_output_register_setting (FILE *stream, |
int regno, |
HOST_WIDEST_INT value, |
int do_begin_end) |
{ |
if (do_begin_end) |
fprintf (stream, "\t"); |
|
if (mmix_shiftable_wyde_value ((unsigned HOST_WIDEST_INT) value)) |
{ |
/* First, the one-insn cases. */ |
mmix_output_shiftvalue_op_from_str (stream, "SET", |
(unsigned HOST_WIDEST_INT) |
value); |
fprintf (stream, " %s,", reg_names[regno]); |
mmix_output_shifted_value (stream, (unsigned HOST_WIDEST_INT) value); |
} |
else if (mmix_shiftable_wyde_value (-(unsigned HOST_WIDEST_INT) value)) |
{ |
/* We do this to get a bit more legible assembly code. The next |
alternative is mostly redundant with this. */ |
|
mmix_output_shiftvalue_op_from_str (stream, "SET", |
-(unsigned HOST_WIDEST_INT) |
value); |
fprintf (stream, " %s,", reg_names[regno]); |
mmix_output_shifted_value (stream, -(unsigned HOST_WIDEST_INT) value); |
fprintf (stream, "\n\tNEGU %s,0,%s", reg_names[regno], |
reg_names[regno]); |
} |
else if (mmix_shiftable_wyde_value (~(unsigned HOST_WIDEST_INT) value)) |
{ |
/* Slightly more expensive, the two-insn cases. */ |
|
/* FIXME: We could of course also test if 0..255-N or ~(N | 1..255) |
is shiftable, or any other one-insn transformation of the value. |
FIXME: Check first if the value is "shiftable" by two loading |
with two insns, since it makes more readable assembly code (if |
anyone else cares). */ |
|
mmix_output_shiftvalue_op_from_str (stream, "SET", |
~(unsigned HOST_WIDEST_INT) |
value); |
fprintf (stream, " %s,", reg_names[regno]); |
mmix_output_shifted_value (stream, ~(unsigned HOST_WIDEST_INT) value); |
fprintf (stream, "\n\tNOR %s,%s,0", reg_names[regno], |
reg_names[regno]); |
} |
else |
{ |
/* The generic case. 2..4 insns. */ |
static const char *const higher_parts[] = {"L", "ML", "MH", "H"}; |
const char *op = "SET"; |
const char *line_begin = ""; |
int insns = 0; |
int i; |
HOST_WIDEST_INT tmpvalue = value; |
|
/* Compute the number of insns needed to output this constant. */ |
for (i = 0; i < 4 && tmpvalue != 0; i++) |
{ |
if (tmpvalue & 65535) |
insns++; |
tmpvalue >>= 16; |
} |
if (TARGET_BASE_ADDRESSES && insns == 3) |
{ |
/* The number three is based on a static observation on |
ghostscript-6.52. Two and four are excluded because there |
are too many such constants, and each unique constant (maybe |
offset by 1..255) were used few times compared to other uses, |
e.g. addresses. |
|
We use base-plus-offset addressing to force it into a global |
register; we just use a "LDA reg,VALUE", which will cause the |
assembler and linker to DTRT (for constants as well as |
addresses). */ |
fprintf (stream, "LDA %s,", reg_names[regno]); |
mmix_output_octa (stream, value, 0); |
} |
else |
{ |
/* Output pertinent parts of the 4-wyde sequence. |
Still more to do if we want this to be optimal, but hey... |
Note that the zero case has been handled above. */ |
for (i = 0; i < 4 && value != 0; i++) |
{ |
if (value & 65535) |
{ |
fprintf (stream, "%s%s%s %s,#%x", line_begin, op, |
higher_parts[i], reg_names[regno], |
(int) (value & 65535)); |
/* The first one sets the rest of the bits to 0, the next |
ones add set bits. */ |
op = "INC"; |
line_begin = "\n\t"; |
} |
|
value >>= 16; |
} |
} |
} |
|
if (do_begin_end) |
fprintf (stream, "\n"); |
} |
|
/* Return 1 if value is 0..65535*2**(16*N) for N=0..3. |
else return 0. */ |
|
int |
mmix_shiftable_wyde_value (unsigned HOST_WIDEST_INT value) |
{ |
/* Shift by 16 bits per group, stop when we've found two groups with |
nonzero bits. */ |
int i; |
int has_candidate = 0; |
|
for (i = 0; i < 4; i++) |
{ |
if (value & 65535) |
{ |
if (has_candidate) |
return 0; |
else |
has_candidate = 1; |
} |
|
value >>= 16; |
} |
|
return 1; |
} |
|
/* Returns zero if code and mode is not a valid condition from a |
compare-type insn. Nonzero if it is. The parameter op, if non-NULL, |
is the comparison of mode is CC-somethingmode. */ |
|
int |
mmix_valid_comparison (RTX_CODE code, enum machine_mode mode, rtx op) |
{ |
if (mode == VOIDmode && op != NULL_RTX) |
mode = GET_MODE (op); |
|
/* We don't care to look at these, they should always be valid. */ |
if (mode == CCmode || mode == CC_UNSmode || mode == DImode) |
return 1; |
|
if ((mode == CC_FPmode || mode == DFmode) |
&& (code == GT || code == LT)) |
return 1; |
|
if ((mode == CC_FPEQmode || mode == DFmode) |
&& (code == EQ || code == NE)) |
return 1; |
|
if ((mode == CC_FUNmode || mode == DFmode) |
&& (code == ORDERED || code == UNORDERED)) |
return 1; |
|
return 0; |
} |
|
/* X and Y are two things to compare using CODE. Emit a compare insn if |
possible and return the rtx for the cc-reg in the proper mode, or |
NULL_RTX if this is not a valid comparison. */ |
|
rtx |
mmix_gen_compare_reg (RTX_CODE code, rtx x, rtx y) |
{ |
enum machine_mode ccmode = SELECT_CC_MODE (code, x, y); |
rtx cc_reg; |
|
/* FIXME: Do we get constants here? Of double mode? */ |
enum machine_mode mode |
= GET_MODE (x) == VOIDmode |
? GET_MODE (y) |
: GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT ? DFmode : DImode; |
|
if (! mmix_valid_comparison (code, mode, x)) |
return NULL_RTX; |
|
cc_reg = gen_reg_rtx (ccmode); |
|
/* FIXME: Can we avoid emitting a compare insn here? */ |
if (! REG_P (x) && ! REG_P (y)) |
x = force_reg (mode, x); |
|
/* If it's not quite right yet, put y in a register. */ |
if (! REG_P (y) |
&& (GET_CODE (y) != CONST_INT |
|| ! CONST_OK_FOR_LETTER_P (INTVAL (y), 'I'))) |
y = force_reg (mode, y); |
|
emit_insn (gen_rtx_SET (VOIDmode, cc_reg, |
gen_rtx_COMPARE (ccmode, x, y))); |
|
return cc_reg; |
} |
|
/* Local (static) helper functions. */ |
|
static void |
mmix_emit_sp_add (HOST_WIDE_INT offset) |
{ |
rtx insn; |
|
if (offset < 0) |
{ |
/* Negative stack-pointer adjustments are allocations and appear in |
the prologue only. We mark them as frame-related so unwind and |
debug info is properly emitted for them. */ |
if (offset > -255) |
insn = emit_insn (gen_adddi3 (stack_pointer_rtx, |
stack_pointer_rtx, |
GEN_INT (offset))); |
else |
{ |
rtx tmpr = gen_rtx_REG (DImode, 255); |
RTX_FRAME_RELATED_P (emit_move_insn (tmpr, GEN_INT (offset))) = 1; |
insn = emit_insn (gen_adddi3 (stack_pointer_rtx, |
stack_pointer_rtx, tmpr)); |
} |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
else |
{ |
/* Positive adjustments are in the epilogue only. Don't mark them |
as "frame-related" for unwind info. */ |
if (CONST_OK_FOR_LETTER_P (offset, 'L')) |
emit_insn (gen_adddi3 (stack_pointer_rtx, |
stack_pointer_rtx, |
GEN_INT (offset))); |
else |
{ |
rtx tmpr = gen_rtx_REG (DImode, 255); |
emit_move_insn (tmpr, GEN_INT (offset)); |
insn = emit_insn (gen_adddi3 (stack_pointer_rtx, |
stack_pointer_rtx, tmpr)); |
} |
} |
} |
|
/* Print operator suitable for doing something with a shiftable |
wyde. The type of operator is passed as an asm output modifier. */ |
|
static void |
mmix_output_shiftvalue_op_from_str (FILE *stream, |
const char *mainop, |
HOST_WIDEST_INT value) |
{ |
static const char *const op_part[] = {"L", "ML", "MH", "H"}; |
int i; |
|
if (! mmix_shiftable_wyde_value (value)) |
{ |
char s[sizeof ("0xffffffffffffffff")]; |
sprintf (s, HOST_WIDEST_INT_PRINT_HEX, value); |
internal_error ("MMIX Internal: %s is not a shiftable int", s); |
} |
|
for (i = 0; i < 4; i++) |
{ |
/* We know we're through when we find one-bits in the low |
16 bits. */ |
if (value & 0xffff) |
{ |
fprintf (stream, "%s%s", mainop, op_part[i]); |
return; |
} |
value >>= 16; |
} |
|
/* No bits set? Then it must have been zero. */ |
fprintf (stream, "%sL", mainop); |
} |
|
/* Print a 64-bit value, optionally prefixed by assembly pseudo. */ |
|
static void |
mmix_output_octa (FILE *stream, HOST_WIDEST_INT value, int do_begin_end) |
{ |
/* Snipped from final.c:output_addr_const. We need to avoid the |
presumed universal "0x" prefix. We can do it by replacing "0x" with |
"#0" here; we must avoid a space in the operands and no, the zero |
won't cause the number to be assumed in octal format. */ |
char hex_format[sizeof (HOST_WIDEST_INT_PRINT_HEX)]; |
|
if (do_begin_end) |
fprintf (stream, "\tOCTA "); |
|
strcpy (hex_format, HOST_WIDEST_INT_PRINT_HEX); |
hex_format[0] = '#'; |
hex_format[1] = '0'; |
|
/* Provide a few alternative output formats depending on the number, to |
improve legibility of assembler output. */ |
if ((value < (HOST_WIDEST_INT) 0 && value > (HOST_WIDEST_INT) -10000) |
|| (value >= (HOST_WIDEST_INT) 0 && value <= (HOST_WIDEST_INT) 16384)) |
fprintf (stream, "%d", (int) value); |
else if (value > (HOST_WIDEST_INT) 0 |
&& value < ((HOST_WIDEST_INT) 1 << 31) * 2) |
fprintf (stream, "#%x", (unsigned int) value); |
else |
fprintf (stream, hex_format, value); |
|
if (do_begin_end) |
fprintf (stream, "\n"); |
} |
|
/* Print the presumed shiftable wyde argument shifted into place (to |
be output with an operand). */ |
|
static void |
mmix_output_shifted_value (FILE *stream, HOST_WIDEST_INT value) |
{ |
int i; |
|
if (! mmix_shiftable_wyde_value (value)) |
{ |
char s[16+2+1]; |
sprintf (s, HOST_WIDEST_INT_PRINT_HEX, value); |
internal_error ("MMIX Internal: %s is not a shiftable int", s); |
} |
|
for (i = 0; i < 4; i++) |
{ |
/* We know we're through when we find one-bits in the low 16 bits. */ |
if (value & 0xffff) |
{ |
fprintf (stream, "#%x", (int) (value & 0xffff)); |
return; |
} |
|
value >>= 16; |
} |
|
/* No bits set? Then it must have been zero. */ |
fprintf (stream, "0"); |
} |
|
/* Output an MMIX condition name corresponding to an operator |
and operands: |
(comparison_operator [(comparison_operator ...) (const_int 0)]) |
which means we have to look at *two* operators. |
|
The argument "reversed" refers to reversal of the condition (not the |
same as swapping the arguments). */ |
|
static void |
mmix_output_condition (FILE *stream, rtx x, int reversed) |
{ |
struct cc_conv |
{ |
RTX_CODE cc; |
|
/* The normal output cc-code. */ |
const char *const normal; |
|
/* The reversed cc-code, or NULL if invalid. */ |
const char *const reversed; |
}; |
|
struct cc_type_conv |
{ |
enum machine_mode cc_mode; |
|
/* Terminated with {UNKNOWN, NULL, NULL} */ |
const struct cc_conv *const convs; |
}; |
|
#undef CCEND |
#define CCEND {UNKNOWN, NULL, NULL} |
|
static const struct cc_conv cc_fun_convs[] |
= {{ORDERED, "Z", "P"}, |
{UNORDERED, "P", "Z"}, |
CCEND}; |
static const struct cc_conv cc_fp_convs[] |
= {{GT, "P", NULL}, |
{LT, "N", NULL}, |
CCEND}; |
static const struct cc_conv cc_fpeq_convs[] |
= {{NE, "Z", "P"}, |
{EQ, "P", "Z"}, |
CCEND}; |
static const struct cc_conv cc_uns_convs[] |
= {{GEU, "NN", "N"}, |
{GTU, "P", "NP"}, |
{LEU, "NP", "P"}, |
{LTU, "N", "NN"}, |
CCEND}; |
static const struct cc_conv cc_signed_convs[] |
= {{NE, "NZ", "Z"}, |
{EQ, "Z", "NZ"}, |
{GE, "NN", "N"}, |
{GT, "P", "NP"}, |
{LE, "NP", "P"}, |
{LT, "N", "NN"}, |
CCEND}; |
static const struct cc_conv cc_di_convs[] |
= {{NE, "NZ", "Z"}, |
{EQ, "Z", "NZ"}, |
{GE, "NN", "N"}, |
{GT, "P", "NP"}, |
{LE, "NP", "P"}, |
{LT, "N", "NN"}, |
{GTU, "NZ", "Z"}, |
{LEU, "Z", "NZ"}, |
CCEND}; |
#undef CCEND |
|
static const struct cc_type_conv cc_convs[] |
= {{CC_FUNmode, cc_fun_convs}, |
{CC_FPmode, cc_fp_convs}, |
{CC_FPEQmode, cc_fpeq_convs}, |
{CC_UNSmode, cc_uns_convs}, |
{CCmode, cc_signed_convs}, |
{DImode, cc_di_convs}}; |
|
size_t i; |
int j; |
|
enum machine_mode mode = GET_MODE (XEXP (x, 0)); |
RTX_CODE cc = GET_CODE (x); |
|
for (i = 0; i < ARRAY_SIZE (cc_convs); i++) |
{ |
if (mode == cc_convs[i].cc_mode) |
{ |
for (j = 0; cc_convs[i].convs[j].cc != UNKNOWN; j++) |
if (cc == cc_convs[i].convs[j].cc) |
{ |
const char *mmix_cc |
= (reversed ? cc_convs[i].convs[j].reversed |
: cc_convs[i].convs[j].normal); |
|
if (mmix_cc == NULL) |
fatal_insn ("MMIX Internal: Trying to output invalidly\ |
reversed condition:", x); |
|
fprintf (stream, "%s", mmix_cc); |
return; |
} |
|
fatal_insn ("MMIX Internal: What's the CC of this?", x); |
} |
} |
|
fatal_insn ("MMIX Internal: What is the CC of this?", x); |
} |
|
/* Return the bit-value for a const_int or const_double. */ |
|
static HOST_WIDEST_INT |
mmix_intval (rtx x) |
{ |
unsigned HOST_WIDEST_INT retval; |
|
if (GET_CODE (x) == CONST_INT) |
return INTVAL (x); |
|
/* We make a little song and dance because converting to long long in |
gcc-2.7.2 is broken. I still want people to be able to use it for |
cross-compilation to MMIX. */ |
if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == VOIDmode) |
{ |
if (sizeof (HOST_WIDE_INT) < sizeof (HOST_WIDEST_INT)) |
{ |
retval = (unsigned) CONST_DOUBLE_LOW (x) / 2; |
retval *= 2; |
retval |= CONST_DOUBLE_LOW (x) & 1; |
|
retval |= |
(unsigned HOST_WIDEST_INT) CONST_DOUBLE_HIGH (x) |
<< (HOST_BITS_PER_LONG); |
} |
else |
retval = CONST_DOUBLE_HIGH (x); |
|
return retval; |
} |
|
if (GET_CODE (x) == CONST_DOUBLE) |
{ |
REAL_VALUE_TYPE value; |
|
/* FIXME: This macro is not in the manual but should be. */ |
REAL_VALUE_FROM_CONST_DOUBLE (value, x); |
|
if (GET_MODE (x) == DFmode) |
{ |
long bits[2]; |
|
REAL_VALUE_TO_TARGET_DOUBLE (value, bits); |
|
/* The double cast is necessary to avoid getting the long |
sign-extended to unsigned long long(!) when they're of |
different size (usually 32-bit hosts). */ |
return |
((unsigned HOST_WIDEST_INT) (unsigned long) bits[0] |
<< (unsigned HOST_WIDEST_INT) 32U) |
| (unsigned HOST_WIDEST_INT) (unsigned long) bits[1]; |
} |
else if (GET_MODE (x) == SFmode) |
{ |
long bits; |
REAL_VALUE_TO_TARGET_SINGLE (value, bits); |
|
return (unsigned long) bits; |
} |
} |
|
fatal_insn ("MMIX Internal: This is not a constant:", x); |
} |
|
/* Worker function for TARGET_STRUCT_VALUE_RTX. */ |
|
static rtx |
mmix_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, |
int incoming ATTRIBUTE_UNUSED) |
{ |
return gen_rtx_REG (Pmode, MMIX_STRUCT_VALUE_REGNUM); |
} |
|
/* |
* Local variables: |
* eval: (c-set-style "gnu") |
* indent-tabs-mode: t |
* End: |
*/ |
/mmix.opt
0,0 → 1,99
; Options for the MMIX port of the compiler. |
|
; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
; |
; This file is part of GCC. |
; |
; GCC is free software; you can redistribute it and/or modify it under |
; the terms of the GNU General Public License as published by the Free |
; Software Foundation; either version 3, or (at your option) any later |
; version. |
; |
; GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
; WARRANTY; without even the implied warranty of MERCHANTABILITY or |
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
; for more details. |
; |
; You should have received a copy of the GNU General Public License |
; along with GCC; see the file COPYING3. If not see |
; <http://www.gnu.org/licenses/>. |
|
; FIXME: Get rid of this one. |
mlibfuncs |
Target Report Mask(LIBFUNC) |
For intrinsics library: pass all parameters in registers |
|
mabi=mmixware |
Target Report RejectNegative InverseMask(ABI_GNU) |
Use register stack for parameters and return value |
|
mabi=gnu |
Target Report RejectNegative Mask(ABI_GNU) |
Use call-clobbered registers for parameters and return value |
|
; FIXME: Provide a way to *load* the epsilon register. |
mepsilon |
Target Report Mask(FCMP_EPSILON) |
Use epsilon-respecting floating point compare instructions |
|
mzero-extend |
Target Report Mask(ZERO_EXTEND) |
Use zero-extending memory loads, not sign-extending ones |
|
mknuthdiv |
Target Report Mask(KNUTH_DIVISION) |
Generate divide results with reminder having the same sign as the divisor (not the dividend) |
|
mtoplevel-symbols |
Target Report Mask(TOPLEVEL_SYMBOLS) |
Prepend global symbols with \":\" (for use with PREFIX) |
|
mno-set-program-start |
Target Report RejectNegative |
Do not provide a default start-address 0x100 of the program |
|
melf |
Target Report RejectNegative |
Link to emit program in ELF format (rather than mmo) |
|
mbranch-predict |
Target Report RejectNegative Mask(BRANCH_PREDICT) |
Use P-mnemonics for branches statically predicted as taken |
|
mno-branch-predict |
Target Report RejectNegative InverseMask(BRANCH_PREDICT) |
Don't use P-mnemonics for branches |
|
; We use the term "base address" since that's what Knuth uses. The base |
; address goes in a global register. When addressing, it's more like |
; "base address plus offset", with the offset being 0..255 from the base, |
; which itself can be a symbol plus an offset. The effect is like having |
; a constant pool in global registers, code offsetting from those |
; registers (automatically causing a request for a suitable constant base |
; address register) without having to know the specific register or the |
; specific offset. The setback is that there's a limited number of |
; registers, and you'll not find out until link time whether you |
; should have compiled with -mno-base-addresses. |
mbase-addresses |
Target Report RejectNegative Mask(BASE_ADDRESSES) |
Use addresses that allocate global registers |
|
mno-base-addresses |
Target Report RejectNegative InverseMask(BASE_ADDRESSES) |
Do not use addresses that allocate global registers |
|
msingle-exit |
Target Report RejectNegative InverseMask(USE_RETURN_INSN) |
Generate a single exit point for each function |
|
mno-single-exit |
Target Report RejectNegative Mask(USE_RETURN_INSN) |
Do not generate a single exit point for each function |
|
mset-program-start= |
Target Report RejectNegative Joined |
Set start-address of the program |
|
mset-data-start= |
Target Report RejectNegative Joined |
Set start-address of data |
/t-mmix
0,0 → 1,30
# See "Target Fragment" in GCC info. That same order is used here. |
|
TARGET_LIBGCC2_CFLAGS = -mlibfuncs -O2 |
|
EXTRA_MULTILIB_PARTS = crti.o crtn.o crtbegin.o crtend.o |
|
# We need to turn off some assumptions on normality for code in crtstuff.c |
# and crt{i,n}.asm, specifically about execution not continuing past the |
# end of the section in the file being compiled. Thus we must stop the |
# assembler from generating stubbable PUSHJ relocs, because that will add |
# stubs at the end of the current section when necessary. |
CRTSTUFF_T_CFLAGS = -Wa,--no-stubs |
|
MULTILIB_OPTIONS = mabi=gnu |
MULTILIB_DIRNAMES = gnuabi |
|
# Don't use global registers in libraries. |
# FIXME: Not applied at "root" level, so disabled at the moment to stop |
# incorrect comparisons with -mabi=gnu. |
#MULTILIB_EXTRA_OPTS = mno-base-addresses |
|
$(T)crti.o: $(srcdir)/config/mmix/crti.asm $(GCC_PASSES) |
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ |
$(CRTSTUFF_T_CFLAGS) -c -o $(T)crti.o -x assembler-with-cpp \ |
$(srcdir)/config/mmix/crti.asm |
|
$(T)crtn.o: $(srcdir)/config/mmix/crtn.asm $(GCC_PASSES) |
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ |
$(CRTSTUFF_T_CFLAGS) -c -o $(T)crtn.o -x assembler-with-cpp \ |
$(srcdir)/config/mmix/crtn.asm |
/mmix-modes.def
0,0 → 1,49
/* Definitions of target machine for GNU compiler, for MMIX. |
Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc. |
Contributed by Hans-Peter Nilsson (hp@bitrange.com) |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 3, or (at your option) |
any later version. |
|
GCC is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING3. If not see |
<http://www.gnu.org/licenses/>. */ |
|
/* Node: Condition Code */ |
|
/* Like other non-CC0 ports, MMIX need to code which combination of |
comparison insn and branch insn or conditional-set insn to use into the |
condition mode. The CC mode depends partly on which condition is used |
and partly on the type of the operands. */ |
|
/* The "usual" CC mode is used for a signed operands integer comparison, |
where the CMP insn is used and the result is (integer) -1, 0 or 1 for |
respectively a < b, a == b and a > b. */ |
|
/* The CC_UNS mode is for an unsigned operands integer comparison using |
the CMPU insn. Result values correspond to those in CCmode. */ |
CC_MODE (CC_UNS); |
|
/* The CC_FP mode is for a non-equality floating-point comparison, using |
the FCMP or FCMPE insn. The result is (integer) -1 or 1 for |
respectively a < b and a > b, otherwise 0. */ |
CC_MODE (CC_FP); |
|
/* The CC_FPEQ mode is for an equality floating-point comparison, using |
the FEQL or FEQLE insn. The result is (integer) 1 for a == b, |
otherwise 0 (including NaN:s). */ |
CC_MODE (CC_FPEQ); |
|
/* The CC_FUN mode is for an ordering comparison, using the FUN or FUNE |
insn. The result is (integer) 1 if a is unordered to b, otherwise the |
result is 0. */ |
CC_MODE (CC_FUN); |