]>
Commit | Line | Data |
---|---|---|
e55e4056 GJL |
1 | ;; This file contains instructions that support fixed-point operations |
2 | ;; for Atmel AVR micro controllers. | |
3 | ;; Copyright (C) 2012 | |
4 | ;; Free Software Foundation, Inc. | |
5 | ;; | |
6 | ;; Contributed by Sean D'Epagnier (sean@depagnier.com) | |
7 | ;; Georg-Johann Lay (avr@gjlay.de) | |
8 | ||
9 | ;; This file is part of GCC. | |
10 | ;; | |
11 | ;; GCC is free software; you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation; either version 3, or (at your option) | |
14 | ;; any later version. | |
15 | ;; | |
16 | ;; GCC is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ;; | |
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GCC; see the file COPYING3. If not see | |
23 | ;; <http://www.gnu.org/licenses/>. | |
24 | ||
25 | (define_mode_iterator ALL1Q [(QQ "") (UQQ "")]) | |
26 | (define_mode_iterator ALL2Q [(HQ "") (UHQ "")]) | |
27 | (define_mode_iterator ALL2A [(HA "") (UHA "")]) | |
28 | (define_mode_iterator ALL2QA [(HQ "") (UHQ "") | |
29 | (HA "") (UHA "")]) | |
30 | (define_mode_iterator ALL4A [(SA "") (USA "")]) | |
31 | ||
51526856 GJL |
32 | (define_mode_iterator ALL2S [HQ HA]) |
33 | (define_mode_iterator ALL4S [SA SQ]) | |
34 | (define_mode_iterator ALL24S [ HQ HA SA SQ]) | |
35 | (define_mode_iterator ALL124S [ QQ HQ HA SA SQ]) | |
36 | (define_mode_iterator ALL124U [UQQ UHQ UHA USA USQ]) | |
37 | ||
e55e4056 GJL |
38 | ;;; Conversions |
39 | ||
40 | (define_mode_iterator FIXED_A | |
41 | [(QQ "") (UQQ "") | |
42 | (HQ "") (UHQ "") (HA "") (UHA "") | |
43 | (SQ "") (USQ "") (SA "") (USA "") | |
44 | (DQ "") (UDQ "") (DA "") (UDA "") | |
45 | (TA "") (UTA "") | |
46 | (QI "") (HI "") (SI "") (DI "")]) | |
47 | ||
48 | ;; Same so that be can build cross products | |
49 | ||
50 | (define_mode_iterator FIXED_B | |
51 | [(QQ "") (UQQ "") | |
52 | (HQ "") (UHQ "") (HA "") (UHA "") | |
53 | (SQ "") (USQ "") (SA "") (USA "") | |
54 | (DQ "") (UDQ "") (DA "") (UDA "") | |
55 | (TA "") (UTA "") | |
56 | (QI "") (HI "") (SI "") (DI "")]) | |
57 | ||
58 | (define_insn "fract<FIXED_B:mode><FIXED_A:mode>2" | |
59 | [(set (match_operand:FIXED_A 0 "register_operand" "=r") | |
60 | (fract_convert:FIXED_A | |
61 | (match_operand:FIXED_B 1 "register_operand" "r")))] | |
62 | "<FIXED_B:MODE>mode != <FIXED_A:MODE>mode" | |
63 | { | |
64 | return avr_out_fract (insn, operands, true, NULL); | |
65 | } | |
66 | [(set_attr "cc" "clobber") | |
67 | (set_attr "adjust_len" "sfract")]) | |
68 | ||
69 | (define_insn "fractuns<FIXED_B:mode><FIXED_A:mode>2" | |
70 | [(set (match_operand:FIXED_A 0 "register_operand" "=r") | |
71 | (unsigned_fract_convert:FIXED_A | |
72 | (match_operand:FIXED_B 1 "register_operand" "r")))] | |
73 | "<FIXED_B:MODE>mode != <FIXED_A:MODE>mode" | |
74 | { | |
75 | return avr_out_fract (insn, operands, false, NULL); | |
76 | } | |
77 | [(set_attr "cc" "clobber") | |
78 | (set_attr "adjust_len" "ufract")]) | |
79 | ||
51526856 GJL |
80 | ;****************************************************************************** |
81 | ;** Saturated Addition and Subtraction | |
82 | ;****************************************************************************** | |
83 | ||
84 | ;; Fixme: It would be nice if we could expand the 32-bit versions to a | |
85 | ;; transparent libgcc call if $2 is a REG. Problem is that it is | |
86 | ;; not possible to describe that addition is commutative. | |
87 | ;; And defining register classes/constraintrs for the involved hard | |
88 | ;; registers and let IRA do the work, yields inacceptable bloated code. | |
89 | ;; Thus, we have to live with the up to 11 instructions that are output | |
90 | ;; for these 32-bit saturated operations. | |
91 | ||
92 | ;; "ssaddqq3" "ssaddhq3" "ssaddha3" "ssaddsq3" "ssaddsa3" | |
93 | ;; "sssubqq3" "sssubhq3" "sssubha3" "sssubsq3" "sssubsa3" | |
94 | (define_insn "<code_stdname><mode>3" | |
95 | [(set (match_operand:ALL124S 0 "register_operand" "=??d,d") | |
96 | (ss_addsub:ALL124S (match_operand:ALL124S 1 "register_operand" "<abelian>0,0") | |
97 | (match_operand:ALL124S 2 "nonmemory_operand" "r,Ynn")))] | |
98 | "" | |
99 | { | |
100 | return avr_out_plus (insn, operands); | |
101 | } | |
102 | [(set_attr "cc" "clobber") | |
103 | (set_attr "adjust_len" "plus")]) | |
104 | ||
105 | ;; "usadduqq3" "usadduhq3" "usadduha3" "usaddusq3" "usaddusa3" | |
106 | ;; "ussubuqq3" "ussubuhq3" "ussubuha3" "ussubusq3" "ussubusa3" | |
107 | (define_insn "<code_stdname><mode>3" | |
108 | [(set (match_operand:ALL124U 0 "register_operand" "=??r,d") | |
109 | (us_addsub:ALL124U (match_operand:ALL124U 1 "register_operand" "<abelian>0,0") | |
110 | (match_operand:ALL124U 2 "nonmemory_operand" "r,Ynn")))] | |
111 | "" | |
112 | { | |
113 | return avr_out_plus (insn, operands); | |
114 | } | |
115 | [(set_attr "cc" "clobber") | |
116 | (set_attr "adjust_len" "plus")]) | |
117 | ||
118 | ;****************************************************************************** | |
119 | ;** Saturated Negation and Absolute Value | |
120 | ;****************************************************************************** | |
121 | ||
122 | ;; Fixme: This will always result in 0. Dunno why simplify-rtx.c says | |
123 | ;; "unknown" on how to optimize this. libgcc call would be in order, | |
124 | ;; but the performance is *PLAIN* *HORROR* because the optimizers don't | |
125 | ;; manage to optimize out MEMCPY that's sprincled all over fixed-bit.c */ | |
126 | ||
127 | (define_expand "usneg<mode>2" | |
128 | [(parallel [(match_operand:ALL124U 0 "register_operand" "") | |
129 | (match_operand:ALL124U 1 "nonmemory_operand" "")])] | |
130 | "" | |
131 | { | |
132 | emit_move_insn (operands[0], CONST0_RTX (<MODE>mode)); | |
133 | DONE; | |
134 | }) | |
135 | ||
136 | (define_insn "ssnegqq2" | |
137 | [(set (match_operand:QQ 0 "register_operand" "=r") | |
138 | (ss_neg:QQ (match_operand:QQ 1 "register_operand" "0")))] | |
139 | "" | |
140 | "neg %0\;brvc 0f\;dec %0\;0:" | |
141 | [(set_attr "cc" "clobber") | |
142 | (set_attr "length" "3")]) | |
143 | ||
144 | (define_insn "ssabsqq2" | |
145 | [(set (match_operand:QQ 0 "register_operand" "=r") | |
146 | (ss_abs:QQ (match_operand:QQ 1 "register_operand" "0")))] | |
147 | "" | |
148 | "sbrc %0,7\;neg %0\;sbrc %0,7\;dec %0" | |
149 | [(set_attr "cc" "clobber") | |
150 | (set_attr "length" "4")]) | |
151 | ||
152 | ;; "ssneghq2" "ssnegha2" "ssnegsq2" "ssnegsa2" | |
153 | ;; "ssabshq2" "ssabsha2" "ssabssq2" "ssabssa2" | |
154 | (define_expand "<code_stdname><mode>2" | |
155 | [(set (match_dup 2) | |
156 | (match_operand:ALL24S 1 "register_operand" "")) | |
157 | (set (match_dup 2) | |
158 | (ss_abs_neg:ALL24S (match_dup 2))) | |
159 | (set (match_operand:ALL24S 0 "register_operand" "") | |
160 | (match_dup 2))] | |
161 | "" | |
162 | { | |
163 | operands[2] = gen_rtx_REG (<MODE>mode, 26 - GET_MODE_SIZE (<MODE>mode)); | |
164 | }) | |
165 | ||
166 | ;; "*ssneghq2" "*ssnegha2" | |
167 | ;; "*ssabshq2" "*ssabsha2" | |
168 | (define_insn "*<code_stdname><mode>2" | |
169 | [(set (reg:ALL2S 24) | |
170 | (ss_abs_neg:ALL2S (reg:ALL2S 24)))] | |
171 | "" | |
172 | "%~call __<code_stdname>_2" | |
173 | [(set_attr "type" "xcall") | |
174 | (set_attr "cc" "clobber")]) | |
175 | ||
176 | ;; "*ssnegsq2" "*ssnegsa2" | |
177 | ;; "*ssabssq2" "*ssabssa2" | |
178 | (define_insn "*<code_stdname><mode>2" | |
179 | [(set (reg:ALL4S 22) | |
180 | (ss_abs_neg:ALL4S (reg:ALL4S 22)))] | |
181 | "" | |
182 | "%~call __<code_stdname>_4" | |
183 | [(set_attr "type" "xcall") | |
184 | (set_attr "cc" "clobber")]) | |
185 | ||
e55e4056 GJL |
186 | ;****************************************************************************** |
187 | ; mul | |
188 | ||
189 | ;; "mulqq3" "muluqq3" | |
190 | (define_expand "mul<mode>3" | |
191 | [(parallel [(match_operand:ALL1Q 0 "register_operand" "") | |
192 | (match_operand:ALL1Q 1 "register_operand" "") | |
193 | (match_operand:ALL1Q 2 "register_operand" "")])] | |
194 | "" | |
195 | { | |
196 | emit_insn (AVR_HAVE_MUL | |
197 | ? gen_mul<mode>3_enh (operands[0], operands[1], operands[2]) | |
198 | : gen_mul<mode>3_nomul (operands[0], operands[1], operands[2])); | |
199 | DONE; | |
200 | }) | |
201 | ||
202 | (define_insn "mulqq3_enh" | |
203 | [(set (match_operand:QQ 0 "register_operand" "=r") | |
204 | (mult:QQ (match_operand:QQ 1 "register_operand" "a") | |
205 | (match_operand:QQ 2 "register_operand" "a")))] | |
206 | "AVR_HAVE_MUL" | |
207 | "fmuls %1,%2\;dec r1\;brvs 0f\;inc r1\;0:\;mov %0,r1\;clr __zero_reg__" | |
208 | [(set_attr "length" "6") | |
209 | (set_attr "cc" "clobber")]) | |
210 | ||
211 | (define_insn "muluqq3_enh" | |
212 | [(set (match_operand:UQQ 0 "register_operand" "=r") | |
213 | (mult:UQQ (match_operand:UQQ 1 "register_operand" "r") | |
214 | (match_operand:UQQ 2 "register_operand" "r")))] | |
215 | "AVR_HAVE_MUL" | |
216 | "mul %1,%2\;mov %0,r1\;clr __zero_reg__" | |
217 | [(set_attr "length" "3") | |
218 | (set_attr "cc" "clobber")]) | |
219 | ||
220 | (define_expand "mulqq3_nomul" | |
221 | [(set (reg:QQ 24) | |
222 | (match_operand:QQ 1 "register_operand" "")) | |
223 | (set (reg:QQ 25) | |
224 | (match_operand:QQ 2 "register_operand" "")) | |
225 | ;; "*mulqq3.call" | |
226 | (parallel [(set (reg:QQ 23) | |
227 | (mult:QQ (reg:QQ 24) | |
228 | (reg:QQ 25))) | |
229 | (clobber (reg:QI 22)) | |
230 | (clobber (reg:HI 24))]) | |
231 | (set (match_operand:QQ 0 "register_operand" "") | |
232 | (reg:QQ 23))] | |
233 | "!AVR_HAVE_MUL") | |
234 | ||
235 | (define_expand "muluqq3_nomul" | |
236 | [(set (reg:UQQ 22) | |
237 | (match_operand:UQQ 1 "register_operand" "")) | |
238 | (set (reg:UQQ 24) | |
239 | (match_operand:UQQ 2 "register_operand" "")) | |
240 | ;; "*umulqihi3.call" | |
241 | (parallel [(set (reg:HI 24) | |
242 | (mult:HI (zero_extend:HI (reg:QI 22)) | |
243 | (zero_extend:HI (reg:QI 24)))) | |
244 | (clobber (reg:QI 21)) | |
245 | (clobber (reg:HI 22))]) | |
246 | (set (match_operand:UQQ 0 "register_operand" "") | |
247 | (reg:UQQ 25))] | |
248 | "!AVR_HAVE_MUL") | |
249 | ||
250 | (define_insn "*mulqq3.call" | |
251 | [(set (reg:QQ 23) | |
252 | (mult:QQ (reg:QQ 24) | |
253 | (reg:QQ 25))) | |
254 | (clobber (reg:QI 22)) | |
255 | (clobber (reg:HI 24))] | |
256 | "!AVR_HAVE_MUL" | |
257 | "%~call __mulqq3" | |
258 | [(set_attr "type" "xcall") | |
259 | (set_attr "cc" "clobber")]) | |
260 | ||
261 | ||
262 | ;; "mulhq3" "muluhq3" | |
263 | ;; "mulha3" "muluha3" | |
264 | (define_expand "mul<mode>3" | |
265 | [(set (reg:ALL2QA 18) | |
266 | (match_operand:ALL2QA 1 "register_operand" "")) | |
267 | (set (reg:ALL2QA 26) | |
268 | (match_operand:ALL2QA 2 "register_operand" "")) | |
269 | ;; "*mulhq3.call.enh" | |
270 | (parallel [(set (reg:ALL2QA 24) | |
271 | (mult:ALL2QA (reg:ALL2QA 18) | |
272 | (reg:ALL2QA 26))) | |
273 | (clobber (reg:HI 22))]) | |
274 | (set (match_operand:ALL2QA 0 "register_operand" "") | |
275 | (reg:ALL2QA 24))] | |
276 | "AVR_HAVE_MUL") | |
277 | ||
278 | ;; "*mulhq3.call" "*muluhq3.call" | |
279 | ;; "*mulha3.call" "*muluha3.call" | |
280 | (define_insn "*mul<mode>3.call" | |
281 | [(set (reg:ALL2QA 24) | |
282 | (mult:ALL2QA (reg:ALL2QA 18) | |
283 | (reg:ALL2QA 26))) | |
284 | (clobber (reg:HI 22))] | |
285 | "AVR_HAVE_MUL" | |
286 | "%~call __mul<mode>3" | |
287 | [(set_attr "type" "xcall") | |
288 | (set_attr "cc" "clobber")]) | |
289 | ||
290 | ||
291 | ;; On the enhanced core, don't clobber either input and use a separate output | |
292 | ||
293 | ;; "mulsa3" "mulusa3" | |
294 | (define_expand "mul<mode>3" | |
295 | [(set (reg:ALL4A 16) | |
296 | (match_operand:ALL4A 1 "register_operand" "")) | |
297 | (set (reg:ALL4A 20) | |
298 | (match_operand:ALL4A 2 "register_operand" "")) | |
299 | (set (reg:ALL4A 24) | |
300 | (mult:ALL4A (reg:ALL4A 16) | |
301 | (reg:ALL4A 20))) | |
302 | (set (match_operand:ALL4A 0 "register_operand" "") | |
303 | (reg:ALL4A 24))] | |
304 | "AVR_HAVE_MUL") | |
305 | ||
306 | ;; "*mulsa3.call" "*mulusa3.call" | |
307 | (define_insn "*mul<mode>3.call" | |
308 | [(set (reg:ALL4A 24) | |
309 | (mult:ALL4A (reg:ALL4A 16) | |
310 | (reg:ALL4A 20)))] | |
311 | "AVR_HAVE_MUL" | |
312 | "%~call __mul<mode>3" | |
313 | [(set_attr "type" "xcall") | |
314 | (set_attr "cc" "clobber")]) | |
315 | ||
316 | ; / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / | |
317 | ; div | |
318 | ||
319 | (define_code_iterator usdiv [udiv div]) | |
320 | ||
321 | ;; "divqq3" "udivuqq3" | |
322 | (define_expand "<code><mode>3" | |
323 | [(set (reg:ALL1Q 25) | |
324 | (match_operand:ALL1Q 1 "register_operand" "")) | |
325 | (set (reg:ALL1Q 22) | |
326 | (match_operand:ALL1Q 2 "register_operand" "")) | |
327 | (parallel [(set (reg:ALL1Q 24) | |
328 | (usdiv:ALL1Q (reg:ALL1Q 25) | |
329 | (reg:ALL1Q 22))) | |
330 | (clobber (reg:QI 25))]) | |
331 | (set (match_operand:ALL1Q 0 "register_operand" "") | |
332 | (reg:ALL1Q 24))]) | |
333 | ||
334 | ;; "*divqq3.call" "*udivuqq3.call" | |
335 | (define_insn "*<code><mode>3.call" | |
336 | [(set (reg:ALL1Q 24) | |
337 | (usdiv:ALL1Q (reg:ALL1Q 25) | |
338 | (reg:ALL1Q 22))) | |
339 | (clobber (reg:QI 25))] | |
340 | "" | |
341 | "%~call __<code><mode>3" | |
342 | [(set_attr "type" "xcall") | |
343 | (set_attr "cc" "clobber")]) | |
344 | ||
345 | ;; "divhq3" "udivuhq3" | |
346 | ;; "divha3" "udivuha3" | |
347 | (define_expand "<code><mode>3" | |
348 | [(set (reg:ALL2QA 26) | |
349 | (match_operand:ALL2QA 1 "register_operand" "")) | |
350 | (set (reg:ALL2QA 22) | |
351 | (match_operand:ALL2QA 2 "register_operand" "")) | |
352 | (parallel [(set (reg:ALL2QA 24) | |
353 | (usdiv:ALL2QA (reg:ALL2QA 26) | |
354 | (reg:ALL2QA 22))) | |
355 | (clobber (reg:HI 26)) | |
356 | (clobber (reg:QI 21))]) | |
357 | (set (match_operand:ALL2QA 0 "register_operand" "") | |
358 | (reg:ALL2QA 24))]) | |
359 | ||
360 | ;; "*divhq3.call" "*udivuhq3.call" | |
361 | ;; "*divha3.call" "*udivuha3.call" | |
362 | (define_insn "*<code><mode>3.call" | |
363 | [(set (reg:ALL2QA 24) | |
364 | (usdiv:ALL2QA (reg:ALL2QA 26) | |
365 | (reg:ALL2QA 22))) | |
366 | (clobber (reg:HI 26)) | |
367 | (clobber (reg:QI 21))] | |
368 | "" | |
369 | "%~call __<code><mode>3" | |
370 | [(set_attr "type" "xcall") | |
371 | (set_attr "cc" "clobber")]) | |
372 | ||
373 | ;; Note the first parameter gets passed in already offset by 2 bytes | |
374 | ||
375 | ;; "divsa3" "udivusa3" | |
376 | (define_expand "<code><mode>3" | |
377 | [(set (reg:ALL4A 24) | |
378 | (match_operand:ALL4A 1 "register_operand" "")) | |
379 | (set (reg:ALL4A 18) | |
380 | (match_operand:ALL4A 2 "register_operand" "")) | |
381 | (parallel [(set (reg:ALL4A 22) | |
382 | (usdiv:ALL4A (reg:ALL4A 24) | |
383 | (reg:ALL4A 18))) | |
384 | (clobber (reg:HI 26)) | |
385 | (clobber (reg:HI 30))]) | |
386 | (set (match_operand:ALL4A 0 "register_operand" "") | |
387 | (reg:ALL4A 22))]) | |
388 | ||
389 | ;; "*divsa3.call" "*udivusa3.call" | |
390 | (define_insn "*<code><mode>3.call" | |
391 | [(set (reg:ALL4A 22) | |
392 | (usdiv:ALL4A (reg:ALL4A 24) | |
393 | (reg:ALL4A 18))) | |
394 | (clobber (reg:HI 26)) | |
395 | (clobber (reg:HI 30))] | |
396 | "" | |
397 | "%~call __<code><mode>3" | |
398 | [(set_attr "type" "xcall") | |
399 | (set_attr "cc" "clobber")]) |