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
putNop(Xbyak::CodeGenerator * gen,int n)11 void putNop(Xbyak::CodeGenerator *gen, int n)
12 {
13 for (int i = 0; i < n; i++) {
14 gen->nop();
15 }
16 }
17
diff(const std::string & a,const std::string & b)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
dump(const std::string & m)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
CYBOZU_TEST_AUTO(test1)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
CYBOZU_TEST_AUTO(testJmpCx)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
CYBOZU_TEST_AUTO(loop)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
CYBOZU_TEST_AUTO(test2)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
add5(int x)341 int add5(int x) { return x + 5; }
add2(int x)342 int add2(int x) { return x + 2; }
343
CYBOZU_TEST_AUTO(test3)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 {
allocMyAllocator379 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 }
freeMyAllocator392 void free(uint8_t *)
393 {
394 }
395 } myAlloc;
396
CYBOZU_TEST_AUTO(test4)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__
CYBOZU_TEST_AUTO(test5)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
getValue(const uint8_t * p)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
checkAddr(const uint8_t * p,size_t offset,size_t expect)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
CYBOZU_TEST_AUTO(MovLabel)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
CYBOZU_TEST_AUTO(testMovLabel2)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
CYBOZU_TEST_AUTO(testF_B)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
CYBOZU_TEST_AUTO(test6)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
CYBOZU_TEST_AUTO(test_jcc)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
CYBOZU_TEST_AUTO(testNewLabel)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
CYBOZU_TEST_AUTO(returnLabel)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
CYBOZU_TEST_AUTO(testAssign)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
CYBOZU_TEST_AUTO(doubleDefine)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 {
testGetAddressCode11032 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 };
CodeLabelTableCodeLabelTable1059 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
CYBOZU_TEST_AUTO(LabelTable)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
CYBOZU_TEST_AUTO(getAddress1)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;
GetAddressCode2GetAddressCode21111 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
CYBOZU_TEST_AUTO(getAddress2)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
CYBOZU_TEST_AUTO(rip)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
ret1234()1191 int ret1234()
1192 {
1193 return 1234;
1194 }
1195
ret9999()1196 int ret9999()
1197 {
1198 return 9999;
1199 }
1200
CYBOZU_TEST_AUTO(rip_jmp)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__
CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf)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 {
ReleaseTestCodeReleaseTestCode1270 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 */
CYBOZU_TEST_AUTO(release_label_after_code)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 {
nopsJmpTypeCode1315 void nops()
1316 {
1317 for (int i = 0; i < 130; i++) {
1318 nop();
1319 }
1320 }
1321 // return jmp code size
genJmpTypeCode1322 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
CYBOZU_TEST_AUTO(setDefaultJmpNEAR)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
CYBOZU_TEST_AUTO(ambiguousFarJmp)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 }
1401