jmp.cpp
1 #include <stdio.h> 2 #include <string.h> 3 #include <string> 4 #define XBYAK_NO_OP_NAMES 5 #include <xbyak/xbyak.h> 6 #include <cybozu/inttype.hpp> 7 #include <cybozu/test.hpp> 8 9 using namespace Xbyak; 10 11 void putNop(Xbyak::CodeGenerator *gen, int n) 12 { 13 for (int i = 0; i < n; i++) { 14 gen->nop(); 15 } 16 } 17 18 void diff(const std::string& a, const std::string& b) 19 { 20 if (a == b) return; 21 if (a.size() != b.size()) printf("size diff %d %d\n", (int)a.size(), (int)b.size()); 22 for (size_t i = 0; i < (std::min)(a.size(), b.size()); i++) { 23 if (a[i] != b[i]) { 24 printf("diff %d(%04x) %02x %02x\n", (int)i, (int)i, (unsigned char)a[i], (unsigned char)b[i]); 25 } 26 } 27 } 28 29 void dump(const std::string& m) 30 { 31 printf("size=%d\n ", (int)m.size()); 32 for (int i = 0; i < 16; i++) { 33 printf("%02x ", i); 34 } 35 printf("\n "); 36 for (int i = 0; i < 16; i++) { 37 printf("---"); 38 } 39 printf("\n"); 40 for (size_t i = 0; i < m.size(); i++) { 41 if ((i % 16) == 0) printf("%04x ", (int)(i / 16)); 42 printf("%02x ", (unsigned char)m[i]); 43 if ((i % 16) == 15) putchar('\n'); 44 } 45 putchar('\n'); 46 } 47 48 CYBOZU_TEST_AUTO(test1) 49 { 50 struct TestJmp : public Xbyak::CodeGenerator { 51 /* 52 4 X0: 53 5 00000004 EBFE jmp short X0 54 6 55 7 X1: 56 8 00000006 <res 00000001> dummyX1 resb 1 57 9 00000007 EBFD jmp short X1 58 10 59 11 X126: 60 12 00000009 <res 0000007E> dummyX126 resb 126 61 13 00000087 EB80 jmp short X126 62 14 63 15 X127: 64 16 00000089 <res 0000007F> dummyX127 resb 127 65 17 00000108 E97CFFFFFF jmp near X127 66 18 67 19 0000010D EB00 jmp short Y0 68 20 Y0: 69 21 70 22 0000010F EB01 jmp short Y1 71 23 00000111 <res 00000001> dummyY1 resb 1 72 24 Y1: 73 25 74 26 00000112 EB7F jmp short Y127 75 27 00000114 <res 0000007F> dummyY127 resb 127 76 28 Y127: 77 29 78 30 00000193 E980000000 jmp near Y128 79 31 00000198 <res 00000080> dummyY128 resb 128 80 32 Y128: 81 */ 82 TestJmp(int offset, bool isBack, bool isShort, bool useNewLabel) 83 { 84 if (useNewLabel) { 85 Label label; 86 if (isBack) { 87 L(label); 88 putNop(this, offset); 89 jmp(label); 90 } else { 91 if (isShort) { 92 jmp(label); 93 } else { 94 jmp(label, T_NEAR); 95 } 96 putNop(this, offset); 97 L(label); 98 } 99 } else { 100 if (isBack) { 101 L("@@"); 102 putNop(this, offset); 103 jmp("@b"); 104 } else { 105 if (isShort) { 106 jmp("@f"); 107 } else { 108 jmp("@f", T_NEAR); 109 } 110 putNop(this, offset); 111 L("@@"); 112 } 113 } 114 } 115 }; 116 static const struct Tbl { 117 int offset; 118 bool isBack; 119 bool isShort; 120 uint8_t result[6]; 121 int size; 122 } tbl[] = { 123 { 0, true, true, { 0xeb, 0xfe }, 2 }, 124 { 1, true, true, { 0xeb, 0xfd }, 2 }, 125 { 126, true, true, { 0xeb, 0x80 }, 2 }, 126 { 127, true, false, {0xe9, 0x7c, 0xff, 0xff, 0xff }, 5 }, 127 { 0, false, true, { 0xeb, 0x00 }, 2 }, 128 { 1, false, true, { 0xeb, 0x01 }, 2 }, 129 { 127, false, true, { 0xeb, 0x7f }, 2 }, 130 { 128, false, false, { 0xe9, 0x80, 0x00, 0x00, 0x00 }, 5 }, 131 }; 132 for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { 133 const Tbl *p = &tbl[i]; 134 for (int k = 0; k < 2; k++) { 135 TestJmp jmp(p->offset, p->isBack, p->isShort, k == 0); 136 const uint8_t *q = (const uint8_t*)jmp.getCode(); 137 if (p->isBack) q += p->offset; /* skip nop */ 138 for (int j = 0; j < p->size; j++) { 139 CYBOZU_TEST_EQUAL(q[j], p->result[j]); 140 } 141 } 142 } 143 } 144 145 CYBOZU_TEST_AUTO(testJmpCx) 146 { 147 struct TestJmpCx : public CodeGenerator { 148 explicit TestJmpCx(void *p, bool useNewLabel) 149 : Xbyak::CodeGenerator(16, p) 150 { 151 if (useNewLabel) { 152 Label lp; 153 L(lp); 154 #ifdef XBYAK64 155 /* 156 67 E3 FD ; jecxz lp 157 E3 FB ; jrcxz lp 158 */ 159 jecxz(lp); 160 jrcxz(lp); 161 #else 162 /* 163 E3FE ; jecxz lp 164 67E3FB ; jcxz lp 165 */ 166 jecxz(lp); 167 jcxz(lp); 168 #endif 169 } else { 170 inLocalLabel(); 171 L(".lp"); 172 #ifdef XBYAK64 173 /* 174 67 E3 FD ; jecxz lp 175 E3 FB ; jrcxz lp 176 */ 177 jecxz(".lp"); 178 jrcxz(".lp"); 179 #else 180 /* 181 E3FE ; jecxz lp 182 67E3FB ; jcxz lp 183 */ 184 jecxz(".lp"); 185 jcxz(".lp"); 186 #endif 187 outLocalLabel(); 188 } 189 } 190 }; 191 const struct { 192 const char *p; 193 size_t len; 194 } tbl = { 195 #ifdef XBYAK64 196 "\x67\xe3\xfd\xe3\xfb", 5 197 #else 198 "\xe3\xfe\x67\xe3\xfb", 5 199 #endif 200 }; 201 for (int j = 0; j < 2; j++) { 202 char buf[16] = {}; 203 TestJmpCx code(buf, j == 0); 204 CYBOZU_TEST_EQUAL(memcmp(buf, tbl.p, tbl.len), 0); 205 } 206 } 207 208 CYBOZU_TEST_AUTO(loop) 209 { 210 const uint8_t ok[] = { 211 // lp: 212 0x31, 0xC0, // xor eax, eax 213 0xE2, 0xFC, // loop lp 214 0xE0, 0xFA, // loopne lp 215 0xE1, 0xF8, // loope lp 216 }; 217 struct Code : CodeGenerator { 218 Code(bool useLabel) 219 { 220 if (useLabel) { 221 Xbyak::Label lp = L(); 222 xor_(eax, eax); 223 loop(lp); 224 loopne(lp); 225 loope(lp); 226 } else { 227 L("@@"); 228 xor_(eax, eax); 229 loop("@b"); 230 loopne("@b"); 231 loope("@b"); 232 } 233 } 234 }; 235 Code code1(false); 236 CYBOZU_TEST_EQUAL(code1.getSize(), sizeof(ok)); 237 CYBOZU_TEST_EQUAL_ARRAY(code1.getCode(), ok, sizeof(ok)); 238 Code code2(true); 239 CYBOZU_TEST_EQUAL(code2.getSize(), sizeof(ok)); 240 CYBOZU_TEST_EQUAL_ARRAY(code2.getCode(), ok, sizeof(ok)); 241 } 242 243 #ifdef _MSC_VER 244 #pragma warning(disable : 4310) 245 #endif 246 CYBOZU_TEST_AUTO(test2) 247 { 248 struct TestJmp2 : public CodeGenerator { 249 /* 250 1 00000000 90 nop 251 2 00000001 90 nop 252 3 f1: 253 4 00000002 <res 0000007E> dummyX1 resb 126 254 6 00000080 EB80 jmp f1 255 7 256 8 f2: 257 9 00000082 <res 0000007F> dummyX2 resb 127 258 11 00000101 E97CFFFFFF jmp f2 259 12 260 13 261 14 00000106 EB7F jmp f3 262 15 00000108 <res 0000007F> dummyX3 resb 127 263 17 f3: 264 18 265 19 00000187 E980000000 jmp f4 266 20 0000018C <res 00000080> dummyX4 resb 128 267 22 f4: 268 */ 269 TestJmp2(void *p, bool useNewLabel) 270 : Xbyak::CodeGenerator(8192, p) 271 { 272 if (useNewLabel) { 273 inLocalLabel(); 274 nop(); 275 nop(); 276 L(".f1"); 277 putNop(this, 126); 278 jmp(".f1"); 279 L(".f2"); 280 putNop(this, 127); 281 jmp(".f2", T_NEAR); 282 283 jmp(".f3"); 284 putNop(this, 127); 285 L(".f3"); 286 jmp(".f4", T_NEAR); 287 putNop(this, 128); 288 L(".f4"); 289 outLocalLabel(); 290 } else { 291 nop(); 292 nop(); 293 Label f1, f2, f3, f4; 294 L(f1); 295 putNop(this, 126); 296 jmp(f1); 297 L(f2); 298 putNop(this, 127); 299 jmp(f2, T_NEAR); 300 301 jmp(f3); 302 putNop(this, 127); 303 L(f3); 304 jmp(f4, T_NEAR); 305 putNop(this, 128); 306 L(f4); 307 } 308 } 309 }; 310 311 std::string ok; 312 ok.resize(0x18C + 128, (char)0x90); 313 ok[0x080] = (char)0xeb; 314 ok[0x081] = (char)0x80; 315 316 ok[0x101] = (char)0xe9; 317 ok[0x102] = (char)0x7c; 318 ok[0x103] = (char)0xff; 319 ok[0x104] = (char)0xff; 320 ok[0x105] = (char)0xff; 321 322 ok[0x106] = (char)0xeb; 323 ok[0x107] = (char)0x7f; 324 325 ok[0x187] = (char)0xe9; 326 ok[0x188] = (char)0x80; 327 ok[0x189] = (char)0x00; 328 ok[0x18a] = (char)0x00; 329 ok[0x18b] = (char)0x00; 330 for (int i = 0; i < 2; i++) { 331 for (int j = 0; j < 2; j++) { 332 TestJmp2 c(i == 0 ? 0 : Xbyak::AutoGrow, j == 0); 333 c.ready(); 334 std::string m((const char*)c.getCode(), c.getSize()); 335 CYBOZU_TEST_EQUAL(m, ok); 336 } 337 } 338 } 339 340 #ifdef XBYAK32 341 int add5(int x) { return x + 5; } 342 int add2(int x) { return x + 2; } 343 344 CYBOZU_TEST_AUTO(test3) 345 { 346 struct Grow : Xbyak::CodeGenerator { 347 Grow(int dummySize) 348 : Xbyak::CodeGenerator(128, Xbyak::AutoGrow) 349 { 350 mov(eax, 100); 351 push(eax); 352 call((void*)add5); 353 add(esp, 4); 354 push(eax); 355 call((void*)add2); 356 add(esp, 4); 357 ret(); 358 for (int i = 0; i < dummySize; i++) { 359 db(0); 360 } 361 } 362 }; 363 for (int dummySize = 0; dummySize < 40000; dummySize += 10000) { 364 printf("dummySize=%d\n", dummySize); 365 Grow g(dummySize); 366 g.ready(); 367 int (*f)() = (int (*)())g.getCode(); 368 int x = f(); 369 const int ok = 107; 370 CYBOZU_TEST_EQUAL(x, ok); 371 } 372 } 373 #endif 374 375 uint8_t bufL[4096 * 32]; 376 uint8_t bufS[4096 * 2]; 377 378 struct MyAllocator : Xbyak::Allocator { 379 uint8_t *alloc(size_t size) 380 { 381 if (size < sizeof(bufS)) { 382 printf("test use bufS(%d)\n", (int)size); 383 return bufS; 384 } 385 if (size < sizeof(bufL)) { 386 printf("test use bufL(%d)\n", (int)size); 387 return bufL; 388 } 389 fprintf(stderr, "no memory %d\n", (int)size); 390 exit(1); 391 } 392 void free(uint8_t *) 393 { 394 } 395 } myAlloc; 396 397 CYBOZU_TEST_AUTO(test4) 398 { 399 struct Test4 : Xbyak::CodeGenerator { 400 Test4(int size, void *mode, bool useNewLabel) 401 : CodeGenerator(size, mode) 402 { 403 if (useNewLabel) { 404 Label x; 405 jmp(x); 406 putNop(this, 10); 407 L(x); 408 ret(); 409 } else { 410 inLocalLabel(); 411 jmp(".x"); 412 putNop(this, 10); 413 L(".x"); 414 ret(); 415 outLocalLabel(); 416 } 417 } 418 }; 419 for (int i = 0; i < 2; i++) { 420 const bool useNewLabel = i == 0; 421 std::string fm, gm; 422 Test4 fc(1024, 0, useNewLabel); 423 Test4 gc(5, Xbyak::AutoGrow, !useNewLabel); 424 gc.ready(); 425 fm.assign((const char*)fc.getCode(), fc.getSize()); 426 gm.assign((const char*)gc.getCode(), gc.getSize()); 427 CYBOZU_TEST_EQUAL(fm, gm); 428 } 429 } 430 431 #ifndef __APPLE__ 432 CYBOZU_TEST_AUTO(test5) 433 { 434 struct Test5 : Xbyak::CodeGenerator { 435 explicit Test5(int size, int count, void *mode) 436 : CodeGenerator(size, mode, &myAlloc) 437 { 438 using namespace Xbyak; 439 inLocalLabel(); 440 mov(ecx, count); 441 xor_(eax, eax); 442 L(".lp"); 443 for (int i = 0; i < count; i++) { 444 L(Label::toStr(i)); 445 add(eax, 1); 446 int to = 0; 447 if (i < count / 2) { 448 to = count - 1 - i; 449 } else { 450 to = count - i; 451 } 452 if (i == count / 2) { 453 jmp(".exit", T_NEAR); 454 } else { 455 jmp(Label::toStr(to), T_NEAR); 456 } 457 } 458 L(".exit"); 459 sub(ecx, 1); 460 jnz(".lp", T_NEAR); 461 ret(); 462 outLocalLabel(); 463 } 464 }; 465 std::string fm, gm; 466 const int count = 50; 467 int ret; 468 Test5 fc(1024 * 64, count, 0); 469 ret = ((int (*)())fc.getCode())(); 470 CYBOZU_TEST_EQUAL(ret, count * count); 471 fm.assign((const char*)fc.getCode(), fc.getSize()); 472 Test5 gc(10, count, Xbyak::AutoGrow); 473 gc.ready(); 474 ret = ((int (*)())gc.getCode())(); 475 CYBOZU_TEST_EQUAL(ret, count * count); 476 gm.assign((const char*)gc.getCode(), gc.getSize()); 477 CYBOZU_TEST_EQUAL(fm, gm); 478 } 479 #endif 480 481 size_t getValue(const uint8_t* p) 482 { 483 size_t v = 0; 484 for (size_t i = 0; i < sizeof(size_t); i++) { 485 v |= size_t(p[i]) << (i * 8); 486 } 487 return v; 488 } 489 490 void checkAddr(const uint8_t *p, size_t offset, size_t expect) 491 { 492 size_t v = getValue(p + offset); 493 CYBOZU_TEST_EQUAL(v, size_t(p) + expect); 494 } 495 496 CYBOZU_TEST_AUTO(MovLabel) 497 { 498 struct MovLabelCode : Xbyak::CodeGenerator { 499 MovLabelCode(bool grow, bool useNewLabel) 500 : Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0) 501 { 502 #ifdef XBYAK64 503 const Reg64& a = rax; 504 #else 505 const Reg32& a = eax; 506 #endif 507 if (useNewLabel) { 508 nop(); // 0x90 509 Label lp1, lp2; 510 L(lp1); 511 nop(); 512 mov(a, lp1); // 0xb8 + <4byte> / 0x48bb + <8byte> 513 nop(); 514 mov(a, lp2); // 0xb8 515 // force realloc if AutoGrow 516 putNop(this, 256); 517 nop(); 518 L(lp2); 519 } else { 520 inLocalLabel(); 521 nop(); // 0x90 522 L(".lp1"); 523 nop(); 524 mov(a, ".lp1"); // 0xb8 + <4byte> / 0x48bb + <8byte> 525 nop(); 526 mov(a, ".lp2"); // 0xb8 527 // force realloc if AutoGrow 528 putNop(this, 256); 529 nop(); 530 L(".lp2"); 531 outLocalLabel(); 532 } 533 } 534 }; 535 536 const struct { 537 int pos; 538 uint8_t ok; 539 } tbl[] = { 540 #ifdef XBYAK32 541 { 0x00, 0x90 }, 542 // lp1:0x001 543 { 0x001, 0x90 }, 544 { 0x002, 0xb8 }, 545 // 0x003 546 { 0x007, 0x90 }, 547 { 0x008, 0xb8 }, 548 // 0x009 549 { 0x10d, 0x90 }, 550 // lp2:0x10e 551 #else 552 { 0x000, 0x90 }, 553 // lp1:0x001 554 { 0x001, 0x90 }, 555 { 0x002, 0x48 }, 556 { 0x003, 0xb8 }, 557 // 0x004 558 { 0x00c, 0x90 }, 559 { 0x00d, 0x48 }, 560 { 0x00e, 0xb8 }, 561 // 0x00f 562 { 0x117, 0x90 }, 563 // lp2:0x118 564 #endif 565 }; 566 for (int j = 0; j < 2; j++) { 567 const bool grow = j == 0; 568 for (int k = 0; k < 2; k++) { 569 const bool useNewLabel = k == 0; 570 MovLabelCode code(grow, useNewLabel); 571 if (grow) code.ready(); 572 const uint8_t* const p = code.getCode(); 573 for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { 574 int pos = tbl[i].pos; 575 uint8_t x = p[pos]; 576 uint8_t ok = tbl[i].ok; 577 CYBOZU_TEST_EQUAL(x, ok); 578 } 579 #ifdef XBYAK32 580 checkAddr(p, 0x03, 0x001); 581 checkAddr(p, 0x09, 0x10e); 582 #else 583 checkAddr(p, 0x04, 0x001); 584 checkAddr(p, 0x0f, 0x118); 585 #endif 586 } 587 } 588 } 589 590 CYBOZU_TEST_AUTO(testMovLabel2) 591 { 592 struct MovLabel2Code : Xbyak::CodeGenerator { 593 MovLabel2Code() 594 { 595 #ifdef XBYAK64 596 const Reg64& a = rax; 597 const Reg64& c = rcx; 598 #else 599 const Reg32& a = eax; 600 const Reg32& c = ecx; 601 #endif 602 xor_(a, a); 603 xor_(c, c); 604 jmp("in"); 605 ud2(); 606 L("@@"); // L1 607 add(a, 2); 608 mov(c, "@f"); 609 jmp(c); // goto L2 610 ud2(); 611 L("in"); 612 mov(c, "@b"); 613 add(a, 1); 614 jmp(c); // goto L1 615 ud2(); 616 L("@@"); // L2 617 add(a, 4); 618 ret(); 619 } 620 }; 621 MovLabel2Code code; 622 int ret = code.getCode<int (*)()>()(); 623 CYBOZU_TEST_EQUAL(ret, 7); 624 } 625 626 CYBOZU_TEST_AUTO(testF_B) 627 { 628 struct Code : Xbyak::CodeGenerator { 629 Code(int type) 630 { 631 inLocalLabel(); 632 xor_(eax, eax); 633 switch (type) { 634 case 0: 635 L("@@"); 636 inc(eax); 637 cmp(eax, 1); 638 je("@b"); 639 break; 640 case 1: 641 test(eax, eax); 642 jz("@f"); 643 ud2(); 644 L("@@"); 645 break; 646 case 2: 647 L("@@"); 648 inc(eax); 649 cmp(eax, 1); // 1, 2 650 je("@b"); 651 cmp(eax, 2); // 2, 3 652 je("@b"); 653 break; 654 case 3: 655 L("@@"); 656 inc(eax); 657 cmp(eax, 1); // 1, 2 658 je("@b"); 659 cmp(eax, 2); // 2, 3 660 je("@b"); 661 jmp("@f"); 662 ud2(); 663 L("@@"); 664 break; 665 case 4: 666 L("@@"); 667 inc(eax); 668 cmp(eax, 1); // 1, 2 669 je("@b"); 670 cmp(eax, 2); // 2, 3 671 je("@b"); 672 jmp("@f"); 673 ud2(); 674 L("@@"); 675 inc(eax); // 4, 5 676 cmp(eax, 4); 677 je("@b"); 678 break; 679 case 5: 680 L("@@"); 681 L("@@"); 682 inc(eax); 683 cmp(eax, 1); 684 je("@b"); 685 break; 686 case 6: 687 L("@@"); 688 L("@@"); 689 L("@@"); 690 inc(eax); 691 cmp(eax, 1); 692 je("@b"); 693 break; 694 case 7: 695 jmp("@f"); 696 L("@@"); 697 inc(eax); // 1, 2 698 cmp(eax, 1); 699 je("@b"); 700 cmp(eax, 2); 701 jne("@f"); // not jmp 702 inc(eax); // 3 703 L("@@"); 704 inc(eax); // 4, 5, 6 705 cmp(eax, 4); 706 je("@b"); 707 cmp(eax, 5); 708 je("@b"); 709 jmp("@f"); 710 jmp("@f"); 711 jmp("@b"); 712 L("@@"); 713 break; 714 } 715 ret(); 716 outLocalLabel(); 717 } 718 }; 719 const int expectedTbl[] = { 720 2, 0, 3, 3, 5, 2, 2, 6 721 }; 722 for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(expectedTbl); i++) { 723 Code code((int)i); 724 int ret = code.getCode<int (*)()>()(); 725 CYBOZU_TEST_EQUAL(ret, expectedTbl[i]); 726 } 727 } 728 729 CYBOZU_TEST_AUTO(test6) 730 { 731 struct TestLocal : public Xbyak::CodeGenerator { 732 TestLocal(bool grow) 733 : Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0) 734 { 735 xor_(eax, eax); 736 inLocalLabel(); 737 jmp("start0", T_NEAR); 738 L(".back"); 739 inc(eax); // 8 740 jmp(".next", T_NEAR); 741 L("start2"); 742 inc(eax); // 7 743 jmp(".back", T_NEAR); 744 inLocalLabel(); 745 L(".back"); 746 inc(eax); // 5 747 putNop(this, 128); 748 jmp(".next", T_NEAR); 749 L("start1"); 750 inc(eax); // 4 751 jmp(".back", T_NEAR); 752 inLocalLabel(); 753 L(".back"); 754 inc(eax); // 2 755 jmp(".next", T_NEAR); 756 L("start0"); 757 inc(eax); // 1 758 jmp(".back", T_NEAR); 759 L(".next"); 760 inc(eax); // 3 761 jmp("start1", T_NEAR); 762 outLocalLabel(); 763 L(".next"); 764 inc(eax); // 6 765 jmp("start2", T_NEAR); 766 outLocalLabel(); 767 L(".next"); 768 inc(eax); // 9 769 jmp("start3", T_NEAR); 770 inLocalLabel(); 771 L(".back"); 772 inc(eax); // 14 773 jmp("exit", T_NEAR); 774 L("start4"); 775 inc(eax); // 13 776 jmp(".back", T_NEAR); 777 outLocalLabel(); 778 L("start3"); 779 inc(eax); // 10 780 inLocalLabel(); 781 jmp(".next", T_NEAR); 782 L(".back"); 783 inc(eax); // 12 784 jmp("start4", T_NEAR); 785 L(".next"); 786 inc(eax); // 11 787 jmp(".back", T_NEAR); 788 outLocalLabel(); 789 outLocalLabel(); 790 L("exit"); 791 inc(eax); // 15 792 ret(); 793 } 794 }; 795 796 for (int i = 0; i < 2; i++) { 797 const bool grow = i == 1; 798 printf("test6 grow=%d\n", i); 799 TestLocal code(grow); 800 if (grow) code.ready(); 801 int (*f)() = code.getCode<int (*)()>(); 802 int a = f(); 803 CYBOZU_TEST_EQUAL(a, 15); 804 } 805 } 806 807 CYBOZU_TEST_AUTO(test_jcc) 808 { 809 struct A : Xbyak::CodeGenerator { 810 A() 811 { 812 add(eax, 5); 813 ret(); 814 } 815 }; 816 struct B : Xbyak::CodeGenerator { 817 B(bool grow, const void *p) : Xbyak::CodeGenerator(grow ? 0 : 4096, grow ? Xbyak::AutoGrow : 0) 818 { 819 mov(eax, 1); 820 add(eax, 2); 821 jnz(p); 822 } 823 }; 824 A a; 825 const void *p = a.getCode<const void*>(); 826 for (int i = 0; i < 2; i++) { 827 bool grow = i == 1; 828 B b(grow, p); 829 if (grow) { 830 b.ready(); 831 } 832 int (*f)() = b.getCode<int (*)()>(); 833 CYBOZU_TEST_EQUAL(f(), 8); 834 } 835 } 836 837 CYBOZU_TEST_AUTO(testNewLabel) 838 { 839 struct Code : Xbyak::CodeGenerator { 840 Code(bool grow) 841 : Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0) 842 { 843 xor_(eax, eax); 844 { 845 Label label1; 846 Label label2; 847 Label label3; 848 Label label4; 849 Label exit; 850 jmp(label1, T_NEAR); 851 L(label2); 852 inc(eax); // 2 853 jmp(label3, T_NEAR); 854 L(label4); 855 inc(eax); // 4 856 jmp(exit, T_NEAR); 857 putNop(this, 128); 858 L(label3); 859 inc(eax); // 3 860 jmp(label4, T_NEAR); 861 L(label1); 862 inc(eax); // 1 863 jmp(label2, T_NEAR); 864 L(exit); 865 } 866 { 867 Label label1; 868 Label label2; 869 Label label3; 870 Label label4; 871 Label exit; 872 jmp(label1); 873 L(label2); 874 inc(eax); // 6 875 jmp(label3); 876 L(label4); 877 inc(eax); // 8 878 jmp(exit); 879 L(label3); 880 inc(eax); // 7 881 jmp(label4); 882 L(label1); 883 inc(eax); // 5 884 jmp(label2); 885 L(exit); 886 } 887 Label callLabel; 888 { // eax == 8 889 Label label1; 890 Label label2; 891 L(label1); 892 inc(eax); // 9, 10, 11, 13 893 cmp(eax, 9); 894 je(label1); 895 // 10, 11, 13 896 inc(eax); // 11, 12, 13 897 cmp(eax, 11); 898 je(label1); 899 // 12, 13 900 cmp(eax, 12); 901 je(label2); 902 inc(eax); // 14 903 cmp(eax, 14); 904 je(label2); 905 ud2(); 906 L(label2); // 14 907 inc(eax); // 13, 15 908 cmp(eax, 13); 909 je(label1); 910 } 911 call(callLabel); 912 ret(); 913 L(callLabel); 914 inc(eax); // 16 915 ret(); 916 } 917 }; 918 for (int i = 0; i < 2; i++) { 919 const bool grow = i == 1; 920 printf("testNewLabel grow=%d\n", grow); 921 Code code(grow); 922 if (grow) code.ready(); 923 int (*f)() = code.getCode<int (*)()>(); 924 int r = f(); 925 CYBOZU_TEST_EQUAL(r, 16); 926 } 927 } 928 929 CYBOZU_TEST_AUTO(returnLabel) 930 { 931 struct Code : Xbyak::CodeGenerator { 932 Code() 933 { 934 xor_(eax, eax); 935 Label L1 = L(); 936 test(eax, eax); 937 Label exit; 938 jnz(exit); 939 inc(eax); // 1 940 Label L2; 941 call(L2); 942 jmp(L1); 943 L(L2); 944 inc(eax); // 2 945 ret(); 946 L(exit); 947 inc(eax); // 3 948 ret(); 949 } 950 }; 951 Code code; 952 int (*f)() = code.getCode<int (*)()>(); 953 int r = f(); 954 CYBOZU_TEST_EQUAL(r, 3); 955 } 956 957 CYBOZU_TEST_AUTO(testAssign) 958 { 959 struct Code : Xbyak::CodeGenerator { 960 Code(bool grow) 961 : Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0) 962 { 963 xor_(eax, eax); 964 Label dst, src; 965 L(src); 966 inc(eax); 967 cmp(eax, 1); 968 je(dst); 969 inc(eax); // 2, 3, 5 970 cmp(eax, 5); 971 putNop(this, 128); 972 jne(dst, T_NEAR); 973 ret(); 974 assignL(dst, src); 975 // test of copy label 976 { 977 Label sss(dst); 978 { 979 Label ttt; 980 ttt = src; 981 } 982 } 983 } 984 }; 985 for (int i = 0; i < 2; i++) { 986 const bool grow = i == 0; 987 printf("testAssign grow=%d\n", grow); 988 Code code(grow); 989 if (grow) code.ready(); 990 int (*f)() = code.getCode<int (*)()>(); 991 int ret = f(); 992 CYBOZU_TEST_EQUAL(ret, 5); 993 } 994 } 995 996 CYBOZU_TEST_AUTO(doubleDefine) 997 { 998 struct Code : Xbyak::CodeGenerator { 999 Code() 1000 { 1001 { 1002 Label label; 1003 L(label); 1004 // forbitten double L() 1005 CYBOZU_TEST_EXCEPTION(L(label), Xbyak::Error); 1006 } 1007 { 1008 Label label; 1009 jmp(label); 1010 CYBOZU_TEST_ASSERT(hasUndefinedLabel()); 1011 } 1012 { 1013 Label label1, label2; 1014 L(label1); 1015 jmp(label2); 1016 assignL(label2, label1); 1017 // forbitten double assignL 1018 CYBOZU_TEST_EXCEPTION(assignL(label2, label1), Xbyak::Error); 1019 } 1020 { 1021 Label label1, label2; 1022 L(label1); 1023 jmp(label2); 1024 // forbitten assignment to label1 set by L() 1025 CYBOZU_TEST_EXCEPTION(assignL(label1, label2), Xbyak::Error); 1026 } 1027 } 1028 } code; 1029 } 1030 1031 struct GetAddressCode1 : Xbyak::CodeGenerator { 1032 void test() 1033 { 1034 Xbyak::Label L1, L2, L3; 1035 nop(); 1036 L(L1); 1037 const uint8_t *p1 = getCurr(); 1038 CYBOZU_TEST_EQUAL_POINTER(L1.getAddress(), p1); 1039 1040 nop(); 1041 jmp(L2); 1042 nop(); 1043 jmp(L3); 1044 L(L2); 1045 CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), getCurr()); 1046 // L3 is not defined 1047 CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), 0); 1048 1049 // L3 is set by L1 1050 assignL(L3, L1); 1051 CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), p1); 1052 } 1053 }; 1054 1055 struct CodeLabelTable : Xbyak::CodeGenerator { 1056 enum { ret0 = 3 }; 1057 enum { ret1 = 5 }; 1058 enum { ret2 = 8 }; 1059 CodeLabelTable() 1060 { 1061 using namespace Xbyak; 1062 #ifdef XBYAK64_WIN 1063 const Reg64& p0 = rcx; 1064 const Reg64& a = rax; 1065 #elif defined (XBYAK64_GCC) 1066 const Reg64& p0 = rdi; 1067 const Reg64& a = rax; 1068 #else 1069 const Reg32& p0 = edx; 1070 const Reg32& a = eax; 1071 mov(edx, ptr [esp + 4]); 1072 #endif 1073 Label labelTbl, L0, L1, L2; 1074 mov(a, labelTbl); 1075 jmp(ptr [a + p0 * sizeof(void*)]); 1076 L(labelTbl); 1077 putL(L0); 1078 putL(L1); 1079 putL(L2); 1080 L(L0); 1081 mov(a, ret0); 1082 ret(); 1083 L(L1); 1084 mov(a, ret1); 1085 ret(); 1086 L(L2); 1087 mov(a, ret2); 1088 ret(); 1089 } 1090 }; 1091 1092 CYBOZU_TEST_AUTO(LabelTable) 1093 { 1094 CodeLabelTable c; 1095 int (*f)(int) = c.getCode<int (*)(int)>(); 1096 CYBOZU_TEST_EQUAL(f(0), c.ret0); 1097 CYBOZU_TEST_EQUAL(f(1), c.ret1); 1098 CYBOZU_TEST_EQUAL(f(2), c.ret2); 1099 } 1100 1101 CYBOZU_TEST_AUTO(getAddress1) 1102 { 1103 GetAddressCode1 c; 1104 c.test(); 1105 } 1106 1107 struct GetAddressCode2 : Xbyak::CodeGenerator { 1108 Xbyak::Label L1, L2, L3; 1109 size_t a1; 1110 size_t a3; 1111 explicit GetAddressCode2(int size) 1112 : Xbyak::CodeGenerator(size, size == 4096 ? 0 : Xbyak::AutoGrow) 1113 , a1(0) 1114 , a3(0) 1115 { 1116 bool autoGrow = size != 4096; 1117 nop(); 1118 L(L1); 1119 if (autoGrow) { 1120 CYBOZU_TEST_EQUAL_POINTER(L1.getAddress(), 0); 1121 } 1122 a1 = getSize(); 1123 nop(); 1124 jmp(L2); 1125 if (autoGrow) { 1126 CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), 0); 1127 } 1128 L(L3); 1129 a3 = getSize(); 1130 if (autoGrow) { 1131 CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), 0); 1132 } 1133 nop(); 1134 assignL(L2, L1); 1135 if (autoGrow) { 1136 CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), 0); 1137 } 1138 } 1139 }; 1140 1141 CYBOZU_TEST_AUTO(getAddress2) 1142 { 1143 const int sizeTbl[] = { 1144 2, 128, // grow 1145 4096 // not grow 1146 }; 1147 for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(sizeTbl); i++) { 1148 int size = sizeTbl[i]; 1149 GetAddressCode2 c(size); 1150 c.ready(); 1151 const uint8_t *p = c.getCode(); 1152 CYBOZU_TEST_EQUAL(c.L1.getAddress(), p + c.a1); 1153 CYBOZU_TEST_EQUAL(c.L3.getAddress(), p + c.a3); 1154 CYBOZU_TEST_EQUAL(c.L2.getAddress(), p + c.a1); 1155 } 1156 } 1157 1158 #ifdef XBYAK64 1159 CYBOZU_TEST_AUTO(rip) 1160 { 1161 int a[] = { 1, 10 }; 1162 int b[] = { 100, 1000 }; 1163 struct Code : Xbyak::CodeGenerator { 1164 Code(const int *a, const int *b) 1165 { 1166 Label label1, label2; 1167 jmp("@f"); 1168 L(label1); 1169 db(a[0], 4); 1170 db(a[1], 4); 1171 L("@@"); 1172 mov(eax, ptr [rip + label1]); // a[0] 1173 mov(ecx, ptr [rip + label1+4]); // a[1] 1174 mov(edx, ptr [rip + label2-8+2+6]); // b[0] 1175 add(ecx, ptr [rip + 16+label2-12]); // b[1] 1176 add(eax, ecx); 1177 add(eax, edx); 1178 ret(); 1179 L(label2); 1180 db(b[0], 4); 1181 db(b[1], 4); 1182 1183 // error 1184 CYBOZU_TEST_EXCEPTION(rip + label1 + label2, Xbyak::Error); 1185 } 1186 } code(a, b); 1187 int ret = code.getCode<int (*)()>()(); 1188 CYBOZU_TEST_EQUAL(ret, a[0] + a[1] + b[0] + b[1]); 1189 } 1190 1191 int ret1234() 1192 { 1193 return 1234; 1194 } 1195 1196 int ret9999() 1197 { 1198 return 9999; 1199 } 1200 1201 CYBOZU_TEST_AUTO(rip_jmp) 1202 { 1203 struct Code : Xbyak::CodeGenerator { 1204 Code() 1205 { 1206 Label label; 1207 xor_(eax, eax); 1208 call(ptr [rip + label]); 1209 mov(ecx, eax); 1210 call(ptr [rip + label + 8]); 1211 add(eax, ecx); 1212 ret(); 1213 L(label); 1214 db((size_t)ret1234, 8); 1215 db((size_t)ret9999, 8); 1216 } 1217 } code; 1218 int ret = code.getCode<int (*)()>()(); 1219 CYBOZU_TEST_EQUAL(ret, ret1234() + ret9999()); 1220 } 1221 1222 #if 0 1223 CYBOZU_TEST_AUTO(rip_addr) 1224 { 1225 /* 1226 we can't assume |&x - &code| < 2GiB anymore 1227 */ 1228 static int x = 5; 1229 struct Code : Xbyak::CodeGenerator { 1230 Code() 1231 { 1232 mov(eax, 123); 1233 mov(ptr[rip + &x], eax); 1234 ret(); 1235 } 1236 } code; 1237 code.getCode<void (*)()>()(); 1238 CYBOZU_TEST_EQUAL(x, 123); 1239 } 1240 #endif 1241 1242 #ifndef __APPLE__ 1243 CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf) 1244 { 1245 MIE_ALIGN(4096) static char buf[8192]; 1246 static char *p = buf + 4096; 1247 static int *x0 = (int*)buf; 1248 static int *x1 = x0 + 1; 1249 struct Code : Xbyak::CodeGenerator { 1250 Code() : Xbyak::CodeGenerator(4096, p) 1251 { 1252 mov(eax, 123); 1253 mov(ptr[rip + x0], eax); 1254 mov(dword[rip + x1], 456); 1255 mov(byte[rip + 1 + x1 + 3], 99); 1256 ret(); 1257 } 1258 } code; 1259 code.setProtectModeRE(); 1260 code.getCode<void (*)()>()(); 1261 CYBOZU_TEST_EQUAL(*x0, 123); 1262 CYBOZU_TEST_EQUAL(*x1, 456); 1263 CYBOZU_TEST_EQUAL(buf[8], 99); 1264 code.setProtectModeRW(); 1265 } 1266 #endif 1267 #endif 1268 1269 struct ReleaseTestCode : Xbyak::CodeGenerator { 1270 ReleaseTestCode(Label& L1, Label& L2, Label& L3) 1271 { 1272 L(L1); 1273 jmp(L1); 1274 L(L2); 1275 jmp(L3); // not assigned 1276 } 1277 }; 1278 1279 /* 1280 code must unlink label if code is destroyed 1281 */ 1282 CYBOZU_TEST_AUTO(release_label_after_code) 1283 { 1284 puts("---"); 1285 { 1286 Label L1, L2, L3, L4, L5; 1287 { 1288 ReleaseTestCode code(L1, L2, L3); 1289 CYBOZU_TEST_ASSERT(L1.getId() > 0); 1290 CYBOZU_TEST_ASSERT(L1.getAddress() != 0); 1291 CYBOZU_TEST_ASSERT(L2.getId() > 0); 1292 CYBOZU_TEST_ASSERT(L2.getAddress() != 0); 1293 CYBOZU_TEST_ASSERT(L3.getId() > 0); 1294 CYBOZU_TEST_ASSERT(L3.getAddress() == 0); // L3 is not assigned 1295 code.assignL(L4, L1); 1296 L5 = L1; 1297 printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId()); 1298 } 1299 puts("code is released"); 1300 CYBOZU_TEST_ASSERT(L1.getId() == 0); 1301 CYBOZU_TEST_ASSERT(L1.getAddress() == 0); 1302 CYBOZU_TEST_ASSERT(L2.getId() == 0); 1303 CYBOZU_TEST_ASSERT(L2.getAddress() == 0); 1304 // CYBOZU_TEST_ASSERT(L3.getId() == 0); // L3 is not assigned so not cleared 1305 CYBOZU_TEST_ASSERT(L3.getAddress() == 0); 1306 CYBOZU_TEST_ASSERT(L4.getId() == 0); 1307 CYBOZU_TEST_ASSERT(L4.getAddress() == 0); 1308 CYBOZU_TEST_ASSERT(L5.getId() == 0); 1309 CYBOZU_TEST_ASSERT(L5.getAddress() == 0); 1310 printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId()); 1311 } 1312 } 1313 1314 struct JmpTypeCode : Xbyak::CodeGenerator { 1315 void nops() 1316 { 1317 for (int i = 0; i < 130; i++) { 1318 nop(); 1319 } 1320 } 1321 // return jmp code size 1322 size_t gen(bool pre, bool large, Xbyak::CodeGenerator::LabelType type) 1323 { 1324 Label label; 1325 if (pre) { 1326 L(label); 1327 if (large) nops(); 1328 size_t pos = getSize(); 1329 jmp(label, type); 1330 return getSize() - pos; 1331 } else { 1332 size_t pos = getSize(); 1333 jmp(label, type); 1334 size_t size = getSize() - pos; 1335 if (large) nops(); 1336 L(label); 1337 return size; 1338 } 1339 } 1340 }; 1341 1342 CYBOZU_TEST_AUTO(setDefaultJmpNEAR) 1343 { 1344 const Xbyak::CodeGenerator::LabelType T_SHORT = Xbyak::CodeGenerator::T_SHORT; 1345 const Xbyak::CodeGenerator::LabelType T_NEAR = Xbyak::CodeGenerator::T_NEAR; 1346 const Xbyak::CodeGenerator::LabelType T_AUTO = Xbyak::CodeGenerator::T_AUTO; 1347 const struct { 1348 bool pre; 1349 bool large; 1350 Xbyak::CodeGenerator::LabelType type; 1351 size_t expect1; // 0 means exception 1352 size_t expect2; 1353 } tbl[] = { 1354 { false, false, T_SHORT, 2, 2 }, 1355 { false, false, T_NEAR, 5, 5 }, 1356 { false, true, T_SHORT, 0, 0 }, 1357 { false, true, T_NEAR, 5, 5 }, 1358 1359 { true, false, T_SHORT, 2, 2 }, 1360 { true, false, T_NEAR, 5, 5 }, 1361 { true, true, T_SHORT, 0, 0 }, 1362 { true, true, T_NEAR, 5, 5 }, 1363 1364 { false, false, T_AUTO, 2, 5 }, 1365 { false, true, T_AUTO, 0, 5 }, 1366 { true, false, T_AUTO, 2, 2 }, 1367 { true, true, T_AUTO, 5, 5 }, 1368 }; 1369 JmpTypeCode code1, code2; 1370 code2.setDefaultJmpNEAR(true); 1371 for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { 1372 if (tbl[i].expect1) { 1373 size_t size = code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type); 1374 CYBOZU_TEST_EQUAL(size, tbl[i].expect1); 1375 } else { 1376 CYBOZU_TEST_EXCEPTION(code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception); 1377 } 1378 if (tbl[i].expect2) { 1379 size_t size = code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type); 1380 CYBOZU_TEST_EQUAL(size, tbl[i].expect2); 1381 } else { 1382 CYBOZU_TEST_EXCEPTION(code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception); 1383 } 1384 } 1385 } 1386 1387 CYBOZU_TEST_AUTO(ambiguousFarJmp) 1388 { 1389 struct Code : Xbyak::CodeGenerator { 1390 #ifdef XBYAK32 1391 void genJmp() { jmp(ptr[eax], T_FAR); } 1392 void genCall() { call(ptr[eax], T_FAR); } 1393 #else 1394 void genJmp() { jmp(ptr[rax], T_FAR); } 1395 void genCall() { call(ptr[rax], T_FAR); } 1396 #endif 1397 } code; 1398 CYBOZU_TEST_EXCEPTION(code.genJmp(), std::exception); 1399 CYBOZU_TEST_EXCEPTION(code.genCall(), std::exception); 1400 }