<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "../../util/alloc.h"

#include "../type.h"
#include "../type_nav.h"
#include "../type_is.h"
#include "../pack.h"

#include "../cc1.h" /* cc1_mstack_align */

#include "val.h"
#include "out.h"
#include "virt.h"
#include "ctx.h"
#include "asm.h"
#include "impl.h"

#define TODO() \
	fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, __func__)

void out_flush_volatile(out_ctx *octx, out_val *val)
{
	out_val_consume(octx, v_to_reg(octx, val));
}

int v_is_const_reg(out_val *v)
{
	return v->type == V_REG
		&& impl_reg_frame_const(&v->bits.regoff.reg);
}

out_val *v_to_stack_mem(out_ctx *octx, out_val *vp, long stack_pos)
{
	out_val *store = v_new_sp3(octx, NULL, vp->t, stack_pos);

	vp = v_to(octx, vp, TO_CONST | TO_REG);

	out_val_retain(octx, store);

	out_store(octx, store, vp);

	return store;
}

void v_reg_to_stack(
		out_ctx *octx,
		const struct vreg *vr,
		type *ty, long where)
{
	out_val *reg = v_new_reg(octx, NULL, ty, vr);
	out_flush_volatile(octx,
			v_to_stack_mem(octx, reg, -where));
}

static int v_in(out_val *vp, enum vto to)
{
	switch(vp->type){
		case V_FLAG:
			break;

		case V_CONST_I:
		case V_CONST_F:
			return !!(to & TO_CONST);

		case V_REG:
			return (to & TO_REG) && vp->bits.regoff.offset == 0;

		case V_REG_SAVE:
		case V_LBL:
			return !!(to & TO_MEM);
	}

	return 0;
}

static out_val *v_save_reg(out_ctx *octx, out_val *vp)
{
	assert(vp->type == V_REG && "not reg");

	out_comment("register spill:");

	v_alloc_stack(octx, type_size(vp->t, NULL), "save reg");

	return v_to_stack_mem(
			octx, vp,
			-octx->stack_sz);
}

out_val *v_to(out_ctx *octx, out_val *vp, enum vto loc)
{
	if(v_in(vp, loc))
		return vp;

	/* TO_CONST can't be done - it should already be const,
	 * or another option should be chosen */

	/* go for register first */
	if(loc & TO_REG){
		return v_to_reg(octx, vp);
	}

	if(loc & TO_MEM){
		vp = v_to_reg(octx, vp);
		return v_save_reg(octx, vp);
	}

	assert(0 && "can't satisfy v_to");
}

static ucc_wur out_val *v_freeup_regp(out_ctx *octx, out_val *vp)
{
	struct vreg r;
	int got_reg;

	assert(vp->type == V_REG && "not reg");

	/* attempt to save to a register first */
	got_reg = v_unused_reg(octx, 0, vp->bits.regoff.reg.is_float, &r);

	if(got_reg){
		/* move 'vp' into the fresh reg */
		impl_reg_cp(octx, vp, &r);

		/* change vp's register to 'r', so that vp's original register is free */
		vp->bits.regoff.reg = r;

		return vp;

	}else{
		/* no free registers, save this one to the stack and mutate vp */
		return v_save_reg(octx, vp);
		/* vp is now a stack value */
	}
}

static out_val *v_find_reg(out_ctx *octx, const struct vreg *reg)
{
	out_val_list *i;

	for(i = octx->val_head; i; i = i->next){
		out_val *v = &i->val;
		if(v->type == V_REG && vreg_eq(&v->bits.regoff.reg, reg))
			return v;
	}

	return NULL;
}

void v_freeup_reg(out_ctx *octx, const struct vreg *r)
{
	out_val *v = v_find_reg(octx, r);

	if(v)
		out_val_consume(octx, v_freeup_regp(octx, v));
}

int v_unused_reg(
		out_ctx *octx,
		int stack_as_backup, int fp,
		struct vreg *out)
{
	unsigned char used[sizeof(octx->reserved_regs)];
	out_val_list *it;
	out_val *first;
	int i;

	memcpy(used, octx->reserved_regs, sizeof used);
	first = NULL;

	for(it = octx->val_head; it; it = it->next){
		out_val *this = &it->val;
		if(this->retains
		&& this->type == V_REG
		&& this->bits.regoff.reg.is_float == fp)
		{
			if(!first)
				first = this;
			used[impl_reg_to_scratch(&this->bits.regoff.reg)] = 1;
		}
	}

	for(i = 0; i < (fp ? N_SCRATCH_REGS_F : N_SCRATCH_REGS_I); i++)
		if(!used[i]){
			impl_scratch_to_reg(i, out);
			out->is_float = fp;
			return 1;
		}

	if(stack_as_backup){
		/* no free regs, move `first` to the stack and claim its reg */
		*out = first->bits.regoff.reg;

		out_val_consume(octx, v_freeup_regp(octx, first));

		return 1;
	}
	return 0;
}

out_val *v_to_reg_given(
		out_ctx *octx, out_val *from,
		const struct vreg *given)
{
	return impl_load(octx, from, given);
}

out_val *v_to_reg_out(out_ctx *octx, out_val *conv, struct vreg *out)
{
	if(conv->type != V_REG){
		struct vreg chosen;
		if(!out)
			out = &chosen;

		/* get a register */
		v_unused_reg(octx, 1, type_is_floating(conv->t), out);

		/* load into register */
		return v_to_reg_given(octx, conv, out);

	}else{
		if(out)
			memcpy_safe(out, &conv->bits.regoff.reg);

		return conv;
	}
}

out_val *v_to_reg(out_ctx *octx, out_val *conv)
{
	return v_to_reg_out(octx, conv, NULL);
}

static int val_present(out_val *v, out_val **ignores)
{
	out_val **i;
	for(i = ignores; i && *i; i++)
		if(v == *i)
			return 1;
	return 0;
}

void v_save_regs(out_ctx *octx, type *func_ty, out_val *ignores[])
{
	/* save all registers except callee save */
	out_val_list *l;

	/* go backwards in case the list is added to */
	for(l = octx->val_tail; l; l = l->prev){
		out_val *v = &l->val;
		int save = 0;

		if(v->retains == 0)
			continue;

		switch(v->type){
			case V_REG_SAVE:
			case V_REG:
				if(val_present(v, ignores)){
					/* don't save */
				}else if(!impl_reg_savable(&v->bits.regoff.reg)){
					/* don't save stack references */
					if(fopt_mode & FOPT_VERBOSE_ASM)
						out_comment("not saving const-reg %d", v->bits.regoff.reg.idx);

				}else if(func_ty
				&& impl_reg_is_callee_save(&v->bits.regoff.reg, func_ty))
				{
					/* only comment for non-const regs */
					out_comment("not saving reg %d - callee save",
							v->bits.regoff.reg.idx);

				}else{
					out_comment("saving register %d", v->bits.regoff.reg.idx);
					save = 1;
				}
				break;

			case V_CONST_I:
			case V_CONST_F:
			case V_LBL:
				save = 0;
				break;

			case V_FLAG:
				assert(v->retains == 1 && "v_save_regs(): retained v");
				v = v_to_reg(octx, v);
				save = 1;
		}

		if(save)
			v_save_reg(octx, v);
	}
}

void v_stack_adj(out_ctx *octx, unsigned amt, int sub)
{
	out_val_consume(
			octx,
			out_op(
				octx, sub ? op_minus : op_plus,
				v_new_sp(octx, NULL),
				out_new_l(
					octx,
					type_nav_btype(cc1_type_nav, type_intptr_t),
					amt)));
}

unsigned v_alloc_stack2(
		out_ctx *octx,
		const unsigned sz_initial, int noop, const char *desc)<commit_msg>v_save_regs() replaces old registers with the stack'd value<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "../../util/alloc.h"

#include "../type.h"
#include "../type_nav.h"
#include "../type_is.h"
#include "../pack.h"

#include "../cc1.h" /* cc1_mstack_align */

#include "val.h"
#include "out.h"
#include "virt.h"
#include "ctx.h"
#include "asm.h"
#include "impl.h"

#define TODO() \
	fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, __func__)

void out_flush_volatile(out_ctx *octx, out_val *val)
{
	out_val_consume(octx, v_to_reg(octx, val));
}

int v_is_const_reg(out_val *v)
{
	return v->type == V_REG
		&& impl_reg_frame_const(&v->bits.regoff.reg);
}

out_val *v_to_stack_mem(out_ctx *octx, out_val *vp, long stack_pos)
{
	out_val *store = v_new_sp3(octx, NULL, vp->t, stack_pos);

	vp = v_to(octx, vp, TO_CONST | TO_REG);

	out_val_retain(octx, store);

	out_store(octx, store, vp);

	return store;
}

void v_reg_to_stack(
		out_ctx *octx,
		const struct vreg *vr,
		type *ty, long where)
{
	out_val *reg = v_new_reg(octx, NULL, ty, vr);
	out_flush_volatile(octx,
			v_to_stack_mem(octx, reg, -where));
}

static int v_in(out_val *vp, enum vto to)
{
	switch(vp->type){
		case V_FLAG:
			break;

		case V_CONST_I:
		case V_CONST_F:
			return !!(to & TO_CONST);

		case V_REG:
			return (to & TO_REG) && vp->bits.regoff.offset == 0;

		case V_REG_SAVE:
		case V_LBL:
			return !!(to & TO_MEM);
	}

	return 0;
}

static ucc_wur out_val *v_save_reg(out_ctx *octx, out_val *vp)
{
	assert(vp->type == V_REG && "not reg");

	out_comment("register spill:");

	v_alloc_stack(octx, type_size(vp->t, NULL), "save reg");

	return v_to_stack_mem(
			octx, vp,
			-octx->stack_sz);
}

out_val *v_to(out_ctx *octx, out_val *vp, enum vto loc)
{
	if(v_in(vp, loc))
		return vp;

	/* TO_CONST can't be done - it should already be const,
	 * or another option should be chosen */

	/* go for register first */
	if(loc & TO_REG){
		return v_to_reg(octx, vp);
	}

	if(loc & TO_MEM){
		vp = v_to_reg(octx, vp);
		return v_save_reg(octx, vp);
	}

	assert(0 && "can't satisfy v_to");
}

static ucc_wur out_val *v_freeup_regp(out_ctx *octx, out_val *vp)
{
	struct vreg r;
	int got_reg;

	assert(vp->type == V_REG && "not reg");

	/* attempt to save to a register first */
	got_reg = v_unused_reg(octx, 0, vp->bits.regoff.reg.is_float, &r);

	if(got_reg){
		/* move 'vp' into the fresh reg */
		impl_reg_cp(octx, vp, &r);

		/* change vp's register to 'r', so that vp's original register is free */
		vp->bits.regoff.reg = r;

		return vp;

	}else{
		/* no free registers, save this one to the stack and mutate vp */
		return v_save_reg(octx, vp);
		/* vp is now a stack value */
	}
}

static out_val *v_find_reg(out_ctx *octx, const struct vreg *reg)
{
	out_val_list *i;

	for(i = octx->val_head; i; i = i->next){
		out_val *v = &i->val;
		if(v->type == V_REG && vreg_eq(&v->bits.regoff.reg, reg))
			return v;
	}

	return NULL;
}

void v_freeup_reg(out_ctx *octx, const struct vreg *r)
{
	out_val *v = v_find_reg(octx, r);

	if(v)
		out_val_consume(octx, v_freeup_regp(octx, v));
}

int v_unused_reg(
		out_ctx *octx,
		int stack_as_backup, int fp,
		struct vreg *out)
{
	unsigned char used[sizeof(octx->reserved_regs)];
	out_val_list *it;
	out_val *first;
	int i;

	memcpy(used, octx->reserved_regs, sizeof used);
	first = NULL;

	for(it = octx->val_head; it; it = it->next){
		out_val *this = &it->val;
		if(this->retains
		&& this->type == V_REG
		&& this->bits.regoff.reg.is_float == fp)
		{
			if(!first)
				first = this;
			used[impl_reg_to_scratch(&this->bits.regoff.reg)] = 1;
		}
	}

	for(i = 0; i < (fp ? N_SCRATCH_REGS_F : N_SCRATCH_REGS_I); i++)
		if(!used[i]){
			impl_scratch_to_reg(i, out);
			out->is_float = fp;
			return 1;
		}

	if(stack_as_backup){
		/* no free regs, move `first` to the stack and claim its reg */
		*out = first->bits.regoff.reg;

		out_val_consume(octx, v_freeup_regp(octx, first));

		return 1;
	}
	return 0;
}

out_val *v_to_reg_given(
		out_ctx *octx, out_val *from,
		const struct vreg *given)
{
	return impl_load(octx, from, given);
}

out_val *v_to_reg_out(out_ctx *octx, out_val *conv, struct vreg *out)
{
	if(conv->type != V_REG){
		struct vreg chosen;
		if(!out)
			out = &chosen;

		/* get a register */
		v_unused_reg(octx, 1, type_is_floating(conv->t), out);

		/* load into register */
		return v_to_reg_given(octx, conv, out);

	}else{
		if(out)
			memcpy_safe(out, &conv->bits.regoff.reg);

		return conv;
	}
}

out_val *v_to_reg(out_ctx *octx, out_val *conv)
{
	return v_to_reg_out(octx, conv, NULL);
}

static int val_present(out_val *v, out_val **ignores)
{
	out_val **i;
	for(i = ignores; i && *i; i++)
		if(v == *i)
			return 1;
	return 0;
}

void v_save_regs(out_ctx *octx, type *func_ty, out_val *ignores[])
{
	/* save all registers except callee save */
	out_val_list *l;

	/* go backwards in case the list is added to */
	for(l = octx->val_tail; l; l = l->prev){
		out_val *v = &l->val;
		int save = 0;

		if(v->retains == 0)
			continue;

		switch(v->type){
			case V_REG_SAVE:
			case V_REG:
				if(val_present(v, ignores)){
					/* don't save */
				}else if(!impl_reg_savable(&v->bits.regoff.reg)){
					/* don't save stack references */
					if(fopt_mode & FOPT_VERBOSE_ASM)
						out_comment("not saving const-reg %d", v->bits.regoff.reg.idx);

				}else if(func_ty
				&& impl_reg_is_callee_save(&v->bits.regoff.reg, func_ty))
				{
					/* only comment for non-const regs */
					out_comment("not saving reg %d - callee save",
							v->bits.regoff.reg.idx);

				}else{
					out_comment("saving register %d", v->bits.regoff.reg.idx);
					save = 1;
				}
				break;

			case V_CONST_I:
			case V_CONST_F:
			case V_LBL:
				save = 0;
				break;

			case V_FLAG:
				assert(v->retains == 1 && "v_save_regs(): retained v");
				v = v_to_reg(octx, v);
				save = 1;
		}

		if(save){
			out_val *new = v_save_reg(octx, v);
			out_val_overwrite(v, new);
			out_val_release(octx, new);
		}
	}
}

void v_stack_adj(out_ctx *octx, unsigned amt, int sub)
{
	out_val_consume(
			octx,
			out_op(
				octx, sub ? op_minus : op_plus,
				v_new_sp(octx, NULL),
				out_new_l(
					octx,
					type_nav_btype(cc1_type_nav, type_intptr_t),
					amt)));
}

unsigned v_alloc_stack2(
		out_ctx *octx,
		const unsigned sz_initial, int noop, const char *desc)
{'fraction_non_alphanumeric': 0.1577776011445831, 'fraction_numerical': 0.003656307129798903, 'mean_word_length': 3.5112943707422013, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '722683', 'n_tokens_mistral': 6037, 'n_tokens_neox': 5454, 'n_words': 1261}