diff options
Diffstat (limited to 'gcc/config/arm/thumb.h')
-rw-r--r-- | gcc/config/arm/thumb.h | 1102 |
1 files changed, 1102 insertions, 0 deletions
diff --git a/gcc/config/arm/thumb.h b/gcc/config/arm/thumb.h new file mode 100644 index 0000000..6121866 --- /dev/null +++ b/gcc/config/arm/thumb.h @@ -0,0 +1,1102 @@ +/* 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 */ + +/* 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)) + +#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}, \ + {"", 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) \ + fprintf (file, ".thumb_func\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(); |