aboutsummaryrefslogtreecommitdiff
#include "wasm_compile.h"
#include "wasm.h"
#include "stack_machine_instruction.h"

/* instruction structs are connected in a circular list */
void free_expr(struct instruction *expr)
{
	struct instruction *tmp;

	if (expr) {
		tmp = expr->next;
		while (tmp != expr) {
			tmp = tmp->next;
			free(tmp->prev);
		}

		free(expr);
	}
}

/* instructions are stored in a circular list */
int add_instruction(struct instruction **expr, uint16_t encoding,
		    struct instruction_data data, const char *name)
{
	struct instruction *new;

	new = malloc(sizeof(struct instruction));

	if (!new) {
		PRERR(MSG_ALLOC_FAIL(sizeof(struct instruction)));
		return -1;
	}

	new->address_assigned = false;
	new->encoding = encoding;
	new->data = data;
	new->name = name;

	if (!*expr) {
		new->next = new;
		new->prev = new;
		*expr = new;
	} else {
		new->next = *expr;
		new->prev = (*expr)->prev;
		(*expr)->prev->next = new;
		(*expr)->prev = new;
	}

	return 0;
}

static uint8_t estimate_instruction_size(struct instruction *instruction)
{
	switch (instruction->data.info) {
	case DATA_NONE :
		return 2;
	case DATA_KNOWN :
	case DATA_INSTR_ADDR :
	case DATA_ADDR_AFTER :
		return 6;
	case DATA_KNOWN_21_BITS :
		return 4;
	default /* case DATA_KNOWN_6_BITS */ :
		return 2;
	}
}

static uint64_t estimate_expr_size(struct instruction *expr)
{
	struct instruction *tmp = expr;
	uint64_t max_expr_size = 0;

	do {
		max_expr_size += estimate_instruction_size(tmp);
		tmp = tmp->next;
	} while (tmp != expr);

	return max_expr_size;
}

static void assign_addresses_and_sizes(struct instruction *expr,
				       uint32_t address)
{
	struct instruction *tmp = expr;
	uint32_t target_addr;

	do {
		if ((tmp->data.info == DATA_INSTR_ADDR ||
		     tmp->data.info == DATA_ADDR_AFTER) &&
		    *tmp->data.data.ptr &&
		    (*tmp->data.data.ptr)->address_assigned) {
			target_addr = (*tmp->data.data.ptr)->address;

			if (tmp->data.info == DATA_ADDR_AFTER) {
				target_addr += estimate_instruction_size
					(*tmp->data.data.ptr);
			}

			tmp->data = im(target_addr);
		}

		tmp->address = address;
		tmp->address_assigned = true;

		address += estimate_instruction_size(tmp);
		tmp = tmp->next;
	} while (tmp != expr);
}

static uint16_t im_instruction(uint16_t payload)
{
	return payload | (((uint16_t) 1) << 15);
}

static void encode_instruction(struct instruction *instruction,
			       struct translated_word *memory)
{
	uint32_t im = 0;
	uint16_t encoding = instruction->encoding;
	struct translated_word *dest = memory + instruction->address / 2;

	dest->instr = instruction;

	if (instruction->data.info == DATA_INSTR_ADDR) {
		im = (*instruction->data.data.ptr)->address;
	} else if (instruction->data.info == DATA_ADDR_AFTER) {
		im = (*instruction->data.data.ptr)->address +
			estimate_instruction_size(*instruction->data.data.ptr);
	} else if (instruction->data.info != DATA_NONE) {
		im = instruction->data.data.im;
	}

	switch (instruction->data.info) {
	case DATA_INSTR_ADDR :
	case DATA_ADDR_AFTER :
	case DATA_KNOWN :
		(dest++)->contents = im_instruction(im >> 22);
	case DATA_KNOWN_21_BITS :
		(dest++)->contents = im_instruction(im >> 7);
	case DATA_KNOWN_6_BITS :
		encoding |= im & 0x7F;
	}

	dest->contents = encoding;
}

static void encode_expr(struct instruction *expr,
			struct translated_word *memory)
{
	struct instruction *tmp = expr;

	do {
		encode_instruction(tmp, memory);
		tmp = tmp->next;
	} while (tmp != expr);
}

int assemble(uint32_t memory_size, struct translated_word memory[memory_size],
	     struct module *module)
{
	uint32_t i;
	struct function *main_function = NULL;
	uint32_t current_address;
	uint64_t function_size;
	struct instruction **startup = &module->startup;
	unsigned short startup_size;
	int retval = -1;

	for (i = 0; i < module->exports_count; i++) {
		if (module->exports[i].desc == EXPORT_FUNCIDX &&
		    !strcmp(module->exports[i].name, "main")) {
			main_function = module->functions +
				module->exports[i].idx;
			break;
		}
	}

	if (!main_function) {
		PRERR("No 'main' function\n");
		goto fail;
	}

	/*
	 * We're first writing some value at STACK_FRAME_BACKUP_ADDR to be able
	 * to check, if the functions we call restore frame address properly
	 */
	if (i_const(im(0x23),                             startup) ||
	    i_store(im(STACK_FRAME_BACKUP_ADDR),          startup) ||
	    i_call (ptr(&main_function->translated_body), startup) ||
	    i_halt (                                      startup))
		goto fail;

	startup_size = estimate_expr_size(*startup);

	i = module->functions_count;
	current_address = CODE_TOP_ADDR;

	while (i--) {
		function_size = estimate_expr_size(module->functions[i]
						   .translated_body);
		if (function_size > current_address - startup_size) {
			PRERR("Not enough space for code\n");
			goto fail;
		}

		current_address -= function_size;
		module->functions[i].start_addr = current_address;
	}

	i = module->functions_count;
	while (i--)
		assign_addresses_and_sizes(module->functions[i].translated_body,
					   module->functions[i].start_addr);

	assign_addresses_and_sizes(*startup, 0x0);

	i = module->functions_count;
	while (i--)
		encode_expr(module->functions[i].translated_body, memory);

	encode_expr(*startup, memory);

	retval = 0;

fail:
	if (retval)
		PRERR("Couldn't assemble code for stack machine\n");

	return retval;
}