/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * 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 unmodified, 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 AUTHOR ``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 AUTHOR 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.
 *
 *
 *    Copyright (c) 1993 Open Software Foundation, Inc.
 *
 *
 *    All Rights Reserved
 *
 *
 *    Permission to use, copy, modify, and distribute this software
 *    and its documentation for any purpose and without fee is hereby
 *    granted, provided that the above copyright notice appears in all
 *    copies and that both the copyright notice and this permission
 *    notice appear in supporting documentation.
 *
 *
 *    OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
 *    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *    PARTICULAR PURPOSE.
 *
 *
 *    IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
 *    CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 *    LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
 *    NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 *    WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * $TenDRA: tendra/src/installers/power/common/move.c,v 1.7 2004/06/28 03:31:08 bp Exp $
 */

/*
 *    		 Crown Copyright (c) 1997
 *    
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *    
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *    
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *    
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *    
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 */





/**********************************************************************
 *		move.c
 *
 *	The procedure move produces code to move a value from a to the
 *destination dest. This takes the form of a switch test on the parameter
 *a (type ans) which is either a reg, freg instore or bitad value. In
 *each of the three cases the ans field of the dest is similarly dealt
 *with to determine the necessary instructions for the move. Sizes and
 *alignment are taken from the ash field of the destination.
 *
 *Delivers register used if 1-word destination is instore; otherwise NOREG.

**********************************************************************/
#include "config.h"
#include "memtdf.h"
#include "codegen.h"
#include "geninst.h"

#include "myassert.h"
#include "comment.h"
#include "proc.h"			/* for mem_temp() */
#include "maxminmacs.h"
#include "makecode.h"

#include "move.h"


#define	MAX_STEPS_INLINE_MOVE	12	/* 24 instructions */


#define NBITMASK(n)		((unsigned long)((n)==32 ? ~0L : ((1<<(n))-1)))


/*
 *	ins_sgn_pair[FALSE]		unsigned instruction
 *	ins_sgn_pair[TRUE]		signed instruction
 */
typedef Instruction_P ins_sgn_pair[2 /* FALSE..TRUE */ ];

static /* const */ ins_sgn_pair ld_ins_sz[] =
{
	/* 0 */	{&INSTRUCTION_I_NIL,    &INSTRUCTION_I_NIL},
	/* 8 */	{&INSTRUCTION_i_lbz,    &INSTRUCTION_i_lbz},	/* no signed byte load on POWER,
															 * sign bit must be propagated after load */
	/* 16 */	{&INSTRUCTION_i_lhz,    &INSTRUCTION_i_lha},
	/* 24 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 32 */	{&INSTRUCTION_i_l,	&INSTRUCTION_i_l},
	/* 40 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 48 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 56 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 64 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL}
};

static /* const */ ins_sgn_pair st_ins_sz[] =
{
	/* 0 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 8 */	{&INSTRUCTION_i_stb,	&INSTRUCTION_i_stb},
	/* 16 */	{&INSTRUCTION_i_sth,	&INSTRUCTION_i_sth},
	/* 24 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 32 */	{&INSTRUCTION_i_st,	&INSTRUCTION_i_st},
	/* 40 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 48 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 56 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL},
	/* 64 */	{&INSTRUCTION_I_NIL,	&INSTRUCTION_I_NIL}
};



/* the ld instruction for object sized bits, and sgned or not */
Instruction_P
i_ld_sz(int bits, int sgned)
{
	ASSERT((bits&7)==0);
	ASSERT(bits<=64);
	ASSERT(ld_ins_sz[(bits)/8][sgned]!=I_NIL);
	return ld_ins_sz[(bits)/8][sgned];
}


/* the st instruction for object sized bits */
Instruction_P
i_st_sz(int bits)
{
	ASSERT((bits&7)==0);
	ASSERT(bits<=64);
	ASSERT(st_ins_sz[(bits)/8][0]!=I_NIL);
	return st_ins_sz[(bits)/8][0];
}


/* load address represented by is into reg */
void
ld_addr(instore is, int reg)
{
	COMMENT1("ld_addr: adval=%d", is.adval);
	
	if (is.adval)
	{
		if (IS_FIXREG(is.b.base))
		{
			rir_ins(i_a, is.b.base, is.b.offset, reg);
		}
		else
		{
			set_ins(is.b, reg);
		}
	}
	else
	{
		ld_ins(i_l, is.b, reg);
	}
}


/* get address represented by is into a reg */
int
addr_reg(instore is, long regs)
{
	int r;
	
	COMMENT1("addr_reg: adval=%d", is.adval);
	
	if (is.adval && IS_FIXREG(is.b.base) && is.b.offset == 0)
	{
		/* simply return base reg */
		return is.b.base;
	}
	
	/* otherwise load address into reg */
	r = getreg(regs);
	ld_addr(is, r);
	return r;
}


/* store, sorting out temp reg required */
static void
store(Instruction_P st, int r, instore is,
	  long regs)
{
	if (is.adval)		/* is the value an address? */
	{
		if (IS_FIXREG(is.b.base))
		{
			st_ro_ins(st, r, is.b);comment(NIL);
		}
		else if (IMM_SIZE(is.b.offset))
		{
			if (freeregs(regs) >= 1)
			{
				/* load base address reg, then store using offset */
				int addr_reg = getreg(regs);
				baseoff b;
				
				b.base = is.b.base;
				b.offset = 0;
				set_ins(b, addr_reg);
				
				b.base = addr_reg;
				b.offset = is.b.offset;
				st_ro_ins(st, r, b);comment(NIL);
			}
			else
			{
				/* st_ins will do this correctly with R_TMP0 using an extra instruction */
				st_ins(st, r, is.b);
			}
		}
		else
		{
			/* st_ins would need 2 tmp regs which are not available */
			baseoff b;
			
			b.base = getreg(regs);
			b.offset = 0;
			
			/* address+offset into b.base */
			set_ins(is.b, b.base);
			
			/* store r to [b+0] */
			st_ro_ins(st, r, b);comment(NIL);
		}
	}
	else
	{
		baseoff b;
		
		ASSERT(r!=R_TMP0);
#if 0
		b.base = R_TMP0;
#else 
		b.base = getreg(regs);
#endif
		b.offset = 0;
		ld_ins(i_l, is.b, b.base);
		st_ro_ins(st, r, b);comment(NIL);
	}
}


#if 0
/*
 * Copy a large inmem object with a loop.
 * Compact code, but slower than loopmove2() so no longer used.
 */
static void
loopmove1(instore iss, instore isd, int bytes_per_step,
		  int no_steps, Instruction_P ld,
		  Instruction_P st, long regs)
{
	/*
	 * Copy with loop.
	 *
	 * Currently generate:
	 *
	 *		!%srcptr and %destptr set
	 *		lil	%cnt,bytes
	 *	loop:
	 *		ai.	%cnt,%cnt,-bytes_per_step
	 *		lx	%tmp,[%srcptr+%cnt]
	 *		stX	%tmp,[%destptr+%cnt]
	 *		bnz	loop
	 *
	 * +++ unroll, and use two copy regs to separate ld and st using same reg
	 * +++ use CR
	 * +++ use lu/stu
	 * +++ use lsi/stsi
	 */
	
	int srcptr_reg;
	int destptr_reg;
	int cnt_reg;
	int copy_reg;
	int loop = new_label();
	
	COMMENT("loopmove1: loop move");
	
	/* moves of addresses not handled by this long move */
	ASSERT(!iss.adval);
	
	ASSERT(bytes_per_step <= 4);	/* only using 1 word regs */
	
	cnt_reg = getreg(regs);
	regs |= RMASK(cnt_reg);
	
	ASSERT(!iss.adval);
	iss.adval = 1;	/* we want address of value */
	srcptr_reg = addr_reg(iss, regs);
	regs |= RMASK(srcptr_reg);
	
	destptr_reg = addr_reg(isd, regs);
	regs |= RMASK(destptr_reg);
	
	copy_reg = R_TMP0;
	
	ld_const_ins(bytes_per_step * no_steps, cnt_reg);
	
	set_label(loop);
	
	rir_ins(i_a_cr, cnt_reg, -bytes_per_step, cnt_reg);
	ld_rr_ins(ld, srcptr_reg, cnt_reg, copy_reg);
	st_rr_ins(st, copy_reg, destptr_reg, cnt_reg);
	
	bc_ins(i_bnz, 0, loop,LIKELY_TO_JUMP);
	clear_reg(cnt_reg);
}
#endif


/*
 * Copy a large inmem object with a loop, using only 2 regs and R_TMP.
 */
static void
loopmove2(instore iss, instore isd, int bytes_per_step,
		  int no_steps, Instruction_P ld,
		  Instruction_P st, long regs)
{
	/*
	 * Copy with loop, need 2 regs and R_TMP.
	 *
	 * Currently generate:
	 *
	 *		!%srcptr and %destptr set in regs that will be changed
	 *		lil	%tmp,steps
	 *		mtctr	%tmp
	 *		ai	%srcptr,%srcptr,-bytes_per_step [if needed]
	 *		ai	%dstptr,%dstptr,-bytes_per_step [if needed]
	 *	loop:
	 *		lXu	%tmp,[%srcptr+bytes_per_step]
	 *		stXu	%tmp,[%destptr+bytes_per_step]
	 *		bdn	loop
	 *
	 * +++ do extra plain ld/st outside loop to avoid decrements
	 * +++ use lsi/stsi
	 */
	
	Instruction_P ldu, stu;
	int srcptr_reg;
	int destptr_reg;
	baseoff src_bo;
	baseoff dest_bo;
	int copy_reg;
	int loop = new_label();
	
	COMMENT("loopmove2: loop move");
	
	ASSERT(bytes_per_step <= 4);	/* only using 1 word regs */
	
	switch (bytes_per_step)
	{
	case 1:	ldu = i_lbzu; stu = i_stbu; break;
	case 2:	ldu = i_lhzu; stu = i_sthu; break;
	case 4:	ldu = i_lu; stu = i_stu; break;
	default:	fail("bad bytes_per_step in loopmove");
	}
	
	ld_const_ins(no_steps, R_TMP0);
	mt_ins(i_mtctr, R_TMP0);
	
	/* moves of addresses not handled by this long move */
	ASSERT(!iss.adval);
	iss.adval = 1;	/* we want address of value */
	iss.b.offset -= bytes_per_step;
	srcptr_reg = getreg(regs);
	regs |= RMASK(srcptr_reg);
	ld_addr(iss, srcptr_reg);
	
	destptr_reg = getreg(regs);
	regs |= RMASK(destptr_reg);
	if (isd.adval)
	{
		isd.b.offset -= bytes_per_step;
		ld_addr(isd, destptr_reg);
	}
	else
	{
		ld_addr(isd, destptr_reg);
		rir_ins(i_a, destptr_reg, -bytes_per_step, destptr_reg);
	}
	
	copy_reg = R_TMP0;
	
	set_label(loop);
	
	src_bo.base = srcptr_reg;
	src_bo.offset = bytes_per_step;
	ld_ro_ins(ldu, src_bo, copy_reg);comment(NIL);
	
	dest_bo.base = destptr_reg;
	dest_bo.offset = bytes_per_step;
	st_ro_ins(stu, copy_reg, dest_bo);comment(NIL);
	
	uncond_ins(i_bdn, loop);
	
	clear_reg(srcptr_reg);
	clear_reg(destptr_reg);
}


/*
 * Copy a large inmem object with unrolled loop, using 3 regs and R_TMP.
 */
static void
loopmove3(instore iss, instore isd, int bytes_per_step,
		  int no_steps, Instruction_P ld,
		  Instruction_P st, long regs)
{
	/*
	 * Copy with unrolled loop, need 3 regs and R_TMP.
	 *
	 * Currently generate:
	 *
	 *		!%srcptr and %destptr set in regs that will be changed
	 *		ai	%srcptr,%srcptr,-bytes_per_step [if needed]
	 *		ai	%dstptr,%dstptr,-bytes_per_step [if needed]
	 *		lXu	%tmp2,[%srcptr+bytes_per_step]	[if needed]
	 *		lil	%tmp1,half_steps
	 *		mtctr	%tmp1
	 *		stXu	%tmp2,[%destptr+bytes_per_step]	[if needed]
	 *	loop:
	 *		lXu	%tmp1,[%srcptr+bytes_per_step]
	 *		lXu	%tmp2,[%srcptr+bytes_per_step]
	 *		stXu	%tmp1,[%destptr+bytes_per_step]
	 *		stXu	%tmp2,[%destptr+bytes_per_step]
	 *		bdn	loop
	 *
	 * +++ use lsi/stsi
	 */
	
	int half_no_steps = no_steps/2;
	Instruction_P ldu, stu;
	int srcptr_reg;
	int destptr_reg;
	baseoff src_bo;
	baseoff dest_bo;
	int copy1_reg;
	int copy2_reg;
	bool decr_destptr_reg;
	int loop = new_label();
	
	COMMENT("loopmove3: loop move");
	
	ASSERT(bytes_per_step <= 4);	/* only using 1 word regs */
	ASSERT(half_no_steps>=1);
	
	switch (bytes_per_step)
	{
	case 1:	ldu = i_lbzu; stu = i_stbu; break;
	case 2:	ldu = i_lhzu; stu = i_sthu; break;
	case 4:	ldu = i_lu; stu = i_stu; break;
	default:	fail("bad bytes_per_step in loopmove");
	}
	
	/* moves of addresses not handled by this long move */
	ASSERT(!iss.adval);
	iss.adval = 1;	/* we want address of value */
	iss.b.offset -= bytes_per_step;
	srcptr_reg = getreg(regs);
	regs |= RMASK(srcptr_reg);
	ld_addr(iss, srcptr_reg);
	
	destptr_reg = getreg(regs);
	regs |= RMASK(destptr_reg);
	if (isd.adval)
	{
		if (2*half_no_steps == no_steps)
		{
			/* no opportunity to avoid decr later */
			isd.b.offset -= bytes_per_step;
			decr_destptr_reg = 0;
		}
		else
		{
			decr_destptr_reg = 1;
		}
		ld_addr(isd, destptr_reg);
	}
	else
	{
		ld_addr(isd, destptr_reg);
		decr_destptr_reg = 1;
	}
	
	copy1_reg = R_TMP0;
	copy2_reg = getreg(regs);
	regs |= RMASK(copy2_reg);
	
	src_bo.base = srcptr_reg;
	src_bo.offset = bytes_per_step;
	
	dest_bo.base = destptr_reg;
	dest_bo.offset = bytes_per_step;
	
	if (2*half_no_steps < no_steps)
		ld_ro_ins(ldu, src_bo, copy2_reg);comment(NIL);
	
	ld_const_ins(half_no_steps, copy1_reg);
	mt_ins(i_mtctr, copy1_reg);
	
	if (2*half_no_steps < no_steps)
	{
		if (decr_destptr_reg)
		{
			/* no need to do the decr, use plain st not stu for first step */
			dest_bo.offset -= bytes_per_step;
			st_ro_ins(st, copy2_reg, dest_bo);comment(NIL);
			dest_bo.offset += bytes_per_step;
		}
		else
		{
			st_ro_ins(stu, copy2_reg, dest_bo);comment(NIL);
		}
	}
	else
	{
		if (decr_destptr_reg)
			rir_ins(i_a, destptr_reg, -bytes_per_step, destptr_reg);
	}
	
	set_label(loop);
	
	ld_ro_ins(ldu, src_bo, copy1_reg);comment(NIL);
	ld_ro_ins(ldu, src_bo, copy2_reg);comment(NIL);
	
	st_ro_ins(stu, copy1_reg, dest_bo);comment(NIL);
	st_ro_ins(stu, copy2_reg, dest_bo);comment(NIL);
	
	uncond_ins(i_bdn, loop);
	
	clear_reg(srcptr_reg);
	clear_reg(destptr_reg);
}


/*
 * Memory to memory move.
 * If copy of object left in a fixed point reg, return reg, otherwise NOREG.
 */
static int
moveinstore(instore iss, instore isd, int size,
			int al, long regs, bool sgned)
{
	int bits;
	int bits_per_step;
	int bytes_per_step;
	int no_steps;
	Instruction_P st;
	Instruction_P ld;
	bool unalign = al < 32;
	
	if (iss.b.base == isd.b.base 
		&& iss.b.offset == isd.b.offset 
		&& iss.adval==0 
		&& isd.adval==1)
	{
		return NOREG;
	}
	
	/* we are limited by 32 bit regs */
	bits_per_step = min(al, 32);
	
	bytes_per_step = bits_per_step / 8;
	
	/*
	 * .ashsize gives precise size in bits, not as rounded up as if
	 * object is an array element. So we round up bits to convenient
	 * size, less than alignment.
	 */
	bits = (size + bits_per_step - 1) & ~(bits_per_step - 1);
	
	no_steps = (bits + bits_per_step - 1) / bits_per_step;
	
	COMMENT2("moveinstore: mem to mem size,align=%d,%d",
			 size, al);
	COMMENT4("moveinstore: mem to mem bits=%d align=%d, bytes_per_step=%d no_steps=%d",
			 bits, al, bytes_per_step, no_steps);
	
	if ((al % 8) != 0 || (bits % 8) != 0)
	{
		fail("moveinstore: bits mem to mem move");
		return NOREG;
	}
	
	/*
	 * we are assuming the following, eg 8 bit object cannot have 32 bit
	 * alignment
	 */
	ASSERT((bits % al) == 0);
	
	ASSERT(bytes_per_step > 0 && bytes_per_step <= 4);
	ASSERT(no_steps > 0);
	ASSERT((no_steps * bytes_per_step) == (bits / 8));
	
	/* multi step objects by unsigned move, single step special cased below */
	ld = i_ld_sz(bits_per_step, 0 /* unsgned */);
	st = i_st_sz(bits_per_step);
	
	/* +++ use fp reg for float, except not passed free fp regs */
	/* +++ use actual alignment which may be better than nominal alignment */
	
	if (no_steps <= MAX_STEPS_INLINE_MOVE)
	{
		/* move in line */
		
		if (no_steps == 1)
		{
			int r = getreg(regs);	/* register for holding values
									 * transferred */
			
			if (iss.adval)
			{
				/* generate address of source */
				if (IS_FIXREG(iss.b.base))
				{
					if (iss.b.offset == 0)
					{
						COMMENT("moveinstore: using adval base reg directly");
						r = iss.b.base;
					}
					else
					{
						rir_ins(i_a, iss.b.base, iss.b.offset, r);
					}
				}
				else
					set_ins(iss.b, r);
			}
			else
			{
				/* load source */
				ld_ins(i_ld_sz(bits_per_step, sgned), iss.b, r);
				if (sgned && bits_per_step == 8)
				{
					/* POWER has no load signed byte instruction, so propagate sign */
					adjust_to_size(ulonghd,r,scharhd,r,NO_ERROR_JUMP);
				}
			}
			
			store(st, r, isd, regs);
			
			return (unalign) ? NOREG : r;
		}
		else
		{
			/*
			 * Move using 2 regs ensuring load delay slot not occupied.
			 */
			int ld_steps = no_steps;
			int st_steps = no_steps;
			
			int r1, r2;	/* regs used to copy object */
			
			COMMENT("moveinstore: inline move");
			
			ASSERT(ld_steps >= 2);
			
			/* moves of addresses not handled by this long move */
			ASSERT(!iss.adval);
			
			ASSERT(bits_per_step <= 32);	/* only using byte regs */
			
			r1 = getreg(regs);
			regs |= RMASK(r1);
			
			r2 = getreg(regs);
			regs |= RMASK(r2);
			
			if (!IS_FIXREG(iss.b.base))
			{
				/* load source ptr in reg, note R_TMP0 possibly in use for dest */
				
				int pr = getreg(regs);
				
				regs |= RMASK(pr);
				
				COMMENT("moveinstore: load ptr to source");
				
				set_ins(iss.b, pr);
				iss.b.base = pr;
				iss.b.offset = 0;
			}
			
			if (!isd.adval)
			{
				int pr = getreg(regs);
				
				regs |= RMASK(pr);
				
				COMMENT("moveinstore: dest !adval");
				ld_ins(i_l, isd.b, pr);
				isd.b.base = pr;
				isd.b.offset = 0;
			}
			else if (!IS_FIXREG(isd.b.base))
			{
				int pr = getreg(regs);
				
				regs |= RMASK(pr);
				
				COMMENT("moveinstore: load ptr to dest");
				
				set_ins(isd.b, pr);
				isd.b.base = pr;
				isd.b.offset = 0;
			}
			
			/* first, pre-load both regs */
			ld_ro_ins(ld, iss.b, r1);comment("moveinstore initial store->reg1");
			ld_steps--;
			iss.b.offset += bytes_per_step;
			
			ld_ro_ins(ld, iss.b, r2);comment("moveinstore initial store->reg2");
			ld_steps--;
			iss.b.offset += bytes_per_step;
			
			/*
			 * now generate overlapping sequence with ld rX separated from
			 * following st rX
			 *
			 *	st	r1
			 *	ld	r1
			 *	st	r2
			 *	ld	r2
			 *
			 * while there's still data
			 */
			while (st_steps > 0)
			{
				/* st r1 */
				st_ro_ins(st, r1, isd.b);comment("moveinstore reg1->store");
				st_steps--;
				isd.b.offset += bytes_per_step;
				
				/* ld r1 */
				if (ld_steps > 0)
				{
					ld_ro_ins(ld, iss.b, r1);comment("moveinstore store->reg1");
					ld_steps--;
					iss.b.offset += bytes_per_step;
				}
				
				/* st r2 */
				if (st_steps > 0)
				{
					st_ro_ins(st, r2, isd.b);comment("moveinstore reg2->store");
					st_steps--;
					isd.b.offset += bytes_per_step;
				}
				
				/* ld r2 */
				if (ld_steps > 0)
				{
					ld_ro_ins(ld, iss.b, r2);comment("moveinstore store->reg2");
					ld_steps--;
					iss.b.offset += bytes_per_step;
				}
			}
			
			COMMENT("moveinstore: end inline move");
			
			ASSERT(ld_steps == 0);
			
			return NOREG;
		}
	}			/* inline end */
	else
	{
		/*
		 * Copy using a loop.
		 * scan() has only reseved 2 regs (no more available sometimes),
		 * but if more happen to be free use them for an unrolled loop.
		 */
		
		if (freeregs(regs) < 3)
			loopmove2(iss, isd, bytes_per_step, no_steps, ld, st, regs);
		else
			loopmove3(iss, isd, bytes_per_step, no_steps, ld, st, regs);
		
		return NOREG;
	}
}


/*
 * Generate code to move 'a' to 'dest'.
 * If copy of object left in a fixed point reg, return reg, otherwise NOREG.
 * ans a;
 * a.discrim is an enumeration
 *  it can be inreg
 *            infreg
 *            notinreg
 *            bitad
 *            insomereg
 *            insomefreg
 * a.val is a union anstu
 *            this can be int regans;			 register number 
 *                        freg fregans;
 *                        instore instoreans;
 *                        instore bitadans;
 *                        somefreg somefregans;	      not yet used
 *                        somereg someregans;
 * dest.answhere is an ans
 * dest.ashwhere is an ash
 *               dest.ashwhere.ashsize is the minimum size of bits in a shape
 *               dest.ashwhere.ashalign is the required alignment in bits
 *
 * regs is a long with the bits masked out for which registers you cannot use
 */
int
move(ans a, where dest, long regs, bool sgned)
{
	int al = dest.ashwhere.ashalign; /* al is the alignment of the destination */
	int size = dest.ashwhere.ashsize;
	if (size==0)
		return NOREG;
	
	FULLCOMMENT4("move: %d -> %d, dest ashsize,ashalign = %d,%d",
				 a.discrim, dest.answhere.discrim, dest.ashwhere.ashsize, dest.ashwhere.ashalign);
#if 0
	ASSERT((dest.answhere.discrim == inreg && dest.answhere.val.regans == R_0)	/* nowhere */
		   || dest.ashwhere.ashsize > 0);	/* unitialised dest.ashwhere */
#endif
	
  start:
	
	/* Switch on the source */
	switch (a.discrim)
	{
	case insomereg:
	case insomefreg:
    {
		fail("move: source somereg not specified");
		return NOREG;
    }
	case inreg:
		/* source in fixed point register */
    {
		int r = regalt(a);
		
		switch (dest.answhere.discrim)
		{
		case inreg:
			/* source and dest in fixed register */
		{
			int rd = regalt(dest.answhere);
			
			if (rd != R_0 /* nowhere */ && rd != r)
			{
				/* move reg r to reg rd */
				mov_rr_ins(r, rd);comment(NIL);
			}
			return NOREG;
		}			/* end inreg dest */
		
		case insomereg:
			/* source and dest in fixed register */
		{
			int *sr = someregalt(dest.answhere);
			
			if (*sr != -1)
			{
				fail("move: somereg already set");
			}
			*sr = r;
			return NOREG;
		}
		
		case infreg:
			/* dest in floating point register */
		{
			freg fr;
			
			fr = fregalt(dest.answhere);
			st_ro_ins(i_st, r, mem_temp(0));comment(NIL);
			if (fr.dble)
			{
				st_ro_ins(i_st, r + 1, mem_temp(4));comment(NIL);
				ldf_ro_ins(i_lfd, mem_temp(0), fr.fr);
			}
			else
			{
				ldf_ro_ins(i_lfs, mem_temp(0), fr.fr);
			}
			return NOREG;
		}			/* end infreg dest */
		
		case notinreg:
			/* dest instore */
		{
			Instruction_P st;
			
			if (al==1)
			{
				if (size<=8)
				{
					al = 8;
				}
				else if (size<=16)
				{
					al = 16;
				}
				else
				{
					al = 32;
				}
			}
			st = i_st_sz(al);
			store(st, r, insalt(dest.answhere), regs);
			
			return r;
		}			/* end notinreg dest */
		default:
			break;
		}				/* end switch dest */
    }				/* end inreg a */
	
	case infreg:
		/* source in floating point register */
    {
		freg fr;
		
		fr = fregalt(a);
		switch (dest.answhere.discrim)
		{
		case inreg:
			/* dest in fixed point register */
		{
			int rd = regalt(dest.answhere);
			
			if (rd != 0)
			{
				/* store and load to move to fixed reg */
				if (fr.dble)
				{
					stf_ins(i_stfd, fr.fr, mem_temp(0));
					ld_ro_ins(i_l, mem_temp(0), rd);comment(NIL);
					ld_ro_ins(i_l, mem_temp(4), rd + 1);comment(NIL);
				}
				else
				{
					stf_ins(i_stfs, fr.fr, mem_temp(0));
					ld_ro_ins(i_l, mem_temp(0), rd);comment(NIL);
				}
			}
			return NOREG;
		}			/* end inreg dest */
		
		case insomereg:
			/* source in flt reg, can choose dest reg */
		{
			int * sr = someregalt(dest.answhere);
			
			if (*sr != -1)
			{
				fail("move: somereg already set");
			}
			*sr = getreg(regs);
			setregalt(dest.answhere, *sr);
			goto start;
		}
		
		case infreg:
			/* source and dest in floating point registers */
		{
			freg frd;	frd = fregalt(dest.answhere);	/* for XLC compiler bug */
			
			if (fr.fr != frd.fr)
				rrf_ins(i_fmr, fr.fr, frd.fr);
			
			return NOREG;
		}			/* end infreg dest */
		
		case notinreg:
			/* source in flt reg, dest instore */
		{
			Instruction_P st = (fr.dble) ? i_stfd : i_stfs;
			instore is;
			
			if ((dest.ashwhere.ashsize == 64 && !fr.dble) ||
				(dest.ashwhere.ashsize == 32 && fr.dble))
			{
				fail("inconsistent sizes");
			}
			is = insalt(dest.answhere);
			if (is.adval)
			{
				if (fr.dble)
				{
					stf_ins(i_stfd, fr.fr, is.b);
				}
				else
				{
					stf_ins(i_stfs, fr.fr, is.b);
				}
			}
			else
			{
				baseoff b;
				
				b.base = getreg(regs);
				b.offset = 0;
				ld_ro_ins(i_l, is.b, b.base);comment(NIL);
				stf_ro_ins(st, fr.fr, b);
			};
			
			return ((fr.dble) ? -(fr.fr + 32) : (fr.fr + 32));
		}			/* end notinreg dest */
		default:
			break;
		}				/* end switch dest */
    }				/* end infreg a */
	
	case notinreg:
		/* source instore */
    {
		/* get into register and repeat */
		instore iss;
		
		iss = insalt(a);
		
		if (iss.adval)
			COMMENT("move: source adval");
		
		if (iss.adval && iss.b.offset == 0 && IS_FIXREG(iss.b.base))
		{
			/* address of [base_reg+0] is base_reg */
			setregalt(a, iss.b.base);
			goto start;
		}
		if (al==1)
		{
			if (size<=8)
			{
				al = 8;
			}
			else if (size<=16)
			{
				al = 16;
			}
			else
			{
				al = 32;
			}
		}
		
		/* determine which load instruction to use from al and adval */
		
		switch (dest.answhere.discrim)
		{
		case insomereg:
			/* source instore, can choose dest reg */
		{
			int *sr = someregalt(dest.answhere);
			
			if (*sr != -1)
			{
				fail("move: somereg already set");
			}
			*sr = getreg(regs);
			setregalt(dest.answhere, *sr);
			/* and continue to next case */
		}
		
		case inreg:
			/* source instore, dest in fixpnt reg */
		{
			int rd = regalt(dest.answhere);
			
			if (rd != R_0 /* nowhere */)
			{
				if (iss.adval)
				{
					/* generate address of source */
					if (IS_FIXREG(iss.b.base))
						rir_ins(i_a, iss.b.base, iss.b.offset, rd);
					else
						set_ins(iss.b, rd);
				}
				else
				{
					/* load source */
					ld_ins(i_ld_sz(al, sgned), iss.b, rd);
					if (sgned && al == 8)
					{
						/* POWER has no load signed byte instruction, so propagate sign */
						/* +++ word aligned byte: load word then sra 24 */
						/* +++ halfword aligned byte: lha then sra 8 */
						/* +++ 0 offset: lsi 1 byte then sra 24 */
						adjust_to_size(ulonghd,rd,scharhd,rd,NO_ERROR_JUMP);
					}
				}
			}
			return NOREG;
		}			/* end inreg dest */
		
		case infreg:
			/* source instore, dest in floating pnt reg */
		{
			freg frd;
			
			frd = fregalt(dest.answhere);
			
			ASSERT(!iss.adval);	/* address should never go to float reg */
			/* allow doubles not to be double aligned in mem, ie param */
			if (frd.dble)
			{
				ldf_ins(i_lfd, iss.b, frd.fr);
			}
			else
			{
				ldf_ins(i_lfs, iss.b, frd.fr);
			}
			return NOREG;
		}			/* end infreg dest */
		case notinreg:
			/* source and dest instore */
		{
			return moveinstore(iss, insalt(dest.answhere), dest.ashwhere.ashsize, al, regs, sgned);
		}
		default:
			break;
		}				/* end switch dest */
    }				/* end notinreg a */
	}				/* end switch a */
	
	fail("move not handled");
	return NOREG;
	/*NOTREACHED*/
}

