Skip to content
Snippets Groups Projects
demo.c 4.7 KiB
Newer Older
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
Michal Vlasák's avatar
Michal Vlasák committed

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

typedef unsigned char u8;
typedef int i32;
typedef unsigned int u32;

#if _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif

#if defined(__APPLE__) && defined(__MACH__)
#define MAP_JIT_VALUE MAP_JIT
void sys_icache_invalidate(void *start, size_t len);
#else
#define MAP_JIT_VALUE 0
#endif

Michal Vlasák's avatar
Michal Vlasák committed
#define Dst       ds
#define Dst_DECL  dasm_State **ds
#define Dst_REF   (*ds)

#define DASM_CHECKS

#include "dasm_proto.h"

#include "dasm_x86.h"

//|.arch x64
//|.actionlist our_dasm_actions
//|.globals DASM_LBL_
//|.section code
Michal Vlasák's avatar
Michal Vlasák committed

static void *
our_dasm_link_and_encode(Dst_DECL)
Michal Vlasák's avatar
Michal Vlasák committed
{
	size_t size;
	void* code;
Michal Vlasák's avatar
Michal Vlasák committed
	assert(dasm_checkstep(Dst, 0) == DASM_S_OK);
	assert(dasm_link(Dst, &size) == DASM_S_OK);
Michal Vlasák's avatar
Michal Vlasák committed
#ifdef _WIN32
	code = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Michal Vlasák's avatar
Michal Vlasák committed
#else
	code = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT_VALUE, -1, 0);
Michal Vlasák's avatar
Michal Vlasák committed
#endif
	assert(dasm_encode(Dst, code) == DASM_S_OK);
Michal Vlasák's avatar
Michal Vlasák committed
#ifdef _WIN32
	DWORD original;
	VirtualProtect(code, size, PAGE_EXECUTE_READ, &original);
Michal Vlasák's avatar
Michal Vlasák committed
#else
	mprotect(code, size, PROT_READ | PROT_EXEC);
#endif
#if defined(__APPLE__) && defined(__MACH__)
	sys_icache_invalidate(code, size);
Michal Vlasák's avatar
Michal Vlasák committed
#endif
Michal Vlasák's avatar
Michal Vlasák committed
}

enum op {
	OP_CONSTANT,
	OP_ADD,
	OP_PRINT,
	OP_INPUT,
	OP_DISCARD,
	OP_GET,
	OP_SET,
	OP_CMP,
	OP_JGT,
	OP_HALT,
};

static void *
compile(u8 *program, size_t program_len)
{
	dasm_State *dasm_state;
	dasm_State **ds = &dasm_state;
	dasm_init(Dst, DASM_MAXSECTION);
Michal Vlasák's avatar
Michal Vlasák committed
	void *our_dasm_labels[DASM_LBL__MAX];
	dasm_setupglobal(Dst, our_dasm_labels, DASM_LBL__MAX);

	dasm_setup(Dst, our_dasm_actions);
	dasm_growpc(Dst, program_len);

Michal Vlasák's avatar
Michal Vlasák committed
	//| .type STACK, int, r12
	//| .type INPUT, int, rbx

Michal Vlasák's avatar
Michal Vlasák committed
	//| push rbp
	//| mov rbp, rsp
Michal Vlasák's avatar
Michal Vlasák committed
	//| push INPUT
	//| push STACK
	//
	//| mov INPUT, rdi
Michal Vlasák's avatar
Michal Vlasák committed
	//|
Michal Vlasák's avatar
Michal Vlasák committed
	//| sub rsp, 64 * #STACK
	//| mov STACK, rsp
Michal Vlasák's avatar
Michal Vlasák committed

	u8 *instrptr = program;
	u8 *end = program + program_len;

	#define OPERAND() ((i32) ( \
		((u32)instrptr[1] << 0) | \
		((u32)instrptr[2] << 8) | \
		((u32)instrptr[3] << 16) | \
		((u32)instrptr[4] << 24)))

Michal Vlasák's avatar
Michal Vlasák committed
	while (instrptr < end) {
		enum op op = (enum op)*instrptr;

		int offset = (int) (instrptr - program);
		//|=> offset:
		//! int3

		switch (op) {
		case OP_CONSTANT: {
Michal Vlasák's avatar
Michal Vlasák committed
			int32_t operand = OPERAND();
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov dword STACK[0], operand
			//| add STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 5; break;
		}
		case OP_ADD: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov ecx, STACK[-1]
			//| add STACK[-2], ecx
			//| sub STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 1; break;
		}
		case OP_PRINT: {
			//| mov64 rdi, ((uintptr_t) "%zd\n")
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov esi, STACK[-1]
			//| sub STACK, 4
			//| mov64 rax, ((uintptr_t) printf)
Michal Vlasák's avatar
Michal Vlasák committed
			//| call rax
			instrptr += 1; break;
		}
		case OP_INPUT: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov eax, INPUT[0]
			//| mov dword STACK[0], eax
			//| add STACK, #STACK
			//| add INPUT, #INPUT
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 1; break;
		}
		case OP_DISCARD: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| sub STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 1; break;
		}
		case OP_GET: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov eax, STACK[-1 - OPERAND()]
			//| mov STACK[0], eax
			//| add STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 5; break;
		}
		case OP_SET: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov eax, STACK[-1]
			//| sub STACK, #STACK
			//| mov STACK[-1 - OPERAND()], eax
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 5; break;
		}
		case OP_CMP: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov ecx, STACK[-1]
			//| cmp STACK[-2], ecx
Michal Vlasák's avatar
Michal Vlasák committed
			//| jg >1
			//| je >2
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov ecx, -1
Michal Vlasák's avatar
Michal Vlasák committed
			//| jmp >3
			//|1:
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov ecx, 1
Michal Vlasák's avatar
Michal Vlasák committed
			//| jmp >3
			//|2:
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov ecx, 0
Michal Vlasák's avatar
Michal Vlasák committed
			//|3:
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov STACK[-2], ecx
			//| sub STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			instrptr += 1; break;
		}
Michal Vlasák's avatar
Michal Vlasák committed
		case OP_JGT: {
Michal Vlasák's avatar
Michal Vlasák committed
			int offset = (int) (instrptr - program + OPERAND());
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov eax, STACK[-1]
			//| sub STACK, #STACK
Michal Vlasák's avatar
Michal Vlasák committed
			//| test rax, rax
			//| jg => offset
			instrptr += 5; break;
Michal Vlasák's avatar
Michal Vlasák committed
		}
		case OP_HALT: {
Michal Vlasák's avatar
Michal Vlasák committed
			//| add rsp, 64 * #STACK
			//| pop STACK
			//| pop INPUT
Michal Vlasák's avatar
Michal Vlasák committed
			//| mov rsp, rbp
			//| pop rbp
			//| ret
			instrptr += 1; break;
		}
Michal Vlasák's avatar
Michal Vlasák committed
		}
Michal Vlasák's avatar
Michal Vlasák committed
	void *code = our_dasm_link_and_encode(Dst);
Michal Vlasák's avatar
Michal Vlasák committed

	dasm_free(Dst);
	return code;
}

int
main(int argc, char **argv)
{
	u8 program[] = {
		OP_INPUT, OP_INPUT,
		OP_CONSTANT, 0, 0, 0, 0,

		OP_GET, 0, 0, 0, 0,
		OP_GET, 3, 0, 0, 0,
		OP_ADD,
		OP_SET, 0, 0, 0, 0,

		OP_GET, 1, 0, 0, 0,
		OP_CONSTANT, 0xff, 0xff, 0xff, 0xff, // -1 32-bit little-endian (two's complement)
		OP_ADD,
		OP_SET, 1, 0, 0, 0,

		OP_GET, 1, 0, 0, 0,
		OP_CONSTANT, 0, 0, 0, 0,
		OP_CMP,
		OP_JGT, 0xd5, 0xff, 0xff, 0xff, // -43 in 32-bit little-endian (two's complement)

		OP_GET, 0, 0, 0, 0,
		OP_PRINT,

		OP_HALT,
	};

	if (argc != 3) {
		fprintf(stderr, "Expected exactly 2 arguments\n");
		return 1;
	}
	i32 input[] = { atoi(argv[1]), atoi(argv[2]) };
Michal Vlasák's avatar
Michal Vlasák committed

Michal Vlasák's avatar
Michal Vlasák committed
	void (*fun)(i32 *input) = compile(program, sizeof(program));
Michal Vlasák's avatar
Michal Vlasák committed
	fun(input);
Michal Vlasák's avatar
Michal Vlasák committed
	return 0;
}