/*
	sample of move(reg, LABEL);, L(LABEL), putL(LABEL);
*/
#include <stdio.h>
#define XBYAK_NO_OP_NAMES
#include <xbyak/xbyak.h>

const int expectTbl[] = {
	5, 9, 12
};

struct Code : Xbyak::CodeGenerator {
	explicit Code(int mode, size_t size, void *p)
		: Xbyak::CodeGenerator(size, p)
	{
		inLocalLabel();
#ifdef XBYAK64
		const Xbyak::Reg64& a = rax;
		const Xbyak::Reg64& c = rcx;
#ifdef XBYAK64_WIN
		mov(rax, rcx);
#else
		mov(rax, rdi);
#endif
#else
		const Xbyak::Reg32& a = eax;
		const Xbyak::Reg32& c = ecx;
		mov(a, ptr [esp + 4]);
#endif

		switch (mode) {
		case 0:
			mov(c, ".jmp_table");
			lea(c, ptr [c + a * 8]);
			jmp(c);
		align(8);
			L(".jmp_table");
			mov(a, expectTbl[0]);
			ret();
		align(8);
			mov(a, expectTbl[1]);
			ret();
		align(8);
			mov(a, expectTbl[2]);
			ret();
			break;

		case 1:
			/*
				the label for putL is defined when called
			*/
			mov(c, ".jmp_table");
			jmp(ptr [c + a * (int)sizeof(size_t)]);
		L(".label1");
			mov(a, expectTbl[0]);
			jmp(".end");
		L(".label2");
			mov(a, expectTbl[1]);
			jmp(".end");
		L(".label3");
			mov(a, expectTbl[2]);
			jmp(".end");
		L(".end");
			ret();
			ud2();

			align(8);
		L(".jmp_table");
		putL(".label1");
		putL(".label2");
		putL(".label3");
			break;

		case 2:
			/*
				the label for putL is not defined when called
			*/
			jmp(".in");
			ud2();
			align(8);
		L(".jmp_table");
		putL(".label1");
		putL(".label2");
		putL(".label3");
		L(".in");
			mov(c, ".jmp_table");
			jmp(ptr [c + a * (int)sizeof(size_t)]);
		L(".label1");
			mov(a, expectTbl[0]);
			jmp(".end");
		L(".label2");
			mov(a, expectTbl[1]);
			jmp(".end");
		L(".label3");
			mov(a, expectTbl[2]);
			jmp(".end");
		L(".end");
			ret();
			break;
		}
		outLocalLabel();
	}
};

int main()
	try
{
	for (int mode = 0; mode < 3; mode++) {
		printf("mode=%d\n", mode);
		for (int grow = 0; grow < 2; grow++) {
			printf("auto grow=%s\n", grow ? "on" : "off");
			Code c(mode, grow ? 30 : 4096, grow ? Xbyak::AutoGrow : 0);
			int (*f)(int) = c.getCode<int (*)(int)>();
			c.ready();
			for (int i = 0; i < 3; i++) {
				const int a = expectTbl[i];
				const int b = f(i);
				if (a != b) {
					printf("ERR i=%d, a=%d, b=%d\n", i, a, b);
					exit(1);
				}
			}
		}
	}
	puts("ok");
} catch (std::exception& e) {
	printf("ERR %s\n", e.what());
}
