// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
//
//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//	Portions Copyright © 1997-1999 Vita Nuova Limited
//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
//	Portions Copyright © 2004,2006 Bruce Ellis
//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <link.h>
#include "../cmd/9l/9.out.h"
#include "../runtime/stack.h"
#include "../runtime/funcdata.h"

static Prog zprg = {
	.as = AGOK,
	.reg = NREG,
	.from = {
		.name = D_NONE,
		.type = D_NONE,
		.reg = NREG,
	},
	.from3 = {
		.name = D_NONE,
		.type = D_NONE,
		.reg = NREG,
	},
	.to = {
		.name = D_NONE,
		.type = D_NONE,
		.reg = NREG,
	},
};

static int
symtype(Addr *a)
{
	return a->name;
}

static int
isdata(Prog *p)
{
	return p->as == ADATA || p->as == AGLOBL;
}

static int
iscall(Prog *p)
{
	return p->as == ABL;
}

static int
datasize(Prog *p)
{
	return p->reg;
}

static int
textflag(Prog *p)
{
	return p->reg;
}

static void
settextflag(Prog *p, int f)
{
	p->reg = f;
}

static void
progedit(Link *ctxt, Prog *p)
{
	char literal[64];
	LSym *s;

	USED(ctxt);

	p->from.class = 0;
	p->to.class = 0;

	// Rewrite BR/BL to symbol as D_BRANCH.
	switch(p->as) {
	case ABR:
	case ABL:
	case ARETURN:
	case ADUFFZERO:
	case ADUFFCOPY:
		if(p->to.sym != nil)
			p->to.type = D_BRANCH;
		break;
	}

	// Rewrite float constants to values stored in memory.
	switch(p->as) {
	case AFMOVS:
		if(p->from.type == D_FCONST) {
			int32 i32;
			float32 f32;
			f32 = p->from.u.dval;
			memmove(&i32, &f32, 4);
			sprint(literal, "$f32.%08ux", (uint32)i32);
			s = linklookup(ctxt, literal, 0);
			s->size = 4;
			p->from.type = D_OREG;
			p->from.sym = s;
			p->from.name = D_EXTERN;
			p->from.offset = 0;
		}
		break;
	case AFMOVD:
		if(p->from.type == D_FCONST) {
			int64 i64;
			memmove(&i64, &p->from.u.dval, 8);
			sprint(literal, "$f64.%016llux", (uvlong)i64);
			s = linklookup(ctxt, literal, 0);
			s->size = 8;
			p->from.type = D_OREG;
			p->from.sym = s;
			p->from.name = D_EXTERN;
			p->from.offset = 0;
		}
		break;
	case AMOVD:
		if(p->from.type == D_CONST && p->from.name == D_NONE && (int64)(uint32)p->from.offset != p->from.offset) {
			sprint(literal, "$i64.%016llux", (uvlong)p->from.offset);
			s = linklookup(ctxt, literal, 0);
			s->size = 8;
			p->from.type = D_OREG;
			p->from.sym = s;
			p->from.name = D_EXTERN;
			p->from.offset = 0;
		}
	}

	// Rewrite SUB constants into ADD.
	switch(p->as) {
	case ASUBC:
		if(p->from.type == D_CONST) {
			p->from.offset = -p->from.offset;
			p->as = AADDC;
		}
		break;

	case ASUBCCC:
		if(p->from.type == D_CONST) {
			p->from.offset = -p->from.offset;
			p->as = AADDCCC;
		}
		break;

	case ASUB:
		if(p->from.type == D_CONST) {
			p->from.offset = -p->from.offset;
			p->as = AADD;
		}
		break;
	}
}

static Prog*	stacksplit(Link*, Prog*, int32, int);

static void
parsetextconst(vlong arg, vlong *textstksiz, vlong *textarg)
{
	*textstksiz = arg & 0xffffffffLL;
	if(*textstksiz & 0x80000000LL)
		*textstksiz = -(-*textstksiz & 0xffffffffLL);

	*textarg = (arg >> 32) & 0xffffffffLL;
	if(*textarg & 0x80000000LL)
		*textarg = 0;
	*textarg = (*textarg+7) & ~7LL;
}

static void
addstacksplit(Link *ctxt, LSym *cursym)
{
	Prog *p, *q, *p1, *p2, *q1;
	int o, mov, aoffset;
	vlong textstksiz, textarg;
	int32 autoffset, autosize;

	if(ctxt->symmorestack[0] == nil) {
		ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0);
		ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0);
		// TODO(minux): add morestack short-cuts with small fixed frame-size.
	}

	ctxt->cursym = cursym;

	if(cursym->text == nil || cursym->text->link == nil)
		return;				

	p = cursym->text;
	parsetextconst(p->to.offset, &textstksiz, &textarg);
	autoffset = textstksiz;
	if(autoffset < 0)
		autoffset = 0;
	
	cursym->args = p->to.offset>>32;
	cursym->locals = textstksiz;

	/*
	 * find leaf subroutines
	 * strip NOPs
	 * expand RET
	 * expand BECOME pseudo
	 */

	if(ctxt->debugvlog)
		Bprint(ctxt->bso, "%5.2f noops\n", cputime());
	Bflush(ctxt->bso);

	q = nil;
	for(p = cursym->text; p != nil; p = p->link) {
		switch(p->as) {
		/* too hard, just leave alone */
		case ATEXT:
			q = p;
			p->mark |= LABEL|LEAF|SYNC;
			if(p->link)
				p->link->mark |= LABEL;
			break;

		case ANOR:
			q = p;
			if(p->to.type == D_REG)
				if(p->to.reg == REGZERO)
					p->mark |= LABEL|SYNC;
			break;

		case ALWAR:
		case ASTWCCC:
		case AECIWX:
		case AECOWX:
		case AEIEIO:
		case AICBI:
		case AISYNC:
		case ATLBIE:
		case ATLBIEL:
		case ASLBIA:
		case ASLBIE:
		case ASLBMFEE:
		case ASLBMFEV:
		case ASLBMTE:
		case ADCBF:
		case ADCBI:
		case ADCBST:
		case ADCBT:
		case ADCBTST:
		case ADCBZ:
		case ASYNC:
		case ATLBSYNC:
		case APTESYNC:
		case ATW:
		case AWORD:
		case ARFI:
		case ARFCI:
		case ARFID:
		case AHRFID:
			q = p;
			p->mark |= LABEL|SYNC;
			continue;

		case AMOVW:
		case AMOVWZ:
		case AMOVD:
			q = p;
			switch(p->from.type) {
			case D_MSR:
			case D_SPR:
			case D_FPSCR:
			case D_CREG:
			case D_DCR:
				p->mark |= LABEL|SYNC;
			}
			switch(p->to.type) {
			case D_MSR:
			case D_SPR:
			case D_FPSCR:
			case D_CREG:
			case D_DCR:
				p->mark |= LABEL|SYNC;
			}
			continue;

		case AFABS:
		case AFABSCC:
		case AFADD:
		case AFADDCC:
		case AFCTIW:
		case AFCTIWCC:
		case AFCTIWZ:
		case AFCTIWZCC:
		case AFDIV:
		case AFDIVCC:
		case AFMADD:
		case AFMADDCC:
		case AFMOVD:
		case AFMOVDU:
		/* case AFMOVDS: */
		case AFMOVS:
		case AFMOVSU:
		/* case AFMOVSD: */
		case AFMSUB:
		case AFMSUBCC:
		case AFMUL:
		case AFMULCC:
		case AFNABS:
		case AFNABSCC:
		case AFNEG:
		case AFNEGCC:
		case AFNMADD:
		case AFNMADDCC:
		case AFNMSUB:
		case AFNMSUBCC:
		case AFRSP:
		case AFRSPCC:
		case AFSUB:
		case AFSUBCC:
			q = p;
			p->mark |= FLOAT;
			continue;

		case ABL:
		case ABCL:
		case ADUFFZERO:
		case ADUFFCOPY:
			cursym->text->mark &= ~LEAF;

		case ABC:
		case ABEQ:
		case ABGE:
		case ABGT:
		case ABLE:
		case ABLT:
		case ABNE:
		case ABR:
		case ABVC:
		case ABVS:
			p->mark |= BRANCH;
			q = p;
			q1 = p->pcond;
			if(q1 != nil) {
				while(q1->as == ANOP) {
					q1 = q1->link;
					p->pcond = q1;
				}
				if(!(q1->mark & LEAF))
					q1->mark |= LABEL;
			} else
				p->mark |= LABEL;
			q1 = p->link;
			if(q1 != nil)
				q1->mark |= LABEL;
			continue;

		case AFCMPO:
		case AFCMPU:
			q = p;
			p->mark |= FCMP|FLOAT;
			continue;

		case ARETURN:
			q = p;
			if(p->link != nil)
				p->link->mark |= LABEL;
			continue;

		case ANOP:
			q1 = p->link;
			q->link = q1;		/* q is non-nop */
			q1->mark |= p->mark;
			continue;

		default:
			q = p;
			continue;
		}
	}

	autosize = 0;
	for(p = cursym->text; p != nil; p = p->link) {
		o = p->as;
		switch(o) {
		case ATEXT:
			mov = AMOVD;
			aoffset = 0;
			autosize = textstksiz + 8;
			if((p->mark & LEAF) && autosize <= 8)
				autosize = 0;
			else
				if(autosize & 4)
					autosize += 4;
			p->to.offset = (p->to.offset & (0xffffffffull<<32)) | (uint32)(autosize-8);

			if(!(p->reg & NOSPLIT))
				p = stacksplit(ctxt, p, autosize, !(cursym->text->reg&NEEDCTXT)); // emit split check

			q = p;
			if(autosize) {
				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
				if(!(cursym->text->mark & LEAF) && autosize >= -BIG && autosize <= BIG) {
					mov = AMOVDU;
					aoffset = -autosize;
				} else {
					q = appendp(ctxt, p);
					q->as = AADD;
					q->lineno = p->lineno;
					q->from.type = D_CONST;
					q->from.offset = -autosize;
					q->to.type = D_REG;
					q->to.reg = REGSP;
					q->spadj = +autosize;
				}
			} else
			if(!(cursym->text->mark & LEAF)) {
				if(ctxt->debugvlog) {
					Bprint(ctxt->bso, "save suppressed in: %s\n",
						cursym->name);
					Bflush(ctxt->bso);
				}
				cursym->text->mark |= LEAF;
			}

			if(cursym->text->mark & LEAF) {
				cursym->leaf = 1;
				break;
			}

			q = appendp(ctxt, q);
			q->as = AMOVD;
			q->lineno = p->lineno;
			q->from.type = D_SPR;
			q->from.offset = D_LR;
			q->to.type = D_REG;
			q->to.reg = REGTMP;

			q = appendp(ctxt, q);
			q->as = mov;
			q->lineno = p->lineno;
			q->from.type = D_REG;
			q->from.reg = REGTMP;
			q->to.type = D_OREG;
			q->to.offset = aoffset;
			q->to.reg = REGSP;
			if(q->as == AMOVDU)
				q->spadj = -aoffset;

			if(cursym->text->reg & WRAPPER) {
				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
				//
				//	MOVD g_panic(g), R3
				//	CMP R0, R3
				//	BEQ end
				//	MOVD panic_argp(R3), R4
				//	ADD $(autosize+8), R1, R5
				//	CMP R4, R5
				//	BNE end
				//	ADD $8, R1, R6
				//	MOVD R6, panic_argp(R3)
				// end:
				//	NOP
				//
				// The NOP is needed to give the jumps somewhere to land.
				// It is a liblink NOP, not a Power64 NOP: it encodes to 0 instruction bytes.


				p = appendp(ctxt, p);
				p->as = AMOVD;
				p->from.type = D_OREG;
				p->from.reg = REGG;
				p->from.offset = 4*ctxt->arch->ptrsize; // G.panic
				p->to.type = D_REG;
				p->to.reg = 3;

				p = appendp(ctxt, p);
				p->as = ACMP;
				p->from.type = D_REG;
				p->from.reg = 0;
				p->to.type = D_REG;
				p->to.reg = 3;

				p = appendp(ctxt, p);
				p->as = ABEQ;
				p->to.type = D_BRANCH;
				p1 = p;

				p = appendp(ctxt, p);
				p->as = AMOVD;
				p->from.type = D_OREG;
				p->from.reg = 3;
				p->from.offset = 0; // Panic.argp
				p->to.type = D_REG;
				p->to.reg = 4;

				p = appendp(ctxt, p);
				p->as = AADD;
				p->from.type = D_CONST;
				p->from.offset = autosize+8;
				p->reg = REGSP;
				p->to.type = D_REG;
				p->to.reg = 5;

				p = appendp(ctxt, p);
				p->as = ACMP;
				p->from.type = D_REG;
				p->from.reg = 4;
				p->to.type = D_REG;
				p->to.reg = 5;

				p = appendp(ctxt, p);
				p->as = ABNE;
				p->to.type = D_BRANCH;
				p2 = p;

				p = appendp(ctxt, p);
				p->as = AADD;
				p->from.type = D_CONST;
				p->from.offset = 8;
				p->reg = REGSP;
				p->to.type = D_REG;
				p->to.reg = 6;

				p = appendp(ctxt, p);
				p->as = AMOVD;
				p->from.type = D_REG;
				p->from.reg = 6;
				p->to.type = D_OREG;
				p->to.reg = 3;
				p->to.offset = 0; // Panic.argp

				p = appendp(ctxt, p);
				p->as = ANOP;
				p1->pcond = p;
				p2->pcond = p;
			}

			break;

		case ARETURN:
			if(p->from.type == D_CONST) {
				ctxt->diag("using BECOME (%P) is not supported!", p);
				break;
			}
			if(p->to.sym) { // retjmp
				p->as = ABR;
				p->to.type = D_BRANCH;
				break;
			}
			if(cursym->text->mark & LEAF) {
				if(!autosize) {
					p->as = ABR;
					p->from = zprg.from;
					p->to.type = D_SPR;
					p->to.offset = D_LR;
					p->mark |= BRANCH;
					break;
				}

				p->as = AADD;
				p->from.type = D_CONST;
				p->from.offset = autosize;
				p->to.type = D_REG;
				p->to.reg = REGSP;
				p->spadj = -autosize;

				q = ctxt->arch->prg();
				q->as = ABR;
				q->lineno = p->lineno;
				q->to.type = D_SPR;
				q->to.offset = D_LR;
				q->mark |= BRANCH;
				q->spadj = +autosize;

				q->link = p->link;
				p->link = q;
				break;
			}

			p->as = AMOVD;
			p->from.type = D_OREG;
			p->from.offset = 0;
			p->from.reg = REGSP;
			p->to.type = D_REG;
			p->to.reg = REGTMP;

			q = ctxt->arch->prg();
			q->as = AMOVD;
			q->lineno = p->lineno;
			q->from.type = D_REG;
			q->from.reg = REGTMP;
			q->to.type = D_SPR;
			q->to.offset = D_LR;

			q->link = p->link;
			p->link = q;
			p = q;

			if(autosize) {
				q = ctxt->arch->prg();
				q->as = AADD;
				q->lineno = p->lineno;
				q->from.type = D_CONST;
				q->from.offset = autosize;
				q->to.type = D_REG;
				q->to.reg = REGSP;
				q->spadj = -autosize;

				q->link = p->link;
				p->link = q;
			}

			q1 = ctxt->arch->prg();
			q1->as = ABR;
			q1->lineno = p->lineno;
			q1->to.type = D_SPR;
			q1->to.offset = D_LR;
			q1->mark |= BRANCH;
			q1->spadj = +autosize;

			q1->link = q->link;
			q->link = q1;
			break;

		case AADD:
			if(p->to.type == D_REG && p->to.reg == REGSP && p->from.type == D_CONST)
				p->spadj = -p->from.offset;
			break;
		}
	}

#if 0 // instruction scheduling
	if(debug['Q'] == 0)
		return;

	curtext = nil;
	q = nil;	/* p - 1 */
	q1 = firstp;	/* top of block */
	o = 0;		/* count of instructions */
	for(p = firstp; p != nil; p = p1) {
		p1 = p->link;
		o++;
		if(p->mark & NOSCHED){
			if(q1 != p){
				sched(q1, q);
			}
			for(; p != nil; p = p->link){
				if(!(p->mark & NOSCHED))
					break;
				q = p;
			}
			p1 = p;
			q1 = p;
			o = 0;
			continue;
		}
		if(p->mark & (LABEL|SYNC)) {
			if(q1 != p)
				sched(q1, q);
			q1 = p;
			o = 1;
		}
		if(p->mark & (BRANCH|SYNC)) {
			sched(q1, p);
			q1 = p1;
			o = 0;
		}
		if(o >= NSCHED) {
			sched(q1, p);
			q1 = p1;
			o = 0;
		}
		q = p;
	}
#endif
}

static Prog*
stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt)
{
	Prog *q1;

	// MOVD	g_stackguard(g), R3
	p = appendp(ctxt, p);
	p->as = AMOVD;
	p->from.type = D_OREG;
	p->from.reg = REGG;
	p->from.offset = 2*ctxt->arch->ptrsize;	// G.stackguard0
	if(ctxt->cursym->cfunc)
		p->from.offset = 3*ctxt->arch->ptrsize;	// G.stackguard1
	p->to.type = D_REG;
	p->to.reg = 3;

	if(framesize <= StackSmall) {
		// small stack: SP < stackguard
		//	CMP	stackguard, SP
		p = appendp(ctxt, p);
		p->as = ACMPU;
		p->from.type = D_REG;
		p->from.reg = 3;
		p->to.type = D_REG;
		p->to.reg = REGSP;
	} else if(framesize <= StackBig) {
		// large stack: SP-framesize < stackguard-StackSmall
		//	ADD $-framesize, SP, R4
		//	CMP stackguard, R4
		p = appendp(ctxt, p);
		p->as = AADD;
		p->from.type = D_CONST;
		p->from.offset = -framesize;
		p->reg = REGSP;
		p->to.type = D_REG;
		p->to.reg = 4;

		p = appendp(ctxt, p);
		p->as = ACMPU;
		p->from.type = D_REG;
		p->from.reg = 3;
		p->to.type = D_REG;
		p->to.reg = 4;
	} else {
		// Such a large stack we need to protect against wraparound.
		// If SP is close to zero:
		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
		// The +StackGuard on both sides is required to keep the left side positive:
		// SP is allowed to be slightly below stackguard. See stack.h.
		//
		// Preemption sets stackguard to StackPreempt, a very large value.
		// That breaks the math above, so we have to check for that explicitly.
		//	// stackguard is R3
		//	CMP	R3, $StackPreempt
		//	BEQ	label-of-call-to-morestack
		//	ADD	$StackGuard, SP, R4
		//	SUB	R3, R4
		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
		//	CMPU	R31, R4
		p = appendp(ctxt, p);
		p->as = ACMP;
		p->from.type = D_REG;
		p->from.reg = 3;
		p->to.type = D_CONST;
		p->to.offset = StackPreempt;

		p = appendp(ctxt, p);
		p->as = ABEQ;
		p->to.type = D_BRANCH;

		p = appendp(ctxt, p);
		p->as = AADD;
		p->from.type = D_CONST;
		p->from.offset = StackGuard;
		p->reg = REGSP;
		p->to.type = D_REG;
		p->to.reg = 4;

		p = appendp(ctxt, p);
		p->as = ASUB;
		p->from.type = D_REG;
		p->from.reg = 3;
		p->to.type = D_REG;
		p->to.reg = 4;

		p = appendp(ctxt, p);
		p->as = AMOVD;
		p->from.type = D_CONST;
		p->from.offset = framesize + StackGuard - StackSmall;
		p->to.type = D_REG;
		p->to.reg = REGTMP;

		p = appendp(ctxt, p);
		p->as = ACMPU;
		p->from.type = D_REG;
		p->from.reg = REGTMP;
		p->to.type = D_REG;
		p->to.reg = 4;
	}

	// q1: BLT	done
	q1 = p = appendp(ctxt, p);
	p->as = ABLT;
	p->to.type = D_BRANCH;

	// MOVD	LR, R5
	p = appendp(ctxt, p);
	p->as = AMOVD;
	p->from.type = D_SPR;
	p->from.offset = D_LR;
	p->to.type = D_REG;
	p->to.reg = 5;

	// BL	runtime.morestack(SB)
	p = appendp(ctxt, p);
	p->as = ABL;
	p->to.type = D_BRANCH;
	if(ctxt->cursym->cfunc)
		p->to.sym = linklookup(ctxt, "runtime.morestackc", 0);
	else
		p->to.sym = ctxt->symmorestack[noctxt];

	// BR	start
	p = appendp(ctxt, p);
	p->as = ABR;
	p->to.type = D_BRANCH;
	p->pcond = ctxt->cursym->text->link;

	// placeholder for q1's jump target
	p = appendp(ctxt, p);
	p->as = ANOP; // zero-width place holder
	q1->pcond = p;

	return p;
}

static void xfol(Link*, Prog*, Prog**);

static void
follow(Link *ctxt, LSym *s)
{
	Prog *firstp, *lastp;

	ctxt->cursym = s;

	firstp = ctxt->arch->prg();
	lastp = firstp;
	xfol(ctxt, s->text, &lastp);
	lastp->link = nil;
	s->text = firstp->link;
}

static int
relinv(int a)
{

	switch(a) {
	case ABEQ:	return ABNE;
	case ABNE:	return ABEQ;

	case ABGE:	return ABLT;
	case ABLT:	return ABGE;

	case ABGT:	return ABLE;
	case ABLE:	return ABGT;

	case ABVC:	return ABVS;
	case ABVS:	return ABVC;
	}
	return 0;
}

static void
xfol(Link *ctxt, Prog *p, Prog **last)
{
	Prog *q, *r;
	int a, b, i;

loop:
	if(p == nil)
		return;
	a = p->as;
	if(a == ABR) {
		q = p->pcond;
		if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
			p->mark |= FOLL;
			(*last)->link = p;
			*last = p;
			p = p->link;
			xfol(ctxt, p, last);
			p = q;
			if(p && !(p->mark & FOLL))
				goto loop;
			return;
		}
		if(q != nil) {
			p->mark |= FOLL;
			p = q;
			if(!(p->mark & FOLL))
				goto loop;
		}
	}
	if(p->mark & FOLL) {
		for(i=0,q=p; i<4; i++,q=q->link) {
			if(q == *last || (q->mark&NOSCHED))
				break;
			b = 0;		/* set */
			a = q->as;
			if(a == ANOP) {
				i--;
				continue;
			}
			if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID)
				goto copy;
			if(!q->pcond || (q->pcond->mark&FOLL))
				continue;
			b = relinv(a);
			if(!b)
				continue;
		copy:
			for(;;) {
				r = ctxt->arch->prg();
				*r = *p;
				if(!(r->mark&FOLL))
					print("cant happen 1\n");
				r->mark |= FOLL;
				if(p != q) {
					p = p->link;
					(*last)->link = r;
					*last = r;
					continue;
				}
				(*last)->link = r;
				*last = r;
				if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID)
					return;
				r->as = b;
				r->pcond = p->link;
				r->link = p->pcond;
				if(!(r->link->mark&FOLL))
					xfol(ctxt, r->link, last);
				if(!(r->pcond->mark&FOLL))
					print("cant happen 2\n");
				return;
			}
		}

		a = ABR;
		q = ctxt->arch->prg();
		q->as = a;
		q->lineno = p->lineno;
		q->to.type = D_BRANCH;
		q->to.offset = p->pc;
		q->pcond = p;
		p = q;
	}
	p->mark |= FOLL;
	(*last)->link = p;
	*last = p;
	if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID){
		if(p->mark & NOSCHED){
			p = p->link;
			goto loop;
		}
		return;
	}
	if(p->pcond != nil)
	if(a != ABL && p->link != nil) {
		xfol(ctxt, p->link, last);
		p = p->pcond;
		if(p == nil || (p->mark&FOLL))
			return;
		goto loop;
	}
	p = p->link;
	goto loop;
}

static Prog*
prg(void)
{
	Prog *p;

	p = emallocz(sizeof(*p));
	*p = zprg;
	return p;
}

LinkArch linkpower64 = {
	.name = "power64",
	.thechar = '9',
	.endian = BigEndian,

	.addstacksplit = addstacksplit,
	.assemble = span9,
	.datasize = datasize,
	.follow = follow,
	.iscall = iscall,
	.isdata = isdata,
	.prg = prg,
	.progedit = progedit,
	.settextflag = settextflag,
	.symtype = symtype,
	.textflag = textflag,

	.minlc = 4,
	.ptrsize = 8,
	.regsize = 8,

	.D_ADDR = D_ADDR,
	.D_AUTO = D_AUTO,
	.D_BRANCH = D_BRANCH,
	.D_CONST = D_CONST,
	.D_EXTERN = D_EXTERN,
	.D_FCONST = D_FCONST,
	.D_NONE = D_NONE,
	.D_PARAM = D_PARAM,
	.D_SCONST = D_SCONST,
	.D_STATIC = D_STATIC,
	.D_OREG = D_OREG,

	.ACALL = ABL,
	.ADATA = ADATA,
	.AEND = AEND,
	.AFUNCDATA = AFUNCDATA,
	.AGLOBL = AGLOBL,
	.AJMP = ABR,
	.ANOP = ANOP,
	.APCDATA = APCDATA,
	.ARET = ARETURN,
	.ATEXT = ATEXT,
	.ATYPE = ATYPE,
	.AUSEFIELD = AUSEFIELD,
};

LinkArch linkpower64le = {
	.name = "power64le",
	.thechar = '9',
	.endian = LittleEndian,

	.addstacksplit = addstacksplit,
	.assemble = span9,
	.datasize = datasize,
	.follow = follow,
	.iscall = iscall,
	.isdata = isdata,
	.prg = prg,
	.progedit = progedit,
	.settextflag = settextflag,
	.symtype = symtype,
	.textflag = textflag,

	.minlc = 4,
	.ptrsize = 8,
	.regsize = 8,

	.D_ADDR = D_ADDR,
	.D_AUTO = D_AUTO,
	.D_BRANCH = D_BRANCH,
	.D_CONST = D_CONST,
	.D_EXTERN = D_EXTERN,
	.D_FCONST = D_FCONST,
	.D_NONE = D_NONE,
	.D_PARAM = D_PARAM,
	.D_SCONST = D_SCONST,
	.D_STATIC = D_STATIC,
	.D_OREG = D_OREG,

	.ACALL = ABL,
	.ADATA = ADATA,
	.AEND = AEND,
	.AFUNCDATA = AFUNCDATA,
	.AGLOBL = AGLOBL,
	.AJMP = ABR,
	.ANOP = ANOP,
	.APCDATA = APCDATA,
	.ARET = ARETURN,
	.ATEXT = ATEXT,
	.ATYPE = ATYPE,
	.AUSEFIELD = AUSEFIELD,
};
