]>
Commit | Line | Data |
---|---|---|
2c9c2489 | 1 | /* Subroutines for gcc2 for pdp11. |
a5544970 | 2 | Copyright (C) 1994-2019 Free Software Foundation, Inc. |
2c9c2489 RK |
3 | Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at). |
4 | ||
7ec022b2 | 5 | This file is part of GCC. |
2c9c2489 | 6 | |
7ec022b2 | 7 | GCC is free software; you can redistribute it and/or modify |
2c9c2489 | 8 | it under the terms of the GNU General Public License as published by |
2f83c7d6 | 9 | the Free Software Foundation; either version 3, or (at your option) |
2c9c2489 RK |
10 | any later version. |
11 | ||
7ec022b2 | 12 | GCC is distributed in the hope that it will be useful, |
2c9c2489 RK |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
2f83c7d6 NC |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ | |
2c9c2489 | 20 | |
8fcc61f8 RS |
21 | #define IN_TARGET_CODE 1 |
22 | ||
2c9c2489 | 23 | #include "config.h" |
c5c76735 | 24 | #include "system.h" |
4977bab6 | 25 | #include "coretypes.h" |
c7131fb2 | 26 | #include "backend.h" |
e11c4407 | 27 | #include "target.h" |
2c9c2489 | 28 | #include "rtl.h" |
e11c4407 | 29 | #include "tree.h" |
314e6352 ML |
30 | #include "stringpool.h" |
31 | #include "attribs.h" | |
c7131fb2 | 32 | #include "df.h" |
4d0cdd0c | 33 | #include "memmodel.h" |
e11c4407 | 34 | #include "tm_p.h" |
2c9c2489 | 35 | #include "insn-config.h" |
aad2444d | 36 | #include "insn-attr.h" |
e11c4407 AM |
37 | #include "regs.h" |
38 | #include "emit-rtl.h" | |
39 | #include "recog.h" | |
2c9c2489 | 40 | #include "conditions.h" |
2c9c2489 | 41 | #include "output.h" |
d8a2d370 DN |
42 | #include "stor-layout.h" |
43 | #include "varasm.h" | |
44 | #include "calls.h" | |
19652adf | 45 | #include "expr.h" |
9b2b7279 | 46 | #include "builtins.h" |
3623815a | 47 | #include "dbxout.h" |
a3368b8e | 48 | #include "explow.h" |
b4324a14 | 49 | #include "expmed.h" |
2c9c2489 | 50 | |
994c5d85 | 51 | /* This file should be included last. */ |
d58627a0 RS |
52 | #include "target-def.h" |
53 | ||
2c9c2489 RK |
54 | /* this is the current value returned by the macro FIRST_PARM_OFFSET |
55 | defined in tm.h */ | |
56 | int current_first_parm_offset; | |
57 | ||
e621b588 PK |
58 | /* Routines to encode/decode pdp11 floats */ |
59 | static void encode_pdp11_f (const struct real_format *fmt, | |
60 | long *, const REAL_VALUE_TYPE *); | |
61 | static void decode_pdp11_f (const struct real_format *, | |
62 | REAL_VALUE_TYPE *, const long *); | |
63 | static void encode_pdp11_d (const struct real_format *fmt, | |
64 | long *, const REAL_VALUE_TYPE *); | |
65 | static void decode_pdp11_d (const struct real_format *, | |
66 | REAL_VALUE_TYPE *, const long *); | |
67 | ||
68 | /* These two are taken from the corresponding vax descriptors | |
69 | in real.c, changing only the encode/decode routine pointers. */ | |
70 | const struct real_format pdp11_f_format = | |
71 | { | |
72 | encode_pdp11_f, | |
73 | decode_pdp11_f, | |
74 | 2, | |
e621b588 PK |
75 | 24, |
76 | 24, | |
77 | -127, | |
78 | 127, | |
79 | 15, | |
6e2f8c15 | 80 | 15, |
c65699ef | 81 | 0, |
e621b588 PK |
82 | false, |
83 | false, | |
84 | false, | |
85 | false, | |
58145e4d | 86 | false, |
3e479de3 | 87 | false, |
4099e2c2 | 88 | false, |
db847fa8 JJ |
89 | false, |
90 | "pdp11_f" | |
e621b588 PK |
91 | }; |
92 | ||
93 | const struct real_format pdp11_d_format = | |
94 | { | |
95 | encode_pdp11_d, | |
96 | decode_pdp11_d, | |
97 | 2, | |
e621b588 PK |
98 | 56, |
99 | 56, | |
100 | -127, | |
101 | 127, | |
102 | 15, | |
6e2f8c15 | 103 | 15, |
c65699ef | 104 | 0, |
e621b588 PK |
105 | false, |
106 | false, | |
107 | false, | |
108 | false, | |
58145e4d | 109 | false, |
3e479de3 | 110 | false, |
4099e2c2 | 111 | false, |
db847fa8 JJ |
112 | false, |
113 | "pdp11_d" | |
e621b588 PK |
114 | }; |
115 | ||
116 | static void | |
117 | encode_pdp11_f (const struct real_format *fmt ATTRIBUTE_UNUSED, long *buf, | |
118 | const REAL_VALUE_TYPE *r) | |
119 | { | |
120 | (*vax_f_format.encode) (fmt, buf, r); | |
121 | buf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16); | |
122 | } | |
123 | ||
124 | static void | |
125 | decode_pdp11_f (const struct real_format *fmt ATTRIBUTE_UNUSED, | |
126 | REAL_VALUE_TYPE *r, const long *buf) | |
127 | { | |
128 | long tbuf; | |
129 | tbuf = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16); | |
130 | (*vax_f_format.decode) (fmt, r, &tbuf); | |
131 | } | |
132 | ||
133 | static void | |
134 | encode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, long *buf, | |
135 | const REAL_VALUE_TYPE *r) | |
136 | { | |
137 | (*vax_d_format.encode) (fmt, buf, r); | |
138 | buf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16); | |
139 | buf[1] = ((buf[1] >> 16) & 0xffff) | ((buf[1] & 0xffff) << 16); | |
140 | } | |
141 | ||
142 | static void | |
143 | decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, | |
144 | REAL_VALUE_TYPE *r, const long *buf) | |
145 | { | |
146 | long tbuf[2]; | |
147 | tbuf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16); | |
148 | tbuf[1] = ((buf[1] >> 16) & 0xffff) | ((buf[1] & 0xffff) << 16); | |
149 | (*vax_d_format.decode) (fmt, r, tbuf); | |
150 | } | |
151 | ||
f12b3fc8 SB |
152 | static const char *singlemove_string (rtx *); |
153 | static bool pdp11_assemble_integer (rtx, unsigned int, int); | |
e548c9df | 154 | static bool pdp11_rtx_costs (rtx, machine_mode, int, int, int *, bool); |
aad2444d PK |
155 | static int pdp11_addr_cost (rtx, machine_mode, addr_space_t, bool); |
156 | static int pdp11_insn_cost (rtx_insn *insn, bool speed); | |
157 | static rtx_insn *pdp11_md_asm_adjust (vec<rtx> &, vec<rtx> &, | |
158 | vec<const char *> &, | |
159 | vec<rtx> &, HARD_REG_SET &); | |
586de218 | 160 | static bool pdp11_return_in_memory (const_tree, const_tree); |
c547eb0d | 161 | static rtx pdp11_function_value (const_tree, const_tree, bool); |
ef4bddc2 | 162 | static rtx pdp11_libcall_value (machine_mode, const_rtx); |
c547eb0d | 163 | static bool pdp11_function_value_regno_p (const unsigned int); |
8dd65c37 | 164 | static void pdp11_trampoline_init (rtx, tree, rtx); |
ef4bddc2 | 165 | static rtx pdp11_function_arg (cumulative_args_t, machine_mode, |
a5f4f531 | 166 | const_tree, bool); |
d5cc9181 | 167 | static void pdp11_function_arg_advance (cumulative_args_t, |
ef4bddc2 | 168 | machine_mode, const_tree, bool); |
5efd84c5 | 169 | static void pdp11_conditional_register_usage (void); |
ef4bddc2 | 170 | static bool pdp11_legitimate_constant_p (machine_mode, rtx); |
8cc4b7a2 | 171 | |
18e2a8b8 | 172 | static bool pdp11_scalar_mode_supported_p (scalar_mode); |
672a6f42 NB |
173 | \f |
174 | /* Initialize the GCC target structure. */ | |
301d03af RS |
175 | #undef TARGET_ASM_BYTE_OP |
176 | #define TARGET_ASM_BYTE_OP NULL | |
177 | #undef TARGET_ASM_ALIGNED_HI_OP | |
178 | #define TARGET_ASM_ALIGNED_HI_OP NULL | |
179 | #undef TARGET_ASM_ALIGNED_SI_OP | |
180 | #define TARGET_ASM_ALIGNED_SI_OP NULL | |
181 | #undef TARGET_ASM_INTEGER | |
182 | #define TARGET_ASM_INTEGER pdp11_assemble_integer | |
183 | ||
aad2444d PK |
184 | /* These two apply to Unix and GNU assembler; for DEC, they are |
185 | overridden during option processing. */ | |
17b53c33 NB |
186 | #undef TARGET_ASM_OPEN_PAREN |
187 | #define TARGET_ASM_OPEN_PAREN "[" | |
188 | #undef TARGET_ASM_CLOSE_PAREN | |
189 | #define TARGET_ASM_CLOSE_PAREN "]" | |
190 | ||
3c50106f RH |
191 | #undef TARGET_RTX_COSTS |
192 | #define TARGET_RTX_COSTS pdp11_rtx_costs | |
193 | ||
aad2444d PK |
194 | #undef TARGET_ADDRESS_COST |
195 | #define TARGET_ADDRESS_COST pdp11_addr_cost | |
196 | ||
197 | #undef TARGET_INSN_COST | |
198 | #define TARGET_INSN_COST pdp11_insn_cost | |
199 | ||
200 | #undef TARGET_MD_ASM_ADJUST | |
201 | #define TARGET_MD_ASM_ADJUST pdp11_md_asm_adjust | |
202 | ||
a5f4f531 NF |
203 | #undef TARGET_FUNCTION_ARG |
204 | #define TARGET_FUNCTION_ARG pdp11_function_arg | |
205 | #undef TARGET_FUNCTION_ARG_ADVANCE | |
206 | #define TARGET_FUNCTION_ARG_ADVANCE pdp11_function_arg_advance | |
207 | ||
04e9daaf KH |
208 | #undef TARGET_RETURN_IN_MEMORY |
209 | #define TARGET_RETURN_IN_MEMORY pdp11_return_in_memory | |
210 | ||
c547eb0d AS |
211 | #undef TARGET_FUNCTION_VALUE |
212 | #define TARGET_FUNCTION_VALUE pdp11_function_value | |
213 | #undef TARGET_LIBCALL_VALUE | |
214 | #define TARGET_LIBCALL_VALUE pdp11_libcall_value | |
215 | #undef TARGET_FUNCTION_VALUE_REGNO_P | |
216 | #define TARGET_FUNCTION_VALUE_REGNO_P pdp11_function_value_regno_p | |
217 | ||
8dd65c37 RH |
218 | #undef TARGET_TRAMPOLINE_INIT |
219 | #define TARGET_TRAMPOLINE_INIT pdp11_trampoline_init | |
220 | ||
a01c666c PK |
221 | #undef TARGET_SECONDARY_RELOAD |
222 | #define TARGET_SECONDARY_RELOAD pdp11_secondary_reload | |
223 | ||
224 | #undef TARGET_REGISTER_MOVE_COST | |
225 | #define TARGET_REGISTER_MOVE_COST pdp11_register_move_cost | |
226 | ||
227 | #undef TARGET_PREFERRED_RELOAD_CLASS | |
228 | #define TARGET_PREFERRED_RELOAD_CLASS pdp11_preferred_reload_class | |
229 | ||
230 | #undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS | |
231 | #define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS pdp11_preferred_output_reload_class | |
e4942929 | 232 | |
4aef57c9 | 233 | #undef TARGET_LRA_P |
0a7eb8df | 234 | #define TARGET_LRA_P pdp11_lra_p |
d81db636 | 235 | |
e4942929 PK |
236 | #undef TARGET_LEGITIMATE_ADDRESS_P |
237 | #define TARGET_LEGITIMATE_ADDRESS_P pdp11_legitimate_address_p | |
5efd84c5 NF |
238 | |
239 | #undef TARGET_CONDITIONAL_REGISTER_USAGE | |
240 | #define TARGET_CONDITIONAL_REGISTER_USAGE pdp11_conditional_register_usage | |
baddb677 | 241 | |
4aef57c9 PK |
242 | #undef TARGET_OPTION_OVERRIDE |
243 | #define TARGET_OPTION_OVERRIDE pdp11_option_override | |
244 | ||
245 | #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE | |
246 | #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true | |
247 | ||
248 | #undef TARGET_ASM_OUTPUT_IDENT | |
249 | #define TARGET_ASM_OUTPUT_IDENT pdp11_output_ident | |
250 | ||
baddb677 PK |
251 | #undef TARGET_ASM_FUNCTION_SECTION |
252 | #define TARGET_ASM_FUNCTION_SECTION pdp11_function_section | |
12eb6ed3 | 253 | |
4aef57c9 PK |
254 | #undef TARGET_ASM_NAMED_SECTION |
255 | #define TARGET_ASM_NAMED_SECTION pdp11_asm_named_section | |
256 | ||
257 | #undef TARGET_ASM_INIT_SECTIONS | |
258 | #define TARGET_ASM_INIT_SECTIONS pdp11_asm_init_sections | |
259 | ||
260 | #undef TARGET_ASM_FILE_START | |
261 | #define TARGET_ASM_FILE_START pdp11_file_start | |
262 | ||
263 | #undef TARGET_ASM_FILE_END | |
264 | #define TARGET_ASM_FILE_END pdp11_file_end | |
265 | ||
12eb6ed3 PK |
266 | #undef TARGET_PRINT_OPERAND |
267 | #define TARGET_PRINT_OPERAND pdp11_asm_print_operand | |
268 | ||
269 | #undef TARGET_PRINT_OPERAND_PUNCT_VALID_P | |
270 | #define TARGET_PRINT_OPERAND_PUNCT_VALID_P pdp11_asm_print_operand_punct_valid_p | |
1a627b35 RS |
271 | |
272 | #undef TARGET_LEGITIMATE_CONSTANT_P | |
273 | #define TARGET_LEGITIMATE_CONSTANT_P pdp11_legitimate_constant_p | |
8cc4b7a2 JM |
274 | |
275 | #undef TARGET_SCALAR_MODE_SUPPORTED_P | |
276 | #define TARGET_SCALAR_MODE_SUPPORTED_P pdp11_scalar_mode_supported_p | |
f939c3e6 | 277 | |
c43f4279 RS |
278 | #undef TARGET_HARD_REGNO_NREGS |
279 | #define TARGET_HARD_REGNO_NREGS pdp11_hard_regno_nregs | |
4aef57c9 | 280 | |
f939c3e6 RS |
281 | #undef TARGET_HARD_REGNO_MODE_OK |
282 | #define TARGET_HARD_REGNO_MODE_OK pdp11_hard_regno_mode_ok | |
99e1629f RS |
283 | |
284 | #undef TARGET_MODES_TIEABLE_P | |
285 | #define TARGET_MODES_TIEABLE_P pdp11_modes_tieable_p | |
f15643d4 RS |
286 | |
287 | #undef TARGET_SECONDARY_MEMORY_NEEDED | |
288 | #define TARGET_SECONDARY_MEMORY_NEEDED pdp11_secondary_memory_needed | |
0d803030 RS |
289 | |
290 | #undef TARGET_CAN_CHANGE_MODE_CLASS | |
291 | #define TARGET_CAN_CHANGE_MODE_CLASS pdp11_can_change_mode_class | |
aad2444d PK |
292 | |
293 | #undef TARGET_INVALID_WITHIN_DOLOOP | |
294 | #define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_insn_null | |
31f52518 | 295 | |
7717110a PK |
296 | #undef TARGET_CXX_GUARD_TYPE |
297 | #define TARGET_CXX_GUARD_TYPE pdp11_guard_type | |
298 | ||
299 | #undef TARGET_CXX_CLASS_DATA_ALWAYS_COMDAT | |
300 | #define TARGET_CXX_CLASS_DATA_ALWAYS_COMDAT hook_bool_void_false | |
301 | ||
302 | #undef TARGET_CXX_LIBRARY_RTTI_COMDAT | |
303 | #define TARGET_CXX_LIBRARY_RTTI_COMDAT hook_bool_void_false | |
304 | ||
31f52518 RE |
305 | #undef TARGET_HAVE_SPECULATION_SAFE_VALUE |
306 | #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed | |
442fcea7 PK |
307 | |
308 | #undef TARGET_STACK_PROTECT_RUNTIME_ENABLED_P | |
309 | #define TARGET_STACK_PROTECT_RUNTIME_ENABLED_P hook_bool_void_false | |
672a6f42 | 310 | \f |
8662b2ba RH |
311 | /* A helper function to determine if REGNO should be saved in the |
312 | current function's stack frame. */ | |
2c9c2489 | 313 | |
8662b2ba RH |
314 | static inline bool |
315 | pdp11_saved_regno (unsigned regno) | |
316 | { | |
317 | return !call_used_regs[regno] && df_regs_ever_live_p (regno); | |
318 | } | |
08c148a8 | 319 | |
8662b2ba | 320 | /* Expand the function prologue. */ |
2c9c2489 | 321 | |
442fcea7 PK |
322 | /* Frame layout, from high to low memory (stack push order): |
323 | return address (from jsr instruction) | |
324 | saved CPU registers, lowest number first | |
325 | saved FPU registers, lowest number first, always 64 bit mode | |
326 | *** frame pointer points here *** | |
327 | local variables | |
328 | alloca storage if any. */ | |
8662b2ba RH |
329 | void |
330 | pdp11_expand_prologue (void) | |
331 | { | |
332 | HOST_WIDE_INT fsize = get_frame_size (); | |
333 | unsigned regno; | |
334 | rtx x, via_ac = NULL; | |
335 | ||
336 | /* If we are outputting code for main, the switch FPU to the | |
337 | right mode if TARGET_FPU. */ | |
338 | if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU) | |
2c9c2489 | 339 | { |
8662b2ba RH |
340 | emit_insn (gen_setd ()); |
341 | emit_insn (gen_seti ()); | |
2c9c2489 RK |
342 | } |
343 | ||
8662b2ba RH |
344 | /* Save CPU registers. */ |
345 | for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++) | |
442fcea7 | 346 | if (pdp11_saved_regno (regno)) |
8662b2ba RH |
347 | { |
348 | x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); | |
349 | x = gen_frame_mem (Pmode, x); | |
350 | emit_move_insn (x, gen_rtx_REG (Pmode, regno)); | |
351 | } | |
352 | ||
353 | /* Save FPU registers. */ | |
354 | for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++) | |
355 | if (pdp11_saved_regno (regno)) | |
356 | { | |
357 | x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); | |
358 | x = gen_frame_mem (DFmode, x); | |
359 | via_ac = gen_rtx_REG (DFmode, regno); | |
360 | emit_move_insn (x, via_ac); | |
361 | } | |
362 | ||
363 | /* ??? Maybe make ac4, ac5 call used regs?? */ | |
364 | for (regno = AC4_REGNUM; regno <= AC5_REGNUM; regno++) | |
365 | if (pdp11_saved_regno (regno)) | |
366 | { | |
367 | gcc_assert (via_ac != NULL); | |
368 | emit_move_insn (via_ac, gen_rtx_REG (DFmode, regno)); | |
369 | ||
370 | x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); | |
371 | x = gen_frame_mem (DFmode, x); | |
372 | emit_move_insn (x, via_ac); | |
373 | } | |
2c9c2489 | 374 | |
442fcea7 PK |
375 | if (frame_pointer_needed) |
376 | emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); | |
c65916a6 | 377 | |
442fcea7 PK |
378 | /* Make local variable space. */ |
379 | if (fsize) | |
380 | emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, | |
381 | GEN_INT (-fsize))); | |
382 | } | |
2c9c2489 | 383 | |
442fcea7 PK |
384 | /* Generate epilogue. This uses the frame pointer to pop the local |
385 | variables and any alloca data off the stack. If there is no alloca | |
386 | and frame pointer elimination hasn't been disabled, there is no | |
387 | frame pointer and the local variables are popped by adjusting the | |
388 | stack pointer instead. */ | |
2c9c2489 | 389 | |
8662b2ba RH |
390 | void |
391 | pdp11_expand_epilogue (void) | |
2c9c2489 | 392 | { |
8662b2ba RH |
393 | HOST_WIDE_INT fsize = get_frame_size (); |
394 | unsigned regno; | |
395 | rtx x, reg, via_ac = NULL; | |
2c9c2489 | 396 | |
442fcea7 PK |
397 | /* Deallocate the local variables. */ |
398 | if (fsize) | |
399 | { | |
400 | if (frame_pointer_needed) | |
401 | { | |
402 | /* We can deallocate the frame with a single move. */ | |
403 | emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); | |
404 | } | |
405 | else | |
406 | emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, | |
407 | GEN_INT (fsize))); | |
408 | } | |
409 | ||
410 | /* Restore the FPU registers. */ | |
8662b2ba RH |
411 | if (pdp11_saved_regno (AC4_REGNUM) || pdp11_saved_regno (AC5_REGNUM)) |
412 | { | |
413 | /* Find a temporary with which to restore AC4/5. */ | |
414 | for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++) | |
415 | if (pdp11_saved_regno (regno)) | |
416 | { | |
417 | via_ac = gen_rtx_REG (DFmode, regno); | |
418 | break; | |
419 | } | |
420 | } | |
2c9c2489 | 421 | |
442fcea7 | 422 | /* Restore registers via pops. */ |
8662b2ba | 423 | |
442fcea7 PK |
424 | for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--) |
425 | if (pdp11_saved_regno (regno)) | |
426 | { | |
427 | x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); | |
428 | x = gen_frame_mem (DFmode, x); | |
429 | reg = gen_rtx_REG (DFmode, regno); | |
8662b2ba | 430 | |
442fcea7 PK |
431 | if (LOAD_FPU_REG_P (regno)) |
432 | emit_move_insn (reg, x); | |
433 | else | |
8662b2ba | 434 | { |
442fcea7 PK |
435 | emit_move_insn (via_ac, x); |
436 | emit_move_insn (reg, via_ac); | |
8662b2ba | 437 | } |
442fcea7 | 438 | } |
2c9c2489 | 439 | |
442fcea7 PK |
440 | for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--) |
441 | if (pdp11_saved_regno (regno)) | |
442 | { | |
443 | x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); | |
444 | x = gen_frame_mem (Pmode, x); | |
445 | emit_move_insn (gen_rtx_REG (Pmode, regno), x); | |
446 | } | |
8662b2ba | 447 | |
442fcea7 | 448 | emit_jump_insn (gen_rtspc ()); |
2c9c2489 | 449 | } |
08c148a8 | 450 | |
2c9c2489 RK |
451 | /* Return the best assembler insn template |
452 | for moving operands[1] into operands[0] as a fullword. */ | |
b0106b24 | 453 | static const char * |
f12b3fc8 | 454 | singlemove_string (rtx *operands) |
2c9c2489 RK |
455 | { |
456 | if (operands[1] != const0_rtx) | |
4aef57c9 | 457 | return "mov\t%1,%0"; |
2c9c2489 | 458 | |
4aef57c9 | 459 | return "clr\t%0"; |
2c9c2489 RK |
460 | } |
461 | ||
462 | \f | |
30442c59 | 463 | /* Expand multi-word operands (SImode or DImode) into the 2 or 4 |
442fcea7 PK |
464 | corresponding HImode operands. The number of operands is given as |
465 | the third argument, the word count for the mode as the fourth | |
466 | argument, and the required order of parts as the sixth argument. | |
467 | The word count is explicit because sometimes we're asked to compare | |
468 | two constants, both of which have mode VOIDmode, so we can't always | |
469 | rely on the input operand mode to imply the operand size. */ | |
30442c59 | 470 | bool |
442fcea7 PK |
471 | pdp11_expand_operands (rtx *operands, rtx exops[][2], |
472 | int opcount, int words, | |
30442c59 | 473 | pdp11_action *action, pdp11_partorder order) |
2c9c2489 | 474 | { |
442fcea7 | 475 | int op, w, i, sh; |
30442c59 PK |
476 | pdp11_partorder useorder; |
477 | bool sameoff = false; | |
478 | enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype; | |
30442c59 PK |
479 | long sval[2]; |
480 | ||
30442c59 PK |
481 | /* If either piece order is accepted and one is pre-decrement |
482 | while the other is post-increment, set order to be high order | |
483 | word first. That will force the pre-decrement to be turned | |
484 | into a pointer adjust, then offset addressing. | |
485 | Otherwise, if either operand uses pre-decrement, that means | |
486 | the order is low order first. | |
487 | Otherwise, if both operands are registers and destination is | |
488 | higher than source and they overlap, do low order word (highest | |
489 | register number) first. */ | |
490 | useorder = either; | |
491 | if (opcount == 2) | |
2c9c2489 | 492 | { |
442fcea7 PK |
493 | if (GET_CODE (operands[0]) == MEM && |
494 | GET_CODE (operands[1]) == MEM && | |
30442c59 PK |
495 | ((GET_CODE (XEXP (operands[0], 0)) == POST_INC && |
496 | GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) || | |
497 | (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC && | |
498 | GET_CODE (XEXP (operands[1], 0)) == POST_INC))) | |
499 | useorder = big; | |
442fcea7 | 500 | else if ((GET_CODE (operands[0]) == MEM && |
30442c59 | 501 | GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) || |
442fcea7 | 502 | (GET_CODE (operands[1]) == MEM && |
30442c59 PK |
503 | GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)) |
504 | useorder = little; | |
505 | else if (REG_P (operands[0]) && REG_P (operands[1]) && | |
506 | REGNO (operands[0]) > REGNO (operands[1]) && | |
507 | REGNO (operands[0]) < REGNO (operands[1]) + words) | |
508 | useorder = little; | |
509 | ||
510 | /* Check for source == offset from register and dest == push of | |
511 | the same register. In that case, we have to use the same | |
512 | offset (the one for the low order word) for all words, because | |
513 | the push increases the offset to each source word. | |
514 | In theory there are other cases like this, for example dest == pop, | |
515 | but those don't occur in real life so ignore those. */ | |
516 | if (GET_CODE (operands[0]) == MEM | |
517 | && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC | |
518 | && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM | |
519 | && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) | |
520 | sameoff = true; | |
2c9c2489 RK |
521 | } |
522 | ||
30442c59 PK |
523 | /* If the caller didn't specify order, use the one we computed, |
524 | or high word first if we don't care either. If the caller did | |
525 | specify, verify we don't have a problem with that order. | |
526 | (If it matters to the caller, constraints need to be used to | |
527 | ensure this case doesn't occur). */ | |
528 | if (order == either) | |
529 | order = (useorder == either) ? big : useorder; | |
2c9c2489 | 530 | else |
30442c59 | 531 | gcc_assert (useorder == either || useorder == order); |
2c9c2489 | 532 | |
30442c59 PK |
533 | |
534 | for (op = 0; op < opcount; op++) | |
2c9c2489 | 535 | { |
30442c59 PK |
536 | /* First classify the operand. */ |
537 | if (REG_P (operands[op])) | |
538 | optype = REGOP; | |
442fcea7 | 539 | else if (CONST_INT_P (operands[op]) |
30442c59 PK |
540 | || GET_CODE (operands[op]) == CONST_DOUBLE) |
541 | optype = CNSTOP; | |
542 | else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC) | |
543 | optype = POPOP; | |
544 | else if (GET_CODE (XEXP (operands[op], 0)) == PRE_DEC) | |
545 | optype = PUSHOP; | |
546 | else if (!reload_in_progress || offsettable_memref_p (operands[op])) | |
547 | optype = OFFSOP; | |
548 | else if (GET_CODE (operands[op]) == MEM) | |
549 | optype = MEMOP; | |
550 | else | |
551 | optype = RNDOP; | |
552 | ||
553 | /* Check for the cases that the operand constraints are not | |
554 | supposed to allow to happen. Return failure for such cases. */ | |
555 | if (optype == RNDOP) | |
556 | return false; | |
557 | ||
558 | if (action != NULL) | |
559 | action[op] = no_action; | |
560 | ||
561 | /* If the operand uses pre-decrement addressing but we | |
562 | want to get the parts high order first, | |
563 | decrement the former register explicitly | |
564 | and change the operand into ordinary indexing. */ | |
565 | if (optype == PUSHOP && order == big) | |
566 | { | |
567 | gcc_assert (action != NULL); | |
568 | action[op] = dec_before; | |
569 | operands[op] = gen_rtx_MEM (GET_MODE (operands[op]), | |
570 | XEXP (XEXP (operands[op], 0), 0)); | |
571 | optype = OFFSOP; | |
572 | } | |
573 | /* If the operand uses post-increment mode but we want | |
574 | to get the parts low order first, change the operand | |
575 | into ordinary indexing and remember to increment | |
576 | the register explicitly when we're done. */ | |
577 | else if (optype == POPOP && order == little) | |
2c9c2489 | 578 | { |
30442c59 PK |
579 | gcc_assert (action != NULL); |
580 | action[op] = inc_after; | |
581 | operands[op] = gen_rtx_MEM (GET_MODE (operands[op]), | |
582 | XEXP (XEXP (operands[op], 0), 0)); | |
583 | optype = OFFSOP; | |
584 | } | |
2c9c2489 | 585 | |
30442c59 | 586 | if (GET_CODE (operands[op]) == CONST_DOUBLE) |
442fcea7 PK |
587 | { |
588 | gcc_assert (GET_MODE (operands[op]) != VOIDmode); | |
589 | REAL_VALUE_TO_TARGET_DOUBLE | |
590 | (*CONST_DOUBLE_REAL_VALUE (operands[op]), sval); | |
591 | } | |
30442c59 PK |
592 | |
593 | for (i = 0; i < words; i++) | |
594 | { | |
595 | if (order == big) | |
596 | w = i; | |
597 | else if (sameoff) | |
598 | w = words - 1; | |
599 | else | |
600 | w = words - 1 - i; | |
601 | ||
602 | /* Set the output operand to be word "w" of the input. */ | |
603 | if (optype == REGOP) | |
604 | exops[i][op] = gen_rtx_REG (HImode, REGNO (operands[op]) + w); | |
605 | else if (optype == OFFSOP) | |
606 | exops[i][op] = adjust_address (operands[op], HImode, w * 2); | |
607 | else if (optype == CNSTOP) | |
608 | { | |
609 | if (GET_CODE (operands[op]) == CONST_DOUBLE) | |
610 | { | |
611 | sh = 16 - (w & 1) * 16; | |
612 | exops[i][op] = gen_rtx_CONST_INT (HImode, (sval[w / 2] >> sh) & 0xffff); | |
613 | } | |
614 | else | |
615 | { | |
616 | sh = ((words - 1 - w) * 16); | |
617 | exops[i][op] = gen_rtx_CONST_INT (HImode, trunc_int_for_mode (INTVAL(operands[op]) >> sh, HImode)); | |
618 | } | |
619 | } | |
620 | else | |
621 | exops[i][op] = operands[op]; | |
2c9c2489 | 622 | } |
2c9c2489 | 623 | } |
30442c59 | 624 | return true; |
2c9c2489 | 625 | } |
30442c59 PK |
626 | |
627 | /* Output assembler code to perform a multiple-word move insn | |
628 | with operands OPERANDS. This moves 2 or 4 words depending | |
629 | on the machine mode of the operands. */ | |
2c9c2489 | 630 | |
b0106b24 | 631 | const char * |
30442c59 | 632 | output_move_multiple (rtx *operands) |
2c9c2489 | 633 | { |
442fcea7 | 634 | rtx inops[2]; |
30442c59 | 635 | rtx exops[4][2]; |
442fcea7 PK |
636 | rtx adjops[2]; |
637 | ||
30442c59 PK |
638 | pdp11_action action[2]; |
639 | int i, words; | |
2c9c2489 | 640 | |
30442c59 | 641 | words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16; |
442fcea7 | 642 | adjops[1] = gen_rtx_CONST_INT (HImode, words * 2); |
2c9c2489 | 643 | |
442fcea7 PK |
644 | inops[0] = operands[0]; |
645 | inops[1] = operands[1]; | |
646 | ||
647 | pdp11_expand_operands (inops, exops, 2, words, action, either); | |
2c9c2489 | 648 | |
30442c59 PK |
649 | /* Check for explicit decrement before. */ |
650 | if (action[0] == dec_before) | |
2c9c2489 | 651 | { |
442fcea7 PK |
652 | adjops[0] = XEXP (XEXP (operands[0], 0), 0); |
653 | output_asm_insn ("sub\t%1,%0", adjops); | |
2c9c2489 | 654 | } |
30442c59 | 655 | if (action[1] == dec_before) |
2c9c2489 | 656 | { |
442fcea7 PK |
657 | adjops[0] = XEXP (XEXP (operands[1], 0), 0); |
658 | output_asm_insn ("sub\t%1,%0", adjops); | |
2c9c2489 RK |
659 | } |
660 | ||
30442c59 PK |
661 | /* Do the words. */ |
662 | for (i = 0; i < words; i++) | |
663 | output_asm_insn (singlemove_string (exops[i]), exops[i]); | |
2c9c2489 | 664 | |
30442c59 PK |
665 | /* Check for increment after. */ |
666 | if (action[0] == inc_after) | |
2c9c2489 | 667 | { |
442fcea7 PK |
668 | adjops[0] = XEXP (XEXP (operands[0], 0), 0); |
669 | output_asm_insn ("add\t%1,%0", adjops); | |
2c9c2489 | 670 | } |
30442c59 | 671 | if (action[1] == inc_after) |
2c9c2489 | 672 | { |
442fcea7 PK |
673 | adjops[0] = XEXP (XEXP (operands[1], 0), 0); |
674 | output_asm_insn ("add\t%1,%0", adjops); | |
2c9c2489 RK |
675 | } |
676 | ||
2c9c2489 RK |
677 | return ""; |
678 | } | |
2c9c2489 | 679 | \f |
4aef57c9 PK |
680 | /* Build an internal label. */ |
681 | void | |
682 | pdp11_gen_int_label (char *label, const char *prefix, int num) | |
683 | { | |
684 | if (TARGET_DEC_ASM) | |
685 | /* +1 because GCC numbers labels starting at zero. */ | |
442fcea7 | 686 | sprintf (label, "*%u$", num + 1); |
4aef57c9 | 687 | else |
442fcea7 | 688 | sprintf (label, "*%s_%u", prefix, num); |
4aef57c9 PK |
689 | } |
690 | ||
2c9c2489 | 691 | /* Output an ascii string. */ |
09b893bb | 692 | void |
f12b3fc8 | 693 | output_ascii (FILE *file, const char *p, int size) |
2c9c2489 | 694 | { |
4aef57c9 PK |
695 | int i, c; |
696 | const char *pseudo = "\t.ascii\t"; | |
697 | bool delim = false; | |
698 | ||
699 | if (TARGET_DEC_ASM) | |
700 | { | |
701 | if (p[size - 1] == '\0') | |
702 | { | |
703 | pseudo = "\t.asciz\t"; | |
704 | size--; | |
705 | } | |
706 | fputs (pseudo, file); | |
707 | for (i = 0; i < size; i++) | |
708 | { | |
709 | c = *p++ & 0xff; | |
710 | if (c < 32 || c == '"' || c > 126) | |
711 | { | |
712 | if (delim) | |
713 | putc ('"', file); | |
442fcea7 | 714 | fprintf (file, "<%o>", c); |
4aef57c9 PK |
715 | delim = false; |
716 | } | |
717 | else | |
718 | { | |
719 | if (!delim) | |
720 | putc ('"', file); | |
721 | delim = true; | |
722 | putc (c, file); | |
723 | } | |
724 | } | |
725 | if (delim) | |
726 | putc ('"', file); | |
727 | putc ('\n', file); | |
728 | } | |
729 | else | |
2c9c2489 | 730 | { |
4aef57c9 PK |
731 | fprintf (file, "\t.byte "); |
732 | ||
733 | for (i = 0; i < size; i++) | |
734 | { | |
735 | fprintf (file, "%#o", *p++ & 0xff); | |
736 | if (i < size - 1) | |
737 | putc (',', file); | |
738 | } | |
739 | putc ('\n', file); | |
2c9c2489 | 740 | } |
2c9c2489 RK |
741 | } |
742 | ||
dad6bca9 PK |
743 | void |
744 | pdp11_asm_output_var (FILE *file, const char *name, int size, | |
745 | int align, bool global) | |
746 | { | |
747 | if (align > 8) | |
4aef57c9 | 748 | fprintf (file, "\t.even\n"); |
442fcea7 | 749 | if (TARGET_DEC_ASM) |
12eb6ed3 | 750 | { |
12eb6ed3 | 751 | assemble_name (file, name); |
442fcea7 PK |
752 | if (global) |
753 | fputs ("::", file); | |
754 | else | |
755 | fputs (":", file); | |
756 | if (align > 8) | |
757 | fprintf (file, "\t.blkw\t%o\n", (size & 0xffff) / 2); | |
758 | else | |
759 | fprintf (file, "\t.blkb\t%o\n", size & 0xffff); | |
12eb6ed3 | 760 | } |
442fcea7 PK |
761 | else |
762 | { | |
763 | if (global) | |
764 | { | |
765 | fprintf (file, ".globl "); | |
766 | assemble_name (file, name); | |
767 | } | |
768 | fprintf (file, "\n"); | |
769 | assemble_name (file, name); | |
770 | fputs (":", file); | |
771 | ASM_OUTPUT_SKIP (file, size); | |
772 | } | |
dad6bca9 PK |
773 | } |
774 | ||
4aef57c9 PK |
775 | /* Special format operators handled here: |
776 | # -- output the correct immediate operand marker for the assembler | |
777 | dialect. | |
778 | @ -- output the correct indirect marker for the assembler dialect. | |
779 | o -- emit a constant value as a number (not an immediate operand) | |
780 | in octal. */ | |
12eb6ed3 PK |
781 | static void |
782 | pdp11_asm_print_operand (FILE *file, rtx x, int code) | |
783 | { | |
12eb6ed3 PK |
784 | long sval[2]; |
785 | ||
786 | if (code == '#') | |
4aef57c9 PK |
787 | { |
788 | if (TARGET_DEC_ASM) | |
789 | putc ('#', file); | |
790 | else | |
791 | putc ('$', file); | |
792 | } | |
12eb6ed3 PK |
793 | else if (code == '@') |
794 | { | |
795 | if (TARGET_UNIX_ASM) | |
796 | fprintf (file, "*"); | |
797 | else | |
798 | fprintf (file, "@"); | |
799 | } | |
800 | else if (GET_CODE (x) == REG) | |
801 | fprintf (file, "%s", reg_names[REGNO (x)]); | |
802 | else if (GET_CODE (x) == MEM) | |
cc8ca59e | 803 | output_address (GET_MODE (x), XEXP (x, 0)); |
442fcea7 | 804 | else if (GET_CODE (x) == CONST_DOUBLE && FLOAT_MODE_P (GET_MODE (x))) |
12eb6ed3 | 805 | { |
34a72c33 | 806 | REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (x), sval); |
4aef57c9 PK |
807 | if (TARGET_DEC_ASM) |
808 | fprintf (file, "#%lo", (sval[0] >> 16) & 0xffff); | |
809 | else | |
810 | fprintf (file, "$%#lo", (sval[0] >> 16) & 0xffff); | |
12eb6ed3 PK |
811 | } |
812 | else | |
813 | { | |
4aef57c9 PK |
814 | if (code != 'o') |
815 | { | |
816 | if (TARGET_DEC_ASM) | |
817 | putc ('#', file); | |
818 | else | |
819 | putc ('$', file); | |
820 | } | |
12eb6ed3 PK |
821 | output_addr_const_pdp11 (file, x); |
822 | } | |
823 | } | |
824 | ||
825 | static bool | |
522654e6 | 826 | pdp11_asm_print_operand_punct_valid_p (unsigned char c) |
12eb6ed3 PK |
827 | { |
828 | return (c == '#' || c == '@'); | |
829 | } | |
830 | ||
09b893bb | 831 | void |
f12b3fc8 | 832 | print_operand_address (FILE *file, register rtx addr) |
2c9c2489 | 833 | { |
c153355f | 834 | register rtx breg; |
2c9c2489 | 835 | rtx offset; |
c153355f PK |
836 | int again = 0; |
837 | ||
2c9c2489 RK |
838 | retry: |
839 | ||
840 | switch (GET_CODE (addr)) | |
841 | { | |
842 | case MEM: | |
d14ff9bd JM |
843 | if (TARGET_UNIX_ASM) |
844 | fprintf (file, "*"); | |
845 | else | |
846 | fprintf (file, "@"); | |
2c9c2489 | 847 | addr = XEXP (addr, 0); |
c153355f | 848 | again = 1; |
2c9c2489 RK |
849 | goto retry; |
850 | ||
851 | case REG: | |
852 | fprintf (file, "(%s)", reg_names[REGNO (addr)]); | |
853 | break; | |
854 | ||
e7f9979a | 855 | case PRE_MODIFY: |
2c9c2489 RK |
856 | case PRE_DEC: |
857 | fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); | |
858 | break; | |
859 | ||
e7f9979a | 860 | case POST_MODIFY: |
2c9c2489 RK |
861 | case POST_INC: |
862 | fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]); | |
863 | break; | |
864 | ||
865 | case PLUS: | |
c153355f | 866 | breg = 0; |
2c9c2489 RK |
867 | offset = 0; |
868 | if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) | |
869 | || GET_CODE (XEXP (addr, 0)) == MEM) | |
870 | { | |
871 | offset = XEXP (addr, 0); | |
872 | addr = XEXP (addr, 1); | |
873 | } | |
874 | else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) | |
875 | || GET_CODE (XEXP (addr, 1)) == MEM) | |
876 | { | |
877 | offset = XEXP (addr, 1); | |
878 | addr = XEXP (addr, 0); | |
879 | } | |
880 | if (GET_CODE (addr) != PLUS) | |
881 | ; | |
2c9c2489 RK |
882 | else if (GET_CODE (XEXP (addr, 0)) == REG) |
883 | { | |
c153355f | 884 | breg = XEXP (addr, 0); |
2c9c2489 RK |
885 | addr = XEXP (addr, 1); |
886 | } | |
887 | else if (GET_CODE (XEXP (addr, 1)) == REG) | |
888 | { | |
c153355f | 889 | breg = XEXP (addr, 1); |
2c9c2489 RK |
890 | addr = XEXP (addr, 0); |
891 | } | |
c153355f | 892 | if (GET_CODE (addr) == REG) |
2c9c2489 | 893 | { |
c153355f PK |
894 | gcc_assert (breg == 0); |
895 | breg = addr; | |
2c9c2489 RK |
896 | addr = 0; |
897 | } | |
898 | if (offset != 0) | |
899 | { | |
d35d9223 | 900 | gcc_assert (addr == 0); |
2c9c2489 RK |
901 | addr = offset; |
902 | } | |
2c9c2489 | 903 | if (addr != 0) |
c153355f | 904 | output_addr_const_pdp11 (file, addr); |
2c9c2489 RK |
905 | if (breg != 0) |
906 | { | |
d35d9223 | 907 | gcc_assert (GET_CODE (breg) == REG); |
2c9c2489 RK |
908 | fprintf (file, "(%s)", reg_names[REGNO (breg)]); |
909 | } | |
2c9c2489 RK |
910 | break; |
911 | ||
912 | default: | |
c153355f PK |
913 | if (!again && GET_CODE (addr) == CONST_INT) |
914 | { | |
915 | /* Absolute (integer number) address. */ | |
4aef57c9 PK |
916 | if (TARGET_DEC_ASM) |
917 | fprintf (file, "@#"); | |
918 | else if (!TARGET_UNIX_ASM) | |
c153355f PK |
919 | fprintf (file, "@$"); |
920 | } | |
af36a4d2 | 921 | output_addr_const_pdp11 (file, addr); |
2c9c2489 RK |
922 | } |
923 | } | |
924 | ||
301d03af RS |
925 | /* Target hook to assemble integer objects. We need to use the |
926 | pdp-specific version of output_addr_const. */ | |
927 | ||
928 | static bool | |
f12b3fc8 | 929 | pdp11_assemble_integer (rtx x, unsigned int size, int aligned_p) |
301d03af RS |
930 | { |
931 | if (aligned_p) | |
932 | switch (size) | |
933 | { | |
934 | case 1: | |
935 | fprintf (asm_out_file, "\t.byte\t"); | |
b9cb66d2 | 936 | output_addr_const_pdp11 (asm_out_file, GEN_INT (INTVAL (x) & 0xff)); |
b4324a14 | 937 | fputs ("\n", asm_out_file); |
301d03af RS |
938 | return true; |
939 | ||
940 | case 2: | |
941 | fprintf (asm_out_file, TARGET_UNIX_ASM ? "\t" : "\t.word\t"); | |
942 | output_addr_const_pdp11 (asm_out_file, x); | |
b4324a14 | 943 | fputs ("\n", asm_out_file); |
301d03af RS |
944 | return true; |
945 | } | |
946 | return default_assemble_integer (x, size, aligned_p); | |
947 | } | |
948 | ||
949 | ||
0a7eb8df PK |
950 | static bool |
951 | pdp11_lra_p (void) | |
952 | { | |
953 | return TARGET_LRA; | |
954 | } | |
955 | ||
aad2444d PK |
956 | /* Register to register moves are cheap if both are general |
957 | registers. */ | |
a01c666c | 958 | static int |
ef4bddc2 | 959 | pdp11_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED, |
a01c666c | 960 | reg_class_t c1, reg_class_t c2) |
2c9c2489 | 961 | { |
442fcea7 | 962 | if (CPU_REG_CLASS (c1) && CPU_REG_CLASS (c2)) |
b4324a14 PK |
963 | return 2; |
964 | else if ((c1 >= LOAD_FPU_REGS && c1 <= FPU_REGS && c2 == LOAD_FPU_REGS) || | |
965 | (c2 >= LOAD_FPU_REGS && c2 <= FPU_REGS && c1 == LOAD_FPU_REGS)) | |
aad2444d | 966 | return 2; |
b4324a14 PK |
967 | else |
968 | return 22; | |
2c9c2489 RK |
969 | } |
970 | ||
aad2444d PK |
971 | /* This tries to approximate what pdp11_insn_cost would do, but |
972 | without visibility into the actual instruction being generated it's | |
973 | inevitably a rough approximation. */ | |
3c50106f | 974 | static bool |
aad2444d PK |
975 | pdp11_rtx_costs (rtx x, machine_mode mode, int outer_code, |
976 | int opno ATTRIBUTE_UNUSED, int *total, bool speed) | |
3c50106f | 977 | { |
aad2444d PK |
978 | const int code = GET_CODE (x); |
979 | const int asize = (mode == QImode) ? 2 : GET_MODE_SIZE (mode); | |
980 | rtx src, dest; | |
ba8d277a | 981 | const char *fmt; |
aad2444d | 982 | |
3c50106f RH |
983 | switch (code) |
984 | { | |
985 | case CONST_INT: | |
aad2444d PK |
986 | /* Treat -1, 0, 1 as things that are optimized as clr or dec |
987 | etc. though that doesn't apply to every case. */ | |
988 | if (INTVAL (x) >= -1 && INTVAL (x) <= 1) | |
3c50106f RH |
989 | { |
990 | *total = 0; | |
991 | return true; | |
992 | } | |
aad2444d PK |
993 | /* FALL THROUGH. */ |
994 | case REG: | |
995 | case MEM: | |
3c50106f RH |
996 | case CONST: |
997 | case LABEL_REF: | |
998 | case SYMBOL_REF: | |
3c50106f | 999 | case CONST_DOUBLE: |
aad2444d | 1000 | *total = pdp11_addr_cost (x, mode, ADDR_SPACE_GENERIC, speed); |
3c50106f | 1001 | return true; |
aad2444d | 1002 | } |
ba8d277a PK |
1003 | if (GET_RTX_LENGTH (code) == 0) |
1004 | { | |
1005 | if (speed) | |
1006 | *total = 0; | |
1007 | else | |
1008 | *total = 2; | |
1009 | return true; | |
1010 | } | |
3c50106f | 1011 | |
aad2444d PK |
1012 | /* Pick up source and dest. We don't necessarily use the standard |
1013 | recursion in rtx_costs to figure the cost, because that would | |
1014 | count the destination operand twice for three-operand insns. | |
1015 | Also, this way we can catch special cases like move of zero, or | |
1016 | add one. */ | |
ba8d277a PK |
1017 | fmt = GET_RTX_FORMAT (code); |
1018 | if (fmt[0] != 'e' || (GET_RTX_LENGTH (code) > 1 && fmt[1] != 'e')) | |
1019 | { | |
1020 | if (speed) | |
1021 | *total = 0; | |
1022 | else | |
1023 | *total = 2; | |
1024 | return true; | |
1025 | } | |
aad2444d PK |
1026 | if (GET_RTX_LENGTH (code) > 1) |
1027 | src = XEXP (x, 1); | |
1028 | dest = XEXP (x, 0); | |
1029 | ||
1030 | /* If optimizing for size, claim everything costs 2 per word, plus | |
1031 | whatever the operands require. */ | |
1032 | if (!speed) | |
1033 | *total = asize; | |
1034 | else | |
1035 | { | |
1036 | if (FLOAT_MODE_P (mode)) | |
1037 | { | |
1038 | switch (code) | |
1039 | { | |
1040 | case MULT: | |
1041 | case DIV: | |
1042 | case MOD: | |
1043 | *total = 20; | |
1044 | break; | |
3c50106f | 1045 | |
aad2444d PK |
1046 | case COMPARE: |
1047 | *total = 4; | |
1048 | break; | |
3c50106f | 1049 | |
aad2444d PK |
1050 | case PLUS: |
1051 | case MINUS: | |
1052 | *total = 6; | |
1053 | break; | |
1054 | ||
1055 | default: | |
1056 | *total = 2; | |
1057 | break; | |
1058 | } | |
1059 | } | |
3c50106f | 1060 | else |
aad2444d PK |
1061 | { |
1062 | /* Integer operations are scaled for SI and DI modes, though the | |
1063 | scaling is not exactly accurate. */ | |
1064 | switch (code) | |
1065 | { | |
1066 | case MULT: | |
1067 | *total = 5 * asize * asize; | |
1068 | break; | |
3c50106f | 1069 | |
aad2444d PK |
1070 | case DIV: |
1071 | *total = 10 * asize * asize; | |
1072 | break; | |
1073 | ||
1074 | case MOD: | |
1075 | /* Fake value because it's accounted for under DIV, since we | |
1076 | use a divmod pattern. */ | |
1077 | total = 0; | |
1078 | break; | |
3c50106f | 1079 | |
aad2444d PK |
1080 | case ASHIFT: |
1081 | case ASHIFTRT: | |
1082 | case LSHIFTRT: | |
1083 | /* This is a bit problematic because the cost depends on the | |
1084 | shift amount. Make it <asize> for now, which is for the | |
1085 | case of a one bit shift. */ | |
1086 | *total = asize; | |
1087 | break; | |
1088 | ||
1089 | default: | |
1090 | *total = asize; | |
1091 | break; | |
1092 | } | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | /* Now see if we're looking at a SET. If yes, then look at the | |
1097 | source to see if this is a move or an arithmetic operation, and | |
1098 | continue accordingly to handle the operands. */ | |
1099 | if (code == SET) | |
1100 | { | |
1101 | switch (GET_CODE (src)) | |
1102 | { | |
1103 | case REG: | |
1104 | case MEM: | |
1105 | case CONST_INT: | |
1106 | case CONST: | |
1107 | case LABEL_REF: | |
1108 | case SYMBOL_REF: | |
1109 | case CONST_DOUBLE: | |
1110 | /* It's a move. */ | |
1111 | *total += pdp11_addr_cost (dest, mode, ADDR_SPACE_GENERIC, speed); | |
1112 | if (src != const0_rtx) | |
1113 | *total += pdp11_addr_cost (src, mode, ADDR_SPACE_GENERIC, speed); | |
1114 | return true; | |
1115 | default: | |
1116 | /* Not a move. Get the cost of the source operand and add | |
1117 | that in, but not the destination operand since we're | |
1118 | dealing with read/modify/write operands. */ | |
1119 | *total += rtx_cost (src, mode, (enum rtx_code) outer_code, 1, speed); | |
1120 | return true; | |
1121 | } | |
1122 | } | |
1123 | else if (code == PLUS || code == MINUS) | |
1124 | { | |
1125 | if (GET_CODE (src) == CONST_INT && | |
1126 | (INTVAL (src) == 1 || INTVAL (src) == -1)) | |
1127 | { | |
1128 | *total += rtx_cost (dest, mode, (enum rtx_code) outer_code, 0, speed); | |
1129 | return true; | |
1130 | } | |
1131 | } | |
1132 | return false; | |
1133 | } | |
1134 | ||
1135 | /* Return cost of accessing the supplied operand. Registers are free. | |
1136 | Anything else starts with a cost of two. Add to that for memory | |
1137 | references the memory accesses of the addressing mode (if any) plus | |
1138 | the data reference; for other operands just the memory access (if | |
1139 | any) for the mode. */ | |
1140 | static int | |
1141 | pdp11_addr_cost (rtx addr, machine_mode mode, addr_space_t as ATTRIBUTE_UNUSED, | |
1142 | bool speed) | |
1143 | { | |
1144 | int cost = 0; | |
1145 | ||
1146 | if (GET_CODE (addr) != REG) | |
1147 | { | |
1148 | if (!simple_memory_operand (addr, mode)) | |
1149 | cost = 2; | |
1150 | ||
1151 | /* If optimizing for speed, account for the memory reference if | |
1152 | any. */ | |
1153 | if (speed && !CONSTANT_P (addr)) | |
1154 | cost += (mode == QImode) ? 2 : GET_MODE_SIZE (mode); | |
1155 | } | |
1156 | return cost; | |
1157 | } | |
3c50106f | 1158 | |
b4324a14 | 1159 | |
aad2444d PK |
1160 | static int |
1161 | pdp11_insn_cost (rtx_insn *insn, bool speed) | |
1162 | { | |
1163 | int base_cost, i; | |
1164 | rtx pat, set, dest, src, src2; | |
1165 | machine_mode mode; | |
1166 | const char *fmt; | |
1167 | enum rtx_code op; | |
1168 | ||
1169 | if (recog_memoized (insn) < 0) | |
1170 | return 0; | |
1171 | ||
1172 | /* If optimizing for size, we want the insn size. */ | |
1173 | if (!speed) | |
1174 | return get_attr_length (insn); | |
1175 | else | |
1176 | { | |
1177 | /* Optimizing for speed. Get the base cost of the insn, then | |
1178 | adjust for the cost of accessing operands. Zero means use | |
1179 | the length as the cost even when optimizing for speed. */ | |
1180 | base_cost = get_attr_base_cost (insn); | |
1181 | if (base_cost <= 0) | |
1182 | base_cost = get_attr_length (insn); | |
1183 | } | |
1184 | /* Look for the operands. Often we have a PARALLEL that's either | |
1185 | the actual operation plus a clobber, or the implicit compare plus | |
1186 | the actual operation. Find the actual operation. */ | |
1187 | pat = PATTERN (insn); | |
1188 | ||
1189 | if (GET_CODE (pat) == PARALLEL) | |
1190 | { | |
1191 | set = XVECEXP (pat, 0, 0); | |
1192 | if (GET_CODE (set) != SET || GET_CODE (XEXP (set, 1)) == COMPARE) | |
1193 | set = XVECEXP (pat, 0, 1); | |
1194 | if (GET_CODE (set) != SET || GET_CODE (XEXP (set, 1)) == COMPARE) | |
1195 | return 0; | |
1196 | } | |
1197 | else | |
1198 | { | |
1199 | set = pat; | |
1200 | if (GET_CODE (set) != SET) | |
1201 | return 0; | |
1202 | } | |
1203 | ||
1204 | /* Pick up the SET source and destination RTL. */ | |
1205 | dest = XEXP (set, 0); | |
1206 | src = XEXP (set, 1); | |
1207 | mode = GET_MODE (dest); | |
1208 | ||
1209 | /* See if we have a move, or some arithmetic operation. If a move, | |
1210 | account for source and destination operand costs. Otherwise, | |
1211 | account for the destination and for the second operand of the | |
1212 | operation -- the first is also destination and we don't want to | |
1213 | double-count it. */ | |
1214 | base_cost += pdp11_addr_cost (dest, mode, ADDR_SPACE_GENERIC, speed); | |
1215 | op = GET_CODE (src); | |
1216 | switch (op) | |
1217 | { | |
1218 | case REG: | |
1219 | case MEM: | |
1220 | case CONST_INT: | |
1221 | case CONST: | |
1222 | case LABEL_REF: | |
1223 | case SYMBOL_REF: | |
1224 | case CONST_DOUBLE: | |
1225 | /* It's a move. */ | |
1226 | if (src != const0_rtx) | |
1227 | base_cost += pdp11_addr_cost (src, mode, ADDR_SPACE_GENERIC, speed); | |
1228 | return base_cost; | |
3c50106f | 1229 | default: |
aad2444d PK |
1230 | break; |
1231 | } | |
1232 | /* There are some other cases where souce and dest are distinct. */ | |
1233 | if (FLOAT_MODE_P (mode) && | |
1234 | (op == FLOAT_TRUNCATE || op == FLOAT_EXTEND || op == FIX || op == FLOAT)) | |
1235 | { | |
1236 | src2 = XEXP (src, 0); | |
1237 | base_cost += pdp11_addr_cost (src2, mode, ADDR_SPACE_GENERIC, speed); | |
3c50106f | 1238 | } |
aad2444d PK |
1239 | /* Otherwise, pick up the second operand of the arithmetic |
1240 | operation, if it has two operands. */ | |
1241 | else if (op != SUBREG && op != UNSPEC && GET_RTX_LENGTH (op) > 1) | |
1242 | { | |
1243 | src2 = XEXP (src, 1); | |
1244 | base_cost += pdp11_addr_cost (src2, mode, ADDR_SPACE_GENERIC, speed); | |
1245 | } | |
1246 | ||
1247 | return base_cost; | |
3c50106f RH |
1248 | } |
1249 | ||
b0106b24 | 1250 | const char * |
b4324a14 | 1251 | output_jump (rtx *operands, int ccnz, int length) |
2c9c2489 | 1252 | { |
b4324a14 PK |
1253 | rtx tmpop[1]; |
1254 | static char buf[100]; | |
1255 | const char *pos, *neg; | |
1256 | enum rtx_code code = GET_CODE (operands[0]); | |
f90b7a5a | 1257 | |
b4324a14 | 1258 | if (ccnz) |
2c9c2489 | 1259 | { |
b4324a14 PK |
1260 | /* These are the branches valid for CCNZmode, i.e., a comparison |
1261 | with zero where the V bit is not set to zero. These cases | |
1262 | occur when CC or FCC are set as a side effect of some data | |
1263 | manipulation, such as the ADD instruction. */ | |
1264 | switch (code) | |
1265 | { | |
1266 | case EQ: pos = "beq", neg = "bne"; break; | |
1267 | case NE: pos = "bne", neg = "beq"; break; | |
1268 | case LT: pos = "bmi", neg = "bpl"; break; | |
1269 | case GE: pos = "bpl", neg = "bmi"; break; | |
1270 | default: gcc_unreachable (); | |
1271 | } | |
1272 | } | |
1273 | else | |
1274 | { | |
1275 | switch (code) | |
1276 | { | |
1277 | case EQ: pos = "beq", neg = "bne"; break; | |
1278 | case NE: pos = "bne", neg = "beq"; break; | |
1279 | case GT: pos = "bgt", neg = "ble"; break; | |
1280 | case GTU: pos = "bhi", neg = "blos"; break; | |
1281 | case LT: pos = "blt", neg = "bge"; break; | |
1282 | case LTU: pos = "blo", neg = "bhis"; break; | |
1283 | case GE: pos = "bge", neg = "blt"; break; | |
1284 | case GEU: pos = "bhis", neg = "blo"; break; | |
1285 | case LE: pos = "ble", neg = "bgt"; break; | |
1286 | case LEU: pos = "blos", neg = "bhi"; break; | |
1287 | default: gcc_unreachable (); | |
1288 | } | |
1289 | } | |
1290 | switch (length) | |
1291 | { | |
1292 | case 2: | |
4aef57c9 | 1293 | sprintf (buf, "%s\t%%l1", pos); |
b4324a14 PK |
1294 | return buf; |
1295 | case 6: | |
1296 | tmpop[0] = gen_label_rtx (); | |
4aef57c9 | 1297 | sprintf (buf, "%s\t%%l0", neg); |
b4324a14 | 1298 | output_asm_insn (buf, tmpop); |
4aef57c9 | 1299 | output_asm_insn ("jmp\t%l1", operands); |
b4324a14 PK |
1300 | output_asm_label (tmpop[0]); |
1301 | fputs (":\n", asm_out_file); | |
1302 | return ""; | |
1303 | default: | |
1304 | gcc_unreachable (); | |
2c9c2489 | 1305 | } |
2c9c2489 RK |
1306 | } |
1307 | ||
b4324a14 PK |
1308 | /* Select the CC mode to be used for the side effect compare with |
1309 | zero, given the compare operation code in op and the compare | |
1310 | operands in x in and y. */ | |
1311 | machine_mode | |
aad2444d | 1312 | pdp11_cc_mode (enum rtx_code op ATTRIBUTE_UNUSED, rtx x, rtx y ATTRIBUTE_UNUSED) |
2c9c2489 | 1313 | { |
b4324a14 PK |
1314 | if (FLOAT_MODE_P (GET_MODE (x))) |
1315 | { | |
1316 | switch (GET_CODE (x)) | |
1317 | { | |
1318 | case ABS: | |
1319 | case NEG: | |
1320 | case REG: | |
1321 | case MEM: | |
1322 | return CCmode; | |
1323 | default: | |
1324 | return CCNZmode; | |
1325 | } | |
2c9c2489 | 1326 | } |
b4324a14 PK |
1327 | else |
1328 | { | |
1329 | switch (GET_CODE (x)) | |
1330 | { | |
1331 | case XOR: | |
1332 | case AND: | |
1333 | case IOR: | |
1334 | case MULT: | |
1335 | case NOT: | |
1336 | case REG: | |
1337 | case MEM: | |
1338 | return CCmode; | |
1339 | default: | |
1340 | return CCNZmode; | |
1341 | } | |
e9e4977e | 1342 | } |
2c9c2489 RK |
1343 | } |
1344 | ||
1345 | ||
b0106b24 | 1346 | int |
ef4bddc2 | 1347 | simple_memory_operand(rtx op, machine_mode mode ATTRIBUTE_UNUSED) |
2c9c2489 | 1348 | { |
b4324a14 | 1349 | rtx addr; |
2c9c2489 | 1350 | |
b4324a14 PK |
1351 | /* Eliminate non-memory operations */ |
1352 | if (GET_CODE (op) != MEM) | |
1353 | return FALSE; | |
2c9c2489 | 1354 | |
b4324a14 | 1355 | /* Decode the address now. */ |
2c9c2489 | 1356 | |
b4324a14 | 1357 | indirection: |
2c9c2489 | 1358 | |
b4324a14 | 1359 | addr = XEXP (op, 0); |
2c9c2489 | 1360 | |
b4324a14 | 1361 | switch (GET_CODE (addr)) |
2c9c2489 | 1362 | { |
b4324a14 PK |
1363 | case REG: |
1364 | /* (R0) - no extra cost */ | |
1365 | return 1; | |
2c9c2489 | 1366 | |
b4324a14 PK |
1367 | case PRE_DEC: |
1368 | case POST_INC: | |
1369 | case PRE_MODIFY: | |
1370 | case POST_MODIFY: | |
1371 | /* -(R0), (R0)+ - cheap! */ | |
1372 | return 1; | |
1373 | ||
1374 | case MEM: | |
1375 | /* cheap - is encoded in addressing mode info! | |
1376 | ||
1377 | -- except for @(R0), which has to be @0(R0) !!! */ | |
1378 | ||
1379 | if (GET_CODE (XEXP (addr, 0)) == REG) | |
2c9c2489 RK |
1380 | return 0; |
1381 | ||
b4324a14 PK |
1382 | op=addr; |
1383 | goto indirection; | |
1384 | ||
1385 | case CONST_INT: | |
1386 | case LABEL_REF: | |
1387 | case CONST: | |
1388 | case SYMBOL_REF: | |
1389 | /* @#address - extra cost */ | |
1390 | return 0; | |
1391 | ||
1392 | case PLUS: | |
1393 | /* X(R0) - extra cost */ | |
1394 | return 0; | |
1395 | ||
1396 | default: | |
1397 | break; | |
1398 | } | |
1399 | ||
1400 | return FALSE; | |
1401 | } | |
1402 | ||
1403 | /* Similar to simple_memory_operand but doesn't match push/pop. */ | |
1404 | int | |
1405 | no_side_effect_operand(rtx op, machine_mode mode ATTRIBUTE_UNUSED) | |
1406 | { | |
1407 | rtx addr; | |
2c9c2489 | 1408 | |
b4324a14 PK |
1409 | /* Eliminate non-memory operations */ |
1410 | if (GET_CODE (op) != MEM) | |
1411 | return FALSE; | |
1412 | ||
1413 | /* Decode the address now. */ | |
1414 | ||
1415 | indirection: | |
1416 | ||
1417 | addr = XEXP (op, 0); | |
2c9c2489 | 1418 | |
b4324a14 PK |
1419 | switch (GET_CODE (addr)) |
1420 | { | |
1421 | case REG: | |
1422 | /* (R0) - no extra cost */ | |
1423 | return 1; | |
2c9c2489 | 1424 | |
b4324a14 PK |
1425 | case PRE_DEC: |
1426 | case POST_INC: | |
1427 | case PRE_MODIFY: | |
1428 | case POST_MODIFY: | |
1429 | return 0; | |
2c9c2489 | 1430 | |
b4324a14 PK |
1431 | case MEM: |
1432 | /* cheap - is encoded in addressing mode info! | |
2c9c2489 | 1433 | |
b4324a14 PK |
1434 | -- except for @(R0), which has to be @0(R0) !!! */ |
1435 | ||
1436 | if (GET_CODE (XEXP (addr, 0)) == REG) | |
2c9c2489 | 1437 | return 0; |
b4324a14 PK |
1438 | |
1439 | op=addr; | |
1440 | goto indirection; | |
1441 | ||
1442 | case CONST_INT: | |
1443 | case LABEL_REF: | |
1444 | case CONST: | |
1445 | case SYMBOL_REF: | |
1446 | /* @#address - extra cost */ | |
1447 | return 0; | |
ed03c6cd | 1448 | |
b4324a14 PK |
1449 | case PLUS: |
1450 | /* X(R0) - extra cost */ | |
1451 | return 0; | |
1452 | ||
1453 | default: | |
1454 | break; | |
2c9c2489 RK |
1455 | } |
1456 | ||
b4324a14 | 1457 | return FALSE; |
2c9c2489 RK |
1458 | } |
1459 | ||
442fcea7 PK |
1460 | /* Return TRUE if op is a push or pop using the register "regno". */ |
1461 | bool | |
1462 | pushpop_regeq (rtx op, int regno) | |
2c9c2489 | 1463 | { |
442fcea7 PK |
1464 | rtx addr; |
1465 | ||
1466 | /* False if not memory reference. */ | |
1467 | if (GET_CODE (op) != MEM) | |
1468 | return FALSE; | |
1469 | ||
1470 | /* Get the address of the memory reference. */ | |
1471 | addr = XEXP (op, 0); | |
1472 | ||
1473 | if (GET_CODE (addr) == MEM) | |
1474 | addr = XEXP (addr, 0); | |
1475 | ||
1476 | switch (GET_CODE (addr)) | |
1477 | { | |
1478 | case PRE_DEC: | |
1479 | case POST_INC: | |
1480 | case PRE_MODIFY: | |
1481 | case POST_MODIFY: | |
1482 | return REGNO (XEXP (addr, 0)) == regno; | |
1483 | default: | |
1484 | return FALSE; | |
1485 | } | |
2c9c2489 RK |
1486 | } |
1487 | ||
e621b588 PK |
1488 | /* This function checks whether a real value can be encoded as |
1489 | a literal, i.e., addressing mode 27. In that mode, real values | |
1490 | are one word values, so the remaining 48 bits have to be zero. */ | |
1491 | int | |
1492 | legitimate_const_double_p (rtx address) | |
1493 | { | |
e621b588 | 1494 | long sval[2]; |
442fcea7 PK |
1495 | |
1496 | /* If it's too big for HOST_WIDE_INT, it's definitely to big here. */ | |
1497 | if (GET_MODE (address) == VOIDmode) | |
1498 | return 0; | |
34a72c33 | 1499 | REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (address), sval); |
442fcea7 | 1500 | |
e621b588 PK |
1501 | if ((sval[0] & 0xffff) == 0 && sval[1] == 0) |
1502 | return 1; | |
1503 | return 0; | |
1504 | } | |
1505 | ||
0d803030 RS |
1506 | /* Implement TARGET_CAN_CHANGE_MODE_CLASS. */ |
1507 | static bool | |
1508 | pdp11_can_change_mode_class (machine_mode from, | |
1509 | machine_mode to, | |
1510 | reg_class_t rclass) | |
a01c666c PK |
1511 | { |
1512 | /* Also, FPU registers contain a whole float value and the parts of | |
1513 | it are not separately accessible. | |
1514 | ||
1515 | So we disallow all mode changes involving FPRs. */ | |
1516 | if (FLOAT_MODE_P (from) != FLOAT_MODE_P (to)) | |
0d803030 | 1517 | return false; |
a01c666c | 1518 | |
0d803030 | 1519 | return !reg_classes_intersect_p (FPU_REGS, rclass); |
a01c666c PK |
1520 | } |
1521 | ||
7717110a PK |
1522 | /* Implement TARGET_CXX_GUARD_TYPE */ |
1523 | static tree | |
1524 | pdp11_guard_type (void) | |
1525 | { | |
1526 | return short_integer_type_node; | |
1527 | } | |
1528 | ||
a01c666c PK |
1529 | /* TARGET_PREFERRED_RELOAD_CLASS |
1530 | ||
1531 | Given an rtx X being reloaded into a reg required to be | |
1532 | in class CLASS, return the class of reg to actually use. | |
1533 | In general this is just CLASS; but on some machines | |
1534 | in some cases it is preferable to use a more restrictive class. | |
1535 | ||
1536 | loading is easier into LOAD_FPU_REGS than FPU_REGS! */ | |
1537 | ||
1538 | static reg_class_t | |
f89a0507 | 1539 | pdp11_preferred_reload_class (rtx x, reg_class_t rclass) |
a01c666c | 1540 | { |
f89a0507 | 1541 | if (rclass == FPU_REGS) |
a01c666c | 1542 | return LOAD_FPU_REGS; |
f89a0507 | 1543 | if (rclass == ALL_REGS) |
a01c666c PK |
1544 | { |
1545 | if (FLOAT_MODE_P (GET_MODE (x))) | |
1546 | return LOAD_FPU_REGS; | |
1547 | else | |
1548 | return GENERAL_REGS; | |
1549 | } | |
f89a0507 | 1550 | return rclass; |
a01c666c PK |
1551 | } |
1552 | ||
1553 | /* TARGET_PREFERRED_OUTPUT_RELOAD_CLASS | |
1554 | ||
1555 | Given an rtx X being reloaded into a reg required to be | |
1556 | in class CLASS, return the class of reg to actually use. | |
1557 | In general this is just CLASS; but on some machines | |
1558 | in some cases it is preferable to use a more restrictive class. | |
1559 | ||
1560 | loading is easier into LOAD_FPU_REGS than FPU_REGS! */ | |
1561 | ||
1562 | static reg_class_t | |
f89a0507 | 1563 | pdp11_preferred_output_reload_class (rtx x, reg_class_t rclass) |
a01c666c | 1564 | { |
f89a0507 | 1565 | if (rclass == FPU_REGS) |
a01c666c | 1566 | return LOAD_FPU_REGS; |
f89a0507 | 1567 | if (rclass == ALL_REGS) |
a01c666c PK |
1568 | { |
1569 | if (FLOAT_MODE_P (GET_MODE (x))) | |
1570 | return LOAD_FPU_REGS; | |
1571 | else | |
1572 | return GENERAL_REGS; | |
1573 | } | |
f89a0507 | 1574 | return rclass; |
a01c666c PK |
1575 | } |
1576 | ||
1577 | ||
1578 | /* TARGET_SECONDARY_RELOAD. | |
1579 | ||
1580 | FPU registers AC4 and AC5 (class NO_LOAD_FPU_REGS) require an | |
1581 | intermediate register (AC0-AC3: LOAD_FPU_REGS). Everything else | |
b4324a14 | 1582 | can be loaded/stored directly. */ |
f89a0507 | 1583 | static reg_class_t |
a01c666c PK |
1584 | pdp11_secondary_reload (bool in_p ATTRIBUTE_UNUSED, |
1585 | rtx x, | |
1586 | reg_class_t reload_class, | |
ef4bddc2 | 1587 | machine_mode reload_mode ATTRIBUTE_UNUSED, |
a01c666c PK |
1588 | secondary_reload_info *sri ATTRIBUTE_UNUSED) |
1589 | { | |
1590 | if (reload_class != NO_LOAD_FPU_REGS || GET_CODE (x) != REG || | |
1591 | REGNO_REG_CLASS (REGNO (x)) == LOAD_FPU_REGS) | |
1592 | return NO_REGS; | |
1593 | ||
1594 | return LOAD_FPU_REGS; | |
1595 | } | |
1596 | ||
f15643d4 | 1597 | /* Implement TARGET_SECONDARY_MEMORY_NEEDED. |
a01c666c | 1598 | |
b4324a14 PK |
1599 | The answer is yes if we're going between general register and FPU |
1600 | registers. The mode doesn't matter in making this check. */ | |
f15643d4 RS |
1601 | static bool |
1602 | pdp11_secondary_memory_needed (machine_mode, reg_class_t c1, reg_class_t c2) | |
a01c666c PK |
1603 | { |
1604 | int fromfloat = (c1 == LOAD_FPU_REGS || c1 == NO_LOAD_FPU_REGS || | |
1605 | c1 == FPU_REGS); | |
1606 | int tofloat = (c2 == LOAD_FPU_REGS || c2 == NO_LOAD_FPU_REGS || | |
1607 | c2 == FPU_REGS); | |
1608 | ||
1609 | return (fromfloat != tofloat); | |
1610 | } | |
1611 | ||
e4942929 PK |
1612 | /* TARGET_LEGITIMATE_ADDRESS_P recognizes an RTL expression |
1613 | that is a valid memory address for an instruction. | |
1614 | The MODE argument is the machine mode for the MEM expression | |
1615 | that wants to use this address. | |
1616 | ||
1617 | */ | |
1618 | ||
1619 | static bool | |
ef4bddc2 | 1620 | pdp11_legitimate_address_p (machine_mode mode, |
e4942929 PK |
1621 | rtx operand, bool strict) |
1622 | { | |
1623 | rtx xfoob; | |
1624 | ||
1625 | /* accept @#address */ | |
1626 | if (CONSTANT_ADDRESS_P (operand)) | |
1627 | return true; | |
1628 | ||
1629 | switch (GET_CODE (operand)) | |
1630 | { | |
1631 | case REG: | |
1632 | /* accept (R0) */ | |
1633 | return !strict || REGNO_OK_FOR_BASE_P (REGNO (operand)); | |
1634 | ||
1635 | case PLUS: | |
1636 | /* accept X(R0) */ | |
1637 | return GET_CODE (XEXP (operand, 0)) == REG | |
1638 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0)))) | |
1639 | && CONSTANT_ADDRESS_P (XEXP (operand, 1)); | |
1640 | ||
1641 | case PRE_DEC: | |
1642 | /* accept -(R0) */ | |
1643 | return GET_CODE (XEXP (operand, 0)) == REG | |
1644 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0)))); | |
1645 | ||
1646 | case POST_INC: | |
1647 | /* accept (R0)+ */ | |
1648 | return GET_CODE (XEXP (operand, 0)) == REG | |
1649 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0)))); | |
1650 | ||
1651 | case PRE_MODIFY: | |
1652 | /* accept -(SP) -- which uses PRE_MODIFY for byte mode */ | |
1653 | return GET_CODE (XEXP (operand, 0)) == REG | |
1654 | && REGNO (XEXP (operand, 0)) == STACK_POINTER_REGNUM | |
1655 | && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS | |
1656 | && GET_CODE (XEXP (xfoob, 0)) == REG | |
1657 | && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM | |
442fcea7 | 1658 | && CONST_INT_P (XEXP (xfoob, 1)) |
e4942929 PK |
1659 | && INTVAL (XEXP (xfoob,1)) == -2; |
1660 | ||
1661 | case POST_MODIFY: | |
1662 | /* accept (SP)+ -- which uses POST_MODIFY for byte mode */ | |
1663 | return GET_CODE (XEXP (operand, 0)) == REG | |
1664 | && REGNO (XEXP (operand, 0)) == STACK_POINTER_REGNUM | |
1665 | && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS | |
1666 | && GET_CODE (XEXP (xfoob, 0)) == REG | |
1667 | && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM | |
442fcea7 | 1668 | && CONST_INT_P (XEXP (xfoob, 1)) |
e4942929 PK |
1669 | && INTVAL (XEXP (xfoob,1)) == 2; |
1670 | ||
1671 | case MEM: | |
1672 | /* handle another level of indirection ! */ | |
1673 | xfoob = XEXP (operand, 0); | |
1674 | ||
1675 | /* (MEM:xx (MEM:xx ())) is not valid for SI, DI and currently | |
1676 | also forbidden for float, because we have to handle this | |
1677 | in output_move_double and/or output_move_quad() - we could | |
1678 | do it, but currently it's not worth it!!! | |
1679 | now that DFmode cannot go into CPU register file, | |
1680 | maybe I should allow float ... | |
1681 | but then I have to handle memory-to-memory moves in movdf ?? */ | |
1682 | if (GET_MODE_BITSIZE(mode) > 16) | |
1683 | return false; | |
1684 | ||
1685 | /* accept @address */ | |
1686 | if (CONSTANT_ADDRESS_P (xfoob)) | |
1687 | return true; | |
1688 | ||
1689 | switch (GET_CODE (xfoob)) | |
1690 | { | |
1691 | case REG: | |
1692 | /* accept @(R0) - which is @0(R0) */ | |
1693 | return !strict || REGNO_OK_FOR_BASE_P(REGNO (xfoob)); | |
1694 | ||
1695 | case PLUS: | |
1696 | /* accept @X(R0) */ | |
1697 | return GET_CODE (XEXP (xfoob, 0)) == REG | |
1698 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0)))) | |
1699 | && CONSTANT_ADDRESS_P (XEXP (xfoob, 1)); | |
1700 | ||
1701 | case PRE_DEC: | |
1702 | /* accept @-(R0) */ | |
1703 | return GET_CODE (XEXP (xfoob, 0)) == REG | |
1704 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0)))); | |
1705 | ||
1706 | case POST_INC: | |
1707 | /* accept @(R0)+ */ | |
1708 | return GET_CODE (XEXP (xfoob, 0)) == REG | |
1709 | && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0)))); | |
1710 | ||
1711 | default: | |
1712 | /* anything else is invalid */ | |
1713 | return false; | |
1714 | } | |
1715 | ||
1716 | default: | |
1717 | /* anything else is invalid */ | |
1718 | return false; | |
1719 | } | |
1720 | } | |
30442c59 | 1721 | |
58dd8e86 PK |
1722 | /* Return the class number of the smallest class containing |
1723 | reg number REGNO. */ | |
1724 | enum reg_class | |
1725 | pdp11_regno_reg_class (int regno) | |
1726 | { | |
442fcea7 PK |
1727 | if (regno == ARG_POINTER_REGNUM) |
1728 | return NOTSP_REG; | |
b4324a14 PK |
1729 | else if (regno == CC_REGNUM || regno == FCC_REGNUM) |
1730 | return CC_REGS; | |
58dd8e86 PK |
1731 | else if (regno > AC3_REGNUM) |
1732 | return NO_LOAD_FPU_REGS; | |
1733 | else if (regno >= AC0_REGNUM) | |
1734 | return LOAD_FPU_REGS; | |
442fcea7 PK |
1735 | else if (regno == 6) |
1736 | return NOTR0_REG; | |
1737 | else if (regno < 6) | |
1738 | return NOTSP_REG; | |
58dd8e86 PK |
1739 | else |
1740 | return GENERAL_REGS; | |
1741 | } | |
1742 | ||
b4324a14 PK |
1743 | /* Return the regnums of the CC registers. */ |
1744 | bool | |
1745 | pdp11_fixed_cc_regs (unsigned int *p1, unsigned int *p2) | |
1746 | { | |
1747 | *p1 = CC_REGNUM; | |
1748 | *p2 = FCC_REGNUM; | |
1749 | return true; | |
1750 | } | |
58dd8e86 | 1751 | |
442fcea7 PK |
1752 | static int |
1753 | pdp11_reg_save_size (void) | |
58dd8e86 PK |
1754 | { |
1755 | int offset = 0, regno; | |
442fcea7 | 1756 | |
58dd8e86 | 1757 | for (regno = 0; regno <= PC_REGNUM; regno++) |
8662b2ba | 1758 | if (pdp11_saved_regno (regno)) |
58dd8e86 PK |
1759 | offset += 2; |
1760 | for (regno = AC0_REGNUM; regno <= AC5_REGNUM; regno++) | |
8662b2ba | 1761 | if (pdp11_saved_regno (regno)) |
58dd8e86 PK |
1762 | offset += 8; |
1763 | ||
1764 | return offset; | |
1765 | } | |
1766 | ||
1767 | /* Return the offset between two registers, one to be eliminated, and the other | |
1768 | its replacement, at the start of a routine. */ | |
1769 | ||
1770 | int | |
1771 | pdp11_initial_elimination_offset (int from, int to) | |
1772 | { | |
442fcea7 | 1773 | /* Get the size of the register save area. */ |
58dd8e86 PK |
1774 | int spoff; |
1775 | ||
442fcea7 PK |
1776 | if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
1777 | return get_frame_size (); | |
1778 | else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) | |
1779 | return pdp11_reg_save_size () + 2; | |
1780 | else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) | |
1781 | return pdp11_reg_save_size () + 2 + get_frame_size (); | |
58dd8e86 | 1782 | else |
442fcea7 PK |
1783 | gcc_assert (0); |
1784 | } | |
a01c666c | 1785 | |
af36a4d2 JM |
1786 | /* A copy of output_addr_const modified for pdp11 expression syntax. |
1787 | output_addr_const also gets called for %cDIGIT and %nDIGIT, which we don't | |
1788 | use, and for debugging output, which we don't support with this port either. | |
1789 | So this copy should get called whenever needed. | |
1790 | */ | |
1791 | void | |
f12b3fc8 | 1792 | output_addr_const_pdp11 (FILE *file, rtx x) |
af36a4d2 JM |
1793 | { |
1794 | char buf[256]; | |
6b208988 PK |
1795 | int i; |
1796 | ||
af36a4d2 JM |
1797 | restart: |
1798 | switch (GET_CODE (x)) | |
1799 | { | |
1800 | case PC: | |
d35d9223 NS |
1801 | gcc_assert (flag_pic); |
1802 | putc ('.', file); | |
af36a4d2 JM |
1803 | break; |
1804 | ||
1805 | case SYMBOL_REF: | |
1806 | assemble_name (file, XSTR (x, 0)); | |
1807 | break; | |
1808 | ||
1809 | case LABEL_REF: | |
1810 | ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); | |
1811 | assemble_name (file, buf); | |
1812 | break; | |
1813 | ||
1814 | case CODE_LABEL: | |
1815 | ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); | |
1816 | assemble_name (file, buf); | |
1817 | break; | |
1818 | ||
1819 | case CONST_INT: | |
6b208988 PK |
1820 | i = INTVAL (x); |
1821 | if (i < 0) | |
1822 | { | |
1823 | i = -i; | |
1824 | fprintf (file, "-"); | |
1825 | } | |
4aef57c9 PK |
1826 | if (TARGET_DEC_ASM) |
1827 | fprintf (file, "%o", i & 0xffff); | |
1828 | else | |
1829 | fprintf (file, "%#o", i & 0xffff); | |
af36a4d2 JM |
1830 | break; |
1831 | ||
1832 | case CONST: | |
af36a4d2 JM |
1833 | output_addr_const_pdp11 (file, XEXP (x, 0)); |
1834 | break; | |
1835 | ||
af36a4d2 | 1836 | case PLUS: |
112cdef5 | 1837 | /* Some assemblers need integer constants to appear last (e.g. masm). */ |
af36a4d2 JM |
1838 | if (GET_CODE (XEXP (x, 0)) == CONST_INT) |
1839 | { | |
1840 | output_addr_const_pdp11 (file, XEXP (x, 1)); | |
1841 | if (INTVAL (XEXP (x, 0)) >= 0) | |
1842 | fprintf (file, "+"); | |
1843 | output_addr_const_pdp11 (file, XEXP (x, 0)); | |
1844 | } | |
1845 | else | |
1846 | { | |
1847 | output_addr_const_pdp11 (file, XEXP (x, 0)); | |
1848 | if (INTVAL (XEXP (x, 1)) >= 0) | |
1849 | fprintf (file, "+"); | |
1850 | output_addr_const_pdp11 (file, XEXP (x, 1)); | |
1851 | } | |
1852 | break; | |
1853 | ||
1854 | case MINUS: | |
1855 | /* Avoid outputting things like x-x or x+5-x, | |
1856 | since some assemblers can't handle that. */ | |
1857 | x = simplify_subtraction (x); | |
1858 | if (GET_CODE (x) != MINUS) | |
1859 | goto restart; | |
1860 | ||
1861 | output_addr_const_pdp11 (file, XEXP (x, 0)); | |
6b208988 PK |
1862 | if (GET_CODE (XEXP (x, 1)) != CONST_INT |
1863 | || INTVAL (XEXP (x, 1)) >= 0) | |
1864 | fprintf (file, "-"); | |
1865 | output_addr_const_pdp11 (file, XEXP (x, 1)); | |
af36a4d2 JM |
1866 | break; |
1867 | ||
1868 | case ZERO_EXTEND: | |
1869 | case SIGN_EXTEND: | |
1870 | output_addr_const_pdp11 (file, XEXP (x, 0)); | |
1871 | break; | |
1872 | ||
1873 | default: | |
1874 | output_operand_lossage ("invalid expression as operand"); | |
1875 | } | |
1876 | } | |
04e9daaf | 1877 | |
bd5bd7ac KH |
1878 | /* Worker function for TARGET_RETURN_IN_MEMORY. */ |
1879 | ||
04e9daaf | 1880 | static bool |
586de218 | 1881 | pdp11_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) |
04e9daaf | 1882 | { |
c6d3802a PK |
1883 | /* Integers 32 bits and under, and scalar floats (if FPU), are returned |
1884 | in registers. The rest go into memory. */ | |
04e9daaf | 1885 | return (TYPE_MODE (type) == DImode |
c6d3802a PK |
1886 | || (FLOAT_MODE_P (TYPE_MODE (type)) && ! TARGET_AC0) |
1887 | || TREE_CODE (type) == VECTOR_TYPE | |
1888 | || COMPLEX_MODE_P (TYPE_MODE (type))); | |
04e9daaf | 1889 | } |
8dd65c37 | 1890 | |
c547eb0d AS |
1891 | /* Worker function for TARGET_FUNCTION_VALUE. |
1892 | ||
1893 | On the pdp11 the value is found in R0 (or ac0??? not without FPU!!!! ) */ | |
1894 | ||
1895 | static rtx | |
1896 | pdp11_function_value (const_tree valtype, | |
1897 | const_tree fntype_or_decl ATTRIBUTE_UNUSED, | |
1898 | bool outgoing ATTRIBUTE_UNUSED) | |
1899 | { | |
1900 | return gen_rtx_REG (TYPE_MODE (valtype), | |
1901 | BASE_RETURN_VALUE_REG(TYPE_MODE(valtype))); | |
1902 | } | |
1903 | ||
1904 | /* Worker function for TARGET_LIBCALL_VALUE. */ | |
1905 | ||
1906 | static rtx | |
ef4bddc2 | 1907 | pdp11_libcall_value (machine_mode mode, |
c547eb0d AS |
1908 | const_rtx fun ATTRIBUTE_UNUSED) |
1909 | { | |
1910 | return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG(mode)); | |
1911 | } | |
1912 | ||
1913 | /* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. | |
1914 | ||
1915 | On the pdp, the first "output" reg is the only register thus used. | |
1916 | ||
1917 | maybe ac0 ? - as option someday! */ | |
1918 | ||
1919 | static bool | |
1920 | pdp11_function_value_regno_p (const unsigned int regno) | |
1921 | { | |
e4942929 | 1922 | return (regno == RETVAL_REGNUM) || (TARGET_AC0 && (regno == AC0_REGNUM)); |
c547eb0d AS |
1923 | } |
1924 | ||
b4324a14 PK |
1925 | /* Used for O constraint, matches if shift count is "small". */ |
1926 | bool | |
1927 | pdp11_small_shift (int n) | |
1928 | { | |
1929 | return (unsigned) n < 4; | |
1930 | } | |
1931 | ||
1932 | /* Expand a shift insn. Returns true if the expansion was done, | |
1933 | false if it needs to be handled by the caller. */ | |
1934 | bool | |
1935 | pdp11_expand_shift (rtx *operands, rtx (*shift_sc) (rtx, rtx, rtx), | |
1936 | rtx (*shift_base) (rtx, rtx, rtx)) | |
1937 | { | |
4aef57c9 PK |
1938 | rtx r, test; |
1939 | rtx_code_label *lb; | |
b4324a14 | 1940 | |
442fcea7 | 1941 | if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]))) |
b4324a14 PK |
1942 | emit_insn ((*shift_sc) (operands[0], operands[1], operands[2])); |
1943 | else if (TARGET_40_PLUS) | |
1944 | return false; | |
1945 | else | |
1946 | { | |
1947 | lb = gen_label_rtx (); | |
1948 | r = gen_reg_rtx (HImode); | |
1949 | emit_move_insn (operands[0], operands[1]); | |
1950 | emit_move_insn (r, operands[2]); | |
442fcea7 | 1951 | if (!CONST_INT_P (operands[2])) |
b4324a14 PK |
1952 | { |
1953 | test = gen_rtx_LE (HImode, r, const0_rtx); | |
1954 | emit_jump_insn (gen_cbranchhi4 (test, r, const0_rtx, lb)); | |
1955 | } | |
1956 | /* It would be nice to expand the loop here, but that's not | |
1957 | possible because shifts may be generated by the loop unroll | |
1958 | optimizer and it doesn't appreciate flow changes happening | |
1959 | while it's doing things. */ | |
1960 | emit_insn ((*shift_base) (operands[0], operands[1], r)); | |
442fcea7 | 1961 | if (!CONST_INT_P (operands[2])) |
b4324a14 PK |
1962 | { |
1963 | emit_label (lb); | |
1964 | ||
1965 | /* Allow REG_NOTES to be set on last insn (labels don't have enough | |
1966 | fields, and can't be used for REG_NOTES anyway). */ | |
1967 | emit_use (stack_pointer_rtx); | |
1968 | } | |
1969 | } | |
1970 | return true; | |
1971 | } | |
1972 | ||
1973 | /* Emit the instructions needed to produce a shift by a small constant | |
1974 | amount (unrolled), or a shift made from a loop for the base machine | |
1975 | case. */ | |
1976 | const char * | |
1977 | pdp11_assemble_shift (rtx *operands, machine_mode m, int code) | |
1978 | { | |
1979 | int i, n; | |
442fcea7 PK |
1980 | rtx inops[2]; |
1981 | rtx exops[2][2]; | |
b4324a14 PK |
1982 | rtx lb[1]; |
1983 | pdp11_action action[2]; | |
442fcea7 | 1984 | const bool small = CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])); |
b4324a14 PK |
1985 | |
1986 | gcc_assert (small || !TARGET_40_PLUS); | |
1987 | ||
1988 | if (m == E_SImode) | |
442fcea7 PK |
1989 | { |
1990 | inops[0] = operands[0]; | |
1991 | pdp11_expand_operands (inops, exops, 1, 2, action, either); | |
1992 | } | |
1993 | ||
b4324a14 PK |
1994 | if (!small) |
1995 | { | |
1996 | /* Loop case, generate the top of loop label. */ | |
1997 | lb[0] = gen_label_rtx (); | |
1998 | output_asm_label (lb[0]); | |
1999 | fputs (":\n", asm_out_file); | |
2000 | n = 1; | |
2001 | } | |
2002 | else | |
2003 | n = INTVAL (operands[2]); | |
2004 | if (code == LSHIFTRT) | |
2005 | { | |
2006 | output_asm_insn ("clc", NULL); | |
2007 | switch (m) | |
2008 | { | |
2009 | case E_QImode: | |
4aef57c9 | 2010 | output_asm_insn ("rorb\t%0", operands); |
b4324a14 PK |
2011 | break; |
2012 | case E_HImode: | |
4aef57c9 | 2013 | output_asm_insn ("ror\t%0", operands); |
b4324a14 PK |
2014 | break; |
2015 | case E_SImode: | |
4aef57c9 PK |
2016 | output_asm_insn ("ror\t%0", exops[0]); |
2017 | output_asm_insn ("ror\t%0", exops[1]); | |
b4324a14 PK |
2018 | break; |
2019 | default: | |
2020 | gcc_unreachable (); | |
2021 | } | |
2022 | n--; | |
2023 | } | |
2024 | for (i = 0; i < n; i++) | |
2025 | { | |
2026 | switch (code) | |
2027 | { | |
2028 | case LSHIFTRT: | |
2029 | case ASHIFTRT: | |
2030 | switch (m) | |
2031 | { | |
2032 | case E_QImode: | |
4aef57c9 | 2033 | output_asm_insn ("asrb\t%0", operands); |
b4324a14 PK |
2034 | break; |
2035 | case E_HImode: | |
4aef57c9 | 2036 | output_asm_insn ("asr\t%0", operands); |
b4324a14 PK |
2037 | break; |
2038 | case E_SImode: | |
4aef57c9 PK |
2039 | output_asm_insn ("asr\t%0", exops[0]); |
2040 | output_asm_insn ("ror\t%0", exops[1]); | |
b4324a14 PK |
2041 | break; |
2042 | default: | |
2043 | gcc_unreachable (); | |
2044 | } | |
2045 | break; | |
2046 | case ASHIFT: | |
2047 | switch (m) | |
2048 | { | |
2049 | case E_QImode: | |
4aef57c9 | 2050 | output_asm_insn ("aslb\t%0", operands); |
b4324a14 PK |
2051 | break; |
2052 | case E_HImode: | |
4aef57c9 | 2053 | output_asm_insn ("asl\t%0", operands); |
b4324a14 PK |
2054 | break; |
2055 | case E_SImode: | |
4aef57c9 PK |
2056 | output_asm_insn ("asl\t%0", exops[1]); |
2057 | output_asm_insn ("rol\t%0", exops[0]); | |
b4324a14 PK |
2058 | break; |
2059 | default: | |
2060 | gcc_unreachable (); | |
2061 | } | |
2062 | break; | |
2063 | } | |
2064 | } | |
2065 | if (!small) | |
2066 | { | |
2067 | /* Loop case, emit the count-down and branch if not done. */ | |
4aef57c9 PK |
2068 | output_asm_insn ("dec\t%2", operands); |
2069 | output_asm_insn ("bne\t%l0", lb); | |
b4324a14 PK |
2070 | } |
2071 | return ""; | |
2072 | } | |
2073 | ||
9cd1665b PK |
2074 | /* Figure out the length of the instructions that will be produced for |
2075 | the given operands by pdp11_assemble_shift above. */ | |
2076 | int | |
2077 | pdp11_shift_length (rtx *operands, machine_mode m, int code, bool simple_operand_p) | |
2078 | { | |
2079 | int shift_size; | |
2080 | ||
2081 | /* Shift by 1 is 2 bytes if simple operand, 4 bytes if 2-word addressing mode. */ | |
2082 | shift_size = simple_operand_p ? 2 : 4; | |
2083 | ||
2084 | /* In SImode, two shifts are needed per data item. */ | |
2085 | if (m == E_SImode) | |
2086 | shift_size *= 2; | |
2087 | ||
2088 | /* If shifting by a small constant, the loop is unrolled by the | |
2089 | shift count. Otherwise, account for the size of the decrement | |
2090 | and branch. */ | |
442fcea7 | 2091 | if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]))) |
9cd1665b PK |
2092 | shift_size *= INTVAL (operands[2]); |
2093 | else | |
2094 | shift_size += 4; | |
2095 | ||
2096 | /* Logical right shift takes one more instruction (CLC). */ | |
2097 | if (code == LSHIFTRT) | |
2098 | shift_size += 2; | |
2099 | ||
2100 | return shift_size; | |
2101 | } | |
2102 | ||
442fcea7 PK |
2103 | /* Return the length of 2 or 4 word integer compares. */ |
2104 | int | |
2105 | pdp11_cmp_length (rtx *operands, int words) | |
2106 | { | |
2107 | rtx inops[2]; | |
2108 | rtx exops[4][2]; | |
2109 | rtx lb[1]; | |
2110 | int i, len = 0; | |
2111 | ||
2112 | if (!reload_completed) | |
2113 | return 2; | |
2114 | ||
2115 | inops[0] = operands[0]; | |
2116 | inops[1] = operands[1]; | |
2117 | ||
2118 | pdp11_expand_operands (inops, exops, 2, words, NULL, big); | |
2119 | ||
2120 | for (i = 0; i < words; i++) | |
2121 | { | |
2122 | len += 4; /* cmp instruction word and branch that follows. */ | |
2123 | if (!REG_P (exops[i][0]) && | |
2124 | !simple_memory_operand (exops[i][0], HImode)) | |
2125 | len += 2; /* first operand extra word. */ | |
2126 | if (!REG_P (exops[i][1]) && | |
2127 | !simple_memory_operand (exops[i][1], HImode) && | |
2128 | !(CONST_INT_P (exops[i][1]) && INTVAL (exops[i][1]) == 0)) | |
2129 | len += 2; /* second operand extra word. */ | |
2130 | } | |
2131 | ||
2132 | /* Deduct one word because there is no branch at the end. */ | |
2133 | return len - 2; | |
2134 | } | |
2135 | ||
aad2444d PK |
2136 | /* Prepend to CLOBBERS hard registers that are automatically clobbered |
2137 | for an asm We do this for CC_REGNUM and FCC_REGNUM (on FPU target) | |
2138 | to maintain source compatibility with the original cc0-based | |
2139 | compiler. */ | |
2140 | ||
2141 | static rtx_insn * | |
2142 | pdp11_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/, | |
2143 | vec<const char *> &/*constraints*/, | |
2144 | vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs) | |
2145 | { | |
2146 | clobbers.safe_push (gen_rtx_REG (CCmode, CC_REGNUM)); | |
2147 | SET_HARD_REG_BIT (clobbered_regs, CC_REGNUM); | |
2148 | if (TARGET_FPU) | |
2149 | { | |
2150 | clobbers.safe_push (gen_rtx_REG (CCmode, FCC_REGNUM)); | |
2151 | SET_HARD_REG_BIT (clobbered_regs, FCC_REGNUM); | |
2152 | } | |
2153 | return NULL; | |
2154 | } | |
2155 | ||
8dd65c37 RH |
2156 | /* Worker function for TARGET_TRAMPOLINE_INIT. |
2157 | ||
2158 | trampoline - how should i do it in separate i+d ? | |
2159 | have some allocate_trampoline magic??? | |
2160 | ||
2161 | the following should work for shared I/D: | |
2162 | ||
ed8eb563 PK |
2163 | MOV #STATIC, $4 01270Y 0x0000 <- STATIC; Y = STATIC_CHAIN_REGNUM |
2164 | JMP @#FUNCTION 000137 0x0000 <- FUNCTION | |
8dd65c37 | 2165 | */ |
8dd65c37 RH |
2166 | static void |
2167 | pdp11_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) | |
2168 | { | |
2169 | rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); | |
2170 | rtx mem; | |
2171 | ||
2172 | gcc_assert (!TARGET_SPLIT); | |
2173 | ||
2174 | mem = adjust_address (m_tramp, HImode, 0); | |
ed8eb563 | 2175 | emit_move_insn (mem, GEN_INT (012700+STATIC_CHAIN_REGNUM)); |
8dd65c37 RH |
2176 | mem = adjust_address (m_tramp, HImode, 2); |
2177 | emit_move_insn (mem, chain_value); | |
2178 | mem = adjust_address (m_tramp, HImode, 4); | |
ed8eb563 | 2179 | emit_move_insn (mem, GEN_INT (000137)); |
8dd65c37 RH |
2180 | emit_move_insn (mem, fnaddr); |
2181 | } | |
a5f4f531 NF |
2182 | |
2183 | /* Worker function for TARGET_FUNCTION_ARG. | |
2184 | ||
2185 | Determine where to put an argument to a function. | |
2186 | Value is zero to push the argument on the stack, | |
2187 | or a hard register in which to store the argument. | |
2188 | ||
2189 | MODE is the argument's machine mode. | |
2190 | TYPE is the data type of the argument (as a tree). | |
2191 | This is null for libcalls where that information may | |
2192 | not be available. | |
2193 | CUM is a variable of type CUMULATIVE_ARGS which gives info about | |
2194 | the preceding args and about the function being called. | |
2195 | NAMED is nonzero if this argument is a named parameter | |
2196 | (otherwise it is an extra parameter matching an ellipsis). */ | |
2197 | ||
2198 | static rtx | |
d5cc9181 | 2199 | pdp11_function_arg (cumulative_args_t cum ATTRIBUTE_UNUSED, |
ef4bddc2 | 2200 | machine_mode mode ATTRIBUTE_UNUSED, |
a5f4f531 NF |
2201 | const_tree type ATTRIBUTE_UNUSED, |
2202 | bool named ATTRIBUTE_UNUSED) | |
2203 | { | |
2204 | return NULL_RTX; | |
2205 | } | |
2206 | ||
2207 | /* Worker function for TARGET_FUNCTION_ARG_ADVANCE. | |
2208 | ||
2209 | Update the data in CUM to advance over an argument of mode MODE and | |
2210 | data type TYPE. (TYPE is null for libcalls where that information | |
2211 | may not be available.) */ | |
2212 | ||
2213 | static void | |
ef4bddc2 | 2214 | pdp11_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, |
a5f4f531 NF |
2215 | const_tree type, bool named ATTRIBUTE_UNUSED) |
2216 | { | |
d5cc9181 JR |
2217 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
2218 | ||
a5f4f531 NF |
2219 | *cum += (mode != BLKmode |
2220 | ? GET_MODE_SIZE (mode) | |
2221 | : int_size_in_bytes (type)); | |
2222 | } | |
a01c666c | 2223 | |
5efd84c5 NF |
2224 | /* Make sure everything's fine if we *don't* have an FPU. |
2225 | This assumes that putting a register in fixed_regs will keep the | |
2226 | compiler's mitts completely off it. We don't bother to zero it out | |
2227 | of register classes. Also fix incompatible register naming with | |
2228 | the UNIX assembler. */ | |
2229 | ||
2230 | static void | |
2231 | pdp11_conditional_register_usage (void) | |
2232 | { | |
2233 | int i; | |
2234 | HARD_REG_SET x; | |
2235 | if (!TARGET_FPU) | |
2236 | { | |
2237 | COPY_HARD_REG_SET (x, reg_class_contents[(int)FPU_REGS]); | |
2238 | for (i = 0; i < FIRST_PSEUDO_REGISTER; i++ ) | |
2239 | if (TEST_HARD_REG_BIT (x, i)) | |
2240 | fixed_regs[i] = call_used_regs[i] = 1; | |
2241 | } | |
2242 | ||
2243 | if (TARGET_AC0) | |
2244 | call_used_regs[AC0_REGNUM] = 1; | |
2245 | if (TARGET_UNIX_ASM) | |
2246 | { | |
2247 | /* Change names of FPU registers for the UNIX assembler. */ | |
2248 | reg_names[8] = "fr0"; | |
2249 | reg_names[9] = "fr1"; | |
2250 | reg_names[10] = "fr2"; | |
2251 | reg_names[11] = "fr3"; | |
2252 | reg_names[12] = "fr4"; | |
2253 | reg_names[13] = "fr5"; | |
2254 | } | |
2255 | } | |
2256 | ||
baddb677 PK |
2257 | static section * |
2258 | pdp11_function_section (tree decl ATTRIBUTE_UNUSED, | |
2259 | enum node_frequency freq ATTRIBUTE_UNUSED, | |
2260 | bool startup ATTRIBUTE_UNUSED, | |
2261 | bool exit ATTRIBUTE_UNUSED) | |
2262 | { | |
2263 | return NULL; | |
2264 | } | |
2265 | ||
4aef57c9 PK |
2266 | /* Support #ident for DEC assembler, but don't process the |
2267 | auto-generated ident string that names the compiler (since its | |
2268 | syntax is not correct for DEC .ident). */ | |
2269 | static void pdp11_output_ident (const char *ident) | |
2270 | { | |
2271 | if (TARGET_DEC_ASM) | |
2272 | { | |
2273 | if (strncmp (ident, "GCC:", 4) != 0) | |
2274 | fprintf (asm_out_file, "\t.ident\t\"%s\"\n", ident); | |
2275 | } | |
2276 | ||
2277 | } | |
2278 | ||
2279 | /* This emits a (user) label, which gets a "_" prefix except for DEC | |
2280 | assembler output. */ | |
2281 | void | |
2282 | pdp11_output_labelref (FILE *file, const char *name) | |
2283 | { | |
2284 | if (!TARGET_DEC_ASM) | |
2285 | fputs (USER_LABEL_PREFIX, file); | |
2286 | fputs (name, file); | |
2287 | } | |
2288 | ||
2289 | /* This equates name with value. */ | |
2290 | void | |
2291 | pdp11_output_def (FILE *file, const char *label1, const char *label2) | |
2292 | { | |
2293 | if (TARGET_DEC_ASM) | |
2294 | { | |
2295 | assemble_name (file, label1); | |
2296 | putc ('=', file); | |
2297 | assemble_name (file, label2); | |
2298 | } | |
2299 | else | |
2300 | { | |
f3475629 | 2301 | fputs ("\t.set\t", file); |
4aef57c9 PK |
2302 | assemble_name (file, label1); |
2303 | putc (',', file); | |
2304 | assemble_name (file, label2); | |
2305 | } | |
2306 | putc ('\n', file); | |
2307 | } | |
2308 | ||
2309 | void | |
2310 | pdp11_output_addr_vec_elt (FILE *file, int value) | |
2311 | { | |
2312 | char buf[256]; | |
2313 | ||
2314 | pdp11_gen_int_label (buf, "L", value); | |
2315 | if (!TARGET_UNIX_ASM) | |
2316 | fprintf (file, "\t.word"); | |
2317 | fprintf (file, "\t%s\n", buf + 1); | |
2318 | } | |
2319 | ||
2320 | /* This overrides some target hooks that are initializer elements so | |
2321 | they can't be variables in the #define. */ | |
2322 | static void | |
2323 | pdp11_option_override (void) | |
2324 | { | |
2325 | if (TARGET_DEC_ASM) | |
2326 | { | |
2327 | targetm.asm_out.open_paren = "<"; | |
2328 | targetm.asm_out.close_paren = ">"; | |
2329 | } | |
2330 | } | |
2331 | ||
2332 | static void | |
2333 | pdp11_asm_named_section (const char *name, unsigned int flags, | |
2334 | tree decl ATTRIBUTE_UNUSED) | |
2335 | { | |
2336 | const char *rwro = (flags & SECTION_WRITE) ? "rw" : "ro"; | |
2337 | const char *insdat = (flags & SECTION_CODE) ? "i" : "d"; | |
2338 | ||
2339 | gcc_assert (TARGET_DEC_ASM); | |
2340 | fprintf (asm_out_file, "\t.psect\t%s,con,%s,%s\n", name, insdat, rwro); | |
2341 | } | |
2342 | ||
2343 | static void | |
2344 | pdp11_asm_init_sections (void) | |
2345 | { | |
2346 | if (TARGET_DEC_ASM) | |
2347 | { | |
2348 | bss_section = data_section; | |
2349 | } | |
2350 | else if (TARGET_GNU_ASM) | |
2351 | { | |
2352 | bss_section = get_unnamed_section (SECTION_WRITE | SECTION_BSS, | |
2353 | output_section_asm_op, | |
2354 | ".bss"); | |
2355 | } | |
2356 | } | |
2357 | ||
2358 | static void | |
2359 | pdp11_file_start (void) | |
2360 | { | |
2361 | default_file_start (); | |
2362 | ||
2363 | if (TARGET_DEC_ASM) | |
2364 | fprintf (asm_out_file, "\t.enabl\tlsb,reg\n\n"); | |
2365 | } | |
2366 | ||
2367 | static void | |
2368 | pdp11_file_end (void) | |
2369 | { | |
2370 | if (TARGET_DEC_ASM) | |
2371 | fprintf (asm_out_file, "\t.end\n"); | |
2372 | } | |
2373 | ||
1a627b35 RS |
2374 | /* Implement TARGET_LEGITIMATE_CONSTANT_P. */ |
2375 | ||
2376 | static bool | |
ef4bddc2 | 2377 | pdp11_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x) |
1a627b35 RS |
2378 | { |
2379 | return GET_CODE (x) != CONST_DOUBLE || legitimate_const_double_p (x); | |
2380 | } | |
2381 | ||
8cc4b7a2 JM |
2382 | /* Implement TARGET_SCALAR_MODE_SUPPORTED_P. */ |
2383 | ||
2384 | static bool | |
18e2a8b8 | 2385 | pdp11_scalar_mode_supported_p (scalar_mode mode) |
8cc4b7a2 JM |
2386 | { |
2387 | /* Support SFmode even with -mfloat64. */ | |
2388 | if (mode == SFmode) | |
2389 | return true; | |
2390 | return default_scalar_mode_supported_p (mode); | |
2391 | } | |
2392 | ||
c43f4279 RS |
2393 | /* Implement TARGET_HARD_REGNO_NREGS. */ |
2394 | ||
2395 | static unsigned int | |
2396 | pdp11_hard_regno_nregs (unsigned int regno, machine_mode mode) | |
2397 | { | |
2398 | if (regno <= PC_REGNUM) | |
2399 | return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD); | |
2400 | return 1; | |
2401 | } | |
2402 | ||
f939c3e6 RS |
2403 | /* Implement TARGET_HARD_REGNO_MODE_OK. On the pdp, the cpu registers |
2404 | can hold any mode other than float (because otherwise we may end up | |
2405 | being asked to move from CPU to FPU register, which isn't a valid | |
2406 | operation on the PDP11). For CPU registers, check alignment. | |
2407 | ||
2408 | FPU accepts SF and DF but actually holds a DF - simplifies life! */ | |
2409 | ||
2410 | static bool | |
2411 | pdp11_hard_regno_mode_ok (unsigned int regno, machine_mode mode) | |
2412 | { | |
2413 | if (regno <= PC_REGNUM) | |
2414 | return (GET_MODE_BITSIZE (mode) <= 16 | |
2415 | || (GET_MODE_BITSIZE (mode) >= 32 | |
2416 | && !(regno & 1) | |
2417 | && !FLOAT_MODE_P (mode))); | |
2418 | ||
2419 | return FLOAT_MODE_P (mode); | |
2420 | } | |
2421 | ||
99e1629f RS |
2422 | /* Implement TARGET_MODES_TIEABLE_P. */ |
2423 | ||
2424 | static bool | |
b4324a14 | 2425 | pdp11_modes_tieable_p (machine_mode mode1, machine_mode mode2) |
99e1629f | 2426 | { |
b4324a14 | 2427 | return mode1 == HImode && mode2 == QImode; |
99e1629f RS |
2428 | } |
2429 | ||
7b4df2bf RS |
2430 | /* Implement PUSH_ROUNDING. On the pdp11, the stack is on an even |
2431 | boundary. */ | |
2432 | ||
2433 | poly_int64 | |
2434 | pdp11_push_rounding (poly_int64 bytes) | |
2435 | { | |
2436 | return (bytes + 1) & ~1; | |
2437 | } | |
2438 | ||
a01c666c | 2439 | struct gcc_target targetm = TARGET_INITIALIZER; |