x64.c (27224B)
1 #include <assert.h> 2 #include <stdbool.h> 3 #include <stdint.h> 4 #include <string.h> 5 6 #include "nooc.h" 7 #include "stack.h" 8 #include "ir.h" 9 #include "util.h" 10 #include "array.h" 11 #include "target.h" 12 13 enum reg { 14 RAX, 15 RCX, 16 RDX, 17 RBX, 18 RSP, 19 RBP, 20 RSI, 21 RDI, 22 R8, 23 R9, 24 R10, 25 R11, 26 R12, 27 R13, 28 R14, 29 R15, 30 }; 31 32 enum rex { 33 REX_B = 0x41, 34 REX_X = 0x42, 35 REX_R = 0x44, 36 REX_W = 0x48, 37 }; 38 39 enum mod { 40 MOD_INDIRECT, 41 MOD_DISP8, 42 MOD_DISP32, 43 MOD_DIRECT 44 }; 45 46 #define OP_SIZE_OVERRIDE 0x66 47 48 char abi_arg[] = {RAX, RDI, RSI, RDX, R10, R8, R9}; 49 unsigned short used_reg; 50 51 static size_t 52 add_r64_imm(struct data *const text, const enum reg dest, const uint64_t imm) 53 { 54 uint8_t temp; 55 if (text) { 56 array_addlit(text, REX_W); 57 array_addlit(text, 0x81); 58 array_addlit(text, (MOD_DIRECT << 6) | dest); 59 array_addlit(text, imm & 0xFF); 60 array_addlit(text, (imm >> 8) & 0xFF); 61 array_addlit(text, (imm >> 16) & 0xFF); 62 array_addlit(text, (imm >> 24) & 0xFF); 63 } 64 65 return 7; 66 } 67 68 static size_t 69 mov_r64_imm(struct data *const text, const enum reg dest, const uint64_t imm) 70 { 71 uint8_t temp; 72 if (text) { 73 array_addlit(text, REX_W | (dest >= 8 ? REX_B : 0)); 74 array_addlit(text, 0xb8 + (dest & 0x7)); 75 array_addlit(text, imm & 0xFF); 76 array_addlit(text, (imm >> 8) & 0xFF); 77 array_addlit(text, (imm >> 16) & 0xFF); 78 array_addlit(text, (imm >> 24) & 0xFF); 79 array_addlit(text, (imm >> 32) & 0xFF); 80 array_addlit(text, (imm >> 40) & 0xFF); 81 array_addlit(text, (imm >> 48) & 0xFF); 82 array_addlit(text, (imm >> 56) & 0xFF); 83 } 84 85 return 10; 86 } 87 88 static size_t 89 mov_r32_imm(struct data *const text, const enum reg dest, const uint32_t imm) 90 { 91 uint8_t temp; 92 if (text) { 93 array_addlit(text, 0xb8 + (dest & 0x7)); 94 array_addlit(text, imm & 0xFF); 95 array_addlit(text, (imm >> 8) & 0xFF); 96 array_addlit(text, (imm >> 16) & 0xFF); 97 array_addlit(text, (imm >> 24) & 0xFF); 98 } 99 100 return 5; 101 } 102 103 static size_t 104 mov_r16_imm(struct data *const text, const enum reg dest, const uint16_t imm) 105 { 106 uint8_t temp; 107 if (text) { 108 array_addlit(text, OP_SIZE_OVERRIDE); 109 array_addlit(text, 0xb8); 110 array_addlit(text, imm & 0xFF); 111 array_addlit(text, (imm >> 8) & 0xFF); 112 } 113 114 return 4; 115 } 116 117 static size_t 118 mov_r8_imm(struct data *const text, const enum reg dest, const uint8_t imm) 119 { 120 uint8_t temp; 121 if (text) { 122 array_addlit(text, 0xb0 + (dest & 0x7)); 123 array_addlit(text, imm); 124 } 125 126 return 2; 127 } 128 129 static size_t 130 mov_r64_m64(struct data *const text, const enum reg dest, const uint64_t addr) 131 { 132 uint8_t temp; 133 if (text) { 134 array_addlit(text, REX_W | (dest >= 8 ? REX_R : 0)); 135 array_addlit(text, 0x8b); 136 array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4); 137 array_addlit(text, 0x25); 138 array_addlit(text, addr & 0xFF); 139 array_addlit(text, (addr >> 8) & 0xFF); 140 array_addlit(text, (addr >> 16) & 0xFF); 141 array_addlit(text, (addr >> 24) & 0xFF); 142 } 143 144 return 8; 145 } 146 147 static size_t 148 mov_r32_m32(struct data *const text, const enum reg dest, const uint32_t addr) 149 { 150 uint8_t temp; 151 if (text) { 152 if (dest >= 8) array_addlit(text, REX_R); 153 array_addlit(text, 0x8b); 154 array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4); 155 array_addlit(text, 0x25); 156 array_addlit(text, addr & 0xFF); 157 array_addlit(text, (addr >> 8) & 0xFF); 158 array_addlit(text, (addr >> 16) & 0xFF); 159 array_addlit(text, (addr >> 24) & 0xFF); 160 } 161 162 return dest >= 8 ? 8 : 7; 163 } 164 165 static size_t 166 mov_r16_m16(struct data *const text, const enum reg dest, const uint32_t addr) 167 { 168 uint8_t temp; 169 if (text) { 170 array_addlit(text, OP_SIZE_OVERRIDE); 171 if (dest >= 8) array_addlit(text, REX_R); 172 array_addlit(text, 0x8b); 173 array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4); 174 array_addlit(text, 0x25); 175 array_addlit(text, addr & 0xFF); 176 array_addlit(text, (addr >> 8) & 0xFF); 177 array_addlit(text, (addr >> 16) & 0xFF); 178 array_addlit(text, (addr >> 24) & 0xFF); 179 } 180 181 return dest >= 8 ? 9 : 8; 182 } 183 184 static size_t 185 mov_r8_m8(struct data *const text, const enum reg dest, const uint32_t addr) 186 { 187 uint8_t temp; 188 if (text) { 189 if (dest >= 8) array_addlit(text, REX_R); 190 array_addlit(text, 0x8a); 191 array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4); 192 array_addlit(text, 0x25); 193 array_addlit(text, addr & 0xFF); 194 array_addlit(text, (addr >> 8) & 0xFF); 195 array_addlit(text, (addr >> 16) & 0xFF); 196 array_addlit(text, (addr >> 24) & 0xFF); 197 } 198 199 return dest >= 8 ? 8 : 7; 200 } 201 202 static size_t 203 mov_m64_r64(struct data *const text, const uint64_t addr, const enum reg src) 204 { 205 uint8_t temp; 206 if (text) { 207 array_addlit(text, REX_W); 208 array_addlit(text, 0xA3); 209 array_addlit(text, addr & 0xFF); 210 array_addlit(text, (addr >> 8) & 0xFF); 211 array_addlit(text, (addr >> 16) & 0xFF); 212 array_addlit(text, (addr >> 24) & 0xFF); 213 array_addlit(text, (addr >> 32) & 0xFF); 214 array_addlit(text, (addr >> 40) & 0xFF); 215 array_addlit(text, (addr >> 48) & 0xFF); 216 array_addlit(text, (addr >> 56) & 0xFF); 217 } 218 219 return 10; 220 } 221 222 static size_t 223 mov_m32_r32(struct data *const text, const uint64_t addr, const enum reg src) 224 { 225 uint8_t temp; 226 if (text) { 227 array_addlit(text, 0xA3); 228 array_addlit(text, addr & 0xFF); 229 array_addlit(text, (addr >> 8) & 0xFF); 230 array_addlit(text, (addr >> 16) & 0xFF); 231 array_addlit(text, (addr >> 24) & 0xFF); 232 array_addlit(text, (addr >> 32) & 0xFF); 233 array_addlit(text, (addr >> 40) & 0xFF); 234 array_addlit(text, (addr >> 48) & 0xFF); 235 array_addlit(text, (addr >> 56) & 0xFF); 236 } 237 238 return 9; 239 } 240 241 #define MOVE_FROMREG 0 242 #define MOVE_TOREG 1 243 244 static size_t 245 _move_between_reg_and_memaddr_in_reg(struct data *const text, const enum reg reg, const enum reg mem, const uint8_t opsize, const bool dir) 246 { 247 uint8_t temp, rex = opsize == 8 ? REX_W : 0; 248 rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0); 249 250 if (text) { 251 if (opsize == 2) 252 array_addlit(text, OP_SIZE_OVERRIDE); 253 254 if (rex) 255 array_addlit(text, rex); 256 257 array_addlit(text, 0x88 + (opsize != 1) + 2*dir); 258 259 array_addlit(text, (MOD_INDIRECT << 6) | ((reg & 7) << 3) | (mem & 7)); 260 } 261 262 // 8 and 2 have a length of 3, but 4 and 1 have a length of 2 263 return !!rex + (opsize == 2) + 2; 264 } 265 266 static size_t 267 mov_mr64_r64(struct data *const text, const enum reg dest, const enum reg src) 268 { 269 return _move_between_reg_and_memaddr_in_reg(text, src, dest, 8, MOVE_FROMREG); 270 } 271 272 static size_t 273 mov_mr32_r32(struct data *const text, const enum reg dest, const enum reg src) 274 { 275 return _move_between_reg_and_memaddr_in_reg(text, src, dest, 4, MOVE_FROMREG); 276 } 277 278 static size_t 279 mov_mr16_r16(struct data *const text, const enum reg dest, const enum reg src) 280 { 281 return _move_between_reg_and_memaddr_in_reg(text, src, dest, 2, MOVE_FROMREG); 282 } 283 284 static size_t 285 mov_mr8_r8(struct data *const text, const enum reg dest, const enum reg src) 286 { 287 return _move_between_reg_and_memaddr_in_reg(text, src, dest, 1, MOVE_FROMREG); 288 } 289 290 static size_t 291 mov_r64_mr64(struct data *const text, const enum reg dest, const enum reg src) 292 { 293 return _move_between_reg_and_memaddr_in_reg(text, dest, src, 8, MOVE_TOREG); 294 } 295 296 static size_t 297 mov_r32_mr32(struct data *const text, const enum reg dest, const enum reg src) 298 { 299 return _move_between_reg_and_memaddr_in_reg(text, dest, src, 4, MOVE_TOREG); 300 } 301 302 static size_t 303 mov_r16_mr16(struct data *const text, const enum reg dest, const enum reg src) 304 { 305 return _move_between_reg_and_memaddr_in_reg(text, dest, src, 2, MOVE_TOREG); 306 } 307 308 static size_t 309 mov_r8_mr8(struct data *const text, const enum reg dest, const enum reg src) 310 { 311 return _move_between_reg_and_memaddr_in_reg(text, dest, src, 1, MOVE_TOREG); 312 } 313 314 static size_t 315 _move_between_reg_and_reg(struct data *const text, const enum reg dest, const enum reg src, const uint8_t opsize) 316 { 317 uint8_t temp, rex = (src >= 8 ? REX_R : 0) | (dest >= 8 ? REX_B : 0) | (opsize == 8 ? REX_W : 0); 318 if (text) { 319 if (opsize == 2) 320 array_addlit(text, OP_SIZE_OVERRIDE); 321 322 if (rex) 323 array_addlit(text, rex); 324 325 array_addlit(text, 0x88 + (opsize != 1)); 326 array_addlit(text, (MOD_DIRECT << 6) | ((src & 7) << 3) | (dest & 7)); 327 } 328 329 return 2 + !!rex + (opsize == 2); 330 } 331 332 static size_t 333 mov_r64_r64(struct data *const text, const enum reg dest, const enum reg src) 334 { 335 return _move_between_reg_and_reg(text, dest, src, 8); 336 } 337 338 static size_t 339 mov_r32_r32(struct data *const text, const enum reg dest, const enum reg src) 340 { 341 return _move_between_reg_and_reg(text, dest, src, 4); 342 } 343 344 static size_t 345 mov_r16_r16(struct data *const text, const enum reg dest, const enum reg src) 346 { 347 return _move_between_reg_and_reg(text, dest, src, 2); 348 } 349 350 static size_t 351 mov_r8_r8(struct data *const text, const enum reg dest, const enum reg src) 352 { 353 return _move_between_reg_and_reg(text, dest, src, 1); 354 } 355 356 static size_t 357 _move_between_reg_and_memaddr_in_reg_with_disp(struct data *const text, const enum reg reg, const enum reg mem, const int8_t disp, const uint8_t opsize, const bool dir) 358 { 359 assert((reg & 7) != 4 && (mem & 7) != 4); 360 uint8_t temp, rex = opsize == 8 ? REX_W : 0; 361 rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0); 362 363 if (text) { 364 if (opsize == 2) 365 array_addlit(text, OP_SIZE_OVERRIDE); 366 367 if (rex) 368 array_addlit(text, rex); 369 370 array_addlit(text, 0x88 + (opsize != 1) + 2*dir); 371 372 array_addlit(text, (MOD_DISP8 << 6) | ((reg & 7) << 3) | (mem & 7)); 373 array_addlit(text, disp); 374 } 375 376 // 8 and 2 have a length of 3, but 4 and 1 have a length of 2 377 return !!rex + (opsize == 2) + 3; 378 } 379 380 static size_t 381 mov_disp8_m64_r64(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src) 382 { 383 return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 8, MOVE_FROMREG); 384 } 385 386 static size_t 387 mov_disp8_m32_r32(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src) 388 { 389 return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 4, MOVE_FROMREG); 390 } 391 392 static size_t 393 mov_disp8_m16_r16(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src) 394 { 395 return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 2, MOVE_FROMREG); 396 } 397 398 static size_t 399 mov_disp8_m8_r8(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src) 400 { 401 return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 1, MOVE_FROMREG); 402 } 403 404 static size_t 405 mov_disp8_r64_m64(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp) 406 { 407 return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 8, MOVE_TOREG); 408 } 409 410 static size_t 411 mov_disp8_r32_m32(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp) 412 { 413 return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 4, MOVE_TOREG); 414 } 415 416 static size_t 417 mov_disp8_r16_m16(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp) 418 { 419 return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 2, MOVE_TOREG); 420 } 421 422 static size_t 423 mov_disp8_r8_m8(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp) 424 { 425 return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 1, MOVE_TOREG); 426 } 427 428 static size_t 429 _movezx_reg_to_reg(struct data *const text, const uint8_t destsize, const uint8_t srcsize, const enum reg dest, const enum reg src) 430 { 431 assert(srcsize == 1 || srcsize == 2); 432 assert(destsize == 1 || destsize == 2 || destsize == 4 || destsize == 8); 433 uint8_t temp; 434 uint8_t rex = (destsize == 8 ? REX_W : 0) | (dest >= 8 ? REX_R : 0) | (src >= 8 ? REX_B : 0); 435 if (text) { 436 if (destsize == 2) 437 array_addlit(text, OP_SIZE_OVERRIDE); 438 439 if (rex) 440 array_addlit(text, rex); 441 442 array_addlit(text, 0x0F); 443 array_addlit(text, 0xB6 + (srcsize == 2)); 444 array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src); 445 } 446 447 return 3 + !!rex + (destsize == 2); 448 } 449 450 static size_t 451 movzx_r64_r8(struct data *const text, const enum reg dest, const enum reg src) 452 { 453 return _movezx_reg_to_reg(text, 8, 1, dest, src); 454 } 455 456 static size_t 457 movzx_r32_r8(struct data *const text, const enum reg dest, const enum reg src) 458 { 459 return _movezx_reg_to_reg(text, 4, 1, dest, src); 460 } 461 462 static size_t 463 movzx_r16_r8(struct data *const text, const enum reg dest, const enum reg src) 464 { 465 return _movezx_reg_to_reg(text, 2, 1, dest, src); 466 } 467 468 static size_t 469 movzx_r64_r16(struct data *const text, const enum reg dest, const enum reg src) 470 { 471 return _movezx_reg_to_reg(text, 8, 2, dest, src); 472 } 473 474 static size_t 475 movzx_r32_r16(struct data *const text, const enum reg dest, const enum reg src) 476 { 477 return _movezx_reg_to_reg(text, 4, 2, dest, src); 478 } 479 480 static size_t 481 lea_disp8(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp) 482 { 483 uint8_t temp; 484 assert(src != 4); 485 if (text) { 486 array_addlit(text, REX_W); 487 array_addlit(text, 0x8d); 488 array_addlit(text, (MOD_DISP8 << 6) | (dest << 3) | src); 489 array_addlit(text, disp); 490 } 491 492 return 4; 493 } 494 495 static size_t 496 add_r64_r64(struct data *const text, const enum reg dest, const enum reg src) 497 { 498 uint8_t temp; 499 if (text) { 500 array_addlit(text, REX_W); 501 array_addlit(text, 0x03); 502 array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src); 503 } 504 505 return 3; 506 } 507 508 static size_t 509 sub_r64_r64(struct data *const text, const enum reg dest, const enum reg src) 510 { 511 uint8_t temp; 512 if (text) { 513 array_addlit(text, REX_W); 514 array_addlit(text, 0x2b); 515 array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src); 516 } 517 518 return 3; 519 } 520 521 static size_t 522 sub_r64_imm(struct data *const text, const enum reg dest, int32_t imm) 523 { 524 uint8_t temp; 525 if (text) { 526 array_addlit(text, REX_W); 527 array_addlit(text, 0x81); 528 array_addlit(text, (MOD_DIRECT << 6) | (5 << 3) | dest); 529 array_addlit(text, imm & 0xFF); 530 array_addlit(text, (imm >> 8) & 0xFF); 531 array_addlit(text, (imm >> 16) & 0xFF); 532 array_addlit(text, (imm >> 24) & 0xFF); 533 } 534 535 return 7; 536 } 537 538 static size_t 539 _cmp_reg_to_reg(struct data *const text, const uint8_t size, const enum reg reg1, const enum reg reg2) 540 { 541 uint8_t temp; 542 uint8_t rex = (size == 8 ? REX_W : 0) | (reg1 >= 8 ? REX_R : 0) | (reg2 >= 8 ? REX_B : 0); 543 if (text) { 544 if (size == 2) 545 array_addlit(text, OP_SIZE_OVERRIDE); 546 547 if (rex) 548 array_addlit(text, rex); 549 550 array_addlit(text, 0x3A + (size != 1)); 551 array_addlit(text, (MOD_DIRECT << 6) | (reg1 << 3) | reg2); 552 } 553 554 return 2 + !!rex + (size == 2); 555 } 556 557 static size_t 558 cmp_r64_r64(struct data *const text, const enum reg reg1, const enum reg reg2) 559 { 560 return _cmp_reg_to_reg(text, 8, reg1, reg2); 561 } 562 563 static size_t 564 cmp_r32_r32(struct data *const text, const enum reg reg1, const enum reg reg2) 565 { 566 return _cmp_reg_to_reg(text, 4, reg1, reg2); 567 } 568 569 static size_t 570 cmp_r16_r16(struct data *const text, const enum reg reg1, const enum reg reg2) 571 { 572 return _cmp_reg_to_reg(text, 2, reg1, reg2); 573 } 574 575 static size_t 576 cmp_r8_r8(struct data *const text, const enum reg reg1, const enum reg reg2) 577 { 578 return _cmp_reg_to_reg(text, 1, reg1, reg2); 579 } 580 581 static size_t 582 cmp_r8_imm(struct data *const text, const enum reg reg, const uint8_t imm) 583 { 584 uint8_t temp; 585 if (text) { 586 if (reg >= 8) 587 array_addlit(text, REX_B); 588 array_addlit(text, 0x80); 589 array_addlit(text, (MOD_DIRECT << 6) | (7 << 3) | (reg & 7)); 590 array_addlit(text, imm); 591 } 592 593 return 3 + !(reg < 8); 594 } 595 596 static size_t 597 jng(struct data *const text, const int64_t offset) 598 { 599 uint8_t temp; 600 if (-256 <= offset && offset <= 255) { 601 int8_t i = offset; 602 if (text) { 603 array_addlit(text, 0x7E); 604 array_addlit(text, i); 605 } 606 return 2; 607 } else { 608 die("unimplemented jng offet!"); 609 } 610 611 return 0; // prevents warning 612 } 613 614 static size_t 615 jg(struct data *const text, const int64_t offset) 616 { 617 uint8_t temp; 618 if (-256 <= offset && offset <= 255) { 619 int8_t i = offset; 620 if (text) { 621 array_addlit(text, 0x7F); 622 array_addlit(text, i); 623 } 624 return 2; 625 } else { 626 die("unimplemented jg offet!"); 627 } 628 629 return 0; // prevents warning 630 } 631 632 static size_t 633 jne(struct data *const text, const int64_t offset) 634 { 635 uint8_t temp; 636 if (-256 <= offset && offset <= 255) { 637 int8_t i = offset; 638 if (text) { 639 array_addlit(text, 0x75); 640 array_addlit(text, i); 641 } 642 return 2; 643 } else { 644 die("unimplemented jne offet!"); 645 } 646 647 return 0; // prevents warning 648 } 649 650 static size_t 651 je(struct data *const text, const int64_t offset) 652 { 653 uint8_t temp; 654 if (-128 <= offset && offset <= 127) { 655 int8_t i = offset; 656 if (text) { 657 array_addlit(text, 0x74); 658 array_add(text, i); 659 } 660 return 2; 661 } else if (-2147483648 <= offset && offset <= 2147483647) { 662 int32_t i = offset; 663 if (text) { 664 array_addlit(text, 0x0F); 665 array_addlit(text, 0x84); 666 array_addlit(text, ((uint32_t) i) & 0xFF); 667 array_addlit(text, (((uint32_t) i) >> 8) & 0xFF); 668 array_addlit(text, (((uint32_t) i) >> 16) & 0xFF); 669 array_addlit(text, (((uint32_t) i) >> 24) & 0xFF); 670 } 671 return 6; 672 } else { 673 die("unimplemented je offet!"); 674 } 675 676 return 0; // prevents warning 677 } 678 679 static size_t 680 sete_reg(struct data *const text, const enum reg reg) 681 { 682 uint8_t temp; 683 if (text) { 684 if (reg >= 8) array_addlit(text, REX_B); 685 array_addlit(text, 0x0F); 686 array_addlit(text, 0x94); 687 array_addlit(text, (MOD_DIRECT << 6) | (reg & 7)); 688 } 689 690 return 3 + !(reg < 8); 691 } 692 693 static size_t 694 setne_reg(struct data *const text, const enum reg reg) 695 { 696 uint8_t temp; 697 if (text) { 698 if (reg >= 8) array_addlit(text, REX_B); 699 array_addlit(text, 0x0F); 700 array_addlit(text, 0x95); 701 array_addlit(text, (MOD_DIRECT << 6) | (reg & 7)); 702 } 703 704 return 3 + !(reg < 8); 705 } 706 707 static size_t 708 jmp(struct data *const text, const int64_t offset) 709 { 710 uint8_t temp; 711 if (-2147483648 <= offset && offset <= 2147483647) { 712 int32_t i = offset; 713 if (text) { 714 array_addlit(text, 0xE9); 715 array_addlit(text, ((uint32_t) i) & 0xFF); 716 array_addlit(text, (((uint32_t) i) >> 8) & 0xFF); 717 array_addlit(text, (((uint32_t) i) >> 16) & 0xFF); 718 array_addlit(text, (((uint32_t) i) >> 24) & 0xFF); 719 } 720 return 5; 721 } else { 722 die("unimplemented jmp offet!"); 723 } 724 725 return 0; // prevents warning 726 } 727 728 static size_t 729 call(struct data *const text, const int32_t offset) 730 { 731 uint8_t temp; 732 if (text) { 733 array_addlit(text, 0xE8); 734 array_addlit(text, (uint32_t) offset & 0xff); 735 array_addlit(text, ((uint32_t) offset >> 8) & 0xff); 736 array_addlit(text, ((uint32_t) offset >> 16) & 0xff); 737 array_addlit(text, ((uint32_t) offset >> 24) & 0xff); 738 } 739 740 return 5; 741 } 742 743 static size_t 744 ret(struct data *const text) 745 { 746 uint8_t temp; 747 if (text) 748 array_addlit(text, 0xC3); 749 750 return 1; 751 } 752 753 static size_t 754 _pushpop_r64(struct data *const text, const uint8_t ioff, const enum reg reg) 755 { 756 uint8_t temp; 757 if (text) { 758 if (reg >= 8) 759 array_addlit(text, REX_B); 760 761 array_addlit(text, 0x50 + ioff + (reg & 7)); 762 } 763 764 return reg >= 8 ? 2 : 1; 765 } 766 767 static size_t 768 push_r64(struct data *const text, const enum reg reg) 769 { 770 return _pushpop_r64(text, 0, reg); 771 } 772 773 static size_t 774 pop_r64(struct data *const text, const enum reg reg) 775 { 776 return _pushpop_r64(text, 8, reg); 777 } 778 779 static size_t emitsyscall(struct data *const text, const uint8_t paramcount); 780 static size_t emitproc(struct data *const text, const struct iproc *const proc); 781 782 const struct target x64_target = { 783 .reserved = (1 << RSP) | (1 << RBP) | (1 << R12) | (1 << R13), 784 .emitsyscall = emitsyscall, 785 .emitproc = emitproc 786 }; 787 788 #define NEXT ins++; assert(ins <= end); 789 790 static size_t 791 emitsyscall(struct data *const text, const uint8_t paramcount) 792 { 793 assert(paramcount < 8); 794 size_t total = 0; 795 uint8_t temp; 796 total += push_r64(text, RBP); 797 total += mov_r64_r64(text, RBP, RSP); 798 799 for (size_t i = 0; i < paramcount; i++) { 800 total += push_r64(text, abi_arg[i]); 801 total += mov_disp8_r64_m64(text, abi_arg[i], RBP, 8*i + 16); 802 } 803 804 if (text) { 805 array_addlit(text, 0x0f); 806 array_addlit(text, 0x05); 807 } 808 809 total += 2; 810 811 total += mov_disp8_r64_m64(text, RDI, RBP, 8*paramcount + 16); 812 total += mov_mr64_r64(text, RDI, RAX); 813 814 for (size_t i = paramcount - 1; i < paramcount; i--) { 815 total += pop_r64(text, abi_arg[i]); 816 } 817 818 total += pop_r64(text, RBP); 819 total += ret(text); 820 821 return total; 822 } 823 824 size_t 825 emitblock(struct data *const text, const struct iproc *const proc, const struct instr *const start, const struct instr *end, uint16_t active, uint16_t curi) 826 { 827 const struct instr *ins = start ? start : proc->data; 828 end = end ? end : &proc->data[proc->len]; 829 830 uint64_t dest, src, size, count, label; 831 int64_t offset; 832 uint64_t localalloc = 0; 833 834 size_t total = 0; 835 if (!start) { 836 total += push_r64(text, RBP); 837 total += mov_r64_r64(text, RBP, RSP); 838 } 839 840 while (ins < end) { 841 curi++; 842 for (size_t j = 0; j < proc->temps.len; j++) { 843 if ((active & (1 << j)) && proc->temps.data[j].end == curi - 1) 844 active &= ~(1 << proc->temps.data[j].reg); 845 846 if (!(active & (1 << j)) && proc->temps.data[j].start == curi) 847 active |= 1 << proc->temps.data[j].reg; 848 } 849 switch (ins->op) { 850 case IR_JUMP: 851 assert(ins->valtype == VT_LABEL); 852 label = ins->val; 853 if (ins < &proc->data[proc->labels.data[label]]) { 854 total += jmp(text, emitblock(NULL, proc, ins + 1, &proc->data[proc->labels.data[label]], active, curi)); 855 } else { 856 total += jmp(text, emitblock(NULL, proc, start, &proc->data[proc->labels.data[label]], 0, 0) - total - 5); 857 } 858 NEXT; 859 break; 860 case IR_CONDJUMP: 861 assert(ins->valtype == VT_LABEL); 862 curi++; 863 label = ins->val; 864 NEXT; 865 assert(ins->op == IR_EXTRA); 866 assert(ins->valtype == VT_TEMP); 867 total += cmp_r8_imm(text, proc->temps.data[ins->val].reg, 0); 868 if (ins < &proc->data[proc->labels.data[label]]) { 869 total += je(text, emitblock(NULL, proc, ins + 1, &proc->data[proc->labels.data[label]], active, curi)); 870 } else { 871 total += je(text, emitblock(NULL, proc, start, &proc->data[proc->labels.data[label]], 0, 0) - total - 2); // FIXME: 2 = size of short jump 872 } 873 NEXT; 874 break; 875 case IR_RETURN: 876 assert(ins->valtype == VT_EMPTY); 877 total += add_r64_imm(text, RSP, localalloc); 878 total += pop_r64(text, RBP); 879 total += ret(text); 880 NEXT; 881 break; 882 case IR_STORE: 883 assert(ins->valtype == VT_TEMP); 884 src = proc->temps.data[ins->val].reg; 885 NEXT; 886 assert(ins->op == IR_EXTRA); 887 assert(ins->valtype == VT_TEMP); 888 switch (proc->temps.data[ins->val].size) { 889 case 8: 890 total += mov_mr64_r64(text, proc->temps.data[ins->val].reg, src); 891 break; 892 case 4: 893 total += mov_mr32_r32(text, proc->temps.data[ins->val].reg, src); 894 break; 895 case 2: 896 total += mov_mr16_r16(text, proc->temps.data[ins->val].reg, src); 897 break; 898 case 1: 899 total += mov_mr8_r8(text, proc->temps.data[ins->val].reg, src); 900 break; 901 default: 902 die("x64: emitblock: IR_STORE: bad size"); 903 } 904 NEXT; 905 break; 906 case IR_ASSIGN: 907 assert(ins->valtype == VT_TEMP); 908 dest = proc->temps.data[ins->val].reg; 909 size = proc->temps.data[ins->val].size; 910 NEXT; 911 912 switch (ins->op) { 913 case IR_NOT: 914 assert(ins->valtype == VT_TEMP); 915 assert(size == 1); 916 total += cmp_r8_imm(text, proc->temps.data[ins->val].reg, 1); 917 total += setne_reg(text, dest); 918 NEXT; 919 break; 920 case IR_CEQ: 921 assert(ins->valtype == VT_TEMP); 922 src = proc->temps.data[ins->val].reg; 923 NEXT; 924 assert(ins->op == IR_EXTRA); 925 assert(ins->valtype == VT_TEMP); 926 switch (size) { 927 case 8: 928 total += cmp_r64_r64(text, src, proc->temps.data[ins->val].reg); 929 break; 930 case 4: 931 total += cmp_r32_r32(text, src, proc->temps.data[ins->val].reg); 932 break; 933 case 2: 934 total += cmp_r16_r16(text, src, proc->temps.data[ins->val].reg); 935 break; 936 case 1: 937 total += cmp_r8_r8(text, src, proc->temps.data[ins->val].reg); 938 break; 939 default: 940 die("x64 emitblock: IR_CEQ: bad size"); 941 } 942 total += sete_reg(text, dest); 943 NEXT; 944 break; 945 case IR_ADD: 946 assert(ins->valtype == VT_TEMP); 947 total += mov_r64_r64(text, dest, proc->temps.data[ins->val].reg); 948 NEXT; 949 assert(ins->op == IR_EXTRA); 950 assert(ins->valtype == VT_TEMP); 951 total += add_r64_r64(text, dest, proc->temps.data[ins->val].reg); 952 NEXT; 953 break; 954 case IR_ZEXT: 955 assert(ins->valtype == VT_TEMP); 956 if (size == 8) { 957 switch (proc->temps.data[ins->val].size) { 958 case 1: 959 total += movzx_r64_r8(text, dest, proc->temps.data[ins->val].reg); 960 break; 961 case 2: 962 total += movzx_r64_r16(text, dest, proc->temps.data[ins->val].reg); 963 break; 964 case 4: // upper 32-bits get cleared automatically in x64 965 total += mov_r32_r32(text, dest, proc->temps.data[ins->val].reg); 966 break; 967 default: 968 die("x64 emitblock: IR_ZEXT size 8: bad size"); 969 } 970 } else if (size == 4) { 971 switch (proc->temps.data[ins->val].size) { 972 case 1: 973 total += movzx_r32_r8(text, dest, proc->temps.data[ins->val].reg); 974 break; 975 case 2: 976 total += movzx_r32_r16(text, dest, proc->temps.data[ins->val].reg); 977 break; 978 case 4: // upper 32-bits get cleared automatically in x64 979 total += mov_r32_r32(text, dest, proc->temps.data[ins->val].reg); 980 break; 981 default: 982 die("x64 emitblock: IR_ZEXT size 4: bad size"); 983 } 984 } else if (size == 2) { 985 switch (proc->temps.data[ins->val].size) { 986 case 1: 987 total += movzx_r16_r8(text, dest, proc->temps.data[ins->val].reg); 988 break; 989 default: 990 die("x64 emitblock: IR_ZEXT size 2: bad size"); 991 } 992 } else die("x64 emitblock: IR_ZEXT cannot zero extend to 1 byte"); 993 NEXT; 994 break; 995 case IR_IMM: 996 assert(ins->valtype == VT_IMM); 997 total += mov_r64_imm(text, dest, ins->val); 998 NEXT; 999 break; 1000 case IR_IN: 1001 total += mov_disp8_r64_m64(text, dest, RBP, 8*ins->val + 16); 1002 NEXT; 1003 break; 1004 case IR_LOAD: 1005 assert(ins->valtype == VT_TEMP); 1006 switch (size) { 1007 case 8: 1008 total += mov_r64_mr64(text, dest, proc->temps.data[ins->val].reg); 1009 break; 1010 case 4: 1011 total += mov_r32_mr32(text, dest, proc->temps.data[ins->val].reg); 1012 break; 1013 case 2: 1014 total += mov_r16_mr16(text, dest, proc->temps.data[ins->val].reg); 1015 break; 1016 case 1: 1017 total += mov_r8_mr8(text, dest, proc->temps.data[ins->val].reg); 1018 break; 1019 default: 1020 die("x64 emitblock: IR_LOAD: bad size"); 1021 } 1022 NEXT; 1023 break; 1024 case IR_ALLOC: 1025 assert(ins->valtype == VT_IMM); 1026 total += mov_r64_r64(text, dest, RSP); 1027 total += sub_r64_imm(text, RSP, 8); // FIXME: hardcoding 1028 localalloc += 8; 1029 NEXT; 1030 break; 1031 default: 1032 die("x64 emitblock: unhandled assign instruction"); 1033 } 1034 break; 1035 case IR_CALL: 1036 assert(ins->valtype == VT_FUNC); 1037 count = 0; 1038 dest = ins->val; 1039 1040 for (int i = 0; i < 16; i++) { 1041 if (active & (1 << i)) { 1042 total += push_r64(text, i); 1043 } 1044 } 1045 1046 NEXT; 1047 while (ins < end && ins->op == IR_CALLARG) { 1048 assert(ins->valtype == VT_TEMP); 1049 count++; 1050 total += push_r64(text, proc->temps.data[ins->val].reg); 1051 NEXT; 1052 } 1053 1054 // we assume call is constant width - this should probably change 1055 offset = -(proc->addr + total - toplevel.code.data[dest].addr + call(NULL, 0)); 1056 total += call(text, offset); 1057 // FIXME: this won't work with non-64-bit things 1058 total += add_r64_imm(text, RSP, 8*count); 1059 for (int i = 15; i >= 0; i--) { 1060 if (active & (1 << i)) { 1061 total += pop_r64(text, i); 1062 } 1063 } 1064 break; 1065 case IR_LABEL: 1066 assert(ins->valtype == VT_LABEL); 1067 NEXT; 1068 break; 1069 case IR_IMM: 1070 case IR_ALLOC: 1071 die("x64 emitblock: invalid start of instruction"); 1072 default: 1073 die("x64 emitblock: unknown instruction"); 1074 } 1075 } 1076 1077 return total; 1078 } 1079 1080 size_t 1081 emitproc(struct data *const text, const struct iproc *const proc) 1082 { 1083 return emitblock(text, proc, NULL, NULL, 0, 0); 1084 }