aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mips/abi64.h
blob: 1cf561a6803f38e69407c9c7405e92bccc19ff1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* Definitions of target machine for GNU compiler.  64 bit ABI support.
   Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/* Macros to implement the 64 bit ABI.  This file is meant to be included
   after mips.h.  */

#undef SUBTARGET_TARGET_OPTIONS
#define SUBTARGET_TARGET_OPTIONS\
  { "abi=", &mips_abi_string	},

#undef STACK_BOUNDARY
#define STACK_BOUNDARY \
  ((mips_abi == ABI_32 || mips_abi == ABI_EABI) ? 64 : 128)

#undef MIPS_STACK_ALIGN
#define MIPS_STACK_ALIGN(LOC)					\
  ((mips_abi == ABI_32 || mips_abi == ABI_EABI)			\
   ? ((LOC) + 7) & ~7						\
   : ((LOC) + 15) & ~15)

#undef GP_ARG_LAST
#define GP_ARG_LAST  (mips_abi == ABI_32 ? GP_REG_FIRST + 7 : GP_REG_FIRST + 11)
#undef FP_ARG_LAST
#define FP_ARG_LAST  (mips_abi == ABI_32 ? FP_REG_FIRST + 15 : FP_REG_FIRST + 19)

#undef SUBTARGET_CONDITIONAL_REGISTER_USAGE
#define SUBTARGET_CONDITIONAL_REGISTER_USAGE \
{									\
  /* fp20-23 are now caller saved.  */					\
  if (mips_abi == ABI_64)						\
    {									\
      int regno;							\
      for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++) \
	call_used_regs[regno] = 1;					\
    }									\
  /* odd registers from fp21 to fp31 are now caller saved.  */		\
  if (mips_abi == ABI_N32)						\
    {									\
      int regno;							\
      for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2) \
	call_used_regs[regno] = 1;					\
    }									\
}

#undef MAX_ARGS_IN_REGISTERS
#define MAX_ARGS_IN_REGISTERS	(mips_abi == ABI_32 ? 4 : 8)

#undef REG_PARM_STACK_SPACE
#if 0
/* ??? This is necessary in order for the ABI_32 support to work.  However,
   expr.c (emit_push_insn) has no support for a REG_PARM_STACK_SPACE
   definition that returns zero.  That would have to be fixed before this
   can be enabled.  */
#define REG_PARM_STACK_SPACE(FNDECL) 					 \
  (mips_abi == ABI_32							 \
   ? (MAX_ARGS_IN_REGISTERS*UNITS_PER_WORD) - FIRST_PARM_OFFSET (FNDECL) \
   : 0)
#endif

#define FUNCTION_ARG_PADDING(MODE, TYPE)				\
  (! BYTES_BIG_ENDIAN							\
   ? upward								\
   : (((MODE) == BLKmode						\
       ? ((TYPE) && TREE_CODE (TYPE_SIZE (TYPE)) == INTEGER_CST		\
	  && int_size_in_bytes (TYPE) < (PARM_BOUNDARY / BITS_PER_UNIT))\
       : (GET_MODE_BITSIZE (MODE) < PARM_BOUNDARY			\
	  && (mips_abi == ABI_32 || mips_abi == ABI_EABI		\
	      || GET_MODE_CLASS (MODE) == MODE_INT)))			\
      ? downward : upward))

#undef RETURN_IN_MEMORY
#define RETURN_IN_MEMORY(TYPE)						\
  (mips_abi == ABI_32							\
   ? TYPE_MODE (TYPE) == BLKmode					\
   : (int_size_in_bytes (TYPE)						\
      > (mips_abi == ABI_EABI ? 2 * UNITS_PER_WORD : 16)))

extern struct rtx_def *mips_function_value ();
#undef FUNCTION_VALUE
#define FUNCTION_VALUE(VALTYPE, FUNC)	mips_function_value (VALTYPE, FUNC)

/* For varargs, we must save the current argument, because it is the fake
   argument va_alist, and will need to be converted to the real argument.
   For stdarg, we do not need to save the current argument, because it
   is a real argument.  */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL)	\
{ int mips_off = (! current_function_varargs) && (! (CUM).last_arg_fp);	\
  int mips_fp_off = (! current_function_varargs) && ((CUM).last_arg_fp); \
  if ((mips_abi != ABI_32						\
       && (CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off)		\
      || (mips_abi == ABI_EABI						\
	  && ! TARGET_SOFT_FLOAT					\
	  && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off))	\
    {									\
      int mips_save_gp_regs =						\
        MAX_ARGS_IN_REGISTERS - (CUM).arg_words - mips_off;		\
      int mips_save_fp_regs =						\
        (mips_abi != ABI_EABI ? 0					\
	 : MAX_ARGS_IN_REGISTERS - (CUM).fp_arg_words - mips_fp_off);	\
									\
      if (mips_save_gp_regs < 0)					\
	mips_save_gp_regs = 0;						\
      if (mips_save_fp_regs < 0)					\
	mips_save_fp_regs = 0;						\
      PRETEND_SIZE = ((mips_save_gp_regs * UNITS_PER_WORD)		\
		      + (mips_save_fp_regs * UNITS_PER_FPREG));		\
									\
      if (! (NO_RTL))							\
	{								\
	  if ((CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off)	\
	    {								\
	      rtx ptr, mem;						\
	      if (mips_abi != ABI_EABI)					\
		ptr = virtual_incoming_args_rtx;			\
	      else							\
		ptr = plus_constant (virtual_incoming_args_rtx,		\
				     - (mips_save_gp_regs		\
					* UNITS_PER_WORD));		\
	      mem = gen_rtx (MEM, BLKmode, ptr);			\
	      /* va_arg is an array access in this case, which causes	\
		 it to get MEM_IN_STRUCT_P set.  We must set it here	\
		 so that the insn scheduler won't assume that these	\
		 stores can't possibly overlap with the va_arg loads.  */ \
	      if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN)		\
	        MEM_IN_STRUCT_P (mem) = 1;				\
	      move_block_from_reg					\
		((CUM).arg_words + GP_ARG_FIRST + mips_off,		\
		 mem,							\
		 mips_save_gp_regs,					\
		 mips_save_gp_regs * UNITS_PER_WORD);			\
	    }								\
	  if (mips_abi == ABI_EABI					\
	      && ! TARGET_SOFT_FLOAT					\
	      && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off) \
	    {								\
	      enum machine_mode mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode; \
	      int size = GET_MODE_SIZE (mode);				\
	      int off;							\
	      int i;							\
	      /* We can't use move_block_from_reg, because it will use	\
                 the wrong mode.  */					\
	      off = - (mips_save_gp_regs * UNITS_PER_WORD);		\
	      if (! TARGET_SINGLE_FLOAT)				\
	        off &= ~ 7;						\
	      if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)		\
		off -= (mips_save_fp_regs / 2) * size;			\
	      else							\
		off -= mips_save_fp_regs * size;			\
	      for (i = 0; i < mips_save_fp_regs; i++)			\
		{							\
		  rtx tem =						\
		    gen_rtx (MEM, mode,					\
			     plus_constant (virtual_incoming_args_rtx,	\
					    off));			\
		  emit_move_insn (tem,					\
				  gen_rtx (REG, mode,			\
					   ((CUM).fp_arg_words		\
					    + FP_ARG_FIRST		\
					    + i				\
					    + mips_fp_off)));		\
		  off += size;						\
		  if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)		\
		    ++i;						\
		}							\
	    }								\
	}								\
    }									\
}

/* ??? Should disable for mips_abi == ABI32.  */
#define STRICT_ARGUMENT_NAMING

/* A C expression that indicates when an argument must be passed by
   reference.  If nonzero for an argument, a copy of that argument is
   made in memory and a pointer to the argument is passed instead of the
   argument itself.  The pointer is passed in whatever way is appropriate
   for passing a pointer to that type.  */
#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED)		\
  (mips_abi == ABI_EABI							\
   && function_arg_pass_by_reference (&CUM, MODE, TYPE, NAMED))

/* A C expression that indicates when it is the called function's
   responsibility to make a copy of arguments passed by invisible
   reference.  Normally, the caller makes a copy and passes the
   address of the copy to the routine being called.  When
   FUNCTION_ARG_CALLEE_COPIES is defined and is nonzero, the caller
   does not make a copy.  Instead, it passes a pointer to the "live"
   value.  The called function must not modify this value.  If it can
   be determined that the value won't be modified, it need not make a
   copy; otherwise a copy must be made.

   ??? The MIPS EABI says that the caller should copy in ``K&R mode.''
   I don't know how to detect that here, since flag_traditional is not
   a back end flag.  */
#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED)		\
  (mips_abi == ABI_EABI && (NAMED)					\
   && FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED))

/* ??? Unimplemented stuff follows.  */

/* ??? Add support for 16 byte/128 bit long doubles here when
   mips_abi != ABI32.  */

/* ??? Make main return zero if user did not specify return value.  */

/* ??? Add support for .interfaces section, so as to get linker warnings
   when stdarg functions called without prototype in scope?  */

/* ??? Could optimize structure passing by putting the right register rtx
   into the field decl, so that if we use the field, we can take the value from
   a register instead of from memory.  */