/*
 * Copyright (c) 2005 - 2007, Nils R. Weller
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include "reg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "scope.h"
#include "decl.h"
#include "type.h"
#include "functions.h"
#include "control.h"
#include "debug.h"
#include "token.h"
#include "error.h"
#include "functions.h"
#include "icode.h"
#include "stack.h"
#include "reg.h"
#include "backend.h"
#include "subexpr.h"
#include "n_libc.h"

int
reg_unused(struct reg *r) {
	struct reg	**comp;
	int		i;

	if (r->used) return 0;

	comp = r->composed_of;
	if (comp == NULL) {
		return 1;
	}
	for (i = 0; comp[i] != NULL; ++i) {
		if (!reg_unused(comp[i])) {
			return 0;
		}
	}
	return 1;
}

int
reg_allocatable(struct reg *r) {
	struct reg	**comp;
	int		i;

	if (!r->allocatable) return 0;
	comp = r->composed_of;
	if (comp == NULL) {
		return 1;
	}
	if (r->size == 8) {
		/* AMD64 */
		return reg_allocatable(r->composed_of[0]);
	}

	for (i = 0; comp[i] != NULL; ++i) {
		if (!reg_unused(comp[i])
			&& !reg_allocatable(comp[i])) {
			return 0;
		}
	}
	return 1;
}

void
reg_set_allocatable(struct reg *r) {
	/*
	 * XXX for some reason gpr0 always becomes allocatable
	 * somewhere :-(, and stuff like   stw foo, 0(0) breaks.
	 * Here's a kludge to fix this for now.
	 * To debug this, avoid calling free_preg() on gpr0 in
	 * change_preg_size(), and then throw in an abort() in
	 * free_preg() if the operand is ever gpr0
	 *
	 *          ---> To hell with PowerPC!!! <---
	 */
	if (r == &power_gprs[0]) {
		return;
	}
	r->allocatable = 1;
}

void
reg_set_unallocatable(struct reg *r) {
	r->allocatable = 0;
}	

int
is_x87_trash(struct vreg *vr) {
	if (is_floating_type(vr->type)
		&& (backend->arch == ARCH_X86
			|| (vr->type->code == TY_LDOUBLE
				&& backend->arch == ARCH_AMD64))) {
		return 1;
	}
	return 0;
}

void
vreg_set_unallocatable(struct vreg *vr) {
	if (vr->pregs[0] && vr->pregs[0]->vreg == vr) {
		reg_set_unallocatable(vr->pregs[0]);
		if (vr->is_multi_reg_obj) {
			reg_set_unallocatable(vr->pregs[1]);
		}
	}
	if (vr->from_ptr != NULL
		&& vr->from_ptr->pregs[0] != NULL
		&& vr->from_ptr->pregs[0]->vreg == vr->from_ptr) {
		reg_set_unallocatable(vr->from_ptr->pregs[0]);
	} else if (vr->parent) {
		struct vreg	*vr2 = get_parent_struct(vr);

		if (vr2->from_ptr
			&& vr2->from_ptr->pregs[0]
			&& vr2->from_ptr->pregs[0]->vreg == vr2->from_ptr) {
			reg_set_unallocatable(vr2->from_ptr->pregs[0]);
		}
	}
}

void
vreg_set_allocatable(struct vreg *vr) {
	if (vr->pregs[0] && vr->pregs[0]->vreg == vr) {
		reg_set_allocatable(vr->pregs[0]);
		if (vr->is_multi_reg_obj) {
			reg_set_allocatable(vr->pregs[1]);
		}
	}	
	if (vr->from_ptr != NULL
		&& vr->from_ptr->pregs[0] != NULL
		&& vr->from_ptr->pregs[0]->vreg == vr->from_ptr) {
		reg_set_allocatable(vr->from_ptr->pregs[0]);
	} else if (vr->parent) {
		struct vreg	*vr2 = get_parent_struct(vr);

		if (vr2->from_ptr
			&& vr2->from_ptr->pregs[0]
			&& vr2->from_ptr->pregs[0]->vreg == vr2->from_ptr) {
			reg_set_allocatable(vr2->from_ptr->pregs[0]);
		}
	}
}	

static struct vreg *
alloc_vreg(void) {
	struct vreg	*ret = n_xmalloc(sizeof *ret);
	static struct vreg	nullvreg;
	*ret = nullvreg;
	return ret;
}

struct vreg *
vreg_alloc(struct decl *dec, struct token *constant, struct vreg *from_ptr,
	struct type *ty0) {

	struct vreg	*vreg;
	struct type	*ty;

	vreg = alloc_vreg();
	if (dec != NULL) {
		/* Variable-backed */
		vreg->var_backed = dec;
		if (dec->dtype->is_vla) {
			vreg->size = 0;
		} else {	
			/* 07/09/10: Array has ptr size as far as vregs are
			 * concerned */  
			if (dec->dtype->tlist != NULL) {
				vreg->size = backend->get_ptr_size();
			} else {	
				vreg->size = backend->get_sizeof_type(dec->dtype, NULL);
			}
		}	
		vreg->type = dec->dtype;
	} else if (from_ptr != NULL) {	
		vreg->from_ptr = from_ptr;

		/* Get size of what pointer points to */
		ty = n_xmemdup(from_ptr->type, sizeof *ty);
		copy_tlist(&ty->tlist, ty->tlist->next);
		vreg->type = ty;

		if (ty->is_vla && is_immediate_vla_type(ty)) {
			vreg->size = 0;
		} else {	
			if (ty->tlist != NULL) {
				vreg->size = backend->get_ptr_size();
			} else {	
				vreg->size = backend->get_sizeof_type(ty, NULL);
			}
		}
	} else if (constant != NULL) {
		/* Const-backed */
		vreg->from_const = constant;
		vreg->size = backend->get_sizeof_const(constant);
		vreg->type = n_xmalloc(sizeof *vreg->type);
		if (constant->type == TOK_STRING_LITERAL) {
			struct ty_string	*ts = constant->data;
			copy_type(vreg->type, ts->ty, 0);
		} else {
			struct type	*ty =
				make_basic_type(constant->type);

			copy_type(vreg->type, ty, 0);
			if (vreg->type->code == TY_INT
				|| vreg->type->code == TY_LONG
				|| vreg->type->code == TY_LLONG) {
				vreg->type->sign = TOK_KEY_SIGNED;
			} else if (vreg->type->code == TY_UINT
				|| vreg->type->code == TY_ULONG
				|| vreg->type->code == TY_ULLONG) {
				vreg->type->sign = TOK_KEY_UNSIGNED;
			}
			vreg->is_nullptr_const = is_nullptr_const(constant, ty);
		}
	} else if (ty0 != NULL) {	
		vreg->type = ty0;
		if (ty0->code != TY_VOID || ty0->tlist != NULL) {
			if (ty0->is_vla) {
				vreg->size = 0;
			} else if (ty0->tlist != NULL) {
				/*
				 * 07/09/10: This was missing. When we are
				 * dealing with arrays, we probably want the
				 * pointer size instead of the array size. See
				 * do_struct_member()
				 */
				vreg->size = backend->get_ptr_size();
			} else {	
				vreg->size = backend->get_sizeof_type(ty0, NULL);
			}	
		} else {
			vreg->size = 0;
		}
	} else {
		/*
		 * Anonymous register. Will be saved on stack if it
		 * gets ``faulted in'', is modified, and then needs
		 * to be saved again
		 */
		;
		vreg->size = backend->get_ptr_size(); /* XXX hm */
	}	
	vreg->pregs[0] = NULL; /* No physical register yet */
	if (vreg->type != NULL) {
		vreg->is_multi_reg_obj
			= backend->is_multi_reg_obj(vreg->type);
	}	
	return vreg;
}


void
vreg_anonymify(struct vreg **vr, struct reg *r,
		struct reg *r2, struct icode_list *il) {
	struct vreg	*tmp;

	if (is_x87_trash(*vr)) {
	}

	if ((*vr)->var_backed
		|| (*vr)->from_const
		|| (*vr)->from_ptr
		|| (*vr)->parent
		|| (*vr)->stack_addr
		) {
		vreg_faultin_x87(r, r2, *vr, il, 0);
		tmp = vreg_alloc(NULL, NULL, NULL, (*vr)->type);

		*tmp = **vr;

		/*
		 * 11/02/07: The stuff below was missing. When
		 * anonymifying an array, it must decay into a
		 * pointer. Otherwise reloading it will go wrong.
		 * This is a VERY fundamental bug which I'm
		 * surprised to find, and which needs verification
		 * and testing
		 */
		if (tmp->type->tlist != NULL
			&& (tmp->type->tlist->type == TN_ARRAY_OF
			|| tmp->type->tlist->type == TN_VARARRAY_OF)) {
			tmp->type = n_xmemdup(tmp->type,
				 sizeof *tmp->type);
			copy_tlist(&tmp->type->tlist, tmp->type->tlist);
			tmp->type->tlist->type = TN_POINTER_TO;
			tmp->size = backend->get_sizeof_type(
				tmp->type, NULL);
		}

		vreg_map_preg(tmp, tmp->pregs[0]);
		if (tmp->pregs[1] != NULL) {
			vreg_map_preg2(tmp, tmp->pregs[1]);
		}	
		*vr = tmp;
		tmp->var_backed = NULL;
		tmp->from_ptr = NULL;
		tmp->from_const = NULL;
		tmp->parent = NULL;
		tmp->stack_addr = NULL;
		if (is_x87_trash(*vr)) {
			free_preg((*vr)->pregs[0], il, 1, 1);
			(*vr)->pregs[0] = NULL;
			tmp->pregs[0] = NULL;
		}
#if 0
	} else if ((*vr)->preg == NULL || (*vr)->preg->vreg != *vr) {
		/* Already anonymous but saved on stack */
		vreg_faultin(NULL, NULL, *vr, il, 0);
#endif
	}	
}

/*
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * does not set var_backed, etc, to null! so it does not really
 * disconnect much at all
 */
struct vreg *
vreg_disconnect(struct vreg *vr) {
	struct vreg	*ret;

	ret = n_xmemdup(vr, sizeof *vr);
	if (ret->pregs[0] && vr->pregs[0]->vreg == vr) {
		vreg_map_preg(ret, ret->pregs[0]);
	}
	if (ret->is_multi_reg_obj && vr->pregs[1]->vreg == vr) {
		vreg_map_preg2(ret, ret->pregs[1]);
	}
	return ret;
}	

static int	yes_really = 0;


struct reg *
vreg_faultin_x87(
	struct reg *r0,
	struct reg *r0_2,
	struct vreg *vr,
	struct icode_list *il,
	int whatfor) {

	struct reg	*ret;

	yes_really = 1;
	/*
	 * XXX sometimes x87 registers wrongly remain associated with 
	 * data items, which is never allowed. We probably aren't using
	 * free_preg() rigorously enough
	 */
	if (is_x87_trash(vr)) {
		vr->pregs[0] = NULL;
	}

	ret = vreg_faultin(r0, r0_2, vr, il, whatfor);
	yes_really = 0;
	return ret;
}
	

/*
 * Faults virtual register ``vr'' into a physical register, if necessary,
 * and returns a pointer to that register.
 * If a physical register needs to be saved before we can use it, it will
 * be saved on curfunc's stack.
 * The caller can supply physical register r0 to use instead of calling
 * alloc_gpr()
 *
 * XXX this assumes that every item will fit into two registers, which
 * is not be true for all cpus :(. In particular, SPARC supports quad-
 * precision floating point by combining four FPRs. Perhaps change to
 * ``struct reg **r0''.
 * I also note that only one GPR is returned, but that does not seem to
 * be a problem because currently the return value is only used for pointer
 * faultins (and pointers should occupy only one GPR on most every arch
 * that will ever be supported.)
 */
struct reg *
vreg_faultin(
	struct reg *r0,
	struct reg *r0_2,
	struct vreg *vr,
	struct icode_list *il,
	int whatfor) {

	struct reg		*r = NULL;
	struct reg		*r2 = NULL;
	struct icode_instr	*ii;
	struct vreg		*vr2 = NULL;

	if (is_x87_trash(vr) && !yes_really) {
		puts("BUG: Attempt to treat x87 register like something");
		puts("     that is actually useful.");
		abort();
	}

	if (vr->pregs[0] != NULL && vr->pregs[0]->vreg == vr) {
		/* XXX needs work for long long .... */
		if (vr->is_multi_reg_obj) {
			if (vr->pregs[1] == NULL
				|| vr->pregs[1]->vreg != vr) {
				/* Second register isn't mapped! */
				free_preg(vr->pregs[0], il, 1, 1);
				goto dofault;
			}
			debug_log_regstuff(vr->pregs[1], vr, DEBUG_LOG_REVIVE);
		}
		debug_log_regstuff(vr->pregs[0], vr, DEBUG_LOG_REVIVE);
		if (r0 == NULL) {
			/* Already loaded */
			vreg_map_preg(vr, vr->pregs[0]);
			if (vr->is_multi_reg_obj) {
				vreg_map_preg2( vr, vr->pregs[1]);
			}	
			r = vr->pregs[0];
			goto out;
		} else {
			/*
			 * The item is already loaded, but the caller wishes
			 * it to be stored in a specific register, so it
			 * may have to be relocated. Note that this is tricky
			 * for multi-register objects. Consider a ``long long''
			 * loaded into ebx,eax that shall be relocated to
			 * eax,edx; We need to ensure that the copy from ebx
			 * to eax does not trash the other part of the object!
			 */
			if (vr->pregs[0] != r0) {
				if (vr->is_multi_reg_obj
					&& r0 == vr->pregs[1]) {
					icode_make_xchg(r0, vr->pregs[0], il);
					vr->pregs[1] = vr->pregs[0];
				} else {

					icode_make_copyreg(r0, vr->pregs[0],
						vr->type, vr->type, il);
					/* XXX shouldn't caller do this? */
					vr->pregs[0]->used = 0;
					/* Record new preg */
				}	
			}
			if (vr->is_multi_reg_obj) {
				/* XXX very broken if the regs overlap */
				if (vr->pregs[1] != r0_2) {
					icode_make_copyreg(r0_2, vr->pregs[1],
						vr->type, vr->type, il);
					/* XXX shouldn't caller do this? */
					vr->pregs[1]->used = 0;
				}	
				vreg_map_preg2(vr, r0_2);
			}	
			r = r0;
			vreg_map_preg(vr, r);
			goto out;
		}
	} else {
		/* Register is owned by someone else ... */
		;
	}
dofault:	

	if (vr->from_ptr) {
		/* 
		 * We may need to fault in the pointer itself first before
		 * we can indirect through it
		 */
		if (r2 != NULL) {
			/*
			 * 11/01/07:
			 * This was missing... for a multi-reg
			 * item, the pointer may not be trashed
			 * when loading the first part, so we
			 * better separate pointer reg clearly
			 * from data regs
			 */
			reg_set_unallocatable(r);
			reg_set_unallocatable(r2);
		}
		
		if (vr->from_ptr->pregs[0] == NULL
			|| vr->from_ptr->pregs[0]->vreg != vr->from_ptr) {
			vreg_faultin(NULL, NULL, vr->from_ptr, il, 0);
		}

		if (r2 != NULL) {
			reg_set_allocatable(r);
			reg_set_allocatable(r2);
		}

		if (vr->from_ptr->type->tlist->type == TN_ARRAY_OF
			&& vr->type->tlist
			&& vr->type->tlist->type == TN_ARRAY_OF) {
			/*
			 * The ``pointer'' is really an array, i.e.
			 * the address of the first element, so there
			 * is no need to indirect
			 */
			vr->pregs[0] = r = vr->from_ptr->pregs[0];
			vr->pregs[0]->vreg = vr;
			goto out;
		}
	} else if (vr->parent) {
		vr2 = get_parent_struct(vr);
		if (vr2->from_ptr) {
			vreg_faultin(NULL, NULL, vr2->from_ptr, il, 0);
		}
	}
	
	if (r0 == NULL) {
		int	size = 0;
		int	is_floating;

		is_floating = is_floating_type(vr->type);

		if (vr->type->tlist != NULL
			&& vr->type->tlist->type == TN_ARRAY_OF) {
			/* Array decays into pointer */
			size = backend->get_ptr_size();
			/*size = 4;*/
#if 0
		} else if (vr->size > 4
			&& !is_floating
			&& !IS_LLONG(vr->type->code)) {
			printf("BAD REGISTER LOAD OF SIZE %d\n", vr->size);
			printf("hm type is %d\n", vr->type->code);
			abort();
#endif
		} else {
			size = vr->size;
		}		

		/* XXX pass whatfor to alloc_gpr() */
		(void) whatfor;
		if (vr->from_ptr) {
			reg_set_unallocatable(vr->from_ptr->pregs[0]);
		}	
		if (is_floating) {
			/* XXX backend->multi_fpr_object ??? */
			r = backend->alloc_fpr(curfunc, size, il, NULL);
		} else {
			r = ALLOC_GPR(curfunc, size, il, NULL);
			if (backend->multi_gpr_object) {
				/* XXX hardcoded x86 */
				vr->is_multi_reg_obj = 2;
				r2 = ALLOC_GPR(curfunc, size, il, NULL);	
			}	
		}	
		if (vr->from_ptr) {
			reg_set_allocatable(vr->from_ptr->pregs[0]);
		}	
	} else {
		r = r0;
		r2 = r0_2;
	}

	debug_log_regstuff(r, vr, DEBUG_LOG_FAULTIN);
	vreg_map_preg(vr, r);

	ii = icode_make_load(r, vr2);
	append_icode_list(il, ii);

	if (r2 != NULL) {
		vreg_map_preg2(vr, r2);
		ii = icode_make_load(r2, vr2);
		append_icode_list(il, ii);
	}

#if 0
	if (vr->from_ptr) {
		free_preg(vr->from_ptr->preg, il, 0, 0);
	} else if (vr->parent && vr2->from_ptr) {
		free_preg(vr2->from_ptr->preg, il, 0, 0);
	}	
#endif

out:	
	return r;
}

struct reg *
vreg_faultin_protected(
	struct vreg *protectme,
	struct reg *r0,
	struct reg *r0_2,
	struct vreg *vr,
	struct icode_list *il,
	int whatfor) {

	struct reg	*preg = NULL;
	struct reg	*preg2 = NULL;
	struct reg	*ret;

	if (protectme->pregs[0] != NULL
		&& protectme->pregs[0]->vreg == protectme) {
		preg = protectme->pregs[0];
	} else if (protectme->from_ptr != NULL
		&& protectme->from_ptr->pregs[0] != NULL	
		&& protectme->from_ptr->pregs[0]->vreg == protectme->from_ptr) {
		preg = protectme->from_ptr->pregs[0];
	}
	
	if (preg != NULL) {
		reg_set_unallocatable(preg);
		if (protectme->is_multi_reg_obj) {
			reg_set_unallocatable(preg2 = protectme->pregs[1]);
		}	
	}

	ret = vreg_faultin(r0, r0_2, vr, il, whatfor);

	if (preg != NULL) {
		reg_set_allocatable(preg);
		if (preg2 != NULL) {
			reg_set_allocatable(preg2);
		}	
	}
	return ret;
}	


/*
 * If the vreg comes from a pointer, load that pointer into a register. The
 * interface is kept simple because it is not likely to be used in many
 * places. Extend as necessary.
 */
struct reg *
vreg_faultin_ptr(struct vreg *vr, struct icode_list *il) {
	struct vreg	*faultme = NULL;

	if (vr->from_ptr) {
		faultme = vr->from_ptr;
	} else if (vr->parent != NULL) {
		struct vreg	*vr2 = get_parent_struct(vr);

		if (vr2->from_ptr) {
			faultme = vr2->from_ptr;
		}
	} else if (vr->type->tlist != NULL
		&& (vr->type->tlist->type == TN_ARRAY_OF
			|| vr->type->tlist->type == TN_VARARRAY_OF
			|| vr->type->tlist->type == TN_FUNCTION)) {
		/*
		 * 07/01/07: Extended for arrays! Loading an array or
		 * a function ``into a register'' loads its address
		 *
		 * 10/31/07: This appears to be nonsense. It was
		 * introduced for variable-length arrays, so at the
		 * very least the tlist->type check was missing. I
		 * don't know whether this ever makes any sense for
		 * functions
		 */
		faultme = vr;
	}
	if (faultme != NULL) {
		vreg_faultin(NULL, NULL, faultme, il, 0);
		return faultme->pregs[0];
	}
	return NULL;
}



/*
 * Use this instead of manual assignments to better track
 * the creation of mappings for debugging
 */
void
vreg_map_preg(struct vreg *vr, struct reg *preg) {
	vr->pregs[0] = preg;
	preg->used = 1;
	if (preg != NULL) {
		vr->pregs[0]->vreg = vr;
	}	
}

void
reg_set_unused(struct reg *r) {
	r->used = 0;
}

void
vreg_map_preg2(struct vreg *vr, struct reg *preg) {
	vr->pregs[1] = preg;
	preg->used = 1;
	if (preg != NULL) {
		vr->pregs[1]->vreg = vr;
	}	
}	
	


struct store_data *
alloc_store_data(void) {
	struct store_data	*ret = n_xmalloc(sizeof *ret);
	static struct store_data	nullsd;
	*ret = nullsd;
	return ret;
}

static void
invalidate_subregs(struct reg *r) {
	r->vreg = NULL;
	if (r->size == 8 && r->composed_of) {
		/* XXX kludge */
		invalidate_subregs(r->composed_of[0]);
		return;
	}	
		
	if (r->composed_of) {
		if (r->composed_of[0]) {
			r->composed_of[0]->vreg = NULL;
		}
		if (r->composed_of[1]) {
			r->composed_of[1]->vreg = NULL;
		}
		if (r->composed_of[0]->composed_of) {
			r->composed_of[0]->composed_of[0]->vreg = NULL;
			if (r->composed_of[0]->composed_of[1]) {
				r->composed_of[0]->composed_of[1]->vreg = NULL;
			}
		}	
	}	
}

static int 
do_free_preg(struct reg *r, struct icode_list *il,
	int invalidate, int savereg) {
	struct vreg	*vr;

	if (r == NULL) return 0;
	vr = r->vreg;

	reg_set_allocatable(r);
	if (!r->used) {
		if (invalidate) r->vreg = NULL;
		return 0;
	}
	debug_log_regstuff(r, vr, DEBUG_LOG_FREEGPR);

	if (savereg
		&& vr && !vr->var_backed && !vr->from_const && !vr->from_ptr
		&& !vr->parent) {
		/*
		 * Must be saved. This doesn't interact well with
		 * multi-register objects
		 * XXX for some reason, is_multi_reg_obj sometimes
		 * isn't set even for a long long x86 object :(
		 * this is triggered in change_preg_size(), where
		 * a long long-associated register is freed
		 * hence the workaround below
		 */
		if (backend->arch == ARCH_X86
			&& IS_LLONG(vr->type->code)
			&& vr->type->tlist == NULL) {
			struct vreg	*copyvr;

			if (vr->pregs[1] == NULL) {
				/*
				 * This apparently happens when freeing
				 * a preg holding part of multi-gpr object.
				 */
				vr->pregs[1] = r;
			}	

			copyvr = n_xmemdup(vr, sizeof *vr);
			copyvr->is_multi_reg_obj = 2;
			copyvr->size = 8;
			icode_make_store(curfunc, copyvr,
				copyvr, il);
			if (copyvr->stack_addr != NULL) {
				vr->stack_addr = copyvr->stack_addr;
			}	
#if 0
			icode_make_store(curfunc, copyvr,
				copyvr, il);
#endif
		} else {
			icode_make_store(curfunc, vr, vr, il);
		}
	}	

	if (backend->free_preg != NULL) {
		backend->free_preg(r, il);
	}	
	
	r->used = 0;
	if (invalidate) r->vreg = NULL;

	return 1;
}

/*
 * Note that if savereg is specified and the register is subsequently used,
 * invalidate also needs to be set in order to avoid bad stale references
 */
void
free_preg(struct reg *r, struct icode_list *il, int invalidate, int savereg) {
	static int	level;

	/*
	 * XXX The stuff below ensures that if a GPR holding a ``long long''
	 * is ever freed, the GPR for the other part will also be freed. This
	 * makes it easier to deal with long longs because that way a long
	 * long is always either resident or non-resident, never half-resident.
	 * Of course this approach is less than optimal speedwise, so it
	 * should be changed some time
	 */
	if (r->vreg && r->vreg->is_multi_reg_obj && level == 0) {
		struct reg	*r2;

		invalidate = 1; /* cached multi-reg objects cause probems */

		if (r->vreg->pregs[0] == r) {
			r2 = r;
			r = r->vreg->pregs[1];
		} else {
			r2 = r->vreg->pregs[0];
		}
		++level;
		free_preg(r2, il, invalidate, savereg);
		--level;
	}

	if (do_free_preg(r, il, invalidate, savereg)) {
		if (invalidate) {
			invalidate_subregs(r);
		}	
		return;
	}
	if (r->size == 8 && r->composed_of) {
		/* XXX ... */
		free_preg(r->composed_of[0], il, invalidate, savereg);
		return;
	} 

	if (r->composed_of) {
		int	rc = 0;
		if (r->composed_of[0]) {
			rc |= do_free_preg(r->composed_of[0], il,
				invalidate, savereg);
		}
		if (r->composed_of[1]) {
			rc |= do_free_preg(r->composed_of[1], il,
				invalidate, savereg);
		}
		if (rc) {
			if (invalidate) {
				invalidate_subregs(r);
			}	
			return;
		}
		if (r->composed_of[0]->composed_of) {
			(void) do_free_preg(r->composed_of[0]->
				composed_of[0], il, invalidate, savereg);
			(void) do_free_preg(r->composed_of[0]->
				composed_of[1], il, invalidate, savereg);
		}
	}
}

/*
 * Free all pregs assigned to a vreg. This should be used for all vregs so
 * that it is transparent whether we are dealing with a vreg that occupies
 * more than preg (case in point: ``long long'' on x86 and objects that
 * were loaded through a pointer)
 */
void
free_pregs_vreg(
	struct vreg *vr,
	struct icode_list *il,
	int invalidate,
	int savereg) {

	if (vr->from_ptr != NULL) {
		if (vr->from_ptr->pregs[0] != NULL
			&& vr->from_ptr->pregs[0]->vreg == vr->from_ptr) {
			free_preg(vr->from_ptr->pregs[0], il,
				invalidate, savereg);
		}
	}
	if (vr->pregs[0] != NULL && vr->pregs[0]->vreg == vr) {
		free_preg(vr->pregs[0], il, invalidate, savereg);
	}
	if (vr->pregs[1] != NULL && vr->pregs[1]->vreg == vr) {
		free_preg(vr->pregs[1], il, invalidate, savereg);
	}
}	

int
is_member_of_reg(struct reg *r, struct reg *member) {
	if (r->composed_of == NULL) return 0;
	if (member == r) {
		return 1;
	}	
	if (r->size == 8) { /* XXX */
		return is_member_of_reg(r->composed_of[0], member);
	}
	if (r->composed_of[0] == member
		|| (r->composed_of[0]->composed_of
		&& (r->composed_of[0]->composed_of[0] == member
		|| r->composed_of[0]->composed_of[1] == member))) { 
		return 1;
	}	
	return 0;
}

void
vreg_set_new_type(struct vreg *vr, struct type *ty) {
	vr->type = ty;
	if (ty->is_vla) {
		vr->size = 0;
		vr->is_multi_reg_obj = 0;
	} else {	
		vr->size = backend->get_sizeof_type(ty, NULL);
		vr->is_multi_reg_obj = backend->is_multi_reg_obj(ty);
	}	
}	

/*
 * Save a scalar item to the stack, loading it into a register
 * to do so if necessary.
 * The vreg is copied, and *vr is set to point to the copy
 * (because in most cases we want to save a vreg that has already
 * been used in other icode instructions.)
 */
void
vreg_save_to_stack(struct vreg **vr0, struct type *ty, struct icode_list *il) {
	struct vreg	*vr = *vr0;

	vr = vreg_disconnect(vr);
	vr->type = ty;
	vr->size = backend->get_sizeof_type(ty, NULL);
	vreg_faultin(NULL, NULL, vr, il, 0);
	free_preg(vr->pregs[0], il, 1, 1);
	*vr0 = vr;
}


void
vreg_reinterpret_as(struct vreg **vr0, struct type *from, struct type *to,
	struct icode_list *il) { 

	struct vreg	*vr;

	vreg_save_to_stack(vr0, from, il);
	vr = vreg_disconnect(*vr0);
	vr->type = to;
	vr->size = backend->get_sizeof_type(to, NULL);
	vreg_faultin(NULL, NULL, vr, il, 0); 
	*vr0 = vr;
}



syntax highlighted by Code2HTML, v. 0.9.1