ftp.nice.ch/pub/next/developer/hardware/dsp/gcc56k.N.s.tar.gz#/gcc56-01/out-dsp56k.c

This is out-dsp56k.c in view mode; [Download] [Up]

/*
    GCC56K -- GCC 1.40 machine description for DSP56000/1 processor
    Copyright (C) 1991 Andrew Sterian and Bell-Northern Research

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with the GCC distribution; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	Contact the author at 'asterian@bnr.ca' or post to the 'comp.dsp'
	newsgroup.
*/

#include <stdio.h>
#include <ctype.h>
#include "tree.h"
#include "flags.h"

void print_operand_address(FILE *, rtx, int);
void extendReg(rtx op);

static int regValid[FIRST_PSEUDO_REGISTER];

int
hard_regno_mode_ok(int regnum, enum machine_mode mode)
{
    if ((mode == DFmode) || (mode == DImode)) {
        switch(regnum) {
         case 0:    /* X1 */
         case 2:    /* Y1 */
         case 5:    /* A1 */
         case 8:    /* B1 */
             return 1;  /* All of the above are OK */

         default:
             return 0;
        }
    }

	/* Ensure A2, B2 don't get allocated */
	if ((regnum==4) || (regnum==7))
		return 0;

	/* A0, B0, and AGU registers can't hold SFmode or SImode */
    if ((mode == SFmode) || (mode == SImode)) {
        if ((regnum >= 10) && (regnum <= 26))
            return 0;
		if ((regnum == 6) || (regnum == 9))
			return 0;
        return 1;
    }

    if ((mode == QImode) || (mode == VOIDmode) || (mode == HImode)) {
        return 1;   /* All regs OK */
    }

	error("aux-output: Mode %u unknown",mode);
	return 0;   /* No other modes accepted */
}

int
regno_reg_class(int regnum)
{
    switch (regnum) {
	 case 0: case 2:					/* X1, Y1 */
		return ALU_DATA_REGS1;

     case 1: case 3:					/* X0, Y0 */
        return ALU_DATA_REGS0;

	/* A0 and B0 have to be included because of address arithmetic */
	/* e.g. (set (reg:HI 8) (subreg:HI (reg:DI 5) 1)) */
	/* transforms into (set (reg:HI 8) (reg:HI 6)) */
	 case 5: case 8:					/* A1, B1 */
		return ACC_REGS_HI;

	 case 6: case 9:					/* A0, B0 */
		return ACC_REGS_LO;

     case 10: case 11: case 12: case 13: /* R0..R7 */
     case 14: case 15: case 16: case 17:
        return ADDRESS_REGS;

     case 18: case 19: case 20: case 21: /* N0..N7 */
     case 22: case 23: case 24: case 25:
        return INDEX_REGS;

     default:				/* A2, B2, Possibly pseudo-regs */
        return NO_REGS;
    }
}

int
reg_class_from_letter(int c)
{
        /* May not use the letters m o < > r i n I J K F G H s g p */

    switch(c) {
	 case 'A':		/* A1, A0, B1, B0, X1, X0, Y1, Y0 */
		return ALU_REGS;

	 case 'C':		/* A1, B1, X1, X0, Y1, Y0 */
		return MATHOP_REGS;

     case 'a':		/* R0..R7 */
        return ADDRESS_REGS;

     case 'd':		/* X1, X0, Y1, Y0 */
        return ALU_DATA_REGS;

	 /* Used for multiply instructions where mpy X1,X1,A is illegal */
     case 'e':      /* X0, Y0 */
        return ALU_DATA_REGS0;

	 /* Not used */
     case 'x':      /* N0..N7 */
        return INDEX_REGS;

     case 'l':      /* A1, B1 */
        return ACC_REGS_HI;

	 case 'u':		/* A1, A0, B1, B0 */
		return ACC_REGS;

     default:
        return NO_REGS;
    }
}

void
function_prologue(FILE *file, int size)
{
	int i;

	fprintf(file,"\tmove R0,%c:-(R6)\n",StackSpace);
    fprintf(file,"\tmove SSH,%c:-(R6)\n",StackSpace);

	if (size > 1)
	    fprintf(file,"\tmove #%u,N6\n",size);

	fprintf(file,"\tlua  (R6)-,R0\n");

	if (size > 1)
		fprintf(file,"\tmove (R6)-N6\n");
	else if (size == 1)
		fprintf(file,"\tmove (R6)-\n");

	for (i=10; i<26; i++) {
   	    if (regs_ever_live[i] && !call_used_regs[i]) {
            fprintf(file,"\tmove %s,%c:-(R6)\n",reg_names[i],StackSpace);
	    }
	}
}

void
function_epilogue(FILE *file, int size)
{
	int i;
	int any_regs_used=0;

    for (i=10; i<26; i++) {
        if (regs_ever_live[i] && !call_used_regs[i]) {
    		any_regs_used = 1;
			break;
        }
    }

	if ((size > 1) && any_regs_used)
		fprintf(file, "\tmove #%u,N0\n",size);

    fprintf(file, "\tlua  (R0)+,R6\n");

	if (any_regs_used) {
		if (size > 1)
			fprintf(file, "\tmove (R0)-N0\n");
		else if (size == 1)
			fprintf(file, "\tmove (R0)-\n");

    	for (i=10; i<26; i++) {
        	if (regs_ever_live[i] && !call_used_regs[i]) {
            	fprintf(file,"\tmove %c:(R0)-,%s\n", StackSpace, reg_names[i]);
        	}
    	}
	} else {
		fprintf(file,"\tnop\n");
	}

    fprintf(file,"\tmove %c:(R6)+,SSH\n", StackSpace);
	fprintf(file,"\tmove %c:(R6)+,R0\n", StackSpace);
	fprintf(file,"\trts\n");
}

/*
 * Codes:
 *      w -- For A and B, output A1 and B1 (or A10, B10)
 *      x -- lower part of 48-bit DFmode or DImode value (X0 instead of X)
 *			 or Y: part of a long-mode memory reference
 *      y -- upper part of 48-bit DFmode or DImode value (X1 instead of X)
 *			 or X: part of a long-mode memory reference
 *		z -- Extension part of 56-bit reg. (A2 instead of A)
 *      p -- Address or (Reg)+ -- first word of a DI/DF mode
 *      q -- Address+1 of (Reg)- -- second word of a DI/DF mode
 *		r -- Don't output size information
 *      s -- Destination is N-register (different from GCC5616)
 *		Z -- Don't output memory space
 */
void
print_operand(FILE *filePtr, rtx operand, int code)
{
    enum machine_mode mode=GET_MODE(operand);
	char *p;
	int c;
	int outval;
	int modeIsLong, modeIsInt, isAcc;
	rtx memop;

	modeIsLong = ((mode==DFmode) || (mode==DImode));
	modeIsInt = ((mode==QImode) || (mode==HImode) || (mode==SImode));

    switch(GET_CODE(operand)) {
     case REG:
        p = reg_names[REGNO(operand)];
        c = *p;
		isAcc = ((REGNO(operand)==5) || (REGNO(operand)==8));

		if (REGNO(operand)==6) {
			fprintf(filePtr,"A0");
		} else if (REGNO(operand)==9) {
			fprintf(filePtr,"B0");
		} else if ((mode==DImode) && isAcc && (code == 'w')) {
            fprintf(filePtr,"%c10",c);
        } else if (code=='x') {
            fprintf(filePtr,"%c0",c);
        } else if (code=='y') {
            fprintf(filePtr,"%c1",c);
		} else if ((code=='z') && isAcc) {
			fprintf(filePtr,"%c2",c);
		} else if (modeIsLong) {
			fprintf(filePtr,"%c",c);
		} else if (isAcc) {
			/* Changed from 'if (modeIsInt && code=='w')' */
			/* 21Oct91 */
			if (code=='w')
				fprintf(filePtr,"%c1",c);
			else
				fprintf(filePtr,"%c",c);
        } else {
			fprintf(filePtr, "%s", p);
		}
        break;

     case MEM:
		memop = XEXP(operand,0);
		if (code != 'Z') {
			/* If this a reference to R0 or R6, use StackSpace */
			do { int memcode=GET_CODE(memop);
			if (modeIsLong && !TARGET_NOLSPACE) {
				if (code=='x')
					fprintf(filePtr,"Y:");
				else if (code=='y')
					fprintf(filePtr,"X:");
				else
					fprintf(filePtr,"L:"); break;
			}
			if ((memcode==REG) || (memcode==PRE_DEC) || 
				(memcode==POST_INC) || (memcode==POST_DEC)) {
				if ((REGNO(memop) == 10) || (REGNO(memop)==16)) {
					fprintf(filePtr,"%c:",StackSpace); break;
				}
			}
			if (memcode==PLUS) {
				rtx opB=XEXP(memop, 0);
				rtx opIx=XEXP(memop, 1);
				if (GET_CODE(opIx)==REG && REGNO(opIx)>=10) {
					opB=opIx; opIx=XEXP(memop, 0);
				}
				if ((REGNO(opB) == 10) || (REGNO(opB)==16)) {
					fprintf(filePtr,"%c:",StackSpace); break;
				}
			}

			fprintf(filePtr,"%c:", DataSpace);
			} while(0);
		}
        print_operand_address(filePtr, memop, code);
        break;

	 case CONST_INT:
		outval = INTVAL(operand);

		if (code=='s') {
			if (outval & 0xFFFFFF00)
				p = "#%d";
			else 
				p = "#<%d";
			if (!(outval & 0x80000000)) 
				outval &= 0xFFFF;
			else
				outval |= 0x7FFF0000;
		} else {
			outval &= 0xFFFFFF;
			if (mode == HImode) outval &= 0xFFFF;
			if (mode == QImode) outval &= 0xFFFF;

			if (code=='r')
				p = "#$%X";
			else
				if (! (outval & 0xFFFF) ) {
					p = "#<$%X";
					outval >>= 16;
				} else
					p = "#>$%X";
		}
	
		fprintf(filePtr, p, outval);
		break;

	 case CONST_DOUBLE:
        putc('#', filePtr);
		output_addr_const(filePtr, operand);
		break;

	 case LABEL_REF:
	 case CONST:
		fprintf(filePtr, "#>");
		output_addr_const(filePtr, operand);
		break;

	 case SYMBOL_REF:
		p = XSTR(operand,0);
		fprintf(filePtr, "#>");
		assemble_name(filePtr, p);
		break;

     default:
		fprintf(stderr,"%s:%u Unknown code %s:%u\n",__FILE__,__LINE__,
			rtx_name[GET_CODE(operand)], GET_MODE(operand));
			/* fall-through */

	 case CODE_LABEL:
        output_addr_const(filePtr, operand);
        break;
    }
}

extern FILE *asm_out_file;

void
output_preloadN(rtx operand)
{
	rtx op0,op1;

	if (GET_CODE(operand) != MEM) return;

	operand = XEXP(operand,0);
	if (GET_CODE(operand) != PLUS) return;

	op0=XEXP(operand,0); op1=XEXP(operand,1);
	if (GET_CODE(op1)==REG && REGNO(op1)>=10) {
		op1=op0; op0=XEXP(operand,1);
	}

	fprintf(asm_out_file,"\tmove ");
	print_operand(asm_out_file, op1, 's');
	fprintf(asm_out_file,",N%u\n",REGNO(op0)-10);
}

void
print_operand_address(FILE *filePtr, rtx operand, int code)
{
	char *p;
	int digit;
	int opval;
	rtx op0,op1;

    switch(GET_CODE(operand)) {
     case REG:
        p = reg_names[REGNO(operand)];
        digit = *(p+1);

        switch(code) {
         case 'p':
            fprintf(filePtr, "(%s)+", p);
            break;

         case 'q':
            fprintf(filePtr, "(%s)-", p);
            break;

         default:
            fprintf(filePtr, "(%s)", p);
        }
        break;

     case PRE_DEC:
        p = reg_names[REGNO(XEXP(operand,0))];

        fprintf(filePtr, "-(%s)", p);
        break;

	 case POST_DEC:
		p = reg_names[REGNO(XEXP(operand,0))];

		fprintf(filePtr, "(%s)-", p);
		break;

     case POST_INC:
        p = reg_names[REGNO(XEXP(operand,0))];

        fprintf(filePtr, "(%s)+", p);
        break;

     case PLUS:
		op0=XEXP(operand,0); op1=XEXP(operand,1);
		if (GET_CODE(op1)==REG && REGNO(op1)>=10) {
			op0=op1; op1=XEXP(operand,0);
		}

        p = reg_names[REGNO(op0)];
        digit = *(p+1);

        fprintf(filePtr, "(%s+N%c)", p, digit);
        break;

	 case SYMBOL_REF:
		p = XSTR(operand, 0);
		assemble_name(filePtr, p);
		if (code=='q') fprintf(filePtr, "+1");
		break;

	 case CONST_INT:
		opval = INTVAL(operand) & 0xFFFF;
		if (code=='q') opval++;
		if (opval < 256)
			p = "<$%X";
		else
			p = ">$%X";
		fprintf(filePtr, p, opval);
		break;

	 case CONST:
		output_addr_const(filePtr, operand);
		if (code=='q') fprintf(filePtr, "+1");
		break;

	 default:
		fprintf(filePtr, "%s:%u Unknown code %u", __FILE__, __LINE__, 
													GET_CODE(operand));
		break;
    }
}

/* Sometime in the future */
int
legitimize_address(rtx x, rtx oldx, enum machine_mode mode)
{
    return 0;
}

void
select_rtx_section(enum machine_mode mode, rtx exp)
{
	switch(mode) {
	 case DFmode: case DImode:
		if (!TARGET_NOLSPACE) {
			l_const_section();
			break;
		}
		/* else fall-through */

     case QImode: case HImode: case SImode: case SFmode:
	 case BLKmode:
		if (TARGET_DATAX)
			x_const_section();
		else
			y_const_section();
		break;

	 default:
		error("%s:%c Don't know how to handle mode %s\n",__FILE__,__LINE__,
				GET_MODE_NAME(mode));
	}
}

/* Given a tree which is a VAR_DECL, find the largest element in the */
/* type of the tree.  Since sizes are either 1 or 2, stop whenever */
/* we've found something of size 2.  This means that the data */
/* structure will reside in L-space.  If all elements of the structure */
/* are of size 1, the structure will reside in DataSpace */
int
find_longest_field(tree x)
{
	int code = TREE_CODE(x);

	if (!x) return 0;

	while ((code==RECORD_TYPE) || (code==UNION_TYPE) || (code==ARRAY_TYPE) ||
		   (code==FIELD_DECL)) {
		if (code==ARRAY_TYPE) {
			x = TREE_TYPE(x);
			code = TREE_CODE(x);
			continue;
		}

		if (code==FIELD_DECL) {
			if (find_longest_field(TREE_TYPE(x))) {
				return 1;
			} else {
				x = TREE_CHAIN(x);
				if (!x) return 0;
				code = TREE_CODE(x);
				continue;
			}
		}

		return find_longest_field(TYPE_FIELDS(x));
	}

	switch(code) {
	 case INTEGER_TYPE:
		return (DECL_MODE(x) == DImode);

	 case REAL_TYPE:
		return (DECL_MODE(x) == DFmode);

	 case ENUMERAL_TYPE: case POINTER_TYPE:
		return 0;

	 default:
		fprintf(stderr,"%s:%u Unknown type\n",__FILE__,__LINE__);
		debug_tree(x);
		return 1;
	}
}

void
select_section(tree x)
{
	unsigned char code=TREE_CODE(x);
	enum machine_mode mode=DECL_MODE(x);
	int constant = 0;
	int longtype = -1;

	switch(code) {
	 case STRING_CST:
	 	if (flag_writable_strings) {
			if (TARGET_DATAX) 
				x_data_section();
			else
				y_data_section();
		} else {
			if (TARGET_DATAX)
				x_const_section();
			else
				y_const_section();
		}
		break;

	 case INTEGER_CST:
	 case REAL_CST:
		mode = DECL_MODE(TREE_TYPE(x));
		switch(mode) {
		 case DFmode: case DImode:
			if (!TARGET_NOLSPACE) {
		 		l_const_section();
				break;
			} 
			/* else fall-through */

		 case QImode: case HImode: case SImode: case SFmode:
		 case BLKmode:
			if (TARGET_DATAX)
				x_const_section();
			else
				y_const_section();
			break;

		 default:
			fprintf(stderr,"%s:%u Unknown mode %d/%s\n",__FILE__,__LINE__,
										mode,GET_MODE_NAME(mode));
			debug_tree(x);
			break;
		}
		break;

	 case VAR_DECL:
		/* No storage allocated for external references */
		if (TREE_EXTERNAL(x)) return;

		if (TREE_STATIC(x) && TREE_READONLY(x)) constant=1;

		if (TARGET_NOLSPACE)
			longtype = 0;
		else
			longtype = find_longest_field(TREE_TYPE(x));

		if (constant)
			if (longtype)
				l_const_section();
			else if (TARGET_DATAX)
				x_const_section();
			else
				y_const_section();
		else
			if (longtype)
				l_data_section();
			else if (TARGET_DATAX)
				x_data_section();
			else
				y_data_section();
		break;

	 default:
		fprintf(stderr,"%s:%u Unknown code %u\n",__FILE__,__LINE__,code);
		debug_tree(x);
		break;
	}
}

typedef union { double d; int i[2]; } float_u;

double ints_to_double(rtx op)
{
	float_u u;

	u.i[0] = CONST_DOUBLE_LOW(op);
	u.i[1] = CONST_DOUBLE_HIGH(op);
	return u.d;
}

int
const_double_to_SF(rtx op, int *intVal)
{
	register int i, outval;
	double inval, mask;

	inval = ints_to_double(op);
	if ((inval < -1.0) || (inval >= 1.0)) return 0;

	outval = 0;
	if (inval < 0) {
		outval = 1;
		inval += 1.0;
	}
	for (i=0, mask=0.5; i<23; i++, mask /= 2.0) {
		outval <<= 1;
		if (inval >= mask) {
			outval |= 1;
			inval -= mask;
		}
	}
	*intVal = outval;
	return 1;
}

int
const_inval_to_DF(double inval, int *intHigh, int *intLow)
{
	register int i, outval1, outval2;
	double mask;

	if ((inval < -1.0) || (inval >= 1.0)) return 0;

	outval1 = 0;
	if (inval < 0) {
		outval1 = 1;
		inval += 1.0;
	}

	for (i=0, mask=0.5; i<23; i++, mask /= 2.0) {
		outval1 <<= 1;
		if (inval >= mask) {
			outval1 |= 1;
			inval -= mask;
		}
	}
	for (i=0, outval2=0; i<24; i++, mask /= 2.0) {
		outval2 <<= 1;
		if (inval >= mask) {
			outval2 |= 1;
			inval -= mask;
		}
	}
	*intHigh = outval1;
	*intLow = outval2;
	return 1;
}

int
const_double_to_DF(rtx op, int *intHigh, int *intLow)
{
	return const_inval_to_DF(ints_to_double(op), intHigh, intLow);
}

int
const_double_ok_for_letter_p(rtx op, int c)
{
	double f;

	if ((c=='F' || c=='G')) {
		f = ints_to_double(op);
		if ((f >= -1.0) && (f < 1.0))
			return 1;
	}
	return 0;
}

int
constant_address_p(rtx op)
{
	int code = GET_CODE(op);

	return (code==LABEL_REF) || (code==SYMBOL_REF) || (code==CONST_INT) ||
			(code==CONST);
}

void
asm_output_ascii(FILE *filePtr, char *name, int len)
{
	int i,j,k;

	while (len) {
		fprintf(filePtr, "\tdc\t");
		for (i=0; (i<10) && (len); i++,len--) {
			if (i) fprintf(filePtr, ",$%02X", *name++); else
			fprintf(filePtr, "$%02X", *name++);
		}

		for (k=i; k<10; k++)
			fprintf(filePtr, "    ");

		fprintf(filePtr, " ;\'");
		for (j=0; j<i; j++) {
			if (isprint(name[j-i])) fprintf(filePtr,"%c",name[j-i]);
			else fprintf(filePtr,".");
		}
		fprintf(filePtr,"\'\n");
	}
}

char *
remove_punct(char *name)
{
	static char outname[40];
	int i;

	/* Skip over leading punctuation */
	while (!isalnum(*name)) name++;

	for (i=0; (i<39) && *name; name++) {
		outname[i++] = (isalnum(*name) ? *name : '_');
	}
	outname[i] = 0;
	return outname;
}

/*
This routine returns the output string for general math ops. which
match the following pattern:	
 (set (match_operand:mode 0 "general_operand" "=l")
	(mathop:mode (match_operand:mode 1 "general_operand" "0")
				 (match_operand:mode 2 "general_operand" "dl")))
*/

#define ADD_56K 	0
#define SUB_56K 	1

char *
mathOpInsn(rtx *operands, int code)
{
	int reg0 = REGNO(operands[0]);
	int reg1 = REGNO(operands[2]);

	switch(code) {
     case ADD_56K:
		if (reg0==reg1) {
			extendReg(operands[0]);
			regValid[reg0] = 0;
			return "asl  %0";
		} else {
			return "add  %2,%0";
		}

	 case SUB_56K:
		if (reg0==reg1) {
			regValid[reg0] = 1;
			return "clr  %0";
		} else {
			return "sub  %2,%0";
		}
	}
}

int
class_max_nregs(enum reg_class class, enum machine_mode mode)
{
	switch(class) {
	 case ALU_DATA_REGS0: case ALU_DATA_REGS1:
	 case ACC_REGS_HI: case ACC_REGS:
	 case ALU_DATA_REGS: case MATHOP_REGS:
	 case ALU_REGS: case GENERAL_REGS: case GENERAL_REGS2:
	 case ALL_REGS:
		return (((mode==DImode) || (mode==DFmode)) ? 2 : 1);

	 case ACC_REGS_LO: case ADDRESS_REGS: case INDEX_REGS: case AGU_REGS:
	 	return 1;

	 default:
		fprintf(stderr,"%s:%u Unexpected class %u\n",__FILE__,__LINE__,
											class);
		error("Internal error");
		return 2;
	}
}

/* We use this to clobber A0 or B0 for every INSN that writes to A or B */
/* Too pessimistic, perhaps make A0 and B0 actually usable in the future */
int
insn_clobbers_regno_p(rtx insn, int regnum)
{
	rtx op=PATTERN(insn);
	int regdest;

	/* Only real INSN's clobber A0/B0 mysteriously */
	if (GET_CODE(insn) != INSN) return 0;

	/* Check for an assignment to register A1 (5) or B1 (8) */
	if (GET_CODE(op) != SET) return 0;
	if (GET_CODE(XEXP(op,0)) != REG) return 0;

	regdest=REGNO(XEXP(op,0));
	if ((regdest != 5) && (regdest != 8)) return 0;

	/* Is reg-to-check related to our destination register ? */
	if (regnum != (regdest+1)) return 0;

	/* Assume all assignments to A or B clobber A0 or B0 */
	return 1;
}

void
asm_output_double(FILE *filePtr, double value)
{
	int intHigh, intLow;

	if (const_inval_to_DF(value, &intHigh, &intLow)) {
		if (TARGET_NOLSPACE) {
			fprintf(filePtr,"\tdc\t$%06X\n",intHigh);
			fprintf(filePtr,"\tdc\t$%06X\n",intLow);
		} else {
			fprintf(filePtr,"\tdc\t%1.20lf\n",value);
		}
	} else {
		error("Floating point constant out of bounds");
	}
}

void
asm_output_float(FILE *filePtr, double value)
{
	if (value < 1.0 && value >= -1.0) {
		fprintf(filePtr,"\tdc\t%1.12lf\n",value);
	} else {
		error("Floating point constant out of bounds");
	}
}

void
double_reg_to_memory(rtx operands[])
{
	int reg0;
	rtx op1 = XEXP(operands[0],0);
	rtx opB, opIx;
	rtx xoperands[2];
	int code = GET_CODE(op1);
	int intVal;

	output_preloadN(operands[0]);

	switch(code) {
	 case REG:
	 case POST_INC:
		if (TARGET_NOLSPACE) {
			output_asm_insn( "move %y1,%p0", operands);
			output_asm_insn( "move %x1,%q0", operands);
		} else {
			output_asm_insn( "move %w1,%0", operands);
		}
		return;

	 case POST_DEC:
		reg0 = REGNO(XEXP(op1,0));
		if (TARGET_NOLSPACE) {
			fprintf(asm_out_file, "\tmove (%s)+\n", reg_names[reg0]);
			output_asm_insn( "move %x1,%p0", operands);
			output_asm_insn( "move %y1,%q0", operands);
			fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		} else {
			output_asm_insn( "move %w1,%0", operands);
		}
		return;

	 case PRE_DEC:
		if (TARGET_NOLSPACE) {
			output_asm_insn( "move %x1,%p0", operands);
			output_asm_insn( "move %y1,%q0", operands);
		} else {
			output_asm_insn( "move %w1,%0", operands);
		}
		return;

	 case PLUS:
		if (!TARGET_NOLSPACE) {
			output_asm_insn( "move %w1,%0", operands);
			return;
		}
    	output_asm_insn( "move %y1,%p0", operands);

		opIx = XEXP(op1, 1);	/* The index */
		opB  = XEXP(op1, 0); 	/* The base */
		if (GET_CODE(opIx)==REG && REGNO(opIx)>=10) {
			opB=opIx; opIx=XEXP(op1, 0);
		}

		reg0 = REGNO(opB);
		fprintf(asm_out_file, "\tmove (%s)+\n", reg_names[reg0]);
		output_asm_insn( "move %x1,%q0", operands);
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		return;

	 case SYMBOL_REF:
	 case CONST_INT:
	 case CONST:
		if (TARGET_NOLSPACE) {
			output_asm_insn( "move %y1,%p0", operands);
			output_asm_insn( "move %x1,%q0", operands);
		} else {
			output_asm_insn( "move %w1,%0", operands);
		}
		return;

	 default:
		fprintf(stderr,"%s:%u Unknown code %d\n",__FILE__,__LINE__,code);
		error("Internal error");
	}
	return;
}

/* This routine must also take care of the move subreg:DI->SI RTL */
/* It is assumed that if the destination reg is not longmode, it */
/* is a result of this kind of RTL and only the low word of the DI */
/* is required. */
void
double_reg_from_memory(rtx operands[])
{
	int reg0,reg1;
	rtx op1=XEXP(operands[1],0);
	rtx opIx, opB;
	int code = GET_CODE(op1);
	enum machine_mode destMode = GET_MODE(operands[0]);
	int destModeIsLong;

	destModeIsLong = ((destMode==DImode) || (destMode==DFmode));

	reg0 = REGNO(operands[0]);
	switch(code) {
	 case REG:
	 case POST_INC:
		if (TARGET_NOLSPACE) {
			if (destModeIsLong) {
				if (reg0 < 4) 
					output_asm_insn( "move %p1,%y0", operands);
				else
					output_asm_insn( "move %p1,%0", operands);
				output_asm_insn( "move %q1,%x0", operands);
			} else {
				/* Special subreg RTL case */
				output_asm_insn( "move %p1,%0 ;dummy", operands);
				output_asm_insn( "move %q1,%0", operands);
			}
		} else {
			if (destModeIsLong) {
				output_asm_insn( "move %1,%0", operands);
			} else {
				output_asm_insn( "move %x1,%0", operands);
			}
		}
		return;

	 case POST_DEC:
		reg1 = REGNO(XEXP(op1,0));

		if (!TARGET_NOLSPACE) {
			if (destModeIsLong) {
				output_asm_insn( "move %1,%0", operands);
			} else {
				output_asm_insn( "move %x1,%0", operands);
			}
			return;
		}

		fprintf(asm_out_file, "\tmove (%s)+\n", reg_names[reg1]);
		if (destModeIsLong) {
			if (reg0 < 4)
				output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);
			output_asm_insn( "move %q1,%x0", operands);
		} else {
			/* Special subreg RTL case */
			output_asm_insn( "move %p1,%0", operands);
			fprintf(asm_out_file, "\tmove (%s)- ;dummy\n", reg_names[reg1]);
		}
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg1]);
		return;

	 case PRE_DEC:
		reg1 = REGNO(XEXP(op1,0));
		if (TARGET_NOLSPACE) {
			if (destModeIsLong) {
				output_asm_insn( "move %p1,%x0", operands);
				output_asm_insn( "move %q1,%y0", operands);
				regValid[reg0] = 0;
			} else {
				/* Special subreg RTL case */
				output_asm_insn( "move %p1,%0", operands);
				fprintf(asm_out_file, "\tmove (%s)- ;dummy\n", reg_names[reg1]);
			}
		} else {
			if (destModeIsLong) {
				output_asm_insn( "move %1,%0", operands);
			} else {
				output_asm_insn( "move %x1,%0", operands);
			}
		}
		return;

	 case PLUS:
		if (!TARGET_NOLSPACE) {
			output_preloadN(operands[1]);
			if (destModeIsLong) {
				output_asm_insn( "move %1,%0", operands);
			} else {
				output_asm_insn( "move %x1,%0", operands);
			}
			return;
		}

		if (destModeIsLong) {
			output_preloadN(operands[1]);
			if (reg0 < 4)
    			output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);
		}

		opIx = XEXP(op1, 1); /* The index */
		opB  = XEXP(op1, 0); /* The base */
		if (GET_CODE(opIx)==REG && REGNO(opIx)>=10) {
			opB=opIx; opIx=XEXP(op1,0);
		}

		reg0 = REGNO(opB);

		if (!destModeIsLong && GET_CODE(opIx)==CONST_INT) {
			/* Special subreg RTL case */
			INTVAL(opIx)++;
			output_preloadN(operands[1]);
			output_asm_insn( "move %1,%0", operands);
			return;
		}

		if (!destModeIsLong)
			output_preloadN(operands[1]);

		fprintf(asm_out_file, "\tmove (%s)+\n", reg_names[reg0]);
		if (destModeIsLong)
			output_asm_insn( "move %q1,%x0", operands);
		else
			/* Special subreg RTL case */
			output_asm_insn( "move %q1,%0", operands);
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		return;

	 case SYMBOL_REF:
	 case CONST_INT:
	 case CONST:
		if (TARGET_NOLSPACE) {
			if (destModeIsLong) {
				if (reg0 < 4)
					output_asm_insn( "move %p1,%y0", operands);
				else
					output_asm_insn( "move %p1,%0", operands);
				output_asm_insn( "move %q1,%x0", operands);
			} else {
				/* Special subreg RTL case */
				output_asm_insn( "move %q1,%0", operands);
			}
		} else {
			if (destModeIsLong) {
				output_asm_insn( "move %1,%0", operands);
			} else {
				output_asm_insn( "move %x1,%0", operands);
			}
		}
		return;

	 default:
		fprintf(stderr,"%s:%u Unknown code %d\n",__FILE__,__LINE__,code);
		error("Internal error");
	}
	return;
}

void
asm_output_double_int(FILE *filePtr, rtx exp)
{
	int intHigh, intLow;

	if (GET_CODE(exp) == CONST_DOUBLE) {
		intHigh = CONST_DOUBLE_HIGH(exp);
		intLow = CONST_DOUBLE_LOW(exp);
	} else {
		intLow = INTVAL(exp);
		intHigh = 0;
	}

	intHigh = ((intHigh << 8) | ((intLow >> 24)&0xFF)) & 0xFFFFFF;
	intLow &= 0xFFFFFF;

	if (TARGET_NOLSPACE) {
		fprintf(filePtr,"\tdc\t$%06X\n",intHigh);
		fprintf(filePtr,"\tdc\t$%06X\n",intLow);
	} else {
		fprintf(filePtr,"\tdc\t@LNG($%06X,$%06X)\n",intHigh,intLow);
	}
}

#define ASHL_56K   		0
#define ASHR_56K   		1
#define LSHL_56K		2
#define LSHR_56K		3
#define ROTL_56K		4
#define ROTR_56K		5

char *
shiftOpInsn(rtx operands[], int op)
{
	int plus1=0, plus2=0;

	if (GET_CODE(operands[2]) == CONST_INT) {
		int intVal=INTVAL(operands[2]);
		if (!intVal) return "; shift 0";
		plus1 = (intVal == 1);
		plus2 = (intVal == 2);
	}
	
    if (operands[2] == const0_rtx) {
		return "; shift 0";
	}

	extendReg(operands[0]);
	regValid[REGNO(operands[0])] = 0;

	switch(op) {
	 case ASHL_56K:
		if (plus1) return "asl  %0";
		else if (plus2) return "asl  %0\n\tasl  %0";
		else return "rep  %r2\n\tasl  %0";

	 case ASHR_56K:
		if (plus1) return "asr  %0";
		else if (plus2) return "asr  %0\n\tasr  %0";
		else return "rep  %r2\n\tasr  %0";
    
	 case LSHL_56K:
		if (plus1) return "lsl  %0";
		else if (plus2) return "lsl  %0\n\tlsl  %0";
		else return "rep  %r2\n\tlsl  %0";

	 case LSHR_56K:
		if (plus1) return "lsr  %0";
		else if (plus2) return "lsr  %0\n\tlsr  %0";
		else return "rep  %r2\n\tlsr  %0";

	 case ROTL_56K:
		if (plus1) return "rol  %0";
		else if (plus2) return "rol  %0\n\trol  %0";
		else return "rep  %r2\n\trol  %0";

	 case ROTR_56K:
		if (plus1) return "ror  %0";
		else if (plus2) return "ror  %0\n\tror  %0";
		else return "rep  %r2\n\tror  %0";
	}
}

int
allRegsInvalid(void)
{
	regValid[5] = regValid[8] = 0;
}

void
extendReg(rtx op)
{
	int regnum = REGNO(op);
	enum machine_mode mode=GET_MODE(op);
	int modeIsLong=((mode==DImode) || (mode==DFmode));

	if (!TARGET_SAFECOMPARE || ((regnum != 5) && (regnum != 8))) return;

	if (!regValid[regnum]) {
		if (modeIsLong) {
			output_asm_insn("move %w0,L:-(R6)", &op);
			output_asm_insn("move L:(R6)+,%0", &op);
		} else {
			output_asm_insn("move %w0,%0 ;sc", &op);
		}
		regValid[regnum]=1;
	}
}

void
asm_output_labelref(FILE *filePtr, char *name)
{
	while(*name && *name == '_') name++;
	fprintf(filePtr,"%s",name);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.