xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/config/avr/avr-fixed.md (revision aef5eb5f59cdfe8314f1b5f78ac04eb144e44010)
1;;   This file contains instructions that support fixed-point operations
2;;   for Atmel AVR micro controllers.
3;;   Copyright (C) 2012-2019 Free Software Foundation, Inc.
4;;
5;;   Contributed by Sean D'Epagnier  (sean@depagnier.com)
6;;                  Georg-Johann Lay (avr@gjlay.de)
7
8;; This file is part of GCC.
9;;
10;; GCC is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 3, or (at your option)
13;; any later version.
14;;
15;; GCC is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18;; GNU General Public License for more details.
19;;
20;; You should have received a copy of the GNU General Public License
21;; along with GCC; see the file COPYING3.  If not see
22;; <http://www.gnu.org/licenses/>.
23
24(define_mode_iterator ALL1Q  [QQ UQQ])
25(define_mode_iterator ALL2Q  [HQ UHQ])
26(define_mode_iterator ALL2A  [HA UHA])
27(define_mode_iterator ALL4A  [SA USA])
28(define_mode_iterator ALL2QA [HQ UHQ HA UHA])
29(define_mode_iterator ALL4QA [SQ USQ SA USA])
30(define_mode_iterator ALL124QA [ QQ   HQ  HA  SA  SQ
31                                UQQ  UHQ UHA USA USQ])
32
33(define_mode_iterator ALL2S [HQ HA])
34(define_mode_iterator ALL4S [SA SQ])
35(define_mode_iterator ALL24S  [     HQ  HA  SA  SQ])
36(define_mode_iterator ALL124S [ QQ  HQ  HA  SA  SQ])
37(define_mode_iterator ALL124U [UQQ UHQ UHA USA USQ])
38
39;;; Conversions
40
41(define_mode_iterator FIXED_A
42  [QQ UQQ
43   HQ UHQ HA UHA
44   SQ USQ SA USA
45   DQ UDQ DA UDA
46   TA UTA
47   QI HI SI DI])
48
49;; Same so that be can build cross products
50
51(define_mode_iterator FIXED_B
52  [QQ UQQ
53   HQ UHQ HA UHA
54   SQ USQ SA USA
55   DQ UDQ DA UDA
56   TA UTA
57   QI HI SI DI])
58
59(define_insn "fract<FIXED_B:mode><FIXED_A:mode>2"
60  [(set (match_operand:FIXED_A 0 "register_operand" "=r")
61        (fract_convert:FIXED_A
62         (match_operand:FIXED_B 1 "register_operand" "r")))]
63  "<FIXED_B:MODE>mode != <FIXED_A:MODE>mode"
64  {
65    return avr_out_fract (insn, operands, true, NULL);
66  }
67  [(set_attr "cc" "clobber")
68   (set_attr "adjust_len" "sfract")])
69
70(define_insn "fractuns<FIXED_B:mode><FIXED_A:mode>2"
71  [(set (match_operand:FIXED_A 0 "register_operand" "=r")
72        (unsigned_fract_convert:FIXED_A
73         (match_operand:FIXED_B 1 "register_operand" "r")))]
74  "<FIXED_B:MODE>mode != <FIXED_A:MODE>mode"
75  {
76    return avr_out_fract (insn, operands, false, NULL);
77  }
78  [(set_attr "cc" "clobber")
79   (set_attr "adjust_len" "ufract")])
80
81;******************************************************************************
82;** Saturated Addition and Subtraction
83;******************************************************************************
84
85;; Fixme:  It would be nice if we could expand the 32-bit versions to a
86;;    transparent libgcc call if $2 is a REG.  Problem is that it is
87;;    not possible to describe that addition is commutative.
88;;    And defining register classes/constraintrs for the involved hard
89;;    registers and let IRA do the work, yields inacceptable bloated code.
90;;    Thus, we have to live with the up to 11 instructions that are output
91;;    for these 32-bit saturated operations.
92
93;; "ssaddqq3"  "ssaddhq3"  "ssaddha3"  "ssaddsq3"  "ssaddsa3"
94;; "sssubqq3"  "sssubhq3"  "sssubha3"  "sssubsq3"  "sssubsa3"
95(define_insn "<code_stdname><mode>3"
96  [(set (match_operand:ALL124S 0 "register_operand"                          "=??d,d")
97        (ss_addsub:ALL124S (match_operand:ALL124S 1 "register_operand" "<abelian>0,0")
98                           (match_operand:ALL124S 2 "nonmemory_operand"         "r,Ynn")))]
99  ""
100  {
101    return avr_out_plus (insn, operands);
102  }
103  [(set_attr "cc" "clobber")
104   (set_attr "adjust_len" "plus")])
105
106;; "usadduqq3"  "usadduhq3"  "usadduha3" "usaddusq3"  "usaddusa3"
107;; "ussubuqq3"  "ussubuhq3"  "ussubuha3" "ussubusq3"  "ussubusa3"
108(define_insn "<code_stdname><mode>3"
109  [(set (match_operand:ALL124U 0 "register_operand"                          "=??r,d")
110        (us_addsub:ALL124U (match_operand:ALL124U 1 "register_operand" "<abelian>0,0")
111                           (match_operand:ALL124U 2 "nonmemory_operand"         "r,Ynn")))]
112  ""
113  {
114    return avr_out_plus (insn, operands);
115  }
116  [(set_attr "cc" "clobber")
117   (set_attr "adjust_len" "plus")])
118
119;******************************************************************************
120;** Saturated Negation and Absolute Value
121;******************************************************************************
122
123;; Fixme: This will always result in 0.  Dunno why simplify-rtx.c says
124;;   "unknown" on how to optimize this.  libgcc call would be in order,
125;;   but the performance is *PLAIN* *HORROR* because the optimizers don't
126;;   manage to optimize out MEMCPY that's sprincled all over fixed-bit.c  */
127
128(define_expand "usneg<mode>2"
129  [(parallel [(match_operand:ALL124U 0 "register_operand" "")
130              (match_operand:ALL124U 1 "nonmemory_operand" "")])]
131  ""
132  {
133    emit_move_insn (operands[0], CONST0_RTX (<MODE>mode));
134    DONE;
135  })
136
137(define_insn "ssnegqq2"
138  [(set (match_operand:QQ 0 "register_operand"            "=r")
139        (ss_neg:QQ (match_operand:QQ 1 "register_operand"  "0")))]
140  ""
141  "neg %0\;brvc 0f\;dec %0\;0:"
142  [(set_attr "cc" "clobber")
143   (set_attr "length" "3")])
144
145(define_insn "ssabsqq2"
146  [(set (match_operand:QQ 0 "register_operand"            "=r")
147        (ss_abs:QQ (match_operand:QQ 1 "register_operand"  "0")))]
148  ""
149  "sbrc %0,7\;neg %0\;sbrc %0,7\;dec %0"
150  [(set_attr "cc" "clobber")
151   (set_attr "length" "4")])
152
153;; "ssneghq2"  "ssnegha2"  "ssnegsq2"  "ssnegsa2"
154;; "ssabshq2"  "ssabsha2"  "ssabssq2"  "ssabssa2"
155(define_expand "<code_stdname><mode>2"
156  [(set (match_dup 2)
157        (match_operand:ALL24S 1 "register_operand" ""))
158   (set (match_dup 2)
159        (ss_abs_neg:ALL24S (match_dup 2)))
160   (set (match_operand:ALL24S 0 "register_operand" "")
161        (match_dup 2))]
162  ""
163  {
164    operands[2] = gen_rtx_REG (<MODE>mode, 26 - GET_MODE_SIZE (<MODE>mode));
165  })
166
167;; "*ssneghq2"  "*ssnegha2"
168;; "*ssabshq2"  "*ssabsha2"
169(define_insn "*<code_stdname><mode>2"
170  [(set (reg:ALL2S 24)
171        (ss_abs_neg:ALL2S (reg:ALL2S 24)))]
172  ""
173  "%~call __<code_stdname>_2"
174  [(set_attr "type" "xcall")
175   (set_attr "cc" "clobber")])
176
177;; "*ssnegsq2"  "*ssnegsa2"
178;; "*ssabssq2"  "*ssabssa2"
179(define_insn "*<code_stdname><mode>2"
180  [(set (reg:ALL4S 22)
181        (ss_abs_neg:ALL4S (reg:ALL4S 22)))]
182  ""
183  "%~call __<code_stdname>_4"
184  [(set_attr "type" "xcall")
185   (set_attr "cc" "clobber")])
186
187;******************************************************************************
188; mul
189
190;; "mulqq3" "muluqq3"
191(define_expand "mul<mode>3"
192  [(parallel [(match_operand:ALL1Q 0 "register_operand" "")
193              (match_operand:ALL1Q 1 "register_operand" "")
194              (match_operand:ALL1Q 2 "register_operand" "")])]
195  ""
196  {
197    emit_insn (AVR_HAVE_MUL
198      ? gen_mul<mode>3_enh (operands[0], operands[1], operands[2])
199      : gen_mul<mode>3_nomul (operands[0], operands[1], operands[2]));
200    DONE;
201  })
202
203(define_insn "mulqq3_enh"
204  [(set (match_operand:QQ 0 "register_operand"         "=r")
205        (mult:QQ (match_operand:QQ 1 "register_operand" "a")
206                 (match_operand:QQ 2 "register_operand" "a")))]
207  "AVR_HAVE_MUL"
208  "fmuls %1,%2\;dec r1\;brvs 0f\;inc r1\;0:\;mov %0,r1\;clr __zero_reg__"
209  [(set_attr "length" "6")
210   (set_attr "cc" "clobber")])
211
212(define_insn "muluqq3_enh"
213  [(set (match_operand:UQQ 0 "register_operand"          "=r")
214        (mult:UQQ (match_operand:UQQ 1 "register_operand" "r")
215                  (match_operand:UQQ 2 "register_operand" "r")))]
216  "AVR_HAVE_MUL"
217  "mul %1,%2\;mov %0,r1\;clr __zero_reg__"
218  [(set_attr "length" "3")
219   (set_attr "cc" "clobber")])
220
221(define_expand "mulqq3_nomul"
222  [(set (reg:QQ 24)
223        (match_operand:QQ 1 "register_operand" ""))
224   (set (reg:QQ 25)
225        (match_operand:QQ 2 "register_operand" ""))
226   ;; "*mulqq3.call"
227   (parallel [(set (reg:QQ 23)
228                   (mult:QQ (reg:QQ 24)
229                            (reg:QQ 25)))
230              (clobber (reg:QI 22))
231              (clobber (reg:HI 24))])
232   (set (match_operand:QQ 0 "register_operand" "")
233        (reg:QQ 23))]
234  "!AVR_HAVE_MUL"
235  {
236    avr_fix_inputs (operands, 1 << 2, regmask (QQmode, 24));
237  })
238
239
240(define_expand "muluqq3_nomul"
241  [(set (reg:UQQ 22)
242        (match_operand:UQQ 1 "register_operand" ""))
243   (set (reg:UQQ 24)
244        (match_operand:UQQ 2 "register_operand" ""))
245   ;; "*umulqihi3.call"
246   (parallel [(set (reg:HI 24)
247                   (mult:HI (zero_extend:HI (reg:QI 22))
248                            (zero_extend:HI (reg:QI 24))))
249              (clobber (reg:QI 21))
250              (clobber (reg:HI 22))])
251   (set (match_operand:UQQ 0 "register_operand" "")
252        (reg:UQQ 25))]
253  "!AVR_HAVE_MUL"
254  {
255    avr_fix_inputs (operands, 1 << 2, regmask (UQQmode, 22));
256  })
257
258(define_insn "*mulqq3.call"
259  [(set (reg:QQ 23)
260        (mult:QQ (reg:QQ 24)
261                 (reg:QQ 25)))
262   (clobber (reg:QI 22))
263   (clobber (reg:HI 24))]
264  "!AVR_HAVE_MUL"
265  "%~call __mulqq3"
266  [(set_attr "type" "xcall")
267   (set_attr "cc" "clobber")])
268
269
270;; "mulhq3" "muluhq3"
271;; "mulha3" "muluha3"
272(define_expand "mul<mode>3"
273  [(set (reg:ALL2QA 18)
274        (match_operand:ALL2QA 1 "register_operand" ""))
275   (set (reg:ALL2QA 26)
276        (match_operand:ALL2QA 2 "register_operand" ""))
277   ;; "*mulhq3.call.enh"
278   (parallel [(set (reg:ALL2QA 24)
279                   (mult:ALL2QA (reg:ALL2QA 18)
280                                (reg:ALL2QA 26)))
281              (clobber (reg:HI 22))])
282   (set (match_operand:ALL2QA 0 "register_operand" "")
283        (reg:ALL2QA 24))]
284  "AVR_HAVE_MUL"
285  {
286    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 18));
287  })
288
289;; "*mulhq3.call"  "*muluhq3.call"
290;; "*mulha3.call"  "*muluha3.call"
291(define_insn "*mul<mode>3.call"
292  [(set (reg:ALL2QA 24)
293        (mult:ALL2QA (reg:ALL2QA 18)
294                     (reg:ALL2QA 26)))
295   (clobber (reg:HI 22))]
296  "AVR_HAVE_MUL"
297  "%~call __mul<mode>3"
298  [(set_attr "type" "xcall")
299   (set_attr "cc" "clobber")])
300
301
302;; On the enhanced core, don't clobber either input and use a separate output
303
304;; "mulsa3" "mulusa3"
305(define_expand "mul<mode>3"
306  [(set (reg:ALL4A 16)
307        (match_operand:ALL4A 1 "register_operand" ""))
308   (set (reg:ALL4A 20)
309        (match_operand:ALL4A 2 "register_operand" ""))
310   (set (reg:ALL4A 24)
311        (mult:ALL4A (reg:ALL4A 16)
312                    (reg:ALL4A 20)))
313   (set (match_operand:ALL4A 0 "register_operand" "")
314        (reg:ALL4A 24))]
315  "AVR_HAVE_MUL"
316  {
317    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 16));
318  })
319
320;; "*mulsa3.call" "*mulusa3.call"
321(define_insn "*mul<mode>3.call"
322  [(set (reg:ALL4A 24)
323        (mult:ALL4A (reg:ALL4A 16)
324                    (reg:ALL4A 20)))]
325  "AVR_HAVE_MUL"
326  "%~call __mul<mode>3"
327  [(set_attr "type" "xcall")
328   (set_attr "cc" "clobber")])
329
330; / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
331; div
332
333(define_code_iterator usdiv [udiv div])
334
335;; "divqq3" "udivuqq3"
336(define_expand "<code><mode>3"
337  [(set (reg:ALL1Q 25)
338        (match_operand:ALL1Q 1 "register_operand" ""))
339   (set (reg:ALL1Q 22)
340        (match_operand:ALL1Q 2 "register_operand" ""))
341   (parallel [(set (reg:ALL1Q 24)
342                   (usdiv:ALL1Q (reg:ALL1Q 25)
343                                (reg:ALL1Q 22)))
344              (clobber (reg:QI 25))])
345   (set (match_operand:ALL1Q 0 "register_operand" "")
346        (reg:ALL1Q 24))]
347  ""
348  {
349    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 25));
350  })
351
352
353;; "*divqq3.call" "*udivuqq3.call"
354(define_insn "*<code><mode>3.call"
355  [(set (reg:ALL1Q 24)
356        (usdiv:ALL1Q (reg:ALL1Q 25)
357                     (reg:ALL1Q 22)))
358   (clobber (reg:QI 25))]
359  ""
360  "%~call __<code><mode>3"
361  [(set_attr "type" "xcall")
362   (set_attr "cc" "clobber")])
363
364;; "divhq3" "udivuhq3"
365;; "divha3" "udivuha3"
366(define_expand "<code><mode>3"
367  [(set (reg:ALL2QA 26)
368        (match_operand:ALL2QA 1 "register_operand" ""))
369   (set (reg:ALL2QA 22)
370        (match_operand:ALL2QA 2 "register_operand" ""))
371   (parallel [(set (reg:ALL2QA 24)
372                   (usdiv:ALL2QA (reg:ALL2QA 26)
373                                 (reg:ALL2QA 22)))
374              (clobber (reg:HI 26))
375              (clobber (reg:QI 21))])
376   (set (match_operand:ALL2QA 0 "register_operand" "")
377        (reg:ALL2QA 24))]
378  ""
379  {
380    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 26));
381  })
382
383;; "*divhq3.call" "*udivuhq3.call"
384;; "*divha3.call" "*udivuha3.call"
385(define_insn "*<code><mode>3.call"
386  [(set (reg:ALL2QA 24)
387        (usdiv:ALL2QA (reg:ALL2QA 26)
388                      (reg:ALL2QA 22)))
389   (clobber (reg:HI 26))
390   (clobber (reg:QI 21))]
391  ""
392  "%~call __<code><mode>3"
393  [(set_attr "type" "xcall")
394   (set_attr "cc" "clobber")])
395
396;; Note the first parameter gets passed in already offset by 2 bytes
397
398;; "divsa3" "udivusa3"
399(define_expand "<code><mode>3"
400  [(set (reg:ALL4A 24)
401        (match_operand:ALL4A 1 "register_operand" ""))
402   (set (reg:ALL4A 18)
403        (match_operand:ALL4A 2 "register_operand" ""))
404   (parallel [(set (reg:ALL4A 22)
405                   (usdiv:ALL4A (reg:ALL4A 24)
406                                (reg:ALL4A 18)))
407              (clobber (reg:HI 26))
408              (clobber (reg:HI 30))])
409   (set (match_operand:ALL4A 0 "register_operand" "")
410        (reg:ALL4A 22))]
411  ""
412  {
413    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 24));
414  })
415
416;; "*divsa3.call" "*udivusa3.call"
417(define_insn "*<code><mode>3.call"
418  [(set (reg:ALL4A 22)
419        (usdiv:ALL4A (reg:ALL4A 24)
420                     (reg:ALL4A 18)))
421   (clobber (reg:HI 26))
422   (clobber (reg:HI 30))]
423  ""
424  "%~call __<code><mode>3"
425  [(set_attr "type" "xcall")
426   (set_attr "cc" "clobber")])
427
428
429;******************************************************************************
430;** Rounding
431;******************************************************************************
432
433;; "roundqq3"  "rounduqq3"
434;; "roundhq3"  "rounduhq3"  "roundha3"  "rounduha3"
435;; "roundsq3"  "roundusq3"  "roundsa3"  "roundusa3"
436(define_expand "round<mode>3"
437  [(set (match_dup 4)
438        (match_operand:ALL124QA 1 "register_operand" ""))
439   (set (reg:QI 24)
440        (match_dup 5))
441   (parallel [(set (match_dup 3)
442                   (unspec:ALL124QA [(match_dup 4)
443                                     (reg:QI 24)] UNSPEC_ROUND))
444              (clobber (match_dup 4))])
445   (set (match_operand:ALL124QA 0 "register_operand" "")
446        (match_dup 3))
447   (use (match_operand:HI 2 "nonmemory_operand" ""))]
448  ""
449  {
450    if (CONST_INT_P (operands[2])
451        && !(optimize_size
452             && 4 == GET_MODE_SIZE (<MODE>mode)))
453      {
454        emit_insn (gen_round<mode>3_const (operands[0], operands[1], operands[2]));
455        DONE;
456      }
457
458    // Input and output of the libgcc function
459    const unsigned int regno_in[]  = { -1U, 22, 22, -1U, 18 };
460    const unsigned int regno_out[] = { -1U, 24, 24, -1U, 22 };
461
462    operands[3] = gen_rtx_REG (<MODE>mode, regno_out[(size_t) GET_MODE_SIZE (<MODE>mode)]);
463    operands[4] = gen_rtx_REG (<MODE>mode,  regno_in[(size_t) GET_MODE_SIZE (<MODE>mode)]);
464    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, REGNO (operands[4])));
465    operands[5] = simplify_gen_subreg (QImode, force_reg (HImode, operands[2]), HImode, 0);
466    // $2 is no more needed, but is referenced for expand.
467    operands[2] = const0_rtx;
468  })
469
470;; Expand rounding with known rounding points inline so that the addend / mask
471;; will be consumed by operation with immediate operands and there is no
472;; need for a shift with variable offset.
473
474;; "roundqq3_const"  "rounduqq3_const"
475;; "roundhq3_const"  "rounduhq3_const"  "roundha3_const"  "rounduha3_const"
476;; "roundsq3_const"  "roundusq3_const"  "roundsa3_const"  "roundusa3_const"
477(define_insn "round<mode>3_const"
478  [(set (match_operand:ALL124QA 0 "register_operand"                  "=d")
479        (unspec:ALL124QA [(match_operand:ALL124QA 1 "register_operand" "0")
480                          (match_operand:HI 2 "const_int_operand"      "n")
481                          (const_int 0)]
482                         UNSPEC_ROUND))]
483  ""
484  {
485    return avr_out_round (insn, operands);
486  }
487  [(set_attr "cc" "clobber")
488   (set_attr "adjust_len" "round")])
489
490
491;; "*roundqq3.libgcc"  "*rounduqq3.libgcc"
492(define_insn "*round<mode>3.libgcc"
493  [(set (reg:ALL1Q 24)
494        (unspec:ALL1Q [(reg:ALL1Q 22)
495                       (reg:QI 24)] UNSPEC_ROUND))
496   (clobber (reg:ALL1Q 22))]
497  ""
498  "%~call __round<mode>3"
499  [(set_attr "type" "xcall")
500   (set_attr "cc" "clobber")])
501
502;; "*roundhq3.libgcc"  "*rounduhq3.libgcc"
503;; "*roundha3.libgcc"  "*rounduha3.libgcc"
504(define_insn "*round<mode>3.libgcc"
505  [(set (reg:ALL2QA 24)
506        (unspec:ALL2QA [(reg:ALL2QA 22)
507                        (reg:QI 24)] UNSPEC_ROUND))
508   (clobber (reg:ALL2QA 22))]
509  ""
510  "%~call __round<mode>3"
511  [(set_attr "type" "xcall")
512   (set_attr "cc" "clobber")])
513
514;; "*roundsq3.libgcc"  "*roundusq3.libgcc"
515;; "*roundsa3.libgcc"  "*roundusa3.libgcc"
516(define_insn "*round<mode>3.libgcc"
517  [(set (reg:ALL4QA 22)
518        (unspec:ALL4QA [(reg:ALL4QA 18)
519                        (reg:QI 24)] UNSPEC_ROUND))
520   (clobber (reg:ALL4QA 18))]
521  ""
522  "%~call __round<mode>3"
523  [(set_attr "type" "xcall")
524   (set_attr "cc" "clobber")])
525