1;; Machine description for GNU compiler, 2;; for Atmel AVR micro controllers. 3;; Copyright (C) 1998-2015 Free Software Foundation, Inc. 4;; Contributed by Georg Lay (avr@gjlay.de) 5;; 6;; This file is part of GCC. 7;; 8;; GCC is free software; you can redistribute it and/or modify 9;; it under the terms of the GNU General Public License as published by 10;; the Free Software Foundation; either version 3, or (at your option) 11;; any later version. 12;; 13;; GCC is distributed in the hope that it will be useful, 14;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16;; GNU General Public License for more details. 17;; 18;; You should have received a copy of the GNU General Public License 19;; along with GCC; see the file COPYING3. If not see 20;; <http://www.gnu.org/licenses/>. 21 22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 23 24;; The purpose of this file is to provide a light-weight DImode 25;; implementation for AVR. The trouble with DImode is that tree -> RTL 26;; lowering leads to really unpleasant code for operations that don't 27;; work byte-wise like NEG, PLUS, MINUS, etc. Defining optabs entries for 28;; them won't help because the optab machinery assumes these operations 29;; are cheap and does not check if a libgcc implementation is available. 30;; 31;; The DImode insns are all straight forward -- except movdi. The approach 32;; of this implementation is to provide DImode insns without the burden of 33;; introducing movdi. 34;; 35;; The caveat is that if there are insns for some mode, there must also be a 36;; respective move insn that describes reloads. Therefore, this 37;; implementation uses an accumulator-based model with two hard-coded, 38;; accumulator-like registers 39;; 40;; A[] = reg:DI 18 41;; B[] = reg:DI 10 42;; 43;; so that no DImode insn contains pseudos or needs reloading. 44 45(define_constants 46 [(ACC_A 18) 47 (ACC_B 10)]) 48 49;; Supported modes that are 8 bytes wide 50(define_mode_iterator ALL8 [DI DQ UDQ DA UDA TA UTA]) 51 52(define_mode_iterator ALL8U [UDQ UDA UTA]) 53(define_mode_iterator ALL8S [ DQ DA TA]) 54 55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 56;; Addition 57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 58 59;; "adddi3" 60;; "adddq3" "addudq3" 61;; "addda3" "adduda3" 62;; "addta3" "adduta3" 63(define_expand "add<mode>3" 64 [(parallel [(match_operand:ALL8 0 "general_operand" "") 65 (match_operand:ALL8 1 "general_operand" "") 66 (match_operand:ALL8 2 "general_operand" "")])] 67 "avr_have_dimode" 68 { 69 rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); 70 71 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 72 emit_move_insn (acc_a, operands[1]); 73 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 } 89 90 emit_move_insn (operands[0], acc_a); 91 DONE; 92 }) 93 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")]) 106 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")]) 115 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")]) 131 132 133;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 134;; Subtraction 135;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 136 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); 148 149 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 150 emit_move_insn (acc_a, operands[1]); 151 152 if (const_operand (operands[2], GET_MODE (operands[2]))) 153 { 154 emit_insn (gen_sub<mode>3_const_insn (operands[2])); 155 } 156 else 157 { 158 emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]); 159 emit_insn (gen_sub<mode>3_insn ()); 160 } 161 162 emit_move_insn (operands[0], acc_a); 163 DONE; 164 }) 165 166;; "subdi3_insn" 167;; "subdq3_insn" "subudq3_insn" 168;; "subda3_insn" "subuda3_insn" 169;; "subta3_insn" "subuta3_insn" 170(define_insn "sub<mode>3_insn" 171 [(set (reg:ALL8 ACC_A) 172 (minus:ALL8 (reg:ALL8 ACC_A) 173 (reg:ALL8 ACC_B)))] 174 "avr_have_dimode" 175 "%~call __subdi3" 176 [(set_attr "adjust_len" "call") 177 (set_attr "cc" "set_czn")]) 178 179;; "subdi3_const_insn" 180;; "subdq3_const_insn" "subudq3_const_insn" 181;; "subda3_const_insn" "subuda3_const_insn" 182;; "subta3_const_insn" "subuta3_const_insn" 183(define_insn "sub<mode>3_const_insn" 184 [(set (reg:ALL8 ACC_A) 185 (minus:ALL8 (reg:ALL8 ACC_A) 186 (match_operand:ALL8 0 "const_operand" "n Ynn")))] 187 "avr_have_dimode" 188 { 189 return avr_out_plus (insn, operands); 190 } 191 [(set_attr "adjust_len" "plus") 192 (set_attr "cc" "clobber")]) 193 194;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 195;; Signed Saturating Addition and Subtraction 196;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 197 198(define_expand "<code_stdname><mode>3" 199 [(set (match_operand:ALL8S 0 "general_operand" "") 200 (ss_addsub:ALL8S (match_operand:ALL8S 1 "general_operand" "") 201 (match_operand:ALL8S 2 "general_operand" "")))] 202 "avr_have_dimode" 203 { 204 rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); 205 206 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 207 emit_move_insn (acc_a, operands[1]); 208 209 if (const_operand (operands[2], GET_MODE (operands[2]))) 210 { 211 emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2])); 212 } 213 else 214 { 215 emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]); 216 emit_insn (gen_<code_stdname><mode>3_insn ()); 217 } 218 219 emit_move_insn (operands[0], acc_a); 220 DONE; 221 }) 222 223(define_insn "<code_stdname><mode>3_insn" 224 [(set (reg:ALL8S ACC_A) 225 (ss_addsub:ALL8S (reg:ALL8S ACC_A) 226 (reg:ALL8S ACC_B)))] 227 "avr_have_dimode" 228 "%~call __<code_stdname><mode>3" 229 [(set_attr "adjust_len" "call") 230 (set_attr "cc" "clobber")]) 231 232(define_insn "<code_stdname><mode>3_const_insn" 233 [(set (reg:ALL8S ACC_A) 234 (ss_addsub:ALL8S (reg:ALL8S ACC_A) 235 (match_operand:ALL8S 0 "const_operand" "n Ynn")))] 236 "avr_have_dimode" 237 { 238 return avr_out_plus (insn, operands); 239 } 240 [(set_attr "adjust_len" "plus") 241 (set_attr "cc" "clobber")]) 242 243;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 244;; Unsigned Saturating Addition and Subtraction 245;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 246 247(define_expand "<code_stdname><mode>3" 248 [(set (match_operand:ALL8U 0 "general_operand" "") 249 (us_addsub:ALL8U (match_operand:ALL8U 1 "general_operand" "") 250 (match_operand:ALL8U 2 "general_operand" "")))] 251 "avr_have_dimode" 252 { 253 rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); 254 255 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 256 emit_move_insn (acc_a, operands[1]); 257 258 if (const_operand (operands[2], GET_MODE (operands[2]))) 259 { 260 emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2])); 261 } 262 else 263 { 264 emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]); 265 emit_insn (gen_<code_stdname><mode>3_insn ()); 266 } 267 268 emit_move_insn (operands[0], acc_a); 269 DONE; 270 }) 271 272(define_insn "<code_stdname><mode>3_insn" 273 [(set (reg:ALL8U ACC_A) 274 (us_addsub:ALL8U (reg:ALL8U ACC_A) 275 (reg:ALL8U ACC_B)))] 276 "avr_have_dimode" 277 "%~call __<code_stdname><mode>3" 278 [(set_attr "adjust_len" "call") 279 (set_attr "cc" "clobber")]) 280 281(define_insn "<code_stdname><mode>3_const_insn" 282 [(set (reg:ALL8U ACC_A) 283 (us_addsub:ALL8U (reg:ALL8U ACC_A) 284 (match_operand:ALL8U 0 "const_operand" "n Ynn")))] 285 "avr_have_dimode" 286 { 287 return avr_out_plus (insn, operands); 288 } 289 [(set_attr "adjust_len" "plus") 290 (set_attr "cc" "clobber")]) 291 292;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 293;; Negation 294;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 295 296(define_expand "negdi2" 297 [(parallel [(match_operand:DI 0 "general_operand" "") 298 (match_operand:DI 1 "general_operand" "")])] 299 "avr_have_dimode" 300 { 301 rtx acc_a = gen_rtx_REG (DImode, ACC_A); 302 303 emit_move_insn (acc_a, operands[1]); 304 emit_insn (gen_negdi2_insn ()); 305 emit_move_insn (operands[0], acc_a); 306 DONE; 307 }) 308 309(define_insn "negdi2_insn" 310 [(set (reg:DI ACC_A) 311 (neg:DI (reg:DI ACC_A)))] 312 "avr_have_dimode" 313 "%~call __negdi2" 314 [(set_attr "adjust_len" "call") 315 (set_attr "cc" "clobber")]) 316 317 318;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 319;; Comparison 320;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 321 322(define_expand "conditional_jump" 323 [(set (pc) 324 (if_then_else 325 (match_operator 0 "ordered_comparison_operator" [(cc0) 326 (const_int 0)]) 327 (label_ref (match_operand 1 "" "")) 328 (pc)))] 329 "avr_have_dimode") 330 331;; "cbranchdi4" 332;; "cbranchdq4" "cbranchudq4" 333;; "cbranchda4" "cbranchuda4" 334;; "cbranchta4" "cbranchuta4" 335(define_expand "cbranch<mode>4" 336 [(parallel [(match_operand:ALL8 1 "register_operand" "") 337 (match_operand:ALL8 2 "nonmemory_operand" "") 338 (match_operator 0 "ordered_comparison_operator" [(cc0) 339 (const_int 0)]) 340 (label_ref (match_operand 3 "" ""))])] 341 "avr_have_dimode" 342 { 343 rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); 344 345 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 346 emit_move_insn (acc_a, operands[1]); 347 348 if (s8_operand (operands[2], VOIDmode)) 349 { 350 emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]); 351 emit_insn (gen_compare_const8_di2 ()); 352 } 353 else if (const_operand (operands[2], GET_MODE (operands[2]))) 354 { 355 emit_insn (gen_compare_const_<mode>2 (operands[2])); 356 } 357 else 358 { 359 emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]); 360 emit_insn (gen_compare_<mode>2 ()); 361 } 362 363 emit_jump_insn (gen_conditional_jump (operands[0], operands[3])); 364 DONE; 365 }) 366 367;; "compare_di2" 368;; "compare_dq2" "compare_udq2" 369;; "compare_da2" "compare_uda2" 370;; "compare_ta2" "compare_uta2" 371(define_insn "compare_<mode>2" 372 [(set (cc0) 373 (compare (reg:ALL8 ACC_A) 374 (reg:ALL8 ACC_B)))] 375 "avr_have_dimode" 376 "%~call __cmpdi2" 377 [(set_attr "adjust_len" "call") 378 (set_attr "cc" "compare")]) 379 380(define_insn "compare_const8_di2" 381 [(set (cc0) 382 (compare (reg:DI ACC_A) 383 (sign_extend:DI (reg:QI REG_X))))] 384 "avr_have_dimode" 385 "%~call __cmpdi2_s8" 386 [(set_attr "adjust_len" "call") 387 (set_attr "cc" "compare")]) 388 389;; "compare_const_di2" 390;; "compare_const_dq2" "compare_const_udq2" 391;; "compare_const_da2" "compare_const_uda2" 392;; "compare_const_ta2" "compare_const_uta2" 393(define_insn "compare_const_<mode>2" 394 [(set (cc0) 395 (compare (reg:ALL8 ACC_A) 396 (match_operand:ALL8 0 "const_operand" "n Ynn"))) 397 (clobber (match_scratch:QI 1 "=&d"))] 398 "avr_have_dimode 399 && !s8_operand (operands[0], VOIDmode)" 400 { 401 return avr_out_compare64 (insn, operands, NULL); 402 } 403 [(set_attr "adjust_len" "compare64") 404 (set_attr "cc" "compare")]) 405 406 407;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 408;; Shifts and Rotate 409;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 410 411(define_code_iterator di_shifts 412 [ashift ashiftrt lshiftrt rotate]) 413 414;; Shift functions from libgcc are called without defining these insns, 415;; but with them we can describe their reduced register footprint. 416 417;; "ashldi3" "ashrdi3" "lshrdi3" "rotldi3" 418;; "ashldq3" "ashrdq3" "lshrdq3" "rotldq3" 419;; "ashlda3" "ashrda3" "lshrda3" "rotlda3" 420;; "ashlta3" "ashrta3" "lshrta3" "rotlta3" 421;; "ashludq3" "ashrudq3" "lshrudq3" "rotludq3" 422;; "ashluda3" "ashruda3" "lshruda3" "rotluda3" 423;; "ashluta3" "ashruta3" "lshruta3" "rotluta3" 424(define_expand "<code_stdname><mode>3" 425 [(parallel [(match_operand:ALL8 0 "general_operand" "") 426 (di_shifts:ALL8 (match_operand:ALL8 1 "general_operand" "") 427 (match_operand:QI 2 "general_operand" ""))])] 428 "avr_have_dimode" 429 { 430 rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); 431 432 avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); 433 emit_move_insn (acc_a, operands[1]); 434 emit_move_insn (gen_rtx_REG (QImode, 16), operands[2]); 435 emit_insn (gen_<code_stdname><mode>3_insn ()); 436 emit_move_insn (operands[0], acc_a); 437 DONE; 438 }) 439 440;; "ashldi3_insn" "ashrdi3_insn" "lshrdi3_insn" "rotldi3_insn" 441;; "ashldq3_insn" "ashrdq3_insn" "lshrdq3_insn" "rotldq3_insn" 442;; "ashlda3_insn" "ashrda3_insn" "lshrda3_insn" "rotlda3_insn" 443;; "ashlta3_insn" "ashrta3_insn" "lshrta3_insn" "rotlta3_insn" 444;; "ashludq3_insn" "ashrudq3_insn" "lshrudq3_insn" "rotludq3_insn" 445;; "ashluda3_insn" "ashruda3_insn" "lshruda3_insn" "rotluda3_insn" 446;; "ashluta3_insn" "ashruta3_insn" "lshruta3_insn" "rotluta3_insn" 447(define_insn "<code_stdname><mode>3_insn" 448 [(set (reg:ALL8 ACC_A) 449 (di_shifts:ALL8 (reg:ALL8 ACC_A) 450 (reg:QI 16)))] 451 "avr_have_dimode" 452 "%~call __<code_stdname>di3" 453 [(set_attr "adjust_len" "call") 454 (set_attr "cc" "clobber")]) 455 456;; "umulsidi3" 457;; "mulsidi3" 458(define_expand "<extend_u>mulsidi3" 459 [(parallel [(match_operand:DI 0 "register_operand" "") 460 (match_operand:SI 1 "general_operand" "") 461 (match_operand:SI 2 "general_operand" "") 462 ;; Just to mention the iterator 463 (clobber (any_extend:SI (match_dup 1)))])] 464 "avr_have_dimode 465 && AVR_HAVE_MUL" 466 { 467 avr_fix_inputs (operands, 1 << 2, regmask (SImode, 22)); 468 emit_move_insn (gen_rtx_REG (SImode, 22), operands[1]); 469 emit_move_insn (gen_rtx_REG (SImode, 18), operands[2]); 470 emit_insn (gen_<extend_u>mulsidi3_insn()); 471 // Use emit_move_insn and not open-coded expand because of missing movdi 472 emit_move_insn (operands[0], gen_rtx_REG (DImode, ACC_A)); 473 DONE; 474 }) 475 476;; "umulsidi3_insn" 477;; "mulsidi3_insn" 478(define_insn "<extend_u>mulsidi3_insn" 479 [(set (reg:DI ACC_A) 480 (mult:DI (any_extend:DI (reg:SI 18)) 481 (any_extend:DI (reg:SI 22)))) 482 (clobber (reg:HI REG_X)) 483 (clobber (reg:HI REG_Z))] 484 "avr_have_dimode 485 && AVR_HAVE_MUL" 486 "%~call __<extend_u>mulsidi3" 487 [(set_attr "adjust_len" "call") 488 (set_attr "cc" "clobber")]) 489