1;; This file contains instructions that support fixed-point operations 2;; for Atmel AVR micro controllers. 3;; Copyright (C) 2012-2013 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(define_expand "muluqq3_nomul" 237 [(set (reg:UQQ 22) 238 (match_operand:UQQ 1 "register_operand" "")) 239 (set (reg:UQQ 24) 240 (match_operand:UQQ 2 "register_operand" "")) 241 ;; "*umulqihi3.call" 242 (parallel [(set (reg:HI 24) 243 (mult:HI (zero_extend:HI (reg:QI 22)) 244 (zero_extend:HI (reg:QI 24)))) 245 (clobber (reg:QI 21)) 246 (clobber (reg:HI 22))]) 247 (set (match_operand:UQQ 0 "register_operand" "") 248 (reg:UQQ 25))] 249 "!AVR_HAVE_MUL") 250 251(define_insn "*mulqq3.call" 252 [(set (reg:QQ 23) 253 (mult:QQ (reg:QQ 24) 254 (reg:QQ 25))) 255 (clobber (reg:QI 22)) 256 (clobber (reg:HI 24))] 257 "!AVR_HAVE_MUL" 258 "%~call __mulqq3" 259 [(set_attr "type" "xcall") 260 (set_attr "cc" "clobber")]) 261 262 263;; "mulhq3" "muluhq3" 264;; "mulha3" "muluha3" 265(define_expand "mul<mode>3" 266 [(set (reg:ALL2QA 18) 267 (match_operand:ALL2QA 1 "register_operand" "")) 268 (set (reg:ALL2QA 26) 269 (match_operand:ALL2QA 2 "register_operand" "")) 270 ;; "*mulhq3.call.enh" 271 (parallel [(set (reg:ALL2QA 24) 272 (mult:ALL2QA (reg:ALL2QA 18) 273 (reg:ALL2QA 26))) 274 (clobber (reg:HI 22))]) 275 (set (match_operand:ALL2QA 0 "register_operand" "") 276 (reg:ALL2QA 24))] 277 "AVR_HAVE_MUL") 278 279;; "*mulhq3.call" "*muluhq3.call" 280;; "*mulha3.call" "*muluha3.call" 281(define_insn "*mul<mode>3.call" 282 [(set (reg:ALL2QA 24) 283 (mult:ALL2QA (reg:ALL2QA 18) 284 (reg:ALL2QA 26))) 285 (clobber (reg:HI 22))] 286 "AVR_HAVE_MUL" 287 "%~call __mul<mode>3" 288 [(set_attr "type" "xcall") 289 (set_attr "cc" "clobber")]) 290 291 292;; On the enhanced core, don't clobber either input and use a separate output 293 294;; "mulsa3" "mulusa3" 295(define_expand "mul<mode>3" 296 [(set (reg:ALL4A 16) 297 (match_operand:ALL4A 1 "register_operand" "")) 298 (set (reg:ALL4A 20) 299 (match_operand:ALL4A 2 "register_operand" "")) 300 (set (reg:ALL4A 24) 301 (mult:ALL4A (reg:ALL4A 16) 302 (reg:ALL4A 20))) 303 (set (match_operand:ALL4A 0 "register_operand" "") 304 (reg:ALL4A 24))] 305 "AVR_HAVE_MUL") 306 307;; "*mulsa3.call" "*mulusa3.call" 308(define_insn "*mul<mode>3.call" 309 [(set (reg:ALL4A 24) 310 (mult:ALL4A (reg:ALL4A 16) 311 (reg:ALL4A 20)))] 312 "AVR_HAVE_MUL" 313 "%~call __mul<mode>3" 314 [(set_attr "type" "xcall") 315 (set_attr "cc" "clobber")]) 316 317; / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 318; div 319 320(define_code_iterator usdiv [udiv div]) 321 322;; "divqq3" "udivuqq3" 323(define_expand "<code><mode>3" 324 [(set (reg:ALL1Q 25) 325 (match_operand:ALL1Q 1 "register_operand" "")) 326 (set (reg:ALL1Q 22) 327 (match_operand:ALL1Q 2 "register_operand" "")) 328 (parallel [(set (reg:ALL1Q 24) 329 (usdiv:ALL1Q (reg:ALL1Q 25) 330 (reg:ALL1Q 22))) 331 (clobber (reg:QI 25))]) 332 (set (match_operand:ALL1Q 0 "register_operand" "") 333 (reg:ALL1Q 24))]) 334 335;; "*divqq3.call" "*udivuqq3.call" 336(define_insn "*<code><mode>3.call" 337 [(set (reg:ALL1Q 24) 338 (usdiv:ALL1Q (reg:ALL1Q 25) 339 (reg:ALL1Q 22))) 340 (clobber (reg:QI 25))] 341 "" 342 "%~call __<code><mode>3" 343 [(set_attr "type" "xcall") 344 (set_attr "cc" "clobber")]) 345 346;; "divhq3" "udivuhq3" 347;; "divha3" "udivuha3" 348(define_expand "<code><mode>3" 349 [(set (reg:ALL2QA 26) 350 (match_operand:ALL2QA 1 "register_operand" "")) 351 (set (reg:ALL2QA 22) 352 (match_operand:ALL2QA 2 "register_operand" "")) 353 (parallel [(set (reg:ALL2QA 24) 354 (usdiv:ALL2QA (reg:ALL2QA 26) 355 (reg:ALL2QA 22))) 356 (clobber (reg:HI 26)) 357 (clobber (reg:QI 21))]) 358 (set (match_operand:ALL2QA 0 "register_operand" "") 359 (reg:ALL2QA 24))]) 360 361;; "*divhq3.call" "*udivuhq3.call" 362;; "*divha3.call" "*udivuha3.call" 363(define_insn "*<code><mode>3.call" 364 [(set (reg:ALL2QA 24) 365 (usdiv:ALL2QA (reg:ALL2QA 26) 366 (reg:ALL2QA 22))) 367 (clobber (reg:HI 26)) 368 (clobber (reg:QI 21))] 369 "" 370 "%~call __<code><mode>3" 371 [(set_attr "type" "xcall") 372 (set_attr "cc" "clobber")]) 373 374;; Note the first parameter gets passed in already offset by 2 bytes 375 376;; "divsa3" "udivusa3" 377(define_expand "<code><mode>3" 378 [(set (reg:ALL4A 24) 379 (match_operand:ALL4A 1 "register_operand" "")) 380 (set (reg:ALL4A 18) 381 (match_operand:ALL4A 2 "register_operand" "")) 382 (parallel [(set (reg:ALL4A 22) 383 (usdiv:ALL4A (reg:ALL4A 24) 384 (reg:ALL4A 18))) 385 (clobber (reg:HI 26)) 386 (clobber (reg:HI 30))]) 387 (set (match_operand:ALL4A 0 "register_operand" "") 388 (reg:ALL4A 22))]) 389 390;; "*divsa3.call" "*udivusa3.call" 391(define_insn "*<code><mode>3.call" 392 [(set (reg:ALL4A 22) 393 (usdiv:ALL4A (reg:ALL4A 24) 394 (reg:ALL4A 18))) 395 (clobber (reg:HI 26)) 396 (clobber (reg:HI 30))] 397 "" 398 "%~call __<code><mode>3" 399 [(set_attr "type" "xcall") 400 (set_attr "cc" "clobber")]) 401 402 403;****************************************************************************** 404;** Rounding 405;****************************************************************************** 406 407;; "roundqq3" "rounduqq3" 408;; "roundhq3" "rounduhq3" "roundha3" "rounduha3" 409;; "roundsq3" "roundusq3" "roundsa3" "roundusa3" 410(define_expand "round<mode>3" 411 [(set (match_dup 4) 412 (match_operand:ALL124QA 1 "register_operand" "")) 413 (set (reg:QI 24) 414 (match_dup 5)) 415 (parallel [(set (match_dup 3) 416 (unspec:ALL124QA [(match_dup 4) 417 (reg:QI 24)] UNSPEC_ROUND)) 418 (clobber (match_dup 4))]) 419 (set (match_operand:ALL124QA 0 "register_operand" "") 420 (match_dup 3)) 421 (use (match_operand:HI 2 "nonmemory_operand" ""))] 422 "" 423 { 424 if (CONST_INT_P (operands[2]) 425 && !(optimize_size 426 && 4 == GET_MODE_SIZE (<MODE>mode))) 427 { 428 emit_insn (gen_round<mode>3_const (operands[0], operands[1], operands[2])); 429 DONE; 430 } 431 432 // Input and output of the libgcc function 433 const unsigned int regno_in[] = { -1U, 22, 22, -1U, 18 }; 434 const unsigned int regno_out[] = { -1U, 24, 24, -1U, 22 }; 435 436 operands[3] = gen_rtx_REG (<MODE>mode, regno_out[(size_t) GET_MODE_SIZE (<MODE>mode)]); 437 operands[4] = gen_rtx_REG (<MODE>mode, regno_in[(size_t) GET_MODE_SIZE (<MODE>mode)]); 438 operands[5] = simplify_gen_subreg (QImode, force_reg (HImode, operands[2]), HImode, 0); 439 // $2 is no more needed, but is referenced for expand. 440 operands[2] = const0_rtx; 441 }) 442 443;; Expand rounding with known rounding points inline so that the addend / mask 444;; will be consumed by operation with immediate operands and there is no 445;; need for a shift with variable offset. 446 447;; "roundqq3_const" "rounduqq3_const" 448;; "roundhq3_const" "rounduhq3_const" "roundha3_const" "rounduha3_const" 449;; "roundsq3_const" "roundusq3_const" "roundsa3_const" "roundusa3_const" 450(define_insn "round<mode>3_const" 451 [(set (match_operand:ALL124QA 0 "register_operand" "=d") 452 (unspec:ALL124QA [(match_operand:ALL124QA 1 "register_operand" "0") 453 (match_operand:HI 2 "const_int_operand" "n") 454 (const_int 0)] 455 UNSPEC_ROUND))] 456 "" 457 { 458 return avr_out_round (insn, operands); 459 } 460 [(set_attr "cc" "clobber") 461 (set_attr "adjust_len" "round")]) 462 463 464;; "*roundqq3.libgcc" "*rounduqq3.libgcc" 465(define_insn "*round<mode>3.libgcc" 466 [(set (reg:ALL1Q 24) 467 (unspec:ALL1Q [(reg:ALL1Q 22) 468 (reg:QI 24)] UNSPEC_ROUND)) 469 (clobber (reg:ALL1Q 22))] 470 "" 471 "%~call __round<mode>3" 472 [(set_attr "type" "xcall") 473 (set_attr "cc" "clobber")]) 474 475;; "*roundhq3.libgcc" "*rounduhq3.libgcc" 476;; "*roundha3.libgcc" "*rounduha3.libgcc" 477(define_insn "*round<mode>3.libgcc" 478 [(set (reg:ALL2QA 24) 479 (unspec:ALL2QA [(reg:ALL2QA 22) 480 (reg:QI 24)] UNSPEC_ROUND)) 481 (clobber (reg:ALL2QA 22))] 482 "" 483 "%~call __round<mode>3" 484 [(set_attr "type" "xcall") 485 (set_attr "cc" "clobber")]) 486 487;; "*roundsq3.libgcc" "*roundusq3.libgcc" 488;; "*roundsa3.libgcc" "*roundusa3.libgcc" 489(define_insn "*round<mode>3.libgcc" 490 [(set (reg:ALL4QA 22) 491 (unspec:ALL4QA [(reg:ALL4QA 18) 492 (reg:QI 24)] UNSPEC_ROUND)) 493 (clobber (reg:ALL4QA 18))] 494 "" 495 "%~call __round<mode>3" 496 [(set_attr "type" "xcall") 497 (set_attr "cc" "clobber")]) 498