
/********************************************
jmp.c

libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

/* this module deals with back patching jumps, breaks and continues,
   and with save and restoring code when we move code.
   There are three stacks.  If we encounter a compile error, the
   stacks are frozen, i.e., we do not attempt error recovery
   on the stacks
*/


#include "mawk.h"
#include "symtype.h"
#include "jmp.h"
#include "code.h"
#include "sizes.h"
#include "init.h"
#include "memory.h"

#define error_state  (MAWK->compile_error_count>0)


/*---------- back patching jumps  ---------------*/

void mawk_code_jmp(mawk_state_t *MAWK, int jtype, INST *target)
{
	if (error_state)
		return;

	/* WARNING: Don't emit any code before using target or
	   relocation might make it invalid */

	if (target)
		mawk_code2op(MAWK, jtype, target - (mawk_code_ptr + 1));
	else {
		register JMP *p = MAWK_ZMALLOC(MAWK, JMP);

		/* stack for back patch */
		mawk_code2op(MAWK, jtype, 0);
		p->source_offset = mawk_code_offset - 1;
		p->link = MAWK->jmp_top;
		MAWK->jmp_top = p;
	}
}

void mawk_patch_jmp(mawk_state_t *MAWK, INST *target)	/* patch a jump on the jmp_stack */
{
	register JMP *p;
	register INST *source;				/* jmp starts here */

	if (!error_state) {
#ifdef	DEBUG
		if (!MAWK->jmp_top)
			mawk_bozo(MAWK, "jmp stack underflow");
#endif

		p = MAWK->jmp_top;
		MAWK->jmp_top = p->link;
		source = p->source_offset + mawk_code_base;
		source->op = target - source;

		MAWK_ZFREE(MAWK, p);
	}
}


/*-- break and continue -------*/

void mawk_BC_new(mawk_state_t * MAWK)
{																/* mark the start of a loop */
	mawk_BC_insert(MAWK, 0, (INST *) 0);
}

void mawk_BC_insert(mawk_state_t *MAWK, int type, INST *address)
{
	register BC *p;

	if (error_state)
		return;

	if (type && !MAWK->bc_top) {
		mawk_compile_error(MAWK, "%s statement outside of loop", type == 'B' ? "break" : "continue");

		return;
	}
	else {
		p = MAWK_ZMALLOC(MAWK, BC);
		p->type = type;
		p->source_offset = address - mawk_code_base;
		p->link = MAWK->bc_top;
		MAWK->bc_top = p;
	}
}


/* patch all break and continues for one loop */
void mawk_BC_clear(mawk_state_t *MAWK, INST *B_address, INST *C_address)
{
	register BC *p, *q;
	INST *source;

	if (error_state)
		return;

	p = MAWK->bc_top;
	/* pop down to the mark node */
	while (p->type) {
		source = mawk_code_base + p->source_offset;
		source->op = (p->type == 'B' ? B_address : C_address)
			- source;

		q = p;
		p = p->link;
		MAWK_ZFREE(MAWK, q);
	}
	/* remove the mark node */
	MAWK->bc_top = p->link;
	MAWK_ZFREE(MAWK, p);
}

/*-----	 moving code --------------------------*/

#define	 NO_SCOPE	-1
 /* means relocation of resolve list not needed */

void mawk_code_push(mawk_state_t *MAWK, INST *code, unsigned len, int scope, FBLOCK *fbp)
{
	register MC *p;

	if (!error_state) {
		p = MAWK_ZMALLOC(MAWK, MC);
		p->len = len;
		p->link = MAWK->mc_top;
		MAWK->mc_top = p;

		if (len) {
			p->code = (INST *) mawk_zmalloc(MAWK, sizeof(INST) * len);
			memcpy(p->code, code, sizeof(INST) * len);
		}
		if (!MAWK->resolve_list)
			p->scope = NO_SCOPE;
		else {
			p->scope = scope;
			p->move_level = MAWK->ps.code_move_level;
			p->fbp = fbp;
			if (code != NULL)
				p->offset = code - mawk_code_base;
			else
				p->offset = 0;
		}
	}
	MAWK->ps.code_move_level++;
}

/* copy the code at the top of the mc stack to target.
   return the number of INSTs moved */

unsigned mawk_code_pop(mawk_state_t *MAWK, INST *target)
{
	register MC *p;
	unsigned len;
	int target_offset;

	if (error_state)
		return 0;

#ifdef	DEBUG
	if (!MAWK->mc_top)
		mawk_bozo(MAWK, "mc underflow");
#endif

	p = MAWK->mc_top;
	MAWK->mc_top = p->link;
	len = p->len;

	while (target + len >= mawk_code_warn) {
		target_offset = target - mawk_code_base;
		mawk_code_grow(MAWK);
		target = mawk_code_base + target_offset;
	}

	if (len) {
		memcpy(target, p->code, len * sizeof(INST));
		mawk_zfree(MAWK, p->code, len * sizeof(INST));
	}

	if (p->scope != NO_SCOPE) {
		target_offset = target - mawk_code_base;
		mawk_relocate_resolve_list(MAWK, p->scope, p->move_level, p->fbp, p->offset, len, target_offset - p->offset);
	}

	MAWK_ZFREE(MAWK, p);
	MAWK->ps.code_move_level--;
	return len;
}
