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 ();
|
||
|