1131 lines
35 KiB
C
1131 lines
35 KiB
C
|
/* Definitions of target machine for GNU compiler, for ARM/Thumb.
|
|||
|
Copyright (C) 19996, 1997, 1998 Free Software Foundation, Inc.
|
|||
|
The basis of this contribution was generated by
|
|||
|
Richard Earnshaw, Advanced RISC Machines Ltd
|
|||
|
|
|||
|
This file is part of GNU CC.
|
|||
|
|
|||
|
GNU CC is free software; you can redistribute it and/or modify
|
|||
|
it under the terms of the GNU General Public License as published by
|
|||
|
the Free Software Foundation; either version 2, or (at your option)
|
|||
|
any later version.
|
|||
|
|
|||
|
GNU CC is distributed in the hope that it will be useful,
|
|||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
GNU General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with GNU CC; see the file COPYING. If not, write to
|
|||
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|||
|
Boston, MA 02111-1307, USA. */
|
|||
|
|
|||
|
/* ??? The files thumb.{c,h,md} are all seriously lacking comments. */
|
|||
|
|
|||
|
/* ??? The files thumb.{c,h,md} need to be reviewed by an experienced
|
|||
|
gcc hacker in their entirety. */
|
|||
|
|
|||
|
/* ??? The files thumb.{c,h,md} and tcoff.h are all separate from the arm
|
|||
|
files, which will lead to many maintenance problems. These files are
|
|||
|
likely missing all bug fixes made to the arm port since they diverged. */
|
|||
|
|
|||
|
/* ??? Many patterns in the md file accept operands that will require a
|
|||
|
reload. These should be eliminated if possible by tightening the
|
|||
|
predicates and/or constraints. This will give faster/smaller code. */
|
|||
|
|
|||
|
/* ??? There is no pattern for the TST instuction. Check for other unsupported
|
|||
|
instructions. */
|
|||
|
|
|||
|
/* Run Time Target Specifications */
|
|||
|
#ifndef CPP_PREDEFINES
|
|||
|
#define CPP_PREDEFINES "-Dthumb -D__thumb -Acpu(arm) -Amachine(arm)"
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef CPP_SPEC
|
|||
|
#define CPP_SPEC "\
|
|||
|
%{mbig-endian:-D__ARMEB__ -D__THUMBEB__} \
|
|||
|
%{mbe:-D__ARMEB__ -D__THUMBEB__} \
|
|||
|
%{!mbe: %{!mbig-endian:-D__ARMEL__ -D__THUMBEL__}} \
|
|||
|
"
|
|||
|
#endif
|
|||
|
|
|||
|
#define ASM_SPEC "-marm7tdmi %{mthumb-interwork:-mthumb-interwork} %{mbig-endian:-EB}"
|
|||
|
#define LINK_SPEC "%{mbig-endian:-EB} -X"
|
|||
|
|
|||
|
#define TARGET_VERSION fputs (" (ARM/THUMB:generic)", stderr);
|
|||
|
|
|||
|
/* Nonzero if we should compile with BYTES_BIG_ENDIAN set to 1. */
|
|||
|
#define THUMB_FLAG_BIG_END 0x0001
|
|||
|
#define THUMB_FLAG_BACKTRACE 0x0002
|
|||
|
#define THUMB_FLAG_LEAF_BACKTRACE 0x0004
|
|||
|
#define ARM_FLAG_THUMB 0x1000 /* same as in arm.h */
|
|||
|
#define THUMB_FLAG_CALLEE_SUPER_INTERWORKING 0x40000
|
|||
|
#define THUMB_FLAG_CALLER_SUPER_INTERWORKING 0x80000
|
|||
|
|
|||
|
|
|||
|
/* Run-time compilation parameters selecting different hardware/software subsets. */
|
|||
|
extern int target_flags;
|
|||
|
#define TARGET_DEFAULT 0 /* ARM_FLAG_THUMB */
|
|||
|
#define TARGET_BIG_END (target_flags & THUMB_FLAG_BIG_END)
|
|||
|
#define TARGET_THUMB_INTERWORK (target_flags & ARM_FLAG_THUMB)
|
|||
|
#define TARGET_BACKTRACE (leaf_function_p() \
|
|||
|
? (target_flags & THUMB_FLAG_LEAF_BACKTRACE) \
|
|||
|
: (target_flags & THUMB_FLAG_BACKTRACE))
|
|||
|
|
|||
|
/* Set if externally visable functions should assume that they
|
|||
|
might be called in ARM mode, from a non-thumb aware code. */
|
|||
|
#define TARGET_CALLEE_INTERWORKING \
|
|||
|
(target_flags & THUMB_FLAG_CALLEE_SUPER_INTERWORKING)
|
|||
|
|
|||
|
/* Set if calls via function pointers should assume that their
|
|||
|
destination is non-Thumb aware. */
|
|||
|
#define TARGET_CALLER_INTERWORKING \
|
|||
|
(target_flags & THUMB_FLAG_CALLER_SUPER_INTERWORKING)
|
|||
|
|
|||
|
/* SUBTARGET_SWITCHES is used to add flags on a per-config basis. */
|
|||
|
#ifndef SUBTARGET_SWITCHES
|
|||
|
#define SUBTARGET_SWITCHES
|
|||
|
#endif
|
|||
|
|
|||
|
#define TARGET_SWITCHES \
|
|||
|
{ \
|
|||
|
{"big-endian", THUMB_FLAG_BIG_END}, \
|
|||
|
{"little-endian", -THUMB_FLAG_BIG_END}, \
|
|||
|
{"thumb-interwork", ARM_FLAG_THUMB}, \
|
|||
|
{"no-thumb-interwork", -ARM_FLAG_THUMB}, \
|
|||
|
{"tpcs-frame", THUMB_FLAG_BACKTRACE}, \
|
|||
|
{"no-tpcs-frame", -THUMB_FLAG_BACKTRACE}, \
|
|||
|
{"tpcs-leaf-frame", THUMB_FLAG_LEAF_BACKTRACE}, \
|
|||
|
{"no-tpcs-leaf-frame", -THUMB_FLAG_LEAF_BACKTRACE}, \
|
|||
|
{"callee-super-interworking", THUMB_FLAG_CALLEE_SUPER_INTERWORKING}, \
|
|||
|
{"no-callee-super-interworking", -THUMB_FLAG_CALLEE_SUPER_INTERWORKING}, \
|
|||
|
{"caller-super-interworking", THUMB_FLAG_CALLER_SUPER_INTERWORKING}, \
|
|||
|
{"no-caller-super-interworking", -THUMB_FLAG_CALLER_SUPER_INTERWORKING}, \
|
|||
|
SUBTARGET_SWITCHES \
|
|||
|
{"", TARGET_DEFAULT} \
|
|||
|
}
|
|||
|
|
|||
|
#define TARGET_OPTIONS \
|
|||
|
{ \
|
|||
|
{ "structure-size-boundary=", & structure_size_string }, \
|
|||
|
}
|
|||
|
|
|||
|
#define REGISTER_PREFIX ""
|
|||
|
|
|||
|
#define CAN_DEBUG_WITHOUT_FP 1
|
|||
|
|
|||
|
#define ASM_APP_ON ""
|
|||
|
#define ASM_APP_OFF "\t.code\t16\n"
|
|||
|
|
|||
|
/* Output a gap. In fact we fill it with nulls. */
|
|||
|
#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \
|
|||
|
fprintf ((STREAM), "\t.space\t%u\n", (NBYTES))
|
|||
|
|
|||
|
/* This is how to output an assembler line
|
|||
|
that says to advance the location counter
|
|||
|
to a multiple of 2**LOG bytes. */
|
|||
|
#define ASM_OUTPUT_ALIGN(STREAM,LOG) \
|
|||
|
{ \
|
|||
|
fprintf (STREAM, "\t.align\t%d\n", (LOG)); \
|
|||
|
}
|
|||
|
|
|||
|
/* Output a common block */
|
|||
|
#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \
|
|||
|
(fprintf ((STREAM), "\t.comm\t"), \
|
|||
|
assemble_name ((STREAM), (NAME)), \
|
|||
|
fprintf((STREAM), ", %d\t%s %d\n", (ROUNDED), (ASM_COMMENT_START), (SIZE)))
|
|||
|
|
|||
|
#define ASM_GENERATE_INTERNAL_LABEL(STRING,PREFIX,NUM) \
|
|||
|
sprintf ((STRING), "*%s%s%d", (LOCAL_LABEL_PREFIX), (PREFIX), (NUM))
|
|||
|
|
|||
|
/* This is how to output an internal numbered label where
|
|||
|
PREFIX is the class of label and NUM is the number within the class. */
|
|||
|
#define ASM_OUTPUT_INTERNAL_LABEL(STREAM,PREFIX,NUM) \
|
|||
|
fprintf ((STREAM), "%s%s%d:\n", (LOCAL_LABEL_PREFIX), (PREFIX), (NUM))
|
|||
|
|
|||
|
/* This is how to output a label which precedes a jumptable. Since
|
|||
|
instructions are 2 bytes, we need explicit alignment here. */
|
|||
|
|
|||
|
#define ASM_OUTPUT_CASE_LABEL(FILE,PREFIX,NUM,JUMPTABLE) \
|
|||
|
do { \
|
|||
|
ASM_OUTPUT_ALIGN (FILE, 2); \
|
|||
|
ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \
|
|||
|
} while (0)
|
|||
|
|
|||
|
/* This says how to define a local common symbol (ie, not visible to
|
|||
|
linker). */
|
|||
|
#define ASM_OUTPUT_LOCAL(STREAM, NAME, SIZE, ROUNDED) \
|
|||
|
(fprintf((STREAM),"\n\t.lcomm\t"), \
|
|||
|
assemble_name((STREAM),(NAME)), \
|
|||
|
fprintf((STREAM),",%u\n",(SIZE)))
|
|||
|
|
|||
|
/* Output a reference to a label. */
|
|||
|
#define ASM_OUTPUT_LABELREF(STREAM,NAME) \
|
|||
|
fprintf ((STREAM), "%s%s", USER_LABEL_PREFIX, (NAME))
|
|||
|
|
|||
|
/* This is how to output an assembler line for a numeric constant byte. */
|
|||
|
#define ASM_OUTPUT_BYTE(STREAM,VALUE) \
|
|||
|
fprintf ((STREAM), "\t.byte\t0x%x\n", (VALUE))
|
|||
|
|
|||
|
#define ASM_OUTPUT_INT(STREAM,VALUE) \
|
|||
|
{ \
|
|||
|
fprintf (STREAM, "\t.word\t"); \
|
|||
|
output_addr_const (STREAM, (VALUE)); \
|
|||
|
fprintf (STREAM, "\n"); \
|
|||
|
}
|
|||
|
|
|||
|
#define ASM_OUTPUT_SHORT(STREAM,VALUE) \
|
|||
|
{ \
|
|||
|
fprintf (STREAM, "\t.short\t"); \
|
|||
|
output_addr_const (STREAM, (VALUE)); \
|
|||
|
fprintf (STREAM, "\n"); \
|
|||
|
}
|
|||
|
|
|||
|
#define ASM_OUTPUT_CHAR(STREAM,VALUE) \
|
|||
|
{ \
|
|||
|
fprintf (STREAM, "\t.byte\t"); \
|
|||
|
output_addr_const (STREAM, (VALUE)); \
|
|||
|
fprintf (STREAM, "\n"); \
|
|||
|
}
|
|||
|
|
|||
|
#define ASM_OUTPUT_LONG_DOUBLE(STREAM,VALUE) \
|
|||
|
do { char dstr[30]; \
|
|||
|
long l[3]; \
|
|||
|
REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
|
|||
|
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
|
|||
|
fprintf (STREAM, "\t.long 0x%lx,0x%lx,0x%lx\t%s long double %s\n", \
|
|||
|
l[0], l[1], l[2], ASM_COMMENT_START, dstr); \
|
|||
|
} while (0)
|
|||
|
|
|||
|
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \
|
|||
|
do { char dstr[30]; \
|
|||
|
long l[2]; \
|
|||
|
REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \
|
|||
|
REAL_VALUE_TO_DECIMAL (VALUE, "%.14g", dstr); \
|
|||
|
fprintf (STREAM, "\t.long 0x%lx, 0x%lx\t%s double %s\n", l[0], \
|
|||
|
l[1], ASM_COMMENT_START, dstr); \
|
|||
|
} while (0)
|
|||
|
|
|||
|
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \
|
|||
|
do { char dstr[30]; \
|
|||
|
long l; \
|
|||
|
REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \
|
|||
|
REAL_VALUE_TO_DECIMAL (VALUE, "%.7g", dstr); \
|
|||
|
fprintf (STREAM, "\t.word 0x%lx\t%s float %s\n", l, \
|
|||
|
ASM_COMMENT_START, dstr); \
|
|||
|
} while (0);
|
|||
|
|
|||
|
/* Define results of standard character escape sequences. */
|
|||
|
#define TARGET_BELL 007
|
|||
|
#define TARGET_BS 010
|
|||
|
#define TARGET_TAB 011
|
|||
|
#define TARGET_NEWLINE 012
|
|||
|
#define TARGET_VT 013
|
|||
|
#define TARGET_FF 014
|
|||
|
#define TARGET_CR 015
|
|||
|
|
|||
|
/* This is how to output a string. */
|
|||
|
#define ASM_OUTPUT_ASCII(STREAM, STRING, LEN) \
|
|||
|
do { \
|
|||
|
register int i, c, len = (LEN), cur_pos = 17; \
|
|||
|
register unsigned char *string = (unsigned char *)(STRING); \
|
|||
|
fprintf ((STREAM), "\t.ascii\t\""); \
|
|||
|
for (i = 0; i < len; i++) \
|
|||
|
{ \
|
|||
|
register int c = string[i]; \
|
|||
|
\
|
|||
|
switch (c) \
|
|||
|
{ \
|
|||
|
case '\"': \
|
|||
|
case '\\': \
|
|||
|
putc ('\\', (STREAM)); \
|
|||
|
putc (c, (STREAM)); \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
case TARGET_NEWLINE: \
|
|||
|
fputs ("\\n", (STREAM)); \
|
|||
|
if (i+1 < len \
|
|||
|
&& (((c = string[i+1]) >= '\040' && c <= '~') \
|
|||
|
|| c == TARGET_TAB)) \
|
|||
|
cur_pos = 32767; /* break right here */ \
|
|||
|
else \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
case TARGET_TAB: \
|
|||
|
fputs ("\\t", (STREAM)); \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
case TARGET_FF: \
|
|||
|
fputs ("\\f", (STREAM)); \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
case TARGET_BS: \
|
|||
|
fputs ("\\b", (STREAM)); \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
case TARGET_CR: \
|
|||
|
fputs ("\\r", (STREAM)); \
|
|||
|
cur_pos += 2; \
|
|||
|
break; \
|
|||
|
\
|
|||
|
default: \
|
|||
|
if (c >= ' ' && c < 0177) \
|
|||
|
{ \
|
|||
|
putc (c, (STREAM)); \
|
|||
|
cur_pos++; \
|
|||
|
} \
|
|||
|
else \
|
|||
|
{ \
|
|||
|
fprintf ((STREAM), "\\%03o", c); \
|
|||
|
cur_pos += 4; \
|
|||
|
} \
|
|||
|
} \
|
|||
|
\
|
|||
|
if (cur_pos > 72 && i+1 < len) \
|
|||
|
{ \
|
|||
|
cur_pos = 17; \
|
|||
|
fprintf ((STREAM), "\"\n\t.ascii\t\""); \
|
|||
|
} \
|
|||
|
} \
|
|||
|
fprintf ((STREAM), "\"\n"); \
|
|||
|
} while (0)
|
|||
|
|
|||
|
/* Output and Generation of Labels */
|
|||
|
#define ASM_OUTPUT_LABEL(STREAM,NAME) \
|
|||
|
(assemble_name ((STREAM), (NAME)), \
|
|||
|
fprintf ((STREAM), ":\n"))
|
|||
|
|
|||
|
#define ASM_GLOBALIZE_LABEL(STREAM,NAME) \
|
|||
|
(fprintf ((STREAM), "\t.globl\t"), \
|
|||
|
assemble_name ((STREAM), (NAME)), \
|
|||
|
fputc ('\n', (STREAM)))
|
|||
|
|
|||
|
/* Construct a private name. */
|
|||
|
#define ASM_FORMAT_PRIVATE_NAME(OUTVAR,NAME,NUMBER) \
|
|||
|
((OUTVAR) = (char *) alloca (strlen (NAME) + 10), \
|
|||
|
sprintf ((OUTVAR), "%s.%d", (NAME), (NUMBER)))
|
|||
|
|
|||
|
/* Switch to the text or data segment. */
|
|||
|
#define TEXT_SECTION_ASM_OP ".text"
|
|||
|
#define DATA_SECTION_ASM_OP ".data"
|
|||
|
#define BSS_SECTION_ASM_OP ".bss"
|
|||
|
|
|||
|
/* The assembler's names for the registers. */
|
|||
|
#ifndef REGISTER_NAMES
|
|||
|
#define REGISTER_NAMES \
|
|||
|
{ \
|
|||
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
|
|||
|
"r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc", "ap" \
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef ADDITIONAL_REGISTER_NAMES
|
|||
|
#define ADDITIONAL_REGISTER_NAMES \
|
|||
|
{ \
|
|||
|
{"a1", 0}, \
|
|||
|
{"a2", 1}, \
|
|||
|
{"a3", 2}, \
|
|||
|
{"a4", 3}, \
|
|||
|
{"v1", 4}, \
|
|||
|
{"v2", 5}, \
|
|||
|
{"v3", 6}, \
|
|||
|
{"v4", 7}, \
|
|||
|
{"v5", 8}, \
|
|||
|
{"v6", 9}, \
|
|||
|
{"sb", 9}, \
|
|||
|
{"v7", 10}, \
|
|||
|
{"r10", 10}, /* sl */ \
|
|||
|
{"r11", 11}, /* fp */ \
|
|||
|
{"r12", 12}, /* ip */ \
|
|||
|
{"r13", 13}, /* sp */ \
|
|||
|
{"r14", 14}, /* lr */ \
|
|||
|
{"r15", 15} /* pc */ \
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/* The assembler's parentheses characters. */
|
|||
|
#define ASM_OPEN_PAREN "("
|
|||
|
#define ASM_CLOSE_PAREN ")"
|
|||
|
|
|||
|
#ifndef ASM_COMMENT_START
|
|||
|
#define ASM_COMMENT_START "@"
|
|||
|
#endif
|
|||
|
|
|||
|
/* Output an element of a dispatch table. */
|
|||
|
#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \
|
|||
|
fprintf (STREAM, "\t.word\t%sL%d\n", (LOCAL_LABEL_PREFIX), (VALUE))
|
|||
|
|
|||
|
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,BODY,VALUE,REL) \
|
|||
|
fprintf (STREAM, "\tb\t%sL%d\n", (LOCAL_LABEL_PREFIX), (VALUE))
|
|||
|
|
|||
|
/* Storage Layout */
|
|||
|
|
|||
|
/* Define this is most significant bit is lowest numbered in
|
|||
|
instructions that operate on numbered bit-fields. */
|
|||
|
#define BITS_BIG_ENDIAN 0
|
|||
|
|
|||
|
/* Define this if most significant byte of a word is the lowest
|
|||
|
numbered. */
|
|||
|
#define BYTES_BIG_ENDIAN (TARGET_BIG_END != 0)
|
|||
|
|
|||
|
#define WORDS_BIG_ENDIAN (BYTES_BIG_ENDIAN)
|
|||
|
|
|||
|
/* LIBGCC2_WORDS_BIG_ENDIAN has to be a constant, so we define this based
|
|||
|
on processor pre-defineds when compiling libgcc2.c. */
|
|||
|
#if defined(__THUMBEB__) && !defined(__THUMBEL__)
|
|||
|
#define LIBGCC2_WORDS_BIG_ENDIAN 1
|
|||
|
#else
|
|||
|
#define LIBGCC2_WORDS_BIG_ENDIAN 0
|
|||
|
#endif
|
|||
|
|
|||
|
#define FLOAT_WORDS_BIG_ENDIAN 1
|
|||
|
|
|||
|
#define BITS_PER_UNIT 8
|
|||
|
#define BITS_PER_WORD 32
|
|||
|
|
|||
|
#define UNITS_PER_WORD 4
|
|||
|
|
|||
|
#define POINTER_SIZE 32
|
|||
|
|
|||
|
#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \
|
|||
|
{ \
|
|||
|
if (GET_MODE_CLASS (MODE) == MODE_INT \
|
|||
|
&& GET_MODE_SIZE (MODE) < 4) \
|
|||
|
{ \
|
|||
|
(UNSIGNEDP) = 1; \
|
|||
|
(MODE) = SImode; \
|
|||
|
} \
|
|||
|
}
|
|||
|
|
|||
|
#define PARM_BOUNDARY 32
|
|||
|
#define STACK_BOUNDARY 32
|
|||
|
|
|||
|
#define FUNCTION_BOUNDARY 32
|
|||
|
#define BIGGEST_ALIGNMENT 32
|
|||
|
|
|||
|
/* Make strings word-aligned so strcpy from constants will be faster. */
|
|||
|
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
|
|||
|
(TREE_CODE (EXP) == STRING_CST \
|
|||
|
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
|
|||
|
|
|||
|
#define EMPTY_FIELD_BOUNDARY 32
|
|||
|
|
|||
|
#define STRUCTURE_SIZE_BOUNDARY 32
|
|||
|
|
|||
|
/* Used when parsing command line option -mstructure_size_boundary. */
|
|||
|
extern char * structure_size_string;
|
|||
|
|
|||
|
#define STRICT_ALIGNMENT 1
|
|||
|
|
|||
|
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
|
|||
|
|
|||
|
|
|||
|
/* Layout of Source Language Data Types */
|
|||
|
|
|||
|
#define DEFAULT_SIGNED_CHAR 0
|
|||
|
|
|||
|
#define TARGET_BELL 007
|
|||
|
#define TARGET_BS 010
|
|||
|
#define TARGET_TAB 011
|
|||
|
#define TARGET_NEWLINE 012
|
|||
|
#define TARGET_VT 013
|
|||
|
#define TARGET_FF 014
|
|||
|
#define TARGET_CR 015
|
|||
|
|
|||
|
|
|||
|
/* Register Usage */
|
|||
|
|
|||
|
/* Note there are 16 hard registers on the Thumb. We invent a 17th register
|
|||
|
which is assigned to ARG_POINTER_REGNUM, but this is later removed by
|
|||
|
elimination passes in the compiler. */
|
|||
|
#define FIRST_PSEUDO_REGISTER 17
|
|||
|
|
|||
|
/* ??? This is questionable. */
|
|||
|
#define FIXED_REGISTERS \
|
|||
|
{ \
|
|||
|
0,0,0,0, \
|
|||
|
0,0,0,0, \
|
|||
|
0,0,0,1, \
|
|||
|
0,1,1,1,1 \
|
|||
|
}
|
|||
|
|
|||
|
/* ??? This is questionable. */
|
|||
|
#define CALL_USED_REGISTERS \
|
|||
|
{ \
|
|||
|
1,1,1,1, \
|
|||
|
0,0,0,0, \
|
|||
|
0,0,0,1, \
|
|||
|
1,1,1,1,1 \
|
|||
|
}
|
|||
|
|
|||
|
#define HARD_REGNO_NREGS(REGNO,MODE) \
|
|||
|
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
|
|||
|
/ UNITS_PER_WORD)
|
|||
|
|
|||
|
/* ??? Probably should only allow DImode/DFmode in even numbered registers. */
|
|||
|
#define HARD_REGNO_MODE_OK(REGNO,MODE) ((GET_MODE_SIZE (MODE) > UNITS_PER_WORD) ? (REGNO < 7) : 1)
|
|||
|
|
|||
|
#define MODES_TIEABLE_P(MODE1,MODE2) 1
|
|||
|
|
|||
|
enum reg_class
|
|||
|
{
|
|||
|
NO_REGS,
|
|||
|
LO_REGS,
|
|||
|
STACK_REG,
|
|||
|
BASE_REGS,
|
|||
|
HI_REGS,
|
|||
|
ALL_REGS,
|
|||
|
LIM_REG_CLASSES
|
|||
|
};
|
|||
|
|
|||
|
#define GENERAL_REGS ALL_REGS
|
|||
|
|
|||
|
#define N_REG_CLASSES (int) LIM_REG_CLASSES
|
|||
|
|
|||
|
#define REG_CLASS_NAMES \
|
|||
|
{ \
|
|||
|
"NO_REGS", \
|
|||
|
"LO_REGS", \
|
|||
|
"STACK_REG", \
|
|||
|
"BASE_REGS", \
|
|||
|
"HI_REGS", \
|
|||
|
"ALL_REGS" \
|
|||
|
}
|
|||
|
|
|||
|
#define REG_CLASS_CONTENTS \
|
|||
|
{ \
|
|||
|
0x00000, \
|
|||
|
0x000ff, \
|
|||
|
0x02000, \
|
|||
|
0x020ff, \
|
|||
|
0x0ff00, \
|
|||
|
0x1ffff, \
|
|||
|
}
|
|||
|
|
|||
|
#define REGNO_REG_CLASS(REGNO) \
|
|||
|
((REGNO) == STACK_POINTER_REGNUM ? STACK_REG \
|
|||
|
: (REGNO) < 8 ? LO_REGS \
|
|||
|
: HI_REGS)
|
|||
|
|
|||
|
#define BASE_REG_CLASS BASE_REGS
|
|||
|
|
|||
|
#define INDEX_REG_CLASS LO_REGS
|
|||
|
|
|||
|
/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows
|
|||
|
registers explicitly used in the rtl to be used as spill registers
|
|||
|
but prevents the compiler from extending the lifetime of these
|
|||
|
registers. */
|
|||
|
|
|||
|
#define SMALL_REGISTER_CLASSES 1
|
|||
|
|
|||
|
#define REG_CLASS_FROM_LETTER(C) \
|
|||
|
((C) == 'l' ? LO_REGS \
|
|||
|
: (C) == 'h' ? HI_REGS \
|
|||
|
: (C) == 'b' ? BASE_REGS \
|
|||
|
: (C) == 'k' ? STACK_REG \
|
|||
|
: NO_REGS)
|
|||
|
|
|||
|
#define REGNO_OK_FOR_BASE_P(REGNO) \
|
|||
|
((REGNO) < 8 \
|
|||
|
|| (REGNO) == STACK_POINTER_REGNUM \
|
|||
|
|| (unsigned) reg_renumber[REGNO] < 8 \
|
|||
|
|| (unsigned) reg_renumber[REGNO] == STACK_POINTER_REGNUM)
|
|||
|
|
|||
|
#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \
|
|||
|
((REGNO) < 8 \
|
|||
|
|| (unsigned) reg_renumber[REGNO] < 8 \
|
|||
|
|| (GET_MODE_SIZE (MODE) >= 4 \
|
|||
|
&& ((REGNO) == STACK_POINTER_REGNUM \
|
|||
|
|| (unsigned) reg_renumber[REGNO] == STACK_POINTER_REGNUM)))
|
|||
|
|
|||
|
#define REGNO_OK_FOR_INDEX_P(REGNO) \
|
|||
|
((REGNO) < 8 \
|
|||
|
|| (unsigned) reg_renumber[REGNO] < 8)
|
|||
|
|
|||
|
/* ??? This looks suspiciously wrong. */
|
|||
|
/* We need to leave BASE_REGS reloads alone, in order to avoid caller_save
|
|||
|
lossage. Caller_saves requests a BASE_REGS reload (caller_save_spill_class)
|
|||
|
and then later we verify that one was allocated. If PREFERRED_RELOAD_CLASS
|
|||
|
says to allocate a LO_REGS spill instead, then this mismatch gives an
|
|||
|
abort. Alternatively, this could be fixed by modifying BASE_REG_CLASS
|
|||
|
to be LO_REGS instead of BASE_REGS. It is not clear what affect this
|
|||
|
change would have. */
|
|||
|
#define PREFERRED_RELOAD_CLASS(X,CLASS) \
|
|||
|
((CLASS) == BASE_REGS ? (CLASS) \
|
|||
|
: LO_REGS)
|
|||
|
/*
|
|||
|
((CONSTANT_P ((X)) && GET_CODE ((X)) != CONST_INT \
|
|||
|
&& ! CONSTANT_POOL_ADDRESS_P((X))) ? NO_REGS \
|
|||
|
: (GET_CODE ((X)) == CONST_INT \
|
|||
|
&& (unsigned HOST_WIDE_INT) INTVAL ((X)) > 255) ? NO_REGS \
|
|||
|
: LO_REGS) */
|
|||
|
|
|||
|
/* Must leave BASE_REGS reloads alone, see comment above. */
|
|||
|
#define SECONDARY_RELOAD_CLASS(CLASS,MODE,X) \
|
|||
|
((CLASS) != LO_REGS && (CLASS) != BASE_REGS \
|
|||
|
? ((true_regnum (X) == -1 ? LO_REGS \
|
|||
|
: (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \
|
|||
|
: NO_REGS)) \
|
|||
|
: NO_REGS)
|
|||
|
|
|||
|
#define CLASS_MAX_NREGS(CLASS,MODE) HARD_REGNO_NREGS(0,(MODE))
|
|||
|
|
|||
|
int thumb_shiftable_const ();
|
|||
|
|
|||
|
#define CONST_OK_FOR_LETTER_P(VAL,C) \
|
|||
|
((C) == 'I' ? (unsigned HOST_WIDE_INT) (VAL) < 256 \
|
|||
|
: (C) == 'J' ? (VAL) > -256 && (VAL) <= 0 \
|
|||
|
: (C) == 'K' ? thumb_shiftable_const (VAL) \
|
|||
|
: (C) == 'L' ? (VAL) > -8 && (VAL) < 8 \
|
|||
|
: (C) == 'M' ? ((unsigned HOST_WIDE_INT) (VAL) < 1024 \
|
|||
|
&& ((VAL) & 3) == 0) \
|
|||
|
: (C) == 'N' ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \
|
|||
|
: (C) == 'O' ? ((VAL) >= -508 && (VAL) <= 508) \
|
|||
|
: 0)
|
|||
|
|
|||
|
#define CONST_DOUBLE_OK_FOR_LETTER_P(VAL,C) 0
|
|||
|
|
|||
|
#define EXTRA_CONSTRAINT(X,C) \
|
|||
|
((C) == 'Q' ? (GET_CODE (X) == MEM \
|
|||
|
&& GET_CODE (XEXP (X, 0)) == LABEL_REF) : 0)
|
|||
|
|
|||
|
/* Stack Layout and Calling Conventions */
|
|||
|
|
|||
|
#define STACK_GROWS_DOWNWARD 1
|
|||
|
|
|||
|
/* #define FRAME_GROWS_DOWNWARD 1 */
|
|||
|
|
|||
|
/* #define ARGS_GROW_DOWNWARD 1 */
|
|||
|
|
|||
|
#define STARTING_FRAME_OFFSET 0
|
|||
|
|
|||
|
#define FIRST_PARM_OFFSET(FNDECL) 0
|
|||
|
|
|||
|
/* Registers that address the stack frame */
|
|||
|
|
|||
|
#define STACK_POINTER_REGNUM 13 /* Defined by the TPCS. */
|
|||
|
|
|||
|
#define FRAME_POINTER_REGNUM 7 /* TPCS defines this as 11 but it does not really mean it. */
|
|||
|
|
|||
|
#define ARG_POINTER_REGNUM 16 /* A fake hard register that is eliminated later on. */
|
|||
|
|
|||
|
#define STATIC_CHAIN_REGNUM 9
|
|||
|
|
|||
|
#define FRAME_POINTER_REQUIRED 0
|
|||
|
|
|||
|
#define ELIMINABLE_REGS \
|
|||
|
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
|
|||
|
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
|
|||
|
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
|
|||
|
|
|||
|
/* On the Thumb we always want to perform the eliminations as we
|
|||
|
actually only have one real register pointing to the stashed
|
|||
|
variables: the stack pointer, and we never use the frame pointer. */
|
|||
|
#define CAN_ELIMINATE(FROM,TO) 1
|
|||
|
|
|||
|
/* Note: This macro must match the code in thumb_function_prologue() in thumb.c. */
|
|||
|
#define INITIAL_ELIMINATION_OFFSET(FROM,TO,OFFSET) \
|
|||
|
{ \
|
|||
|
(OFFSET) = 0; \
|
|||
|
if ((FROM) == ARG_POINTER_REGNUM) \
|
|||
|
{ \
|
|||
|
int count_regs = 0; \
|
|||
|
int regno; \
|
|||
|
(OFFSET) += get_frame_size (); \
|
|||
|
for (regno = 8; regno < 13; regno++) \
|
|||
|
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
|
|||
|
count_regs++; \
|
|||
|
if (count_regs) \
|
|||
|
(OFFSET) += 4 * count_regs; \
|
|||
|
count_regs = 0; \
|
|||
|
for (regno = 0; regno < 8; regno++) \
|
|||
|
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
|
|||
|
count_regs++; \
|
|||
|
if (count_regs || ! leaf_function_p () || far_jump_used_p()) \
|
|||
|
(OFFSET) += 4 * (count_regs + 1); \
|
|||
|
if (TARGET_BACKTRACE) { \
|
|||
|
if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) \
|
|||
|
(OFFSET) += 20; \
|
|||
|
else \
|
|||
|
(OFFSET) += 16; } \
|
|||
|
} \
|
|||
|
if ((TO) == STACK_POINTER_REGNUM) \
|
|||
|
(OFFSET) += current_function_outgoing_args_size; \
|
|||
|
}
|
|||
|
|
|||
|
/* Passing Arguments on the stack */
|
|||
|
|
|||
|
#define PROMOTE_PROTOTYPES 1
|
|||
|
|
|||
|
#define ACCUMULATE_OUTGOING_ARGS 1
|
|||
|
|
|||
|
#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
|
|||
|
|
|||
|
#define FUNCTION_ARG(CUM,MODE,TYPE,NAMED) \
|
|||
|
((NAMED) ? ((CUM) >= 16 ? 0 : gen_rtx (REG, (MODE), (CUM) / 4)) \
|
|||
|
: 0)
|
|||
|
|
|||
|
#define FUNCTION_ARG_PARTIAL_NREGS(CUM,MODE,TYPE,NAMED) \
|
|||
|
(((CUM) < 16 && (CUM) + (((MODE) == BLKmode) \
|
|||
|
? int_size_in_bytes (TYPE) \
|
|||
|
: HARD_REGNO_NREGS (0, (MODE)) * 4) > 16) \
|
|||
|
? 4 - (CUM) / 4 : 0)
|
|||
|
|
|||
|
#define CUMULATIVE_ARGS int
|
|||
|
|
|||
|
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \
|
|||
|
((CUM) = ((FNTYPE) && aggregate_value_p (TREE_TYPE (FNTYPE))) ? 4 : 0)
|
|||
|
|
|||
|
#define FUNCTION_ARG_ADVANCE(CUM,MODE,TYPE,NAMED) \
|
|||
|
(CUM) += ((((MODE) == BLKmode) \
|
|||
|
? int_size_in_bytes (TYPE) \
|
|||
|
: GET_MODE_SIZE (MODE)) + 3) & ~3
|
|||
|
|
|||
|
#define FUNCTION_ARG_REGNO_P(REGNO) \
|
|||
|
((REGNO) >=0 && (REGNO) <= 3)
|
|||
|
|
|||
|
#define FUNCTION_VALUE(VALTYPE,FUNC) gen_rtx (REG, TYPE_MODE (VALTYPE), 0)
|
|||
|
|
|||
|
#define LIBCALL_VALUE(MODE) gen_rtx (REG, (MODE), 0)
|
|||
|
|
|||
|
#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == 0)
|
|||
|
|
|||
|
/* How large values are returned */
|
|||
|
/* A C expression which can inhibit the returning of certain function values
|
|||
|
in registers, based on the type of value. */
|
|||
|
#define RETURN_IN_MEMORY(TYPE) thumb_return_in_memory (TYPE)
|
|||
|
|
|||
|
/* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return
|
|||
|
values must be in memory. On the ARM, they need only do so if larger
|
|||
|
than a word, or if they contain elements offset from zero in the struct. */
|
|||
|
#define DEFAULT_PCC_STRUCT_RETURN 0
|
|||
|
|
|||
|
|
|||
|
#define STRUCT_VALUE_REGNUM 0
|
|||
|
|
|||
|
#define FUNCTION_PROLOGUE(FILE,SIZE) thumb_function_prologue((FILE),(SIZE))
|
|||
|
|
|||
|
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
|
|||
|
|
|||
|
/* Generating code for profiling */
|
|||
|
#define FUNCTION_PROFILER(STREAM,LABELNO) \
|
|||
|
{ \
|
|||
|
fprintf ((STREAM), "\tmov\\tip, lr\n"); \
|
|||
|
fprintf ((STREAM), "\tbl\tmcount\n"); \
|
|||
|
fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
|
|||
|
}
|
|||
|
|
|||
|
/* Implementing the Varargs Macros */
|
|||
|
|
|||
|
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
|
|||
|
{ \
|
|||
|
extern int current_function_anonymous_args; \
|
|||
|
current_function_anonymous_args = 1; \
|
|||
|
if ((CUM) < 16) \
|
|||
|
(PRETEND_SIZE) = 16 - (CUM); \
|
|||
|
}
|
|||
|
|
|||
|
/* Trampolines for nested functions */
|
|||
|
|
|||
|
/* Output assembler code for a block containing the constant parts of
|
|||
|
a trampoline, leaving space for the variable parts.
|
|||
|
|
|||
|
On the Thumb we always switch into ARM mode to execute the trampoline.
|
|||
|
Why - because it is easier. This code will always be branched to via
|
|||
|
a BX instruction and since the compiler magically generates the address
|
|||
|
of the function the linker has no opportunity to ensure that the
|
|||
|
bottom bit is set. Thus the processor will be in ARM mode when it
|
|||
|
reaches this code. So we duplicate the ARM trampoline code and add
|
|||
|
a switch into Thumb mode as well.
|
|||
|
|
|||
|
On the ARM, (if r8 is the static chain regnum, and remembering that
|
|||
|
referencing pc adds an offset of 8) the trampoline looks like:
|
|||
|
ldr r8, [pc, #0]
|
|||
|
ldr pc, [pc]
|
|||
|
.word static chain value
|
|||
|
.word function's address
|
|||
|
??? FIXME: When the trampoline returns, r8 will be clobbered. */
|
|||
|
#define TRAMPOLINE_TEMPLATE(FILE) \
|
|||
|
{ \
|
|||
|
fprintf ((FILE), "\t.code 32\n"); \
|
|||
|
fprintf ((FILE), ".Ltrampoline_start:\n"); \
|
|||
|
fprintf ((FILE), "\tldr\t%s, [%spc, #8]\n", \
|
|||
|
reg_names[STATIC_CHAIN_REGNUM], REGISTER_PREFIX); \
|
|||
|
fprintf ((FILE), "\tldr\t%sip, [%spc, #8]\n", \
|
|||
|
REGISTER_PREFIX, REGISTER_PREFIX); \
|
|||
|
fprintf ((FILE), "\torr\t%sip, %sip, #1\n", \
|
|||
|
REGISTER_PREFIX, REGISTER_PREFIX); \
|
|||
|
fprintf ((FILE), "\tbx\t%sip\n", REGISTER_PREFIX); \
|
|||
|
fprintf ((FILE), "\t.word\t0\n"); \
|
|||
|
fprintf ((FILE), "\t.word\t0\n"); \
|
|||
|
fprintf ((FILE), "\t.code 16\n"); \
|
|||
|
}
|
|||
|
|
|||
|
/* Length in units of the trampoline for entering a nested function. */
|
|||
|
#define TRAMPOLINE_SIZE 24
|
|||
|
|
|||
|
/* Alignment required for a trampoline in units. */
|
|||
|
#define TRAMPOLINE_ALIGN 4
|
|||
|
|
|||
|
#define INITIALIZE_TRAMPOLINE(ADDR,FNADDR,CHAIN) \
|
|||
|
{ \
|
|||
|
emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((ADDR), 16)), \
|
|||
|
(CHAIN)); \
|
|||
|
emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((ADDR), 20)), \
|
|||
|
(FNADDR)); \
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Implicit Calls to Library Routines */
|
|||
|
|
|||
|
#define TARGET_MEM_FUNCTIONS 1
|
|||
|
|
|||
|
#define OVERRIDE_OPTIONS thumb_override_options ()
|
|||
|
|
|||
|
|
|||
|
/* Addressing Modes */
|
|||
|
|
|||
|
#define HAVE_POST_INCREMENT 1
|
|||
|
|
|||
|
#define CONSTANT_ADDRESS_P(X) \
|
|||
|
(GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X))
|
|||
|
|
|||
|
#define MAX_REGS_PER_ADDRESS 2
|
|||
|
|
|||
|
#ifdef REG_OK_STRICT
|
|||
|
|
|||
|
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
|
|||
|
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
|
|||
|
|
|||
|
#define REG_MODE_OK_FOR_BASE_P(X,MODE) \
|
|||
|
REGNO_MODE_OK_FOR_BASE_P (REGNO (X), MODE)
|
|||
|
|
|||
|
#else /* REG_OK_STRICT */
|
|||
|
|
|||
|
#define REG_OK_FOR_BASE_P(X) \
|
|||
|
(REGNO (X) < 8 || REGNO (X) == STACK_POINTER_REGNUM \
|
|||
|
|| (X) == arg_pointer_rtx \
|
|||
|
|| REGNO (X) >= FIRST_PSEUDO_REGISTER)
|
|||
|
|
|||
|
#define REG_MODE_OK_FOR_BASE_P(X,MODE) \
|
|||
|
(REGNO (X) < 8 \
|
|||
|
|| REGNO (X) >= FIRST_PSEUDO_REGISTER \
|
|||
|
|| (GET_MODE_SIZE (MODE) >= 4 \
|
|||
|
&& (REGNO (X) == STACK_POINTER_REGNUM \
|
|||
|
|| (X) == arg_pointer_rtx)))
|
|||
|
|
|||
|
#define REG_OK_FOR_INDEX_P(X) \
|
|||
|
(REGNO (X) < 8 \
|
|||
|
|| REGNO (X) >= FIRST_PSEUDO_REGISTER)
|
|||
|
|
|||
|
#endif /* REG_OK_STRICT */
|
|||
|
|
|||
|
/* In a REG+REG address, both must be INDEX registers. */
|
|||
|
#define REG_OK_FOR_INDEXED_BASE_P(X) REG_OK_FOR_INDEX_P(X)
|
|||
|
|
|||
|
#define LEGITIMATE_OFFSET(MODE,VAL) \
|
|||
|
(GET_MODE_SIZE (MODE) == 1 ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \
|
|||
|
: GET_MODE_SIZE (MODE) == 2 ? ((unsigned HOST_WIDE_INT) (VAL) < 64 \
|
|||
|
&& ((VAL) & 1) == 0) \
|
|||
|
: ((VAL) >= 0 && ((VAL) + GET_MODE_SIZE (MODE)) <= 128 \
|
|||
|
&& ((VAL) & 3) == 0))
|
|||
|
|
|||
|
/* The AP may be eliminated to either the SP or the FP, so we use the
|
|||
|
least common denominator, e.g. SImode, and offsets from 0 to 64. */
|
|||
|
|
|||
|
/* ??? Verify whether the above is the right approach. */
|
|||
|
|
|||
|
/* ??? Also, the FP may be eliminated to the SP, so perhaps that
|
|||
|
needs special handling also. */
|
|||
|
|
|||
|
/* ??? Look at how the mips16 port solves this problem. It probably uses
|
|||
|
better ways to solve some of these problems. */
|
|||
|
|
|||
|
/* Although it is not incorrect, we don't accept QImode and HImode
|
|||
|
addresses based on the frame pointer or arg pointer until the reload pass starts.
|
|||
|
This is so that eliminating such addresses into stack based ones
|
|||
|
won't produce impossible code. */
|
|||
|
#define GO_IF_LEGITIMATE_ADDRESS(MODE,X,WIN) \
|
|||
|
{ \
|
|||
|
/* ??? Not clear if this is right. Experiment. */ \
|
|||
|
if (GET_MODE_SIZE (MODE) < 4 \
|
|||
|
&& ! (reload_in_progress || reload_completed) \
|
|||
|
&& (reg_mentioned_p (frame_pointer_rtx, X) \
|
|||
|
|| reg_mentioned_p (arg_pointer_rtx, X) \
|
|||
|
|| reg_mentioned_p (virtual_incoming_args_rtx, X) \
|
|||
|
|| reg_mentioned_p (virtual_outgoing_args_rtx, X) \
|
|||
|
|| reg_mentioned_p (virtual_stack_dynamic_rtx, X) \
|
|||
|
|| reg_mentioned_p (virtual_stack_vars_rtx, X))) \
|
|||
|
; \
|
|||
|
/* Accept any base register. SP only in SImode or larger. */ \
|
|||
|
else if (GET_CODE (X) == REG && REG_MODE_OK_FOR_BASE_P(X, MODE)) \
|
|||
|
goto WIN; \
|
|||
|
/* This is PC relative data before MACHINE_DEPENDENT_REORG runs. */ \
|
|||
|
else if (GET_MODE_SIZE (MODE) >= 4 && CONSTANT_P (X) \
|
|||
|
&& CONSTANT_POOL_ADDRESS_P (X)) \
|
|||
|
goto WIN; \
|
|||
|
/* This is PC relative data after MACHINE_DEPENDENT_REORG runs. */ \
|
|||
|
else if (GET_MODE_SIZE (MODE) >= 4 && reload_completed \
|
|||
|
&& (GET_CODE (X) == LABEL_REF \
|
|||
|
|| (GET_CODE (X) == CONST \
|
|||
|
&& GET_CODE (XEXP (X, 0)) == PLUS \
|
|||
|
&& GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF \
|
|||
|
&& GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT))) \
|
|||
|
goto WIN; \
|
|||
|
/* Post-inc indexing only supported for SImode and larger. */ \
|
|||
|
else if (GET_CODE (X) == POST_INC && GET_MODE_SIZE (MODE) >= 4 \
|
|||
|
&& GET_CODE (XEXP (X, 0)) == REG \
|
|||
|
&& REG_OK_FOR_INDEX_P (XEXP (X, 0))) \
|
|||
|
goto WIN; \
|
|||
|
else if (GET_CODE (X) == PLUS) \
|
|||
|
{ \
|
|||
|
/* REG+REG address can be any two index registers. */ \
|
|||
|
/* ??? Normally checking the mode here is wrong, since it isn't \
|
|||
|
impossible to use REG+REG with DFmode. However, the movdf \
|
|||
|
pattern requires offsettable addresses, and REG+REG is not \
|
|||
|
offsettable, so it must be rejected somehow. Trying to use \
|
|||
|
'o' fails, because offsettable_address_p does a QImode check. \
|
|||
|
QImode is not valid for stack addresses, and has a smaller \
|
|||
|
range for non-stack bases, and this causes valid addresses \
|
|||
|
to be rejected. So we just eliminate REG+REG here by checking \
|
|||
|
the mode. */ \
|
|||
|
/* We also disallow FRAME+REG addressing since we know that FRAME \
|
|||
|
will be replaced with STACK, and SP relative addressing only \
|
|||
|
permits SP+OFFSET. */ \
|
|||
|
if (GET_MODE_SIZE (MODE) <= 4 \
|
|||
|
&& GET_CODE (XEXP (X, 0)) == REG \
|
|||
|
&& GET_CODE (XEXP (X, 1)) == REG \
|
|||
|
&& REGNO (XEXP (X, 0)) != FRAME_POINTER_REGNUM \
|
|||
|
&& REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
|
|||
|
&& REG_OK_FOR_INDEX_P (XEXP (X, 1))) \
|
|||
|
goto WIN; \
|
|||
|
/* REG+const has 5-7 bit offset for non-SP registers. */ \
|
|||
|
else if (GET_CODE (XEXP (X, 0)) == REG \
|
|||
|
&& (REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
|
|||
|
|| XEXP (X, 0) == arg_pointer_rtx) \
|
|||
|
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
|
|||
|
&& LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \
|
|||
|
goto WIN; \
|
|||
|
/* REG+const has 10 bit offset for SP, but only SImode and \
|
|||
|
larger is supported. */ \
|
|||
|
/* ??? Should probably check for DI/DFmode overflow here \
|
|||
|
just like GO_IF_LEGITIMATE_OFFSET does. */ \
|
|||
|
else if (GET_CODE (XEXP (X, 0)) == REG \
|
|||
|
&& REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \
|
|||
|
&& GET_MODE_SIZE (MODE) >= 4 \
|
|||
|
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
|
|||
|
&& (unsigned HOST_WIDE_INT) INTVAL (XEXP (X, 1)) < 1024 \
|
|||
|
&& (INTVAL (XEXP (X, 1)) & 3) == 0) \
|
|||
|
goto WIN; \
|
|||
|
} \
|
|||
|
}
|
|||
|
|
|||
|
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)
|
|||
|
|
|||
|
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN)
|
|||
|
|
|||
|
#define LEGITIMATE_CONSTANT_P(X) \
|
|||
|
(GET_CODE (X) == CONST_INT \
|
|||
|
|| GET_CODE (X) == CONST_DOUBLE \
|
|||
|
|| CONSTANT_ADDRESS_P (X))
|
|||
|
|
|||
|
|
|||
|
/* Condition Code Status */
|
|||
|
|
|||
|
#define NOTICE_UPDATE_CC(EXP,INSN) \
|
|||
|
{ \
|
|||
|
if (get_attr_conds ((INSN)) != CONDS_UNCHANGED) \
|
|||
|
CC_STATUS_INIT; \
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Describing Relative Costs of Operations */
|
|||
|
|
|||
|
#define SLOW_BYTE_ACCESS 0
|
|||
|
|
|||
|
#define SLOW_UNALIGNED_ACCESS 1
|
|||
|
|
|||
|
#define NO_FUNCTION_CSE 1
|
|||
|
|
|||
|
#define NO_RECURSIVE_FUNCTION_CSE 1
|
|||
|
|
|||
|
#define REGISTER_MOVE_COST(FROM,TO) \
|
|||
|
(((FROM) == HI_REGS || (TO) == HI_REGS) ? 4 : 2)
|
|||
|
|
|||
|
#define MEMORY_MOVE_COST(M,CLASS,IN) \
|
|||
|
((GET_MODE_SIZE(M) < 4 ? 8 : 2 * GET_MODE_SIZE(M)) * (CLASS == LO_REGS ? 1 : 2))
|
|||
|
|
|||
|
/* This will allow better space optimization when compiling with -O */
|
|||
|
#define BRANCH_COST (optimize > 1 ? 1 : 0)
|
|||
|
|
|||
|
#define RTX_COSTS(X,CODE,OUTER) \
|
|||
|
case MULT: \
|
|||
|
if (GET_CODE (XEXP (X, 1)) == CONST_INT) \
|
|||
|
{ \
|
|||
|
int cycles = 0; \
|
|||
|
unsigned HOST_WIDE_INT i = INTVAL (XEXP (X, 1)); \
|
|||
|
while (i) \
|
|||
|
{ \
|
|||
|
i >>= 2; \
|
|||
|
cycles++; \
|
|||
|
} \
|
|||
|
return COSTS_N_INSNS (2) + cycles; \
|
|||
|
} \
|
|||
|
return COSTS_N_INSNS (1) + 16; \
|
|||
|
case ASHIFT: case ASHIFTRT: case LSHIFTRT: case ROTATERT: \
|
|||
|
case PLUS: case MINUS: case COMPARE: case NEG: case NOT: \
|
|||
|
return COSTS_N_INSNS (1); \
|
|||
|
case SET: \
|
|||
|
return (COSTS_N_INSNS (1) \
|
|||
|
+ 4 * ((GET_CODE (SET_SRC (X)) == MEM) \
|
|||
|
+ GET_CODE (SET_DEST (X)) == MEM))
|
|||
|
|
|||
|
#define CONST_COSTS(X,CODE,OUTER) \
|
|||
|
case CONST_INT: \
|
|||
|
if ((OUTER) == SET) \
|
|||
|
{ \
|
|||
|
if ((unsigned HOST_WIDE_INT) INTVAL (X) < 256) \
|
|||
|
return 0; \
|
|||
|
if (thumb_shiftable_const (INTVAL (X))) \
|
|||
|
return COSTS_N_INSNS (2); \
|
|||
|
return COSTS_N_INSNS (3); \
|
|||
|
} \
|
|||
|
else if (OUTER == PLUS \
|
|||
|
&& INTVAL (X) < 256 && INTVAL (X) > -256) \
|
|||
|
return 0; \
|
|||
|
else if (OUTER == COMPARE \
|
|||
|
&& (unsigned HOST_WIDE_INT) INTVAL (X) < 256) \
|
|||
|
return 0; \
|
|||
|
else if (OUTER == ASHIFT || OUTER == ASHIFTRT \
|
|||
|
|| OUTER == LSHIFTRT) \
|
|||
|
return 0; \
|
|||
|
return COSTS_N_INSNS (2); \
|
|||
|
case CONST: \
|
|||
|
case CONST_DOUBLE: \
|
|||
|
case LABEL_REF: \
|
|||
|
case SYMBOL_REF: \
|
|||
|
return COSTS_N_INSNS(3);
|
|||
|
|
|||
|
#define ADDRESS_COST(X) \
|
|||
|
((GET_CODE (X) == REG \
|
|||
|
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
|
|||
|
&& GET_CODE (XEXP (X, 1)) == CONST_INT)) \
|
|||
|
? 1 : 2)
|
|||
|
|
|||
|
|
|||
|
/* Position Independent Code */
|
|||
|
|
|||
|
#define PRINT_OPERAND(STREAM,X,CODE) \
|
|||
|
thumb_print_operand((STREAM), (X), (CODE))
|
|||
|
|
|||
|
#define PRINT_OPERAND_ADDRESS(STREAM,X) \
|
|||
|
{ \
|
|||
|
if (GET_CODE ((X)) == REG) \
|
|||
|
fprintf ((STREAM), "[%s]", reg_names[REGNO ((X))]); \
|
|||
|
else if (GET_CODE ((X)) == POST_INC) \
|
|||
|
fprintf ((STREAM), "%s!", reg_names[REGNO (XEXP (X, 0))]); \
|
|||
|
else if (GET_CODE ((X)) == PLUS) \
|
|||
|
{ \
|
|||
|
if (GET_CODE (XEXP ((X), 1)) == CONST_INT) \
|
|||
|
fprintf ((STREAM), "[%s, #%d]", \
|
|||
|
reg_names[REGNO (XEXP ((X), 0))], \
|
|||
|
(int) INTVAL (XEXP ((X), 1))); \
|
|||
|
else \
|
|||
|
fprintf ((STREAM), "[%s, %s]", \
|
|||
|
reg_names[REGNO (XEXP ((X), 0))], \
|
|||
|
reg_names[REGNO (XEXP ((X), 1))]); \
|
|||
|
} \
|
|||
|
else \
|
|||
|
output_addr_const ((STREAM), (X)); \
|
|||
|
}
|
|||
|
|
|||
|
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '@')
|
|||
|
|
|||
|
/* Emit a special directive when defining a function name.
|
|||
|
This is used by the assembler to assit with interworking. */
|
|||
|
#define ASM_DECLARE_FUNCTION_NAME(file, name, decl) \
|
|||
|
if (! is_called_in_ARM_mode (decl)) \
|
|||
|
fprintf (file, "\t.thumb_func\n") ; \
|
|||
|
else \
|
|||
|
fprintf (file, "\t.code\t32\n") ; \
|
|||
|
ASM_OUTPUT_LABEL (file, name)
|
|||
|
|
|||
|
#define ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \
|
|||
|
asm_fprintf ((STREAM), "\tpush {%R%s}\n", reg_names[(REGNO)])
|
|||
|
|
|||
|
#define ASM_OUTPUT_REG_POP(STREAM,REGNO) \
|
|||
|
fprintf ((STREAM), "\tpop {%R%s}\n", reg_names[(REGNO)])
|
|||
|
|
|||
|
#define FINAL_PRESCAN_INSN(INSN,OPVEC,NOPERANDS) \
|
|||
|
final_prescan_insn((INSN))
|
|||
|
|
|||
|
/* Controlling Debugging Information Format */
|
|||
|
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
|
|||
|
|
|||
|
/* Specific options for DBX Output */
|
|||
|
|
|||
|
#define DBX_DEBUGGING_INFO 1
|
|||
|
|
|||
|
#define DEFAULT_GDB_EXTENSIONS 1
|
|||
|
|
|||
|
|
|||
|
/* Cross Compilation and Floating Point */
|
|||
|
|
|||
|
#define REAL_ARITHMETIC
|
|||
|
|
|||
|
|
|||
|
/* Miscellaneous Parameters */
|
|||
|
|
|||
|
#define PREDICATE_CODES \
|
|||
|
{"thumb_cmp_operand", {SUBREG, REG, CONST_INT}},
|
|||
|
|
|||
|
#define CASE_VECTOR_MODE Pmode
|
|||
|
|
|||
|
#define WORD_REGISTER_OPERATIONS
|
|||
|
|
|||
|
#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND
|
|||
|
|
|||
|
#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
|
|||
|
|
|||
|
#define EASY_DIV_EXPR TRUNC_DIV_EXPR
|
|||
|
|
|||
|
#define MOVE_MAX 4
|
|||
|
|
|||
|
#define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) 1
|
|||
|
|
|||
|
#define STORE_FLAG_VALUE 1
|
|||
|
|
|||
|
#define Pmode SImode
|
|||
|
|
|||
|
#define FUNCTION_MODE SImode
|
|||
|
|
|||
|
#define DOLLARS_IN_IDENTIFIERS 0
|
|||
|
|
|||
|
#define NO_DOLLAR_IN_LABEL 1
|
|||
|
|
|||
|
#define HAVE_ATEXIT
|
|||
|
|
|||
|
/* The literal pool needs to reside in the text area due to the
|
|||
|
limited PC addressing range: */
|
|||
|
#define MACHINE_DEPENDENT_REORG(INSN) thumb_reorg ((INSN))
|
|||
|
|
|||
|
|
|||
|
/* Options specific to Thumb */
|
|||
|
|
|||
|
/* True if a return instruction can be used in this function. */
|
|||
|
int thumb_trivial_epilogue ();
|
|||
|
#define USE_RETURN (reload_completed && thumb_trivial_epilogue ())
|
|||
|
|
|||
|
extern char * thumb_unexpanded_epilogue ();
|
|||
|
extern char * output_move_mem_multiple ();
|
|||
|
extern char * thumb_load_double_from_address ();
|
|||
|
extern char * output_return ();
|
|||
|
extern int far_jump_used_p();
|
|||
|
extern int is_called_in_ARM_mode ();
|
|||
|
|