/* Id: local2.c,v 1.31 2016/01/06 16:11:24 ragge Exp */
/* $NetBSD: local2.c,v 1.1.1.5 2016/02/09 20:28:21 plunky Exp $ */
/*
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* 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.
*/
/*
* MIPS port by Jan Enoksson (janeno-1@student.ltu.se) and
* Simon Olsson (simols-1@student.ltu.se) 2005.
*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "pass1.h"
#include "pass2.h"
#ifdef TARGET_BIG_ENDIAN
int bigendian = 1;
#else
int bigendian = 0;
#endif
int nargregs = MIPS_O32_NARGREGS;
static int argsiz(NODE *p);
void
deflab(int label)
{
printf(LABFMT ":\n", label);
}
static int regoff[32];
static TWORD ftype;
/*
* calculate stack size and offsets
*/
static int
offcalc(struct interpass_prolog * ipp)
{
int i, j, addto;
addto = p2maxautooff;
for (i = ipp->ipp_regs[0], j = 0; i; i >>= 1, j++) {
if (i & 1) {
addto += SZINT / SZCHAR;
regoff[j] = addto;
}
}
/* round to 8-byte boundary */
addto += 7;
addto &= ~7;
return addto;
}
/*
* Print out the prolog assembler.
*/
void
prologue(struct interpass_prolog * ipp)
{
int addto;
int i, j;
ftype = ipp->ipp_type;
printf("\t.align 2\n");
if (ipp->ipp_vis)
printf("\t.globl %s\n", ipp->ipp_name);
printf("\t.ent %s\n", ipp->ipp_name);
printf("%s:\n", ipp->ipp_name);
addto = offcalc(ipp);
/* emit PIC only if -fpic or -fPIC set */
if (kflag > 0) {
printf("\t.frame %s,%d,%s\n",
rnames[FP], ARGINIT/SZCHAR, rnames[RA]);
printf("\t.set noreorder\n");
printf("\t.cpload $25\t# pseudo-op to load GOT ptr into $25\n");
printf("\t.set reorder\n");
}
printf("\t.frame %s,%d,%s\n", rnames[FP], ARGINIT/SZCHAR, rnames[RA]);
printf("\t.set noreorder\n");
printf("\t.cpload $25\t# pseudo-op to load GOT ptr into $25\n");
printf("\t.set reorder\n");
printf("\tsubu %s,%s,%d\n", rnames[SP], rnames[SP], ARGINIT/SZCHAR);
/* emit PIC only if -fpic or -fPIC set */
if (kflag > 0)
printf("\t.cprestore 8\t# pseudo-op to store GOT ptr at 8(sp)\n");
printf("\tsw %s,4(%s)\n", rnames[RA], rnames[SP]);
printf("\tsw %s,(%s)\n", rnames[FP], rnames[SP]);
printf("\tmove %s,%s\n", rnames[FP], rnames[SP]);
#ifdef notyet
/* profiling */
if (pflag) {
printf("\t.set noat\n");
printf("\tmove %s,%s\t# save current return address\n",
rnames[AT], rnames[RA]);
printf("\tsubu %s,%s,8\t# _mcount pops 2 words from stack\n",
rnames[SP], rnames[SP]);
printf("\tjal %s\n", exname("_mcount"));
printf("\tnop\n");
printf("\t.set at\n");
}
#endif
if (addto)
printf("\tsubu %s,%s,%d\n", rnames[SP], rnames[SP], addto);
for (i = ipp->ipp_regs[0], j = 0; i; i >>= 1, j++)
if (i & 1)
printf("\tsw %s,-%d(%s) # save permanent\n",
rnames[j], regoff[j], rnames[FP]);
}
void
eoftn(struct interpass_prolog * ipp)
{
int i, j;
(void) offcalc(ipp);
if (ipp->ipp_ip.ip_lbl == 0)
return; /* no code needs to be generated */
/* return from function code */
for (i = ipp->ipp_regs[0], j = 0; i; i >>= 1, j++) {
if (i & 1)
printf("\tlw %s,-%d(%s)\n\tnop\n",
rnames[j], regoff[j], rnames[FP]);
}
printf("\taddiu %s,%s,%d\n", rnames[SP], rnames[FP], ARGINIT/SZCHAR);
printf("\tlw %s,%d(%s)\n", rnames[RA], 4-ARGINIT/SZCHAR, rnames[SP]);
printf("\tlw %s,%d(%s)\n", rnames[FP], 0-ARGINIT/SZCHAR, rnames[SP]);
printf("\tjr %s\n", rnames[RA]);
printf("\tnop\n");
#ifdef USE_GAS
printf("\t.end %s\n", ipp->ipp_name);
printf("\t.size %s,.-%s\n", ipp->ipp_name, ipp->ipp_name);
#endif
}
/*
* add/sub/...
*
* Param given:
*/
void
hopcode(int f, int o)
{
char *str;
switch (o) {
case EQ:
str = "beqz"; /* pseudo-op */
break;
case NE:
str = "bnez"; /* pseudo-op */
break;
case ULE:
case LE:
str = "blez";
break;
case ULT:
case LT:
str = "bltz";
break;
case UGE:
case GE:
str = "bgez";
break;
case UGT:
case GT:
str = "bgtz";
break;
case PLUS:
str = "add";
break;
case MINUS:
str = "sub";
break;
case AND:
str = "and";
break;
case OR:
str = "or";
break;
case ER:
str = "xor";
break;
default:
comperr("hopcode2: %d", o);
str = 0; /* XXX gcc */
}
printf("%s%c", str, f);
}
char *
rnames[] = {
#ifdef USE_GAS
/* gnu assembler */
"$zero", "$at", "$2", "$3", "$4", "$5", "$6", "$7",
"$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
"$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
"$24", "$25",
"$kt0", "$kt1", "$gp", "$sp", "$fp", "$ra",
"$2!!$3!!",
"$4!!$5!!", "$5!!$6!!", "$6!!$7!!", "$7!!$8!!",
"$8!!$9!!", "$9!!$10!", "$10!$11!", "$11!$12!",
"$12!$13!", "$13!$14!", "$14!$15!", "$15!$24!", "$24!$25!",
"$16!$17!", "$17!$18!", "$18!$19!", "$19!$20!",
"$20!$21!", "$21!$22!", "$22!$23!",
#else
/* mips assembler */
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
"$t8", "$t9",
"$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
"$v0!$v1!",
"$a0!$a1!", "$a1!$a2!", "$a2!$a3!", "$a3!$t0!",
"$t0!$t1!", "$t1!$t2!", "$t2!$t3!", "$t3!$t4!",
"$t4!$t5!", "$t5!$t6!", "$t6!$t7!", "$t7!$t8!", "$t8!$t9!",
"$s0!$s1!", "$s1!$s2!", "$s2!$s3!", "$s3!$s4!",
"$s4!$s5!", "$s5!$s6!", "$s6!$s7!",
#endif
"$f0!$f1!", "$f2!$f3!", "$f4!$f5!", "$f6!$f7!",
"$f8!$f9!", "$f10$f11", "$f12$f13", "$f14$f15",
"$f16$f17", "$f18$f19", "$f20$f21", "$f22$f23",
"$f24$f25", "$f26$f27", "$f28$f29", "$f30$f31",
};
char *
rnames_n32[] = {
/* mips assembler */
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
"$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3",
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
"$t8", "$t9",
"$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
"$v0!$v1!",
"$a0!$a1!", "$a1!$a2!", "$a2!$a3!", "$a3!$a4!",
"$a4!$a5!", "$a5!$a6!", "$a6!$a7!", "$a7!$t0!",
"$t0!$t1!", "$t1!$t2!", "$t2!$t3!", "$t3!$t8!", "$t8!$t9!",
"$s0!$s1!", "$s1!$s2!", "$s2!$s3!", "$s3!$s4!",
"$s4!$s5!", "$s5!$s6!", "$s6!$s7!",
"$f0!$f1!", "$f2!$f3!", "$f4!$f5!", "$f6!$f7!",
"$f8!$f9!", "$f10$f11", "$f12$f13", "$f14$f15",
"$f16$f17", "$f18$f19", "$f20$f21", "$f22$f23",
"$f24$f25", "$f26$f27", "$f28$f29", "$f30$f31",
};
int
tlen(NODE *p)
{
switch (p->n_type) {
case CHAR:
case UCHAR:
return (1);
case SHORT:
case USHORT:
return (SZSHORT / SZCHAR);
case DOUBLE:
return (SZDOUBLE / SZCHAR);
case INT:
case UNSIGNED:
case LONG:
case ULONG:
return (SZINT / SZCHAR);
case LONGLONG:
case ULONGLONG:
return SZLONGLONG / SZCHAR;
default:
if (!ISPTR(p->n_type))
comperr("tlen type %d not pointer");
return SZPOINT(p->n_type) / SZCHAR;
}
}
/*
* Push a structure on stack as argument.
*/
static void
starg(NODE *p)
{
int sz = attr_find(p->n_ap, ATTR_P2STRUCT)->iarg(0);
//assert(p->n_rval == A1);
printf("\tsubu %s,%s,%d\n", rnames[SP], rnames[SP], sz);
/* A0 = dest, A1 = src, A2 = len */
printf("\tmove %s,%s\n", rnames[A0], rnames[SP]);
printf("\tli %s,%d\t# structure size\n", rnames[A2], sz);
printf("\tsubu %s,%s,16\n", rnames[SP], rnames[SP]);
printf("\tjal %s\t# structure copy\n", exname("memcpy"));
printf("\tnop\n");
printf("\taddiu %s,%s,16\n", rnames[SP], rnames[SP]);
}
/*
* Structure assignment.
*/
static void
stasg(NODE *p)
{
assert(p->n_right->n_rval == A1);
/* A0 = dest, A1 = src, A2 = len */
printf("\tli %s,%d\t# structure size\n", rnames[A2],
attr_find(p->n_ap, ATTR_P2STRUCT)->iarg(0));
if (p->n_left->n_op == OREG) {
printf("\taddiu %s,%s," CONFMT "\t# dest address\n",
rnames[A0], rnames[p->n_left->n_rval],
getlval(p->n_left));
} else if (p->n_left->n_op == NAME) {
printf("\tla %s,", rnames[A0]);
adrput(stdout, p->n_left);
printf("\n");
}
printf("\tsubu %s,%s,16\n", rnames[SP], rnames[SP]);
printf("\tjal %s\t# structure copy\n", exname("memcpy"));
printf("\tnop\n");
printf("\taddiu %s,%s,16\n", rnames[SP], rnames[SP]);
}
static void
shiftop(NODE *p)
{
NODE *r = p->n_right;
TWORD ty = p->n_type;
if (p->n_op == LS && r->n_op == ICON && getlval(r) < 32) {
expand(p, INBREG, "\tsrl A1,AL,");
printf(CONFMT "\t# 64-bit left-shift\n", 32 - getlval(r));
expand(p, INBREG, "\tsll U1,UL,AR\n");
expand(p, INBREG, "\tor U1,U1,A1\n");
expand(p, INBREG, "\tsll A1,AL,AR\n");
} else if (p->n_op == LS && r->n_op == ICON && getlval(r) < 64) {
expand(p, INBREG, "\tli A1,0\t# 64-bit left-shift\n");
expand(p, INBREG, "\tsll U1,AL,");
printf(CONFMT "\n", getlval(r) - 32);
} else if (p->n_op == LS && r->n_op == ICON) {
expand(p, INBREG, "\tli A1,0\t# 64-bit left-shift\n");
expand(p, INBREG, "\tli U1,0\n");
} else if (p->n_op == RS && r->n_op == ICON && getlval(r) < 32) {
expand(p, INBREG, "\tsll U1,UL,");
printf(CONFMT "\t# 64-bit right-shift\n", 32 - getlval(r));
expand(p, INBREG, "\tsrl A1,AL,AR\n");
expand(p, INBREG, "\tor A1,A1,U1\n");
if (ty == LONGLONG)
expand(p, INBREG, "\tsra U1,UL,AR\n");
else
expand(p, INBREG, "\tsrl U1,UL,AR\n");
} else if (p->n_op == RS && r->n_op == ICON && getlval(r) < 64) {
if (ty == LONGLONG) {
expand(p, INBREG, "\tsra U1,UL,31\t# 64-bit right-shift\n");
expand(p, INBREG, "\tsra A1,UL,");
}else {
expand(p, INBREG, "\tli U1,0\t# 64-bit right-shift\n");
expand(p, INBREG, "\tsrl A1,UL,");
}
printf(CONFMT "\n", getlval(r) - 32);
} else if (p->n_op == LS && r->n_op == ICON) {
expand(p, INBREG, "\tli A1,0\t# 64-bit right-shift\n");
expand(p, INBREG, "\tli U1,0\n");
} else {
comperr("shiftop");
}
}
/*
* http://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html#Soft-float-library-routines
*/
static void
fpemulop(NODE *p)
{
NODE *l = p->n_left;
char *ch = NULL;
if (p->n_op == PLUS && p->n_type == FLOAT) ch = "addsf3";
else if (p->n_op == PLUS && p->n_type == DOUBLE) ch = "adddf3";
else if (p->n_op == PLUS && p->n_type == LDOUBLE) ch = "addtf3";
else if (p->n_op == MINUS && p->n_type == FLOAT) ch = "subsf3";
else if (p->n_op == MINUS && p->n_type == DOUBLE) ch = "subdf3";
else if (p->n_op == MINUS && p->n_type == LDOUBLE) ch = "subtf3";
else if (p->n_op == MUL && p->n_type == FLOAT) ch = "mulsf3";
else if (p->n_op == MUL && p->n_type == DOUBLE) ch = "muldf3";
else if (p->n_op == MUL && p->n_type == LDOUBLE) ch = "multf3";
else if (p->n_op == DIV && p->n_type == FLOAT) ch = "divsf3";
else if (p->n_op == DIV && p->n_type == DOUBLE) ch = "divdf3";
else if (p->n_op == DIV && p->n_type == LDOUBLE) ch = "divtf3";
else if (p->n_op == UMINUS && p->n_type == FLOAT) ch = "negsf2";
else if (p->n_op == UMINUS && p->n_type == DOUBLE) ch = "negdf2";
else if (p->n_op == UMINUS && p->n_type == LDOUBLE) ch = "negtf2";
else if (p->n_op == EQ && l->n_type == FLOAT) ch = "eqsf2";
else if (p->n_op == EQ && l->n_type == DOUBLE) ch = "eqdf2";
else if (p->n_op == EQ && l->n_type == LDOUBLE) ch = "eqtf2";
else if (p->n_op == NE && l->n_type == FLOAT) ch = "nesf2";
else if (p->n_op == NE && l->n_type == DOUBLE) ch = "nedf2";
else if (p->n_op == NE && l->n_type == LDOUBLE) ch = "netf2";
else if (p->n_op == GE && l->n_type == FLOAT) ch = "gesf2";
else if (p->n_op == GE && l->n_type == DOUBLE) ch = "gedf2";
else if (p->n_op == GE && l->n_type == LDOUBLE) ch = "getf2";
else if (p->n_op == LE && l->n_type == FLOAT) ch = "lesf2";
else if (p->n_op == LE && l->n_type == DOUBLE) ch = "ledf2";
else if (p->n_op == LE && l->n_type == LDOUBLE) ch = "letf2";
else if (p->n_op == GT && l->n_type == FLOAT) ch = "gtsf2";
else if (p->n_op == GT && l->n_type == DOUBLE) ch = "gtdf2";
else if (p->n_op == GT && l->n_type == LDOUBLE) ch = "gttf2";
else if (p->n_op == LT && l->n_type == FLOAT) ch = "ltsf2";
else if (p->n_op == LT && l->n_type == DOUBLE) ch = "ltdf2";
else if (p->n_op == LT && l->n_type == LDOUBLE) ch = "lttf2";
else if (p->n_op == SCONV && p->n_type == FLOAT) {
if (l->n_type == DOUBLE) ch = "truncdfsf2";
else if (l->n_type == LDOUBLE) ch = "trunctfsf2";
else if (l->n_type == ULONGLONG) ch = "floatdisf"; /**/
else if (l->n_type == LONGLONG) ch = "floatdisf";
else if (l->n_type == LONG) ch = "floatsisf";
else if (l->n_type == ULONG) ch = "floatunsisf";
else if (l->n_type == INT) ch = "floatsisf";
else if (l->n_type == UNSIGNED) ch = "floatunsisf";
} else if (p->n_op == SCONV && p->n_type == DOUBLE) {
if (l->n_type == FLOAT) ch = "extendsfdf2";
else if (l->n_type == LDOUBLE) ch = "trunctfdf2";
else if (l->n_type == ULONGLONG) ch = "floatunsdidf";
else if (l->n_type == LONGLONG) ch = "floatdidf";
else if (l->n_type == LONG) ch = "floatsidf";
else if (l->n_type == ULONG) ch = "floatunsidf";
else if (l->n_type == INT) ch = "floatsidf";
else if (l->n_type == UNSIGNED) ch = "floatunsidf";
} else if (p->n_op == SCONV && p->n_type == LDOUBLE) {
if (l->n_type == FLOAT) ch = "extendsftf2";
else if (l->n_type == DOUBLE) ch = "extenddfdf2";
else if (l->n_type == ULONGLONG) ch = "floatunsdidf";
else if (l->n_type == LONGLONG) ch = "floatdidf";
else if (l->n_type == LONG) ch = "floatsidf";
else if (l->n_type == ULONG) ch = "floatunssidf";
else if (l->n_type == INT) ch = "floatsidf";
else if (l->n_type == UNSIGNED) ch = "floatunsidf";
} else if (p->n_op == SCONV && p->n_type == ULONGLONG) {
if (l->n_type == FLOAT) ch = "fixunssfdi";
else if (l->n_type == DOUBLE) ch = "fixunsdfdi";
else if (l->n_type == LDOUBLE) ch = "fixunsdfdi";
} else if (p->n_op == SCONV && p->n_type == LONGLONG) {
if (l->n_type == FLOAT) ch = "fixsfdi";
else if (l->n_type == DOUBLE) ch = "fixdfdi";
else if (l->n_type == LDOUBLE) ch = "fixdfdi";
} else if (p->n_op == SCONV && p->n_type == LONG) {
if (l->n_type == FLOAT) ch = "fixsfsi";
else if (l->n_type == DOUBLE) ch = "fixdfsi";
else if (l->n_type == LDOUBLE) ch = "fixdfsi";
} else if (p->n_op == SCONV && p->n_type == ULONG) {
if (l->n_type == FLOAT) ch = "fixunssfsi";
else if (l->n_type == DOUBLE) ch = "fixunsdfsi";
else if (l->n_type == LDOUBLE) ch = "fixunsdfsi";
} else if (p->n_op == SCONV && p->n_type == INT) {
if (l->n_type == FLOAT) ch = "fixsfsi";
else if (l->n_type == DOUBLE) ch = "fixdfsi";
else if (l->n_type == LDOUBLE) ch = "fixdfsi";
} else if (p->n_op == SCONV && p->n_type == UNSIGNED) {
if (l->n_type == FLOAT) ch = "fixunssfsi";
else if (l->n_type == DOUBLE) ch = "fixunsdfsi";
else if (l->n_type == LDOUBLE) ch = "fixunsdfsi";
}
if (ch == NULL) comperr("ZF: op=0x%x (%d)\n", p->n_op, p->n_op);
if (p->n_op == SCONV) {
if (l->n_type == FLOAT) {
printf("\tmfc1 %s,", rnames[A0]);
adrput(stdout, l);
printf("\n\tnop\n");
} else if (l->n_type == DOUBLE || l->n_type == LDOUBLE) {
printf("\tmfc1 %s,", rnames[A1]);
upput(l, 0);
printf("\n\tnop\n");
printf("\tmfc1 %s,", rnames[A0]);
adrput(stdout, l);
printf("\n\tnop\n");
}
} else {
comperr("ZF: incomplete softfloat - put args in registers");
}
printf("\tjal __%s\t# softfloat operation\n", exname(ch));
printf("\tnop\n");
if (p->n_op >= EQ && p->n_op <= GT)
printf("\tcmp %s,0\n", rnames[V0]);
}
/*
* http://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html#Integer-library-routines
*/
static void
emulop(NODE *p)
{
char *ch = NULL;
if (p->n_op == LS && DEUNSIGN(p->n_type) == LONGLONG) ch = "ashldi3";
else if (p->n_op == LS && (DEUNSIGN(p->n_type) == LONG ||
DEUNSIGN(p->n_type) == INT))
ch = "ashlsi3";
else if (p->n_op == RS && p->n_type == ULONGLONG) ch = "lshrdi3";
else if (p->n_op == RS && (p->n_type == ULONG || p->n_type == INT))
ch = "lshrsi3";
else if (p->n_op == RS && p->n_type == LONGLONG) ch = "ashrdi3";
else if (p->n_op == RS && (p->n_type == LONG || p->n_type == INT))
ch = "ashrsi3";
else if (p->n_op == DIV && p->n_type == LONGLONG) ch = "divdi3";
else if (p->n_op == DIV && (p->n_type == LONG || p->n_type == INT))
ch = "divsi3";
else if (p->n_op == DIV && p->n_type == ULONGLONG) ch = "udivdi3";
else if (p->n_op == DIV && (p->n_type == ULONG ||
p->n_type == UNSIGNED))
ch = "udivsi3";
else if (p->n_op == MOD && p->n_type == LONGLONG) ch = "moddi3";
else if (p->n_op == MOD && (p->n_type == LONG || p->n_type == INT))
ch = "modsi3";
else if (p->n_op == MOD && p->n_type == ULONGLONG) ch = "umoddi3";
else if (p->n_op == MOD && (p->n_type == ULONG ||
p->n_type == UNSIGNED))
ch = "umodsi3";
else if (p->n_op == MUL && p->n_type == LONGLONG) ch = "muldi3";
else if (p->n_op == MUL && (p->n_type == LONG || p->n_type == INT))
ch = "mulsi3";
else if (p->n_op == UMINUS && p->n_type == LONGLONG) ch = "negdi2";
else if (p->n_op == UMINUS && p->n_type == LONG) ch = "negsi2";
else ch = 0, comperr("ZE");
printf("\tsubu %s,%s,16\n", rnames[SP], rnames[SP]);
printf("\tjal __%s\t# emulated operation\n", exname(ch));
printf("\tnop\n");
printf("\taddiu %s,%s,16\n", rnames[SP], rnames[SP]);
}
/*
* Emit code to compare two longlong numbers.
*/
static void
twollcomp(NODE *p)
{
int o = p->n_op;
int s = getlab2();
int e = p->n_label;
int cb1, cb2;
if (o >= ULE)
o -= (ULE-LE);
switch (o) {
case NE:
cb1 = 0;
cb2 = NE;
break;
case EQ:
cb1 = NE;
cb2 = 0;
break;
case LE:
case LT:
cb1 = GT;
cb2 = LT;
break;
case GE:
case GT:
cb1 = LT;
cb2 = GT;
break;
default:
cb1 = cb2 = 0; /* XXX gcc */
}
if (p->n_op >= ULE)
cb1 += 4, cb2 += 4;
expand(p, 0, "\tsub A1,UL,UR\t# compare 64-bit values (upper)\n");
if (cb1) {
printf("\t");
hopcode(' ', cb1);
expand(p, 0, "A1");
printf("," LABFMT "\n", s);
printf("\tnop\n");
}
if (cb2) {
printf("\t");
hopcode(' ', cb2);
expand(p, 0, "A1");
printf("," LABFMT "\n", e);
printf("\tnop\n");
}
expand(p, 0, "\tsub A1,AL,AR\t# (and lower)\n");
printf("\t");
hopcode(' ', o);
expand(p, 0, "A1");
printf("," LABFMT "\n", e);
printf("\tnop\n");
deflab(s);
}
static void
fpcmpops(NODE *p)
{
NODE *l = p->n_left;
switch (p->n_op) {
case EQ:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.eq.s AL,AR\n");
else
expand(p, 0, "\tc.eq.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1t LC\n");
break;
case NE:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.eq.s AL,AR\n");
else
expand(p, 0, "\tc.eq.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1f LC\n");
break;
case LT:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.lt.s AL,AR\n");
else
expand(p, 0, "\tc.lt.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1t LC\n");
break;
case GE:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.lt.s AL,AR\n");
else
expand(p, 0, "\tc.lt.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1f LC\n");
break;
case LE:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.le.s AL,AR\n");
else
expand(p, 0, "\tc.le.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1t LC\n");
break;
case GT:
if (l->n_type == FLOAT)
expand(p, 0, "\tc.le.s AL,AR\n");
else
expand(p, 0, "\tc.le.d AL,AR\n");
expand(p, 0, "\tnop\n\tbc1f LC\n");
break;
}
printf("\tnop\n\tnop\n");
}
void
zzzcode(NODE * p, int c)
{
int sz;
switch (c) {
case 'C': /* remove arguments from stack after subroutine call */
sz = p->n_qual > 16 ? p->n_qual : 16;
printf("\taddiu %s,%s,%d\n", rnames[SP], rnames[SP], sz);
break;
case 'D': /* long long comparison */
twollcomp(p);
break;
case 'E': /* emit emulated ops */
emulop(p);
break;
case 'F': /* emit emulate floating point ops */
fpemulop(p);
break;
case 'G': /* emit hardware floating-point compare op */
fpcmpops(p);
break;
case 'H': /* structure argument */
starg(p);
break;
case 'I': /* high part of init constant */
if (p->n_name[0] != '\0')
comperr("named highword");
printf(CONFMT, (getlval(p) >> 32) & 0xffffffff);
break;
case 'O': /* 64-bit left and right shift operators */
shiftop(p);
break;
case 'Q': /* emit struct assign */
stasg(p);
break;
default:
comperr("zzzcode %c", c);
}
}
/* ARGSUSED */
int
rewfld(NODE * p)
{
return (1);
}
int
fldexpand(NODE *p, int cookie, char **cp)
{
CONSZ val;
if (p->n_op == ASSIGN)
p = p->n_left;
switch (**cp) {
case 'S':
printf("%d", UPKFSZ(p->n_rval));
break;
case 'H':
printf("%d", UPKFOFF(p->n_rval));
break;
case 'M':
case 'N':
val = (CONSZ)1 << UPKFSZ(p->n_rval);
--val;
val <<= UPKFOFF(p->n_rval);
printf("0x%llx", (**cp == 'M' ? val : ~val) & 0xffffffff);
break;
default:
comperr("fldexpand");
}
return 1;
}
/*
* Does the bitfield shape match?
*/
int
flshape(NODE * p)
{
int o = p->n_op;
if (o == OREG || o == REG || o == NAME)
return SRDIR; /* Direct match */
if (o == UMUL && shumul(p->n_left, SOREG))
return SROREG; /* Convert into oreg */
return SRREG; /* put it into a register */
}
/* INTEMP shapes must not contain any temporary registers */
/* XXX should this go away now? */
int
shtemp(NODE * p)
{
return 0;
#if 0
int r;
if (p->n_op == STARG)
p = p->n_left;
switch (p->n_op) {
case REG:
return (!istreg(p->n_rval));
case OREG:
r = p->n_rval;
if (R2TEST(r)) {
if (istreg(R2UPK1(r)))
return (0);
r = R2UPK2(r);
}
return (!istreg(r));
case UMUL:
p = p->n_left;
return (p->n_op != UMUL && shtemp(p));
}
if (optype(p->n_op) != LTYPE)
return (0);
return (1);
#endif
}
void
adrcon(CONSZ val)
{
printf(CONFMT, val);
}
void
conput(FILE *fp, NODE *p)
{
int val = getlval(p);
switch (p->n_op) {
case ICON:
if (p->n_name[0] != '\0') {
fprintf(fp, "%s", p->n_name);
if (getlval(p))
fprintf(fp, "+%d", val);
} else
fprintf(fp, "%d", val);
return;
default:
comperr("illegal conput");
}
}
/* ARGSUSED */
void
insput(NODE * p)
{
comperr("insput");
}
/*
* Print lower or upper name of 64-bit register.
*/
static void
print_reg64name(FILE *fp, int rval, int hi)
{
int off = 4 * (hi != 0);
char *regname = rnames[rval];
fprintf(fp, "%c%c",
regname[off],
regname[off + 1]);
if (regname[off + 2] != '!')
fputc(regname[off + 2], fp);
if (regname[off + 3] != '!')
fputc(regname[off + 3], fp);
}
/*
* Write out the upper address, like the upper register of a 2-register
* reference, or the next memory location.
*/
void
upput(NODE * p, int size)
{
size /= SZCHAR;
switch (p->n_op) {
case REG:
if (GCLASS(p->n_rval) == CLASSB || GCLASS(p->n_rval) == CLASSC)
print_reg64name(stdout, p->n_rval, 1);
else
printf("%s", rnames[p->n_rval]);
break;
case NAME:
case OREG:
setlval(p, getlval(p) + size);
adrput(stdout, p);
setlval(p, getlval(p) - size);
break;
case ICON:
printf(CONFMT, getlval(p) >> 32);
break;
default:
comperr("upput bad op %d size %d", p->n_op, size);
}
}
void
adrput(FILE * io, NODE * p)
{
/* output an address, with offsets, from p */
if (p->n_op == FLD)
p = p->n_left;
switch (p->n_op) {
case NAME:
if (p->n_name[0] != '\0')
fputs(p->n_name, io);
if (getlval(p) != 0)
fprintf(io, "+" CONFMT, getlval(p));
return;
case OREG:
if (getlval(p))
fprintf(io, "%d", (int) getlval(p));
fprintf(io, "(%s)", rnames[p->n_rval]);
return;
case ICON:
/* addressable value of the constant */
conput(io, p);
return;
case REG:
if (GCLASS(p->n_rval) == CLASSB || GCLASS(p->n_rval) == CLASSC)
print_reg64name(io, p->n_rval, 0);
else
fputs(rnames[p->n_rval], io);
return;
default:
comperr("illegal address, op %d, node %p", p->n_op, p);
return;
}
}
/* printf conditional and unconditional branches */
void
cbgen(int o, int lab)
{
}
void
myreader(struct interpass * ipole)
{
}
#if 0
/*
* Calculate the stack size for arguments
*/
static int stacksize;
static void
calcstacksize(NODE *p, void *arg)
{
int sz;
printf("op=%d\n", p->n_op);
if (p->n_op != CALL && p->n_op != STCALL)
return;
sz = argsiz(p->n_right);
if (sz > stacksize)
stacksize = sz;
#ifdef PCC_DEBUG
if (x2debug)
printf("stacksize: %d\n", stacksize);
#endif
}
#endif
/*
* If we're big endian, then all OREG loads of a type
* larger than the destination, must have the
* offset changed to point to the correct bytes in memory.
*/
static void
offchg(NODE *p, void *arg)
{
NODE *l;
if (p->n_op != SCONV)
return;
l = p->n_left;
if (l->n_op != OREG)
return;
switch (l->n_type) {
case SHORT:
case USHORT:
if (DEUNSIGN(p->n_type) == CHAR)
setlval(l, getlval(l) + 1);
break;
case LONG:
case ULONG:
case INT:
case UNSIGNED:
if (DEUNSIGN(p->n_type) == CHAR)
setlval(l, getlval(l + 3));
else if (DEUNSIGN(p->n_type) == SHORT)
setlval(l, getlval(l + 2));
break;
case LONGLONG:
case ULONGLONG:
if (DEUNSIGN(p->n_type) == CHAR)
setlval(l, getlval(l + 7));
else if (DEUNSIGN(p->n_type) == SHORT)
setlval(l, getlval(l + 6));
else if (DEUNSIGN(p->n_type) == INT ||
DEUNSIGN(p->n_type) == LONG)
setlval(l, getlval(l + 4));
break;
default:
comperr("offchg: unknown type");
break;
}
}
/*
* Remove some PCONVs after OREGs are created.
*/
static void
pconv2(NODE * p, void *arg)
{
NODE *q;
if (p->n_op == PLUS) {
if (p->n_type == (PTR | SHORT) || p->n_type == (PTR | USHORT)) {
if (p->n_right->n_op != ICON)
return;
if (p->n_left->n_op != PCONV)
return;
if (p->n_left->n_left->n_op != OREG)
return;
q = p->n_left->n_left;
nfree(p->n_left);
p->n_left = q;
/*
* This will be converted to another OREG later.
*/
}
}
}
void
mycanon(NODE * p)
{
walkf(p, pconv2, 0);
}
void
myoptim(struct interpass * ipole)
{
struct interpass *ip;
#ifdef PCC_DEBUG
if (x2debug)
printf("myoptim:\n");
#endif
#if 0
stacksize = 0;
#endif
DLIST_FOREACH(ip, ipole, qelem) {
if (ip->type != IP_NODE)
continue;
if (bigendian)
walkf(ip->ip_node, offchg, 0);
#if 0
walkf(ip->ip_node, calcstacksize, 0);
#endif
}
}
/*
* Move data between registers. While basic registers aren't a problem,
* we have to handle the special case of overlapping composite registers.
*/
void
rmove(int s, int d, TWORD t)
{
switch (t) {
case LONGLONG:
case ULONGLONG:
if (s == d+1) {
/* dh = sl, copy low word first */
printf("\tmove ");
print_reg64name(stdout, d, 0);
printf(",");
print_reg64name(stdout, s, 0);
printf("\t# 64-bit rmove\n");
printf("\tmove ");
print_reg64name(stdout, d, 1);
printf(",");
print_reg64name(stdout, s, 1);
printf("\n");
} else {
/* copy high word first */
printf("\tmove ");
print_reg64name(stdout, d, 1);
printf(",");
print_reg64name(stdout, s, 1);
printf(" # 64-bit rmove\n");
printf("\tmove ");
print_reg64name(stdout, d, 0);
printf(",");
print_reg64name(stdout, s, 0);
printf("\n");
}
break;
case FLOAT:
case DOUBLE:
case LDOUBLE:
if (t == FLOAT)
printf("\tmov.s ");
else
printf("\tmov.d ");
print_reg64name(stdout, d, 0);
printf(",");
print_reg64name(stdout, s, 0);
printf("\t# float/double rmove\n");
break;
default:
printf("\tmove %s,%s\t# default rmove\n", rnames[d], rnames[s]);
}
}
/*
* For class c, find worst-case displacement of the number of
* registers in the array r[] indexed by class.
*
* On MIPS, we have:
*
* 32 32-bit registers (8 reserved)
* 26 64-bit pseudo registers (1 unavailable)
* 16 floating-point register pairs
*/
int
COLORMAP(int c, int *r)
{
int num = 0;
switch (c) {
case CLASSA:
num += r[CLASSA];
num += 2*r[CLASSB];
return num < 24;
case CLASSB:
num += 2*r[CLASSB];
num += r[CLASSA];
return num < 25;
case CLASSC:
num += r[CLASSC];
return num < 6;
}
comperr("COLORMAP");
return 0; /* XXX gcc */
}
/*
* Return a class suitable for a specific type.
*/
int
gclass(TWORD t)
{
if (t == LONGLONG || t == ULONGLONG)
return CLASSB;
if (t >= FLOAT && t <= LDOUBLE)
return CLASSC;
return CLASSA;
}
/*
* Calculate argument sizes.
*/
void
lastcall(NODE *p)
{
int sz;
#ifdef PCC_DEBUG
if (x2debug)
printf("lastcall:\n");
#endif
p->n_qual = 0;
if (p->n_op != CALL && p->n_op != FORTCALL && p->n_op != STCALL)
return;
sz = argsiz(p->n_right);
if ((sz > 4*nargregs) && (sz & 7) != 0) {
printf("\tsubu %s,%s,4\t# align stack\n",
rnames[SP], rnames[SP]);
sz += 4;
assert((sz & 7) == 0);
}
p->n_qual = sz; /* XXX */
}
static int
argsiz(NODE *p)
{
TWORD t;
int size = 0;
int sz = 0;
if (p->n_op == CM) {
size = argsiz(p->n_left);
p = p->n_right;
}
t = p->n_type;
if (t < LONGLONG || t > BTMASK)
sz = 4;
else if (DEUNSIGN(t) == LONGLONG)
sz = 8;
else if (t == DOUBLE || t == LDOUBLE)
sz = 8;
else if (t == FLOAT)
sz = 4;
else if (t == STRTY || t == UNIONTY)
sz = attr_find(p->n_ap, ATTR_P2STRUCT)->iarg(0);
if (p->n_type == STRTY || p->n_type == UNIONTY) {
return (size + sz);
}
/* alignment */
if (sz == 8 && (size & 7) != 0)
sz += 4;
// printf("size=%d, sz=%d -> %d\n", size, sz, size + sz);
return (size + sz);
}
/*
* Special shapes.
*/
int
special(NODE *p, int shape)
{
int o = p->n_op;
if (o != ICON || p->n_name[0] != 0)
return SRNOPE;
switch(shape) {
case SPCON:
if ((getlval(p) & ~0xffff) == 0)
return SRDIR;
break;
}
return SRNOPE;
}
/*
* Target-dependent command-line options.
*/
void
mflags(char *str)
{
if (strcasecmp(str, "big-endian") == 0) {
bigendian = 1;
} else if (strcasecmp(str, "little-endian") == 0) {
bigendian = 0;
} else {
fprintf(stderr, "unknown m option '%s'\n", str);
exit(1);
}
#if 0
else if (strcasecmp(str, "ips2")) {
} else if (strcasecmp(str, "ips2")) {
} else if (strcasecmp(str, "ips3")) {
} else if (strcasecmp(str, "ips4")) {
} else if (strcasecmp(str, "hard-float")) {
} else if (strcasecmp(str, "soft-float")) {
} else if (strcasecmp(str, "abi=32")) {
nargregs = MIPS_O32_NARGREGS;
} else if (strcasecmp(str, "abi=n32")) {
nargregs = MIPS_N32_NARGREGS;
} else if (strcasecmp(str, "abi=64")) {
nargregs = MIPS_N32_NARGREGS;
}
#endif
}
/*
* Do something target-dependent for xasm arguments.
* Supposed to find target-specific constraints and rewrite them.
*/
int
myxasm(struct interpass *ip, NODE *p)
{
return 0;
}