#define XBYAK_NO_OP_NAMES #include "xbyak/xbyak.h" #include #include #include #include #ifdef _MSC_VER #pragma warning(disable : 4996) // scanf #define snprintf _snprintf_s #endif class Brainfuck : public Xbyak::CodeGenerator { private: enum Direction { B, F }; std::string toStr(int labelNo, Direction dir) { return Xbyak::Label::toStr(labelNo) + (dir == B ? 'B' : 'F'); } public: int getContinuousChar(std::istream& is, char c) { int count = 1; char p; while (is >> p) { if (p != c) break; count++; } is.unget(); return count; } Brainfuck(std::istream& is) : CodeGenerator(100000) { // void (*)(void* putchar, void* getchar, int *stack) using namespace Xbyak; #ifdef XBYAK32 const Reg32& pPutchar(esi); const Reg32& pGetchar(edi); const Reg32& stack(ebp); const Address cur = dword [stack]; push(ebp); // stack push(esi); push(edi); const int P_ = 4 * 3; mov(pPutchar, ptr[esp + P_ + 4]); // putchar mov(pGetchar, ptr[esp + P_ + 8]); // getchar mov(stack, ptr[esp + P_ + 12]); // stack #elif defined(XBYAK64_WIN) const Reg64& pPutchar(rsi); const Reg64& pGetchar(rdi); const Reg64& stack(rbp); // stack const Address cur = dword [stack]; push(rsi); push(rdi); push(rbp); mov(pPutchar, rcx); // putchar mov(pGetchar, rdx); // getchar mov(stack, r8); // stack #else const Reg64& pPutchar(rbx); const Reg64& pGetchar(rbp); const Reg64& stack(r12); // stack const Address cur = dword [stack]; push(rbx); push(rbp); push(r12); mov(pPutchar, rdi); // putchar mov(pGetchar, rsi); // getchar mov(stack, rdx); // stack #endif int labelNo = 0; std::stack keepLabelNo; char c; while (is >> c) { switch (c) { case '+': case '-': { int count = getContinuousChar(is, c); if (count == 1) { c == '+' ? inc(cur) : dec(cur); } else { add(cur, (c == '+' ? count : -count)); } } break; case '>': case '<': { int count = getContinuousChar(is, c); add(stack, 4 * (c == '>' ? count : -count)); } break; case '.': #ifdef XBYAK32 push(cur); call(pPutchar); pop(eax); #elif defined(XBYAK64_WIN) mov(ecx, cur); sub(rsp, 32); call(pPutchar); add(rsp, 32); #else mov(edi, cur); call(pPutchar); #endif break; case ',': #if defined(XBYAK32) || defined(XBYAK64_GCC) call(pGetchar); #elif defined(XBYAK64_WIN) sub(rsp, 32); call(pGetchar); add(rsp, 32); #endif mov(cur, eax); break; case '[': L(toStr(labelNo, B)); mov(eax, cur); test(eax, eax); jz(toStr(labelNo, F), T_NEAR); keepLabelNo.push(labelNo++); break; case ']': { int no = keepLabelNo.top(); keepLabelNo.pop(); jmp(toStr(no, B)); L(toStr(no, F)); } break; default: break; } } #ifdef XBYAK32 pop(edi); pop(esi); pop(ebp); #elif defined(XBYAK64_WIN) pop(rbp); pop(rdi); pop(rsi); #else pop(r12); pop(rbp); pop(rbx); #endif ret(); } }; void dump(const Xbyak::uint8 *code, size_t size) { puts("#include \nstatic int stack[128 * 1024];"); #ifdef _MSC_VER printf("static __declspec(align(4096)) "); #else printf("static __attribute__((aligned(4096)))"); #endif puts("const unsigned char code[] = {"); for (size_t i = 0; i < size; i++) { printf("0x%02x,", code[i]); if ((i % 16) == 15) putchar('\n'); } puts("\n};"); #ifdef _MSC_VER puts("#include "); #else puts("#include "); puts("#include "); #endif puts("int main()\n{"); #ifdef _MSC_VER puts("\tDWORD oldProtect;"); puts("\tVirtualProtect((void*)code, sizeof(code), PAGE_EXECUTE_READWRITE, &oldProtect);"); #else puts("\tlong pageSize = sysconf(_SC_PAGESIZE) - 1;"); puts("\tmprotect((void*)code, (sizeof(code) + pageSize) & ~pageSize, PROT_READ | PROT_EXEC);"); #endif puts( "\t((void (*)(void*, void*, int *))code)((void*)putchar, (void*)getchar, stack);\n" "}" ); } int main(int argc, char *argv[]) { #ifdef XBYAK32 fprintf(stderr, "32bit mode\n"); #else fprintf(stderr, "64bit mode\n"); #endif if (argc == 1) { fprintf(stderr, "bf filename.bf [0|1]\n"); return 1; } std::ifstream ifs(argv[1]); int mode = argc == 3 ? atoi(argv[2]) : 0; try { Brainfuck bf(ifs); if (mode == 0) { static int stack[128 * 1024]; bf.getCode()(Xbyak::CastTo(putchar), Xbyak::CastTo(getchar), stack); } else { dump(bf.getCode(), bf.getSize()); } } catch (std::exception& e) { printf("ERR:%s\n", e.what()); } catch (...) { printf("unknown error\n"); } }