#include <stdio.h>
#include <string.h>
#include <string>
#define XBYAK_NO_OP_NAMES
#include <xbyak/xbyak.h>
#include <cybozu/inttype.hpp>
#include <cybozu/test.hpp>

using namespace Xbyak;

void putNop(Xbyak::CodeGenerator *gen, int n)
{
	for (int i = 0; i < n; i++) {
		gen->nop();
	}
}

void diff(const std::string& a, const std::string& b)
{
	if (a == b) return;
	if (a.size() != b.size()) printf("size diff %d %d\n", (int)a.size(), (int)b.size());
	for (size_t i = 0; i < (std::min)(a.size(), b.size()); i++) {
		if (a[i] != b[i]) {
			printf("diff %d(%04x) %02x %02x\n", (int)i, (int)i, (unsigned char)a[i], (unsigned char)b[i]);
		}
	}
}

void dump(const std::string& m)
{
	printf("size=%d\n     ", (int)m.size());
	for (int i = 0; i < 16; i++) {
		printf("%02x ", i);
	}
	printf("\n     ");
	for (int i = 0; i < 16; i++) {
		printf("---");
	}
	printf("\n");
	for (size_t i = 0; i < m.size(); i++) {
		if ((i % 16) == 0) printf("%04x ", (int)(i / 16));
		printf("%02x ", (unsigned char)m[i]);
		if ((i % 16) == 15) putchar('\n');
	}
	putchar('\n');
}

CYBOZU_TEST_AUTO(test1)
{
	struct TestJmp : public Xbyak::CodeGenerator {
	/*
	     4                                  X0:
	     5 00000004 EBFE                    jmp short X0
	     6
	     7                                  X1:
	     8 00000006 <res 00000001>          dummyX1 resb 1
	     9 00000007 EBFD                    jmp short X1
	    10
	    11                                  X126:
	    12 00000009 <res 0000007E>          dummyX126 resb 126
	    13 00000087 EB80                    jmp short X126
	    14
	    15                                  X127:
	    16 00000089 <res 0000007F>          dummyX127 resb 127
	    17 00000108 E97CFFFFFF              jmp near X127
	    18
	    19 0000010D EB00                    jmp short Y0
	    20                                  Y0:
	    21
	    22 0000010F EB01                    jmp short Y1
	    23 00000111 <res 00000001>          dummyY1 resb 1
	    24                                  Y1:
	    25
	    26 00000112 EB7F                    jmp short Y127
	    27 00000114 <res 0000007F>          dummyY127 resb 127
	    28                                  Y127:
	    29
	    30 00000193 E980000000              jmp near Y128
	    31 00000198 <res 00000080>          dummyY128 resb 128
	    32                                  Y128:
	*/
		TestJmp(int offset, bool isBack, bool isShort, bool useNewLabel)
		{
			if (useNewLabel) {
				Label label;
				if (isBack) {
					L(label);
					putNop(this, offset);
					jmp(label);
				} else {
					if (isShort) {
						jmp(label);
					} else {
						jmp(label, T_NEAR);
					}
					putNop(this, offset);
					L(label);
				}
			} else {
				if (isBack) {
					L("@@");
					putNop(this, offset);
					jmp("@b");
				} else {
					if (isShort) {
						jmp("@f");
					} else {
						jmp("@f", T_NEAR);
					}
					putNop(this, offset);
					L("@@");
				}
			}
		}
	};
	static const struct Tbl {
		int offset;
		bool isBack;
		bool isShort;
		uint8_t result[6];
		int size;
	} tbl[] = {
		{ 0, true, true, { 0xeb, 0xfe }, 2 },
		{ 1, true, true, { 0xeb, 0xfd }, 2 },
		{ 126, true, true, { 0xeb, 0x80 }, 2 },
		{ 127, true, false, {0xe9, 0x7c, 0xff, 0xff, 0xff }, 5 },
		{ 0, false, true, { 0xeb, 0x00 }, 2 },
		{ 1, false, true, { 0xeb, 0x01 }, 2 },
		{ 127, false, true, { 0xeb, 0x7f }, 2 },
		{ 128, false, false, { 0xe9, 0x80, 0x00, 0x00, 0x00 }, 5 },
	};
	for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) {
		const Tbl *p = &tbl[i];
		for (int k = 0; k < 2; k++) {
			TestJmp jmp(p->offset, p->isBack, p->isShort, k == 0);
			const uint8_t *q = (const uint8_t*)jmp.getCode();
			if (p->isBack) q += p->offset; /* skip nop */
			for (int j = 0; j < p->size; j++) {
				CYBOZU_TEST_EQUAL(q[j], p->result[j]);
			}
		}
	}
}

CYBOZU_TEST_AUTO(testJmpCx)
{
	struct TestJmpCx : public CodeGenerator {
		explicit TestJmpCx(void *p, bool useNewLabel)
			: Xbyak::CodeGenerator(16, p)
		{
			if (useNewLabel) {
				Label lp;
			L(lp);
#ifdef XBYAK64
				/*
					67 E3 FD ; jecxz lp
					E3 FB    ; jrcxz lp
				*/
				jecxz(lp);
				jrcxz(lp);
#else
				/*
					E3FE   ; jecxz lp
					67E3FB ; jcxz lp
				*/
				jecxz(lp);
				jcxz(lp);
#endif
			} else {
				inLocalLabel();
			L(".lp");
#ifdef XBYAK64
				/*
					67 E3 FD ; jecxz lp
					E3 FB    ; jrcxz lp
				*/
				jecxz(".lp");
				jrcxz(".lp");
#else
				/*
					E3FE   ; jecxz lp
					67E3FB ; jcxz lp
				*/
				jecxz(".lp");
				jcxz(".lp");
#endif
				outLocalLabel();
			}
		}
	};
	const struct {
		const char *p;
		size_t len;
	} tbl = {
#ifdef XBYAK64
		"\x67\xe3\xfd\xe3\xfb", 5
#else
		"\xe3\xfe\x67\xe3\xfb", 5
#endif
	};
	for (int j = 0; j < 2; j++) {
		char buf[16] = {};
		TestJmpCx code(buf, j == 0);
		CYBOZU_TEST_EQUAL(memcmp(buf, tbl.p, tbl.len), 0);
	}
}

CYBOZU_TEST_AUTO(loop)
{
	const uint8_t ok[] = {
		// lp:
		0x31, 0xC0, // xor eax, eax
		0xE2, 0xFC, // loop lp
		0xE0, 0xFA, // loopne lp
		0xE1, 0xF8, // loope lp
	};
	struct Code : CodeGenerator {
		Code(bool useLabel)
		{
			if (useLabel) {
				Xbyak::Label lp = L();
				xor_(eax, eax);
				loop(lp);
				loopne(lp);
				loope(lp);
			} else {
				L("@@");
				xor_(eax, eax);
				loop("@b");
				loopne("@b");
				loope("@b");
			}
		}
	};
	Code code1(false);
	CYBOZU_TEST_EQUAL(code1.getSize(), sizeof(ok));
	CYBOZU_TEST_EQUAL_ARRAY(code1.getCode(), ok, sizeof(ok));
	Code code2(true);
	CYBOZU_TEST_EQUAL(code2.getSize(), sizeof(ok));
	CYBOZU_TEST_EQUAL_ARRAY(code2.getCode(), ok, sizeof(ok));
}

#ifdef _MSC_VER
	#pragma warning(disable : 4310)
#endif
CYBOZU_TEST_AUTO(test2)
{
	struct TestJmp2 : public CodeGenerator {
	/*
	  1 00000000 90                      nop
	  2 00000001 90                      nop
	  3                                  f1:
	  4 00000002 <res 0000007E>          dummyX1 resb 126
	  6 00000080 EB80                     jmp f1
	  7
	  8                                  f2:
	  9 00000082 <res 0000007F>          dummyX2 resb 127
	 11 00000101 E97CFFFFFF               jmp f2
	 12
	 13
	 14 00000106 EB7F                    jmp f3
	 15 00000108 <res 0000007F>          dummyX3 resb 127
	 17                                  f3:
	 18
	 19 00000187 E980000000              jmp f4
	 20 0000018C <res 00000080>          dummyX4 resb 128
	 22                                  f4:
	*/
		TestJmp2(void *p, bool useNewLabel)
			: Xbyak::CodeGenerator(8192, p)
		{
			if (useNewLabel) {
				inLocalLabel();
				nop();
				nop();
			L(".f1");
				putNop(this, 126);
				jmp(".f1");
			L(".f2");
				putNop(this, 127);
				jmp(".f2", T_NEAR);

				jmp(".f3");
				putNop(this, 127);
			L(".f3");
				jmp(".f4", T_NEAR);
				putNop(this, 128);
			L(".f4");
				outLocalLabel();
			} else {
				nop();
				nop();
				Label f1, f2, f3, f4;
			L(f1);
				putNop(this, 126);
				jmp(f1);
			L(f2);
				putNop(this, 127);
				jmp(f2, T_NEAR);

				jmp(f3);
				putNop(this, 127);
			L(f3);
				jmp(f4, T_NEAR);
				putNop(this, 128);
			L(f4);
			}
		}
	};

	std::string ok;
	ok.resize(0x18C + 128, (char)0x90);
	ok[0x080] = (char)0xeb;
	ok[0x081] = (char)0x80;

	ok[0x101] = (char)0xe9;
	ok[0x102] = (char)0x7c;
	ok[0x103] = (char)0xff;
	ok[0x104] = (char)0xff;
	ok[0x105] = (char)0xff;

	ok[0x106] = (char)0xeb;
	ok[0x107] = (char)0x7f;

	ok[0x187] = (char)0xe9;
	ok[0x188] = (char)0x80;
	ok[0x189] = (char)0x00;
	ok[0x18a] = (char)0x00;
	ok[0x18b] = (char)0x00;
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 2; j++) {
			TestJmp2 c(i == 0 ? 0 : Xbyak::AutoGrow, j == 0);
			c.ready();
			std::string m((const char*)c.getCode(), c.getSize());
			CYBOZU_TEST_EQUAL(m, ok);
		}
	}
}

#ifdef XBYAK32
int add5(int x) { return x + 5; }
int add2(int x) { return x + 2; }

CYBOZU_TEST_AUTO(test3)
{
	struct Grow : Xbyak::CodeGenerator {
		Grow(int dummySize)
			: Xbyak::CodeGenerator(128, Xbyak::AutoGrow)
		{
			mov(eax, 100);
			push(eax);
			call((void*)add5);
			add(esp, 4);
			push(eax);
			call((void*)add2);
			add(esp, 4);
			ret();
			for (int i = 0; i < dummySize; i++) {
				db(0);
			}
		}
	};
	for (int dummySize = 0; dummySize < 40000; dummySize += 10000) {
		printf("dummySize=%d\n", dummySize);
		Grow g(dummySize);
		g.ready();
		int (*f)() = (int (*)())g.getCode();
		int x = f();
		const int ok = 107;
		CYBOZU_TEST_EQUAL(x, ok);
	}
}
#endif

uint8_t bufL[4096 * 32];
uint8_t bufS[4096 * 2];

struct MyAllocator : Xbyak::Allocator {
	uint8_t *alloc(size_t size)
	{
		if (size < sizeof(bufS)) {
			printf("test use bufS(%d)\n", (int)size);
			return bufS;
		}
		if (size < sizeof(bufL)) {
			printf("test use bufL(%d)\n", (int)size);
			return bufL;
		}
		fprintf(stderr, "no memory %d\n", (int)size);
		exit(1);
	}
	void free(uint8_t *)
	{
	}
} myAlloc;

CYBOZU_TEST_AUTO(test4)
{
	struct Test4 : Xbyak::CodeGenerator {
		Test4(int size, void *mode, bool useNewLabel)
			: CodeGenerator(size, mode)
		{
			if (useNewLabel) {
				Label x;
				jmp(x);
				putNop(this, 10);
			L(x);
				ret();
			} else {
				inLocalLabel();
				jmp(".x");
				putNop(this, 10);
			L(".x");
				ret();
				outLocalLabel();
			}
		}
	};
	for (int i = 0; i < 2; i++) {
		const bool useNewLabel = i == 0;
		std::string fm, gm;
		Test4 fc(1024, 0, useNewLabel);
		Test4 gc(5, Xbyak::AutoGrow, !useNewLabel);
		gc.ready();
		fm.assign((const char*)fc.getCode(), fc.getSize());
		gm.assign((const char*)gc.getCode(), gc.getSize());
		CYBOZU_TEST_EQUAL(fm, gm);
	}
}

#ifndef __APPLE__
CYBOZU_TEST_AUTO(test5)
{
	struct Test5 : Xbyak::CodeGenerator {
		explicit Test5(int size, int count, void *mode)
			: CodeGenerator(size, mode, &myAlloc)
		{
			using namespace Xbyak;
			inLocalLabel();
			mov(ecx, count);
			xor_(eax, eax);
		L(".lp");
			for (int i = 0; i < count; i++) {
				L(Label::toStr(i));
				add(eax, 1);
				int to = 0;
				if (i < count / 2) {
					to = count - 1 - i;
				} else {
					to = count  - i;
				}
				if (i == count / 2) {
					jmp(".exit", T_NEAR);
				} else {
					jmp(Label::toStr(to), T_NEAR);
				}
			}
		L(".exit");
			sub(ecx, 1);
			jnz(".lp", T_NEAR);
			ret();
			outLocalLabel();
		}
	};
	std::string fm, gm;
	const int count = 50;
	int ret;
	Test5 fc(1024 * 64, count, 0);
	ret = ((int (*)())fc.getCode())();
	CYBOZU_TEST_EQUAL(ret, count * count);
	fm.assign((const char*)fc.getCode(), fc.getSize());
	Test5 gc(10, count, Xbyak::AutoGrow);
	gc.ready();
	ret = ((int (*)())gc.getCode())();
	CYBOZU_TEST_EQUAL(ret, count * count);
	gm.assign((const char*)gc.getCode(), gc.getSize());
	CYBOZU_TEST_EQUAL(fm, gm);
}
#endif

size_t getValue(const uint8_t* p)
{
	size_t v = 0;
	for (size_t i = 0; i < sizeof(size_t); i++) {
		v |= size_t(p[i]) << (i * 8);
	}
	return v;
}

void checkAddr(const uint8_t *p, size_t offset, size_t expect)
{
	size_t v = getValue(p + offset);
	CYBOZU_TEST_EQUAL(v, size_t(p) + expect);
}

CYBOZU_TEST_AUTO(MovLabel)
{
	struct MovLabelCode : Xbyak::CodeGenerator {
		MovLabelCode(bool grow, bool useNewLabel)
			: Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0)
		{
#ifdef XBYAK64
			const Reg64& a = rax;
#else
			const Reg32& a = eax;
#endif
			if (useNewLabel) {
				nop(); // 0x90
				Label lp1, lp2;
			L(lp1);
				nop();
				mov(a, lp1); // 0xb8 + <4byte> / 0x48bb + <8byte>
				nop();
				mov(a, lp2); // 0xb8
				// force realloc if AutoGrow
				putNop(this, 256);
				nop();
			L(lp2);
			} else {
				inLocalLabel();
				nop(); // 0x90
			L(".lp1");
				nop();
				mov(a, ".lp1"); // 0xb8 + <4byte> / 0x48bb + <8byte>
				nop();
				mov(a, ".lp2"); // 0xb8
				// force realloc if AutoGrow
				putNop(this, 256);
				nop();
			L(".lp2");
				outLocalLabel();
			}
		}
	};

	const struct {
		int pos;
		uint8_t ok;
	} tbl[] = {
#ifdef XBYAK32
		{ 0x00, 0x90 },
		// lp1:0x001
		{ 0x001, 0x90 },
		{ 0x002, 0xb8 },
		// 0x003
		{ 0x007, 0x90 },
		{ 0x008, 0xb8 },
		// 0x009
		{ 0x10d, 0x90 },
		// lp2:0x10e
#else
		{ 0x000, 0x90 },
		// lp1:0x001
		{ 0x001, 0x90 },
		{ 0x002, 0x48 },
		{ 0x003, 0xb8 },
		// 0x004
		{ 0x00c, 0x90 },
		{ 0x00d, 0x48 },
		{ 0x00e, 0xb8 },
		// 0x00f
		{ 0x117, 0x90 },
		// lp2:0x118
#endif
	};
	for (int j = 0; j < 2; j++) {
		const bool grow = j == 0;
		for (int k = 0; k < 2; k++) {
			const bool useNewLabel = k == 0;
			MovLabelCode code(grow, useNewLabel);
			if (grow) code.ready();
			const uint8_t* const p = code.getCode();
			for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) {
				int pos = tbl[i].pos;
				uint8_t x = p[pos];
				uint8_t ok = tbl[i].ok;
				CYBOZU_TEST_EQUAL(x, ok);
			}
#ifdef XBYAK32
			checkAddr(p, 0x03, 0x001);
			checkAddr(p, 0x09, 0x10e);
#else
			checkAddr(p, 0x04, 0x001);
			checkAddr(p, 0x0f, 0x118);
#endif
		}
	}
}

CYBOZU_TEST_AUTO(testMovLabel2)
{
	struct MovLabel2Code : Xbyak::CodeGenerator {
		MovLabel2Code()
		{
#ifdef XBYAK64
			const Reg64& a = rax;
			const Reg64& c = rcx;
#else
			const Reg32& a = eax;
			const Reg32& c = ecx;
#endif
			xor_(a, a);
			xor_(c, c);
			jmp("in");
			ud2();
		L("@@"); // L1
			add(a, 2);
			mov(c, "@f");
			jmp(c); // goto L2
			ud2();
		L("in");
			mov(c, "@b");
			add(a, 1);
			jmp(c); // goto L1
			ud2();
		L("@@"); // L2
			add(a, 4);
			ret();
		}
	};
	MovLabel2Code code;
	int ret = code.getCode<int (*)()>()();
	CYBOZU_TEST_EQUAL(ret, 7);
}

CYBOZU_TEST_AUTO(testF_B)
{
	struct Code : Xbyak::CodeGenerator {
		Code(int type)
		{
			inLocalLabel();
			xor_(eax, eax);
			switch (type) {
			case 0:
			L("@@");
				inc(eax);
				cmp(eax, 1);
				je("@b");
				break;
			case 1:
				test(eax, eax);
				jz("@f");
				ud2();
			L("@@");
				break;
			case 2:
			L("@@");
				inc(eax);
				cmp(eax, 1); // 1, 2
				je("@b");
				cmp(eax, 2); // 2, 3
				je("@b");
				break;
			case 3:
			L("@@");
				inc(eax);
				cmp(eax, 1); // 1, 2
				je("@b");
				cmp(eax, 2); // 2, 3
				je("@b");
				jmp("@f");
				ud2();
			L("@@");
				break;
			case 4:
			L("@@");
				inc(eax);
				cmp(eax, 1); // 1, 2
				je("@b");
				cmp(eax, 2); // 2, 3
				je("@b");
				jmp("@f");
				ud2();
			L("@@");
				inc(eax); // 4, 5
				cmp(eax, 4);
				je("@b");
				break;
			case 5:
			L("@@");
			L("@@");
				inc(eax);
				cmp(eax, 1);
				je("@b");
				break;
			case 6:
			L("@@");
			L("@@");
			L("@@");
				inc(eax);
				cmp(eax, 1);
				je("@b");
				break;
			case 7:
				jmp("@f");
			L("@@");
				inc(eax); // 1, 2
				cmp(eax, 1);
				je("@b");
				cmp(eax, 2);
				jne("@f"); // not jmp
				inc(eax); // 3
			L("@@");
				inc(eax); // 4, 5, 6
				cmp(eax, 4);
				je("@b");
				cmp(eax, 5);
				je("@b");
				jmp("@f");
				jmp("@f");
				jmp("@b");
			L("@@");
				break;
			}
			ret();
			outLocalLabel();
		}
	};
	const int expectedTbl[] = {
		2, 0, 3, 3, 5, 2, 2, 6
	};
	for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(expectedTbl); i++) {
		Code code((int)i);
		int ret = code.getCode<int (*)()>()();
		CYBOZU_TEST_EQUAL(ret, expectedTbl[i]);
	}
}

CYBOZU_TEST_AUTO(test6)
{
	struct TestLocal : public Xbyak::CodeGenerator {
		TestLocal(bool grow)
			: Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0)
		{
			xor_(eax, eax);
			inLocalLabel();
			jmp("start0", T_NEAR);
			L(".back");
			inc(eax); // 8
			jmp(".next", T_NEAR);
			L("start2");
			inc(eax); // 7
			jmp(".back", T_NEAR);
				inLocalLabel();
				L(".back");
				inc(eax); // 5
				putNop(this, 128);
				jmp(".next", T_NEAR);
				L("start1");
				inc(eax); // 4
				jmp(".back", T_NEAR);
					inLocalLabel();
					L(".back");
					inc(eax); // 2
					jmp(".next", T_NEAR);
					L("start0");
					inc(eax); // 1
					jmp(".back", T_NEAR);
					L(".next");
					inc(eax); // 3
					jmp("start1", T_NEAR);
					outLocalLabel();
				L(".next");
				inc(eax); // 6
				jmp("start2", T_NEAR);
				outLocalLabel();
			L(".next");
			inc(eax); // 9
			jmp("start3", T_NEAR);
				inLocalLabel();
				L(".back");
				inc(eax); // 14
				jmp("exit", T_NEAR);
			L("start4");
				inc(eax); // 13
				jmp(".back", T_NEAR);
				outLocalLabel();
			L("start3");
				inc(eax); // 10
				inLocalLabel();
				jmp(".next", T_NEAR);
				L(".back");
				inc(eax); // 12
				jmp("start4", T_NEAR);
				L(".next");
				inc(eax); // 11
				jmp(".back", T_NEAR);
				outLocalLabel();
			outLocalLabel();
			L("exit");
			inc(eax); // 15
			ret();
		}
	};

	for (int i = 0; i < 2; i++) {
		const bool grow = i == 1;
		printf("test6 grow=%d\n", i);
		TestLocal code(grow);
		if (grow) code.ready();
		int (*f)() = code.getCode<int (*)()>();
		int a = f();
		CYBOZU_TEST_EQUAL(a, 15);
	}
}

CYBOZU_TEST_AUTO(test_jcc)
{
	struct A : Xbyak::CodeGenerator {
		A()
		{
			add(eax, 5);
			ret();
		}
	};
	struct B : Xbyak::CodeGenerator {
		B(bool grow, const void *p) : Xbyak::CodeGenerator(grow ? 0 : 4096, grow ? Xbyak::AutoGrow : 0)
		{
			mov(eax, 1);
			add(eax, 2);
			jnz(p);
		}
	};
	A a;
	const void *p = a.getCode<const void*>();
	for (int i = 0; i < 2; i++) {
		bool grow = i == 1;
		B b(grow, p);
		if (grow) {
			b.ready();
		}
		int (*f)() = b.getCode<int (*)()>();
		CYBOZU_TEST_EQUAL(f(), 8);
	}
}

CYBOZU_TEST_AUTO(testNewLabel)
{
	struct Code : Xbyak::CodeGenerator {
		Code(bool grow)
			: Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0)
		{
			xor_(eax, eax);
			{
				Label label1;
				Label label2;
				Label label3;
				Label label4;
				Label exit;
				jmp(label1, T_NEAR);
			L(label2);
				inc(eax); // 2
				jmp(label3, T_NEAR);
			L(label4);
				inc(eax); // 4
				jmp(exit, T_NEAR);
				putNop(this, 128);
			L(label3);
				inc(eax); // 3
				jmp(label4, T_NEAR);
			L(label1);
				inc(eax); // 1
				jmp(label2, T_NEAR);
			L(exit);
			}
			{
				Label label1;
				Label label2;
				Label label3;
				Label label4;
				Label exit;
				jmp(label1);
			L(label2);
				inc(eax); // 6
				jmp(label3);
			L(label4);
				inc(eax); // 8
				jmp(exit);
			L(label3);
				inc(eax); // 7
				jmp(label4);
			L(label1);
				inc(eax); // 5
				jmp(label2);
			L(exit);
			}
			Label callLabel;
			{	// eax == 8
				Label label1;
				Label label2;
			L(label1);
				inc(eax); // 9, 10, 11, 13
				cmp(eax, 9);
				je(label1);
				// 10, 11, 13
				inc(eax); // 11, 12, 13
				cmp(eax, 11);
				je(label1);
				// 12, 13
				cmp(eax, 12);
				je(label2);
				inc(eax); // 14
				cmp(eax, 14);
				je(label2);
				ud2();
			L(label2); // 14
				inc(eax); // 13, 15
				cmp(eax, 13);
				je(label1);
			}
			call(callLabel);
			ret();
		L(callLabel);
			inc(eax); // 16
			ret();
		}
	};
	for (int i = 0; i < 2; i++) {
		const bool grow = i == 1;
		printf("testNewLabel grow=%d\n", grow);
		Code code(grow);
		if (grow) code.ready();
		int (*f)() = code.getCode<int (*)()>();
		int r = f();
		CYBOZU_TEST_EQUAL(r, 16);
	}
}

CYBOZU_TEST_AUTO(returnLabel)
{
	struct Code : Xbyak::CodeGenerator {
		Code()
		{
			xor_(eax, eax);
		Label L1 = L();
			test(eax, eax);
			Label exit;
			jnz(exit);
			inc(eax); // 1
			Label L2;
			call(L2);
			jmp(L1);
		L(L2);
			inc(eax); // 2
			ret();
		L(exit);
			inc(eax); // 3
			ret();
		}
	};
	Code code;
	int (*f)() = code.getCode<int (*)()>();
	int r = f();
	CYBOZU_TEST_EQUAL(r, 3);
}

CYBOZU_TEST_AUTO(testAssign)
{
	struct Code : Xbyak::CodeGenerator {
		Code(bool grow)
			: Xbyak::CodeGenerator(grow ? 128 : 4096, grow ? Xbyak::AutoGrow : 0)
		{
			xor_(eax, eax);
			Label dst, src;
		L(src);
			inc(eax);
			cmp(eax, 1);
			je(dst);
			inc(eax); // 2, 3, 5
			cmp(eax, 5);
			putNop(this, 128);
			jne(dst, T_NEAR);
			ret();
		assignL(dst, src);
			// test of copy  label
			{
				Label sss(dst);
				{
					Label ttt;
					ttt = src;
				}
			}
		}
	};
	for (int i = 0; i < 2; i++) {
		const bool grow = i == 0;
		printf("testAssign grow=%d\n", grow);
		Code code(grow);
		if (grow) code.ready();
		int (*f)() = code.getCode<int (*)()>();
		int ret = f();
		CYBOZU_TEST_EQUAL(ret, 5);
    }
}

CYBOZU_TEST_AUTO(doubleDefine)
{
	struct Code : Xbyak::CodeGenerator {
		Code()
		{
			{
				Label label;
			L(label);
				// forbitten double L()
				CYBOZU_TEST_EXCEPTION(L(label), Xbyak::Error);
			}
			{
				Label label;
				jmp(label);
				CYBOZU_TEST_ASSERT(hasUndefinedLabel());
			}
			{
				Label label1, label2;
			L(label1);
				jmp(label2);
				assignL(label2, label1);
				// forbitten double assignL
				CYBOZU_TEST_EXCEPTION(assignL(label2, label1), Xbyak::Error);
			}
			{
				Label label1, label2;
			L(label1);
				jmp(label2);
				// forbitten assignment to label1 set by L()
				CYBOZU_TEST_EXCEPTION(assignL(label1, label2), Xbyak::Error);
			}
		}
	} code;
}

struct GetAddressCode1 : Xbyak::CodeGenerator {
	void test()
	{
		Xbyak::Label L1, L2, L3;
		nop();
	L(L1);
		const uint8_t *p1 = getCurr();
		CYBOZU_TEST_EQUAL_POINTER(L1.getAddress(), p1);

		nop();
		jmp(L2);
		nop();
		jmp(L3);
	L(L2);
		CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), getCurr());
		// L3 is not defined
		CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), 0);

		// L3 is set by L1
		assignL(L3, L1);
		CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), p1);
	}
};

struct CodeLabelTable : Xbyak::CodeGenerator {
	enum { ret0 = 3 };
	enum { ret1 = 5 };
	enum { ret2 = 8 };
	CodeLabelTable()
	{
		using namespace Xbyak;
#ifdef XBYAK64_WIN
		const Reg64& p0 = rcx;
		const Reg64& a = rax;
#elif defined (XBYAK64_GCC)
		const Reg64& p0 = rdi;
		const Reg64& a = rax;
#else
		const Reg32& p0 = edx;
		const Reg32& a = eax;
		mov(edx, ptr [esp + 4]);
#endif
		Label labelTbl, L0, L1, L2;
		mov(a, labelTbl);
		jmp(ptr [a + p0 * sizeof(void*)]);
	L(labelTbl);
		putL(L0);
		putL(L1);
		putL(L2);
	L(L0);
		mov(a, ret0);
		ret();
	L(L1);
		mov(a, ret1);
		ret();
	L(L2);
		mov(a, ret2);
		ret();
	}
};

CYBOZU_TEST_AUTO(LabelTable)
{
	CodeLabelTable c;
	int (*f)(int) = c.getCode<int (*)(int)>();
	CYBOZU_TEST_EQUAL(f(0), c.ret0);
	CYBOZU_TEST_EQUAL(f(1), c.ret1);
	CYBOZU_TEST_EQUAL(f(2), c.ret2);
}

CYBOZU_TEST_AUTO(getAddress1)
{
	GetAddressCode1 c;
	c.test();
}

struct GetAddressCode2 : Xbyak::CodeGenerator {
	Xbyak::Label L1, L2, L3;
	size_t a1;
	size_t a3;
	explicit GetAddressCode2(int size)
		: Xbyak::CodeGenerator(size, size == 4096 ? 0 : Xbyak::AutoGrow)
		, a1(0)
		, a3(0)
	{
		bool autoGrow = size != 4096;
		nop();
	L(L1);
		if (autoGrow) {
			CYBOZU_TEST_EQUAL_POINTER(L1.getAddress(), 0);
		}
		a1 = getSize();
		nop();
		jmp(L2);
		if (autoGrow) {
			CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), 0);
		}
	L(L3);
		a3 = getSize();
		if (autoGrow) {
			CYBOZU_TEST_EQUAL_POINTER(L3.getAddress(), 0);
		}
		nop();
		assignL(L2, L1);
		if (autoGrow) {
			CYBOZU_TEST_EQUAL_POINTER(L2.getAddress(), 0);
		}
	}
};

CYBOZU_TEST_AUTO(getAddress2)
{
	const int sizeTbl[] = {
		2, 128, // grow
		4096 // not grow
	};
	for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(sizeTbl); i++) {
		int size = sizeTbl[i];
		GetAddressCode2 c(size);
		c.ready();
		const uint8_t *p = c.getCode();
		CYBOZU_TEST_EQUAL(c.L1.getAddress(), p + c.a1);
		CYBOZU_TEST_EQUAL(c.L3.getAddress(), p + c.a3);
		CYBOZU_TEST_EQUAL(c.L2.getAddress(), p + c.a1);
	}
}

#ifdef XBYAK64
CYBOZU_TEST_AUTO(rip)
{
	int a[] = { 1, 10 };
	int b[] = { 100, 1000 };
	struct Code : Xbyak::CodeGenerator {
		Code(const int *a, const int *b)
		{
			Label label1, label2;
			jmp("@f");
		L(label1);
			db(a[0], 4);
			db(a[1], 4);
		L("@@");
			mov(eax, ptr [rip + label1]);       // a[0]
			mov(ecx, ptr [rip + label1+4]);     // a[1]
			mov(edx, ptr [rip + label2-8+2+6]); // b[0]
			add(ecx, ptr [rip + 16+label2-12]); // b[1]
			add(eax, ecx);
			add(eax, edx);
			ret();
		L(label2);
			db(b[0], 4);
			db(b[1], 4);

			// error
			CYBOZU_TEST_EXCEPTION(rip + label1 + label2, Xbyak::Error);
		}
	} code(a, b);
	int ret = code.getCode<int (*)()>()();
	CYBOZU_TEST_EQUAL(ret, a[0] + a[1] + b[0] + b[1]);
}

int ret1234()
{
	return 1234;
}

int ret9999()
{
	return 9999;
}

CYBOZU_TEST_AUTO(rip_jmp)
{
	struct Code : Xbyak::CodeGenerator {
		Code()
		{
			Label label;
			xor_(eax, eax);
			call(ptr [rip + label]);
			mov(ecx, eax);
			call(ptr [rip + label + 8]);
			add(eax, ecx);
			ret();
		L(label);
			db((size_t)ret1234, 8);
			db((size_t)ret9999, 8);
		}
	} code;
	int ret = code.getCode<int (*)()>()();
	CYBOZU_TEST_EQUAL(ret, ret1234() + ret9999());
}

#if 0
CYBOZU_TEST_AUTO(rip_addr)
{
	/*
		we can't assume |&x - &code| < 2GiB anymore
	*/
	static int x = 5;
	struct Code : Xbyak::CodeGenerator {
		Code()
		{
			mov(eax, 123);
			mov(ptr[rip + &x], eax);
			ret();
		}
	} code;
	code.getCode<void (*)()>()();
	CYBOZU_TEST_EQUAL(x, 123);
}
#endif

#ifndef __APPLE__
CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf)
{
	MIE_ALIGN(4096) static char buf[8192];
	static char *p = buf + 4096;
	static int *x0 = (int*)buf;
	static int *x1 = x0 + 1;
	struct Code : Xbyak::CodeGenerator {
		Code() : Xbyak::CodeGenerator(4096, p)
		{
			mov(eax, 123);
			mov(ptr[rip + x0], eax);
			mov(dword[rip + x1], 456);
			mov(byte[rip + 1 + x1 + 3], 99);
			ret();
		}
	} code;
	code.setProtectModeRE();
	code.getCode<void (*)()>()();
	CYBOZU_TEST_EQUAL(*x0, 123);
	CYBOZU_TEST_EQUAL(*x1, 456);
	CYBOZU_TEST_EQUAL(buf[8], 99);
	code.setProtectModeRW();
}
#endif
#endif

struct ReleaseTestCode : Xbyak::CodeGenerator {
	ReleaseTestCode(Label& L1, Label& L2, Label& L3)
	{
		L(L1);
		jmp(L1);
		L(L2);
		jmp(L3); // not assigned
	}
};

/*
	code must unlink label if code is destroyed
*/
CYBOZU_TEST_AUTO(release_label_after_code)
{
	puts("---");
	{
		Label L1, L2, L3, L4, L5;
		{
			ReleaseTestCode code(L1, L2, L3);
			CYBOZU_TEST_ASSERT(L1.getId() > 0);
			CYBOZU_TEST_ASSERT(L1.getAddress() != 0);
			CYBOZU_TEST_ASSERT(L2.getId() > 0);
			CYBOZU_TEST_ASSERT(L2.getAddress() != 0);
			CYBOZU_TEST_ASSERT(L3.getId() > 0);
			CYBOZU_TEST_ASSERT(L3.getAddress() == 0); // L3 is not assigned
			code.assignL(L4, L1);
			L5 = L1;
			printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId());
		}
		puts("code is released");
		CYBOZU_TEST_ASSERT(L1.getId() == 0);
		CYBOZU_TEST_ASSERT(L1.getAddress() == 0);
		CYBOZU_TEST_ASSERT(L2.getId() == 0);
		CYBOZU_TEST_ASSERT(L2.getAddress() == 0);
//		CYBOZU_TEST_ASSERT(L3.getId() == 0); // L3 is not assigned so not cleared
		CYBOZU_TEST_ASSERT(L3.getAddress() == 0);
		CYBOZU_TEST_ASSERT(L4.getId() == 0);
		CYBOZU_TEST_ASSERT(L4.getAddress() == 0);
		CYBOZU_TEST_ASSERT(L5.getId() == 0);
		CYBOZU_TEST_ASSERT(L5.getAddress() == 0);
		printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId());
	}
}

struct JmpTypeCode : Xbyak::CodeGenerator {
	void nops()
	{
		for (int i = 0; i < 130; i++) {
			nop();
		}
	}
	// return jmp code size
	size_t gen(bool pre, bool large, Xbyak::CodeGenerator::LabelType type)
	{
		Label label;
		if (pre) {
			L(label);
			if (large) nops();
			size_t pos = getSize();
			jmp(label, type);
			return getSize() - pos;
		} else {
			size_t pos = getSize();
			jmp(label, type);
			size_t size = getSize() - pos;
			if (large) nops();
			L(label);
			return size;
		}
	}
};

CYBOZU_TEST_AUTO(setDefaultJmpNEAR)
{
	const Xbyak::CodeGenerator::LabelType T_SHORT = Xbyak::CodeGenerator::T_SHORT;
	const Xbyak::CodeGenerator::LabelType T_NEAR = Xbyak::CodeGenerator::T_NEAR;
	const Xbyak::CodeGenerator::LabelType T_AUTO = Xbyak::CodeGenerator::T_AUTO;
	const struct {
		bool pre;
		bool large;
		Xbyak::CodeGenerator::LabelType type;
		size_t expect1; // 0 means exception
		size_t expect2;
	} tbl[] = {
		{ false, false, T_SHORT, 2, 2 },
		{ false, false, T_NEAR, 5, 5 },
		{ false, true, T_SHORT, 0, 0 },
		{ false, true, T_NEAR, 5, 5 },

		{ true, false, T_SHORT, 2, 2 },
		{ true, false, T_NEAR, 5, 5 },
		{ true, true, T_SHORT, 0, 0 },
		{ true, true, T_NEAR, 5, 5 },

		{ false, false, T_AUTO, 2, 5 },
		{ false, true, T_AUTO, 0, 5 },
		{ true, false, T_AUTO, 2, 2 },
		{ true, true, T_AUTO, 5, 5 },
	};
	JmpTypeCode code1, code2;
	code2.setDefaultJmpNEAR(true);
	for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) {
		if (tbl[i].expect1) {
			size_t size = code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type);
			CYBOZU_TEST_EQUAL(size, tbl[i].expect1);
		} else {
			CYBOZU_TEST_EXCEPTION(code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception);
		}
		if (tbl[i].expect2) {
			size_t size = code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type);
			CYBOZU_TEST_EQUAL(size, tbl[i].expect2);
		} else {
			CYBOZU_TEST_EXCEPTION(code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception);
		}
	}
}
