gcc/
[official-gcc.git] / gcc / config / avr / avr-dimode.md
blob31527dbf0412bcfcdf935bc867249ccd8353557c
1 ;;   Machine description for GNU compiler,
2 ;;   for Atmel AVR micro controllers.
3 ;;   Copyright (C) 1998 - 2011
4 ;;   Free Software Foundation, Inc.
5 ;;   Contributed by Georg Lay (avr@gjlay.de)
6 ;;
7 ;; This file is part of GCC.
8 ;;
9 ;; GCC is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
14 ;; GCC is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GCC; see the file COPYING3.  If not see
21 ;; <http://www.gnu.org/licenses/>.
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25 ;; The purpose of this file is to provide a light-weight DImode
26 ;; implementation for AVR.  The trouble with DImode is that tree -> RTL
27 ;; lowering leads to really unpleasant code for operations that don't
28 ;; work byte-wise like NEG, PLUS, MINUS, etc.  Defining optabs entries for
29 ;; them won't help because the optab machinery assumes these operations
30 ;; are cheap and does not check if a libgcc implementation is available.
32 ;; The DImode insns are all straight forward -- except movdi.  The approach
33 ;; of this implementation is to provide DImode insns without the burden of
34 ;; introducing movdi.
35 ;; 
36 ;; The caveat is that if there are insns for some mode, there must also be a
37 ;; respective move insn that describes reloads.  Therefore, this
38 ;; implementation uses an accumulator-based model with two hard-coded,
39 ;; accumulator-like registers
41 ;;    A[] = reg:DI 18
42 ;;    B[] = reg:DI 10
44 ;; so that no DImode insn contains pseudos or needs reloading.
46 (define_constants
47   [(ACC_A       18)
48    (ACC_B       10)])
50 ;; Supported modes that are 8 bytes wide
51 (define_mode_iterator ALL8 [DI DQ UDQ DA UDA TA UTA])
53 (define_mode_iterator ALL8U [UDQ UDA UTA])
54 (define_mode_iterator ALL8S [ DQ  DA  TA])
56 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
57 ;; Addition
58 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60 ;; "adddi3"
61 ;; "adddq3" "addudq3"
62 ;; "addda3" "adduda3"
63 ;; "addta3" "adduta3"
64 (define_expand "add<mode>3"
65   [(parallel [(match_operand:ALL8 0 "general_operand" "")
66               (match_operand:ALL8 1 "general_operand" "")
67               (match_operand:ALL8 2 "general_operand" "")])]
68   "avr_have_dimode"
69   {
70     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
72     emit_move_insn (acc_a, operands[1]);
74     if (DImode == <MODE>mode
75         && s8_operand (operands[2], VOIDmode))
76       {
77         emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]);
78         emit_insn (gen_adddi3_const8_insn ());
79       }        
80     else if (const_operand (operands[2], GET_MODE (operands[2])))
81       {
82         emit_insn (gen_add<mode>3_const_insn (operands[2]));
83       }
84     else
85       {
86         emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
87         emit_insn (gen_add<mode>3_insn ());
88       }
90     emit_move_insn (operands[0], acc_a);
91     DONE;
92   })
94 ;; "adddi3_insn"
95 ;; "adddq3_insn" "addudq3_insn"
96 ;; "addda3_insn" "adduda3_insn"
97 ;; "addta3_insn" "adduta3_insn"
98 (define_insn "add<mode>3_insn"
99   [(set (reg:ALL8 ACC_A)
100         (plus:ALL8 (reg:ALL8 ACC_A)
101                    (reg:ALL8 ACC_B)))]
102   "avr_have_dimode"
103   "%~call __adddi3"
104   [(set_attr "adjust_len" "call")
105    (set_attr "cc" "clobber")])
107 (define_insn "adddi3_const8_insn"
108   [(set (reg:DI ACC_A)
109         (plus:DI (reg:DI ACC_A)
110                  (sign_extend:DI (reg:QI REG_X))))]
111   "avr_have_dimode"
112   "%~call __adddi3_s8"
113   [(set_attr "adjust_len" "call")
114    (set_attr "cc" "clobber")])
116 ;; "adddi3_const_insn"
117 ;; "adddq3_const_insn" "addudq3_const_insn"
118 ;; "addda3_const_insn" "adduda3_const_insn"
119 ;; "addta3_const_insn" "adduta3_const_insn"
120 (define_insn "add<mode>3_const_insn"
121   [(set (reg:ALL8 ACC_A)
122         (plus:ALL8 (reg:ALL8 ACC_A)
123                    (match_operand:ALL8 0 "const_operand" "n Ynn")))]
124   "avr_have_dimode
125    && !s8_operand (operands[0], VOIDmode)"
126   {
127     return avr_out_plus (insn, operands);
128   }
129   [(set_attr "adjust_len" "plus")
130    (set_attr "cc" "clobber")])
133 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
134 ;; Subtraction
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
137 ;; "subdi3"
138 ;; "subdq3" "subudq3"
139 ;; "subda3" "subuda3"
140 ;; "subta3" "subuta3"
141 (define_expand "sub<mode>3"
142   [(parallel [(match_operand:ALL8 0 "general_operand" "")
143               (match_operand:ALL8 1 "general_operand" "")
144               (match_operand:ALL8 2 "general_operand" "")])]
145   "avr_have_dimode"
146   {
147     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
149     emit_move_insn (acc_a, operands[1]);
151     if (const_operand (operands[2], GET_MODE (operands[2])))
152       {
153         emit_insn (gen_sub<mode>3_const_insn (operands[2]));
154       }
155     else
156      {
157        emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
158        emit_insn (gen_sub<mode>3_insn ());
159      }
161     emit_move_insn (operands[0], acc_a);
162     DONE;
163   })
165 ;; "subdi3_insn"
166 ;; "subdq3_insn" "subudq3_insn"
167 ;; "subda3_insn" "subuda3_insn"
168 ;; "subta3_insn" "subuta3_insn"
169 (define_insn "sub<mode>3_insn"
170   [(set (reg:ALL8 ACC_A)
171         (minus:ALL8 (reg:ALL8 ACC_A)
172                     (reg:ALL8 ACC_B)))]
173   "avr_have_dimode"
174   "%~call __subdi3"
175   [(set_attr "adjust_len" "call")
176    (set_attr "cc" "set_czn")])
178 ;; "subdi3_const_insn"
179 ;; "subdq3_const_insn" "subudq3_const_insn"
180 ;; "subda3_const_insn" "subuda3_const_insn"
181 ;; "subta3_const_insn" "subuta3_const_insn"
182 (define_insn "sub<mode>3_const_insn"
183   [(set (reg:ALL8 ACC_A)
184         (minus:ALL8 (reg:ALL8 ACC_A)
185                     (match_operand:ALL8 0 "const_operand" "n Ynn")))]
186   "avr_have_dimode"
187   {
188     return avr_out_plus (insn, operands);
189   }
190   [(set_attr "adjust_len" "plus")
191    (set_attr "cc" "clobber")])
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 ;; Signed Saturating Addition and Subtraction
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
197 (define_expand "<code_stdname><mode>3"
198   [(set (match_operand:ALL8S 0 "general_operand" "")
199         (ss_addsub:ALL8S (match_operand:ALL8S 1 "general_operand" "")
200                          (match_operand:ALL8S 2 "general_operand" "")))]
201   "avr_have_dimode"
202   {
203     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
205     emit_move_insn (acc_a, operands[1]);
207     if (const_operand (operands[2], GET_MODE (operands[2])))
208       {
209         emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2]));
210       }
211     else
212       {
213         emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
214         emit_insn (gen_<code_stdname><mode>3_insn ());
215       }
217     emit_move_insn (operands[0], acc_a);
218     DONE;
219   })
221 (define_insn "<code_stdname><mode>3_insn"
222   [(set (reg:ALL8S ACC_A)
223         (ss_addsub:ALL8S (reg:ALL8S ACC_A)
224                          (reg:ALL8S ACC_B)))]
225   "avr_have_dimode"
226   "%~call __<code_stdname><mode>3"
227   [(set_attr "adjust_len" "call")
228    (set_attr "cc" "clobber")])
230 (define_insn "<code_stdname><mode>3_const_insn"
231   [(set (reg:ALL8S ACC_A)
232         (ss_addsub:ALL8S (reg:ALL8S ACC_A)
233                          (match_operand:ALL8S 0 "const_operand" "n Ynn")))]
234   "avr_have_dimode"
235   {
236     return avr_out_plus (insn, operands);
237   }
238   [(set_attr "adjust_len" "plus")
239    (set_attr "cc" "clobber")])
241 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
242 ;; Unsigned Saturating Addition and Subtraction
243 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
245 (define_expand "<code_stdname><mode>3"
246   [(set (match_operand:ALL8U 0 "general_operand" "")
247         (us_addsub:ALL8U (match_operand:ALL8U 1 "general_operand" "")
248                          (match_operand:ALL8U 2 "general_operand" "")))]
249   "avr_have_dimode"
250   {
251     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
253     emit_move_insn (acc_a, operands[1]);
255     if (const_operand (operands[2], GET_MODE (operands[2])))
256       {
257         emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2]));
258       }
259     else
260       {
261         emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
262         emit_insn (gen_<code_stdname><mode>3_insn ());
263       }
265     emit_move_insn (operands[0], acc_a);
266     DONE;
267   })
269 (define_insn "<code_stdname><mode>3_insn"
270   [(set (reg:ALL8U ACC_A)
271         (us_addsub:ALL8U (reg:ALL8U ACC_A)
272                          (reg:ALL8U ACC_B)))]
273   "avr_have_dimode"
274   "%~call __<code_stdname><mode>3"
275   [(set_attr "adjust_len" "call")
276    (set_attr "cc" "clobber")])
278 (define_insn "<code_stdname><mode>3_const_insn"
279   [(set (reg:ALL8U ACC_A)
280         (us_addsub:ALL8U (reg:ALL8U ACC_A)
281                          (match_operand:ALL8U 0 "const_operand" "n Ynn")))]
282   "avr_have_dimode"
283   {
284     return avr_out_plus (insn, operands);
285   }
286   [(set_attr "adjust_len" "plus")
287    (set_attr "cc" "clobber")])
289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
290 ;; Negation
291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
293 (define_expand "negdi2"
294   [(parallel [(match_operand:DI 0 "general_operand" "")
295               (match_operand:DI 1 "general_operand" "")])]
296   "avr_have_dimode"
297   {
298     rtx acc_a = gen_rtx_REG (DImode, ACC_A);
300     emit_move_insn (acc_a, operands[1]);
301     emit_insn (gen_negdi2_insn ());
302     emit_move_insn (operands[0], acc_a);
303     DONE;
304   })
306 (define_insn "negdi2_insn"
307   [(set (reg:DI ACC_A)
308         (neg:DI (reg:DI ACC_A)))]
309   "avr_have_dimode"
310   "%~call __negdi2"
311   [(set_attr "adjust_len" "call")
312    (set_attr "cc" "clobber")])
315 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
316 ;; Comparison
317 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
319 (define_expand "conditional_jump"
320   [(set (pc)
321         (if_then_else
322          (match_operator 0 "ordered_comparison_operator" [(cc0)
323                                                           (const_int 0)])
324          (label_ref (match_operand 1 "" ""))
325          (pc)))]
326   "avr_have_dimode")
328 ;; "cbranchdi4"
329 ;; "cbranchdq4" "cbranchudq4"
330 ;; "cbranchda4" "cbranchuda4"
331 ;; "cbranchta4" "cbranchuta4"
332 (define_expand "cbranch<mode>4"
333   [(parallel [(match_operand:ALL8 1 "register_operand" "")
334               (match_operand:ALL8 2 "nonmemory_operand" "")
335               (match_operator 0 "ordered_comparison_operator" [(cc0)
336                                                                (const_int 0)])
337               (label_ref (match_operand 3 "" ""))])]
338   "avr_have_dimode"
339   {
340     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
342     emit_move_insn (acc_a, operands[1]);
344     if (s8_operand (operands[2], VOIDmode))
345       {
346         emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]);
347         emit_insn (gen_compare_const8_di2 ());
348       }        
349     else if (const_operand (operands[2], GET_MODE (operands[2])))
350       {
351         emit_insn (gen_compare_const_<mode>2 (operands[2]));
352       }
353     else
354       {
355         emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
356         emit_insn (gen_compare_<mode>2 ());
357       }
359     emit_jump_insn (gen_conditional_jump (operands[0], operands[3]));
360     DONE;
361   })
363 ;; "compare_di2"
364 ;; "compare_dq2" "compare_udq2"
365 ;; "compare_da2" "compare_uda2"
366 ;; "compare_ta2" "compare_uta2"
367 (define_insn "compare_<mode>2"
368   [(set (cc0)
369         (compare (reg:ALL8 ACC_A)
370                  (reg:ALL8 ACC_B)))]
371   "avr_have_dimode"
372   "%~call __cmpdi2"
373   [(set_attr "adjust_len" "call")
374    (set_attr "cc" "compare")])
376 (define_insn "compare_const8_di2"
377   [(set (cc0)
378         (compare (reg:DI ACC_A)
379                  (sign_extend:DI (reg:QI REG_X))))]
380   "avr_have_dimode"
381   "%~call __cmpdi2_s8"
382   [(set_attr "adjust_len" "call")
383    (set_attr "cc" "compare")])
385 ;; "compare_const_di2"
386 ;; "compare_const_dq2" "compare_const_udq2"
387 ;; "compare_const_da2" "compare_const_uda2"
388 ;; "compare_const_ta2" "compare_const_uta2"
389 (define_insn "compare_const_<mode>2"
390   [(set (cc0)
391         (compare (reg:ALL8 ACC_A)
392                  (match_operand:ALL8 0 "const_operand" "n Ynn")))
393    (clobber (match_scratch:QI 1 "=&d"))]
394   "avr_have_dimode
395    && !s8_operand (operands[0], VOIDmode)"
396   {
397     return avr_out_compare64 (insn, operands, NULL);
398   }
399   [(set_attr "adjust_len" "compare64")
400    (set_attr "cc" "compare")])
403 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
404 ;; Shifts and Rotate
405 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
407 (define_code_iterator di_shifts
408   [ashift ashiftrt lshiftrt rotate])
410 ;; Shift functions from libgcc are called without defining these insns,
411 ;; but with them we can describe their reduced register footprint.
413 ;; "ashldi3"   "ashrdi3"   "lshrdi3"   "rotldi3"
414 ;; "ashldq3"   "ashrdq3"   "lshrdq3"   "rotldq3"
415 ;; "ashlda3"   "ashrda3"   "lshrda3"   "rotlda3"
416 ;; "ashlta3"   "ashrta3"   "lshrta3"   "rotlta3"
417 ;; "ashludq3"  "ashrudq3"  "lshrudq3"  "rotludq3"
418 ;; "ashluda3"  "ashruda3"  "lshruda3"  "rotluda3"
419 ;; "ashluta3"  "ashruta3"  "lshruta3"  "rotluta3"
420 (define_expand "<code_stdname><mode>3"
421   [(parallel [(match_operand:ALL8 0 "general_operand" "")
422               (di_shifts:ALL8 (match_operand:ALL8 1 "general_operand" "")
423                               (match_operand:QI 2 "general_operand" ""))])]
424   "avr_have_dimode"
425   {
426     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
428     emit_move_insn (acc_a, operands[1]);
429     emit_move_insn (gen_rtx_REG (QImode, 16), operands[2]);
430     emit_insn (gen_<code_stdname><mode>3_insn ());
431     emit_move_insn (operands[0], acc_a);
432     DONE;
433   })
435 ;; "ashldi3_insn"   "ashrdi3_insn"   "lshrdi3_insn"   "rotldi3_insn"
436 ;; "ashldq3_insn"   "ashrdq3_insn"   "lshrdq3_insn"   "rotldq3_insn"
437 ;; "ashlda3_insn"   "ashrda3_insn"   "lshrda3_insn"   "rotlda3_insn"
438 ;; "ashlta3_insn"   "ashrta3_insn"   "lshrta3_insn"   "rotlta3_insn"
439 ;; "ashludq3_insn"  "ashrudq3_insn"  "lshrudq3_insn"  "rotludq3_insn"
440 ;; "ashluda3_insn"  "ashruda3_insn"  "lshruda3_insn"  "rotluda3_insn"
441 ;; "ashluta3_insn"  "ashruta3_insn"  "lshruta3_insn"  "rotluta3_insn"
442 (define_insn "<code_stdname><mode>3_insn"
443   [(set (reg:ALL8 ACC_A)
444         (di_shifts:ALL8 (reg:ALL8 ACC_A)
445                         (reg:QI 16)))]
446   "avr_have_dimode"
447   "%~call __<code_stdname>di3"
448   [(set_attr "adjust_len" "call")
449    (set_attr "cc" "clobber")])