OpenBSD-4.6/usr.bin/pcc/arm/local2.c
/* $OpenBSD: local2.c,v 1.4 2008/04/11 20:45:52 stefan Exp $ */
/*
* Copyright (c) 2007 Gregory McGarry (g.mcgarry@ieee.org).
* 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.
*/
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "pass2.h"
extern void defalign(int);
#define exname(x) x
char *rnames[] = {
"r0", "r1", "r2", "r3","r4","r5", "r6", "r7",
"r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc",
"r0r1", "r1r2", "r2r3", "r3r4", "r4r5", "r5r6",
"r6r7", "r7r8", "r8r9", "r9r10",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
};
/*
* Handling of integer constants. We have 8 bits + an even
* number of rotates available as a simple immediate.
* If a constant isn't trivially representable, use an ldr
* and a subsequent sequence of orr operations.
*/
static int
trepresent(const unsigned int val)
{
int i;
#define rotate_left(v, n) (v << n | v >> (32 - n))
for (i = 0; i < 32; i += 2)
if (rotate_left(val, i) <= 0xff)
return 1;
return 0;
}
/*
* Return values are:
* 0 - output constant as is (should be covered by trepresent() above)
* 1 - 4 generate 1-4 instructions as needed.
*/
static int
encode_constant(int constant, int *values)
{
int tmp = constant;
int i = 0;
int first_bit, value;
while (tmp) {
first_bit = ffs(tmp);
first_bit -= 1; /* ffs indexes from 1, not 0 */
first_bit &= ~1; /* must use even bit offsets */
value = tmp & (0xff << first_bit);
values[i++] = value;
tmp &= ~value;
}
return i;
}
#if 0
static void
load_constant(NODE *p)
{
int v = p->n_lval & 0xffffffff;
int reg = DECRA(p->n_reg, 1);
load_constant_into_reg(reg, v);
}
#endif
static void
load_constant_into_reg(int reg, int v)
{
if (trepresent(v))
printf("\tmov %s,#%d\n", rnames[reg], v);
else if (trepresent(-v))
printf("\tmvn %s,#%d\n", rnames[reg], -v);
else {
int vals[4], nc, i;
nc = encode_constant(v, vals);
for (i = 0; i < nc; i++) {
if (i == 0) {
printf("\tmov %s,#%d" COM "load constant %d\n",
rnames[reg], vals[i], v);
} else {
printf("\torr %s,%s,#%d\n",
rnames[reg], rnames[reg], vals[i]);
}
}
}
}
static TWORD ftype;
/*
* calculate stack size and offsets
*/
static int
offcalc(struct interpass_prolog *ipp)
{
int addto;
#ifdef PCC_DEBUG
if (x2debug)
printf("offcalc: p2maxautooff=%d\n", p2maxautooff);
#endif
addto = p2maxautooff;
#if 0
addto += 7;
addto &= ~7;
#endif
#ifdef PCC_DEBUG
if (x2debug)
printf("offcalc: addto=%d\n", addto);
#endif
addto -= AUTOINIT / SZCHAR;
return addto;
}
void
prologue(struct interpass_prolog *ipp)
{
int addto;
int vals[4], nc, i;
#ifdef PCC_DEBUG
if (x2debug)
printf("prologue: type=%d, lineno=%d, name=%s, vis=%d, ipptype=%d, regs=0x%x, autos=%d, tmpnum=%d, lblnum=%d\n",
ipp->ipp_ip.type,
ipp->ipp_ip.lineno,
ipp->ipp_name,
ipp->ipp_vis,
ipp->ipp_type,
ipp->ipp_regs,
ipp->ipp_autos,
ipp->ip_tmpnum,
ipp->ip_lblnum);
#endif
ftype = ipp->ipp_type;
#if 0
printf("\t.align 2\n");
if (ipp->ipp_vis)
printf("\t.global %s\n", exname(ipp->ipp_name));
printf("\t.type %s,%%function\n", exname(ipp->ipp_name));
#endif
printf("%s:\n", exname(ipp->ipp_name));
/*
* We here know what register to save and how much to
* add to the stack.
*/
addto = offcalc(ipp);
printf("\tsub %s,%s,#%d\n", rnames[SP], rnames[SP], 16);
printf("\tmov %s,%s\n", rnames[IP], rnames[SP]);
printf("\tstmfd %s!,{%s,%s,%s,%s}\n", rnames[SP], rnames[FP],
rnames[IP], rnames[LR], rnames[PC]);
printf("\tsub %s,%s,#4\n", rnames[FP], rnames[IP]);
if (addto == 0)
return;
if (trepresent(addto)) {
printf("\tsub %s,%s,#%d\n", rnames[SP], rnames[SP], addto);
} else {
nc = encode_constant(addto, vals);
for (i = 0; i < nc; i++)
printf("\tsub %s,%s,#%d\n",
rnames[SP], rnames[SP], vals[i]);
}
}
void
eoftn(struct interpass_prolog *ipp)
{
if (ipp->ipp_ip.ip_lbl == 0)
return; /* no code needs to be generated */
/* struct return needs special treatment */
if (ftype == STRTY || ftype == UNIONTY) {
assert(0);
} else {
printf("\tldmea %s,{%s,%s,%s}\n", rnames[FP], rnames[FP],
rnames[SP], rnames[PC]);
printf("\tadd %s,%s,#%d\n", rnames[SP], rnames[SP], 16);
}
printf("\t.size %s,.-%s\n", exname(ipp->ipp_name),
exname(ipp->ipp_name));
}
/*
* these mnemonics match the order of the preprocessor decls
* EQ, NE, LE, LT, GE, GT, ULE, ULT, UGE, UGT
*/
static char *
ccbranches[] = {
"beq", /* branch if equal */
"bne", /* branch if not-equal */
"ble", /* branch if less-than-or-equal */
"blt", /* branch if less-than */
"bge", /* branch if greater-than-or-equal */
"bgt", /* branch if greater-than */
/* what should these be ? */
"bls", /* branch if lower-than-or-same */
"blo", /* branch if lower-than */
"bhs", /* branch if higher-than-or-same */
"bhi", /* branch if higher-than */
};
/*
* add/sub/...
*
* Param given:
*/
void
hopcode(int f, int o)
{
char *str;
switch (o) {
case PLUS:
str = "add";
break;
case MINUS:
str = "sub";
break;
case AND:
str = "and";
break;
case OR:
str = "orr";
break;
case ER:
str = "eor";
break;
default:
comperr("hopcode2: %d", o);
str = 0; /* XXX gcc */
}
printf("%s%c", str, f);
}
/*
* Return type size in bytes. Used by R2REGS, arg 2 to offset().
*/
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;
}
}
/*
* Emit code to compare two longlong numbers.
*/
static void
twollcomp(NODE *p)
{
int o = p->n_op;
int s = getlab();
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, "\tcmp UR,UL" COM "compare 64-bit values (upper)\n");
if (cb1) cbgen(cb1, s);
if (cb2) cbgen(cb2, e);
expand(p, 0, "\tcmp AR,AL" COM "(and lower)\n");
cbgen(p->n_op, e);
deflab(s);
}
int
fldexpand(NODE *p, int cookie, char **cp)
{
CONSZ val;
int shft;
if (p->n_op == ASSIGN)
p = p->n_left;
if (features(FEATURE_BIGENDIAN))
shft = SZINT - UPKFSZ(p->n_rval) - UPKFOFF(p->n_rval);
else
shft = UPKFOFF(p->n_rval);
switch (**cp) {
case 'S':
printf("#%d", UPKFSZ(p->n_rval));
break;
case 'H':
printf("#%d", shft);
break;
case 'M':
case 'N':
val = (CONSZ)1 << UPKFSZ(p->n_rval);
--val;
val <<= shft;
printf("%lld", (**cp == 'M' ? val : ~val) & 0xffffffff);
break;
default:
comperr("fldexpand");
}
return 1;
}
#if 0
/*
* Assign to a bitfield.
* Clumsy at least, but what to do?
*/
static void
bfasg(NODE *p)
{
NODE *fn = p->n_left;
int shift = UPKFOFF(fn->n_rval);
int fsz = UPKFSZ(fn->n_rval);
int andval, tch = 0;
/* get instruction size */
switch (p->n_type) {
case CHAR: case UCHAR: tch = 'b'; break;
case SHORT: case USHORT: tch = 'w'; break;
case INT: case UNSIGNED: tch = 'l'; break;
default: comperr("bfasg");
}
/* put src into a temporary reg */
fprintf(stdout, " mov%c ", tch);
adrput(stdout, getlr(p, 'R'));
fprintf(stdout, ",");
adrput(stdout, getlr(p, '1'));
fprintf(stdout, "\n");
/* AND away the bits from dest */
andval = ~(((1 << fsz) - 1) << shift);
fprintf(stdout, " and%c $%d,", tch, andval);
adrput(stdout, fn->n_left);
fprintf(stdout, "\n");
/* AND away unwanted bits from src */
andval = ((1 << fsz) - 1);
fprintf(stdout, " and%c $%d,", tch, andval);
adrput(stdout, getlr(p, '1'));
fprintf(stdout, "\n");
/* SHIFT left src number of bits */
if (shift) {
fprintf(stdout, " sal%c $%d,", tch, shift);
adrput(stdout, getlr(p, '1'));
fprintf(stdout, "\n");
}
/* OR in src to dest */
fprintf(stdout, " or%c ", tch);
adrput(stdout, getlr(p, '1'));
fprintf(stdout, ",");
adrput(stdout, fn->n_left);
fprintf(stdout, "\n");
}
#endif
/*
* Push a structure on stack as argument.
* the scratch registers are already free here
*/
static void
stasg(NODE *p)
{
NODE *l = p->n_left;
int val = l->n_lval;
load_constant_into_reg(R2, p->n_stsize);
if (l->n_rval != R0 || l->n_lval != 0) {
if (trepresent(val)) {
printf("\tadd %s,%s,#%d\n",
rnames[R0], rnames[regno(l)], val);
} else {
load_constant_into_reg(R0, val);
printf("\tadd %s,%s,%s\n", rnames[R0],
rnames[R0], rnames[regno(l)]);
}
}
printf("\tbl %s\n", exname("memcpy"));
}
static void
shiftop(NODE *p)
{
NODE *r = p->n_right;
TWORD ty = p->n_type;
char *shifttype;
if (p->n_op == LS && r->n_op == ICON && r->n_lval < 32) {
expand(p, INBREG, "\tmov A1,AL,lsr ");
printf(CONFMT COM "64-bit left-shift\n", 32 - r->n_lval);
expand(p, INBREG, "\tmov U1,UL,asl AR\n");
expand(p, INBREG, "\torr U1,U1,A1\n");
expand(p, INBREG, "\tmov A1,AL,asl AR\n");
} else if (p->n_op == LS && r->n_op == ICON && r->n_lval < 64) {
expand(p, INBREG, "\tmov A1,#0" COM "64-bit left-shift\n");
expand(p, INBREG, "\tmov U1,AL");
if (r->n_lval - 32 != 0)
printf(",asl " CONFMT, r->n_lval - 32);
printf("\n");
} else if (p->n_op == LS && r->n_op == ICON) {
expand(p, INBREG, "\tmov A1,#0" COM "64-bit left-shift\n");
expand(p, INBREG, "\tmov U1,#0\n");
} else if (p->n_op == RS && r->n_op == ICON && r->n_lval < 32) {
expand(p, INBREG, "\tmov U1,UL,asl ");
printf(CONFMT COM "64-bit right-shift\n", 32 - r->n_lval);
expand(p, INBREG, "\tmov A1,AL,lsr AR\n");
expand(p, INBREG, "\torr A1,A1,U1\n");
if (ty == LONGLONG)
expand(p, INBREG, "\tmov U1,UL,asr AR\n");
else
expand(p, INBREG, "\tmov U1,UL,lsr AR\n");
} else if (p->n_op == RS && r->n_op == ICON && r->n_lval < 64) {
if (ty == LONGLONG) {
expand(p, INBREG, "\tmvn U1,#1" COM "64-bit right-shift\n");
expand(p, INBREG, "\tmov A1,UL");
shifttype = "asr";
}else {
expand(p, INBREG, "\tmov U1,#0" COM "64-bit right-shift\n");
expand(p, INBREG, "\tmov A1,UL");
shifttype = "lsr";
}
if (r->n_lval - 32 != 0)
printf(",%s " CONFMT, shifttype, r->n_lval - 32);
printf("\n");
} else if (p->n_op == RS && r->n_op == ICON) {
expand(p, INBREG, "\tmov A1,#0" COM "64-bit right-shift\n");
expand(p, INBREG, "\tmov U1,#0\n");
}
}
/*
* http://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html#Soft-float-library-routines
*/
static void
fpemul(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 = "adddf3";
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 = "subdf3";
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 = "muldf3";
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 = "divdf3";
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 = "negdf2";
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 = "eqdf2";
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 = "nedf2";
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 = "gedf2";
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 = "ledf2";
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 = "gtdf2";
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 = "ltdf2";
else if (p->n_op == SCONV && p->n_type == FLOAT) {
if (l->n_type == DOUBLE) ch = "truncdfsf2";
else if (l->n_type == LDOUBLE) ch = "truncdfsf2";
else if (l->n_type == ULONGLONG) ch = "floatundisf";
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 = "extendsfdf2";
else if (l->n_type == DOUBLE) ch = "extenddftd2";
else if (l->n_type == ULONGLONG) ch = "floatundidf";
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 == 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 = "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 == 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);
printf("\tbl __%s" COM "softfloat operation\n", exname(ch));
if (p->n_op >= EQ && p->n_op <= GT)
printf("\tcmp %s,#0\n", rnames[R0]);
}
/*
* http://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html#Integer-library-routines
*/
static void
emul(NODE *p)
{
char *ch = NULL;
/**/ if (p->n_op == LS && DEUNSIGN(p->n_type) == LONGLONG) ch = "ashlti3";
else if (p->n_op == LS && DEUNSIGN(p->n_type) == LONG) ch = "ashldi3";
else if (p->n_op == LS && DEUNSIGN(p->n_type) == INT) ch = "ashlsi3";
/**/ else if (p->n_op == RS && p->n_type == ULONGLONG) ch = "lshrti3";
else if (p->n_op == RS && p->n_type == ULONG) ch = "lshrdi3";
else if (p->n_op == RS && p->n_type == UNSIGNED) ch = "lshrsi3";
/**/ else if (p->n_op == RS && p->n_type == LONGLONG) ch = "ashrti3";
else if (p->n_op == RS && p->n_type == LONG) ch = "ashrdi3";
else if (p->n_op == RS && 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) ch = "divdi3";
else if (p->n_op == DIV && 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) ch = "udivdi3";
else if (p->n_op == DIV && 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) ch = "moddi3";
else if (p->n_op == MOD && 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) ch = "umoddi3";
else if (p->n_op == MOD && 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) ch = "muldi3";
else if (p->n_op == MUL && p->n_type == INT) ch = "mulsi3";
else if (p->n_op == UMINUS && p->n_type == LONGLONG) ch = "negti2";
else if (p->n_op == UMINUS && p->n_type == LONG) ch = "negdi2";
else ch = 0, comperr("ZE");
printf("\tbl __%s" COM "emulated operation\n", exname(ch));
}
static void
halfword(NODE *p)
{
NODE *r = getlr(p, 'R');
NODE *l = getlr(p, 'L');
int idx0 = 0, idx1 = 1;
if (features(FEATURE_BIGENDIAN)) {
idx0 = 1;
idx1 = 0;
}
if (p->n_op == ASSIGN && r->n_op == OREG) {
/* load */
expand(p, 0, "\tldrb A1,");
printf("[%s," CONFMT "]\n", rnames[r->n_rval], r->n_lval+idx0);
expand(p, 0, "\tldrb AL,");
printf("[%s," CONFMT "]\n", rnames[r->n_rval], r->n_lval+idx1);
expand(p, 0, "\torr AL,A1,AL,asl #8\n");
} else if (p->n_op == ASSIGN && l->n_op == OREG) {
/* store */
expand(p, 0, "\tstrb AR,");
printf("[%s," CONFMT "]\n", rnames[l->n_rval], l->n_lval+idx0);
expand(p, 0, "\tmov A1,AR,asr #8\n");
expand(p, 0, "\tstrb A1,");
printf("[%s," CONFMT "]\n", rnames[l->n_rval], l->n_lval+idx1);
} else if (p->n_op == SCONV || p->n_op == UMUL) {
/* load */
expand(p, 0, "\tldrb A1,");
printf("[%s," CONFMT "]\n", rnames[l->n_rval], l->n_lval+idx0);
expand(p, 0, "\tldrb A2,");
printf("[%s," CONFMT "]\n", rnames[l->n_rval], l->n_lval+idx1);
expand(p, 0, "\torr A1,A1,A2,asl #8\n");
} else if (p->n_op == NAME || p->n_op == ICON || p->n_op == OREG) {
/* load */
expand(p, 0, "\tldrb A1,");
printf("[%s," CONFMT "]\n", rnames[p->n_rval], p->n_lval+idx0);
expand(p, 0, "\tldrb A2,");
printf("[%s," CONFMT "]\n", rnames[p->n_rval], p->n_lval+idx1);
expand(p, 0, "\torr A1,A1,A2,asl #8\n");
} else {
comperr("halfword");
}
}
static void
bfext(NODE *p)
{
int sz;
if (ISUNSIGNED(p->n_right->n_type))
return;
sz = 32 - UPKFSZ(p->n_left->n_rval);
expand(p, 0, "\tmov AD,AD,asl ");
printf("#%d\n", sz);
expand(p, 0, "\tmov AD,AD,asr ");
printf("#%d\n", sz);
}
static int
argsiz(NODE *p)
{
TWORD t = p->n_type;
if (t < LONGLONG || t == FLOAT || t > BTMASK)
return 4;
if (t == LONGLONG || t == ULONGLONG)
return 8;
if (t == DOUBLE || t == LDOUBLE)
return 8;
if (t == STRTY || t == UNIONTY)
return p->n_stsize;
comperr("argsiz");
return 0;
}
void
zzzcode(NODE *p, int c)
{
int pr;
switch (c) {
case 'B': /* bit-field sign extension */
bfext(p);
break;
case 'C': /* remove from stack after subroutine call */
pr = p->n_qual;
#if 0
if (p->n_op == STCALL || p->n_op == USTCALL)
pr += 4;
#endif
if (p->n_op == UCALL)
return; /* XXX remove ZC from UCALL */
if (pr > 0)
printf("\tadd %s,%s,#%d\n", rnames[SP], rnames[SP], pr);
break;
case 'D': /* Long long comparision */
twollcomp(p);
break;
case 'E': /* print out emulated ops */
emul(p);
break;
case 'F': /* print out emulated floating-point ops */
fpemul(p);
break;
case 'H': /* do halfword access */
halfword(p);
break;
case 'I': /* init constant */
if (p->n_name[0] != '\0')
comperr("named init");
load_constant_into_reg(DECRA(p->n_reg, 1),
p->n_lval & 0xffffffff);
break;
case 'J': /* init longlong constant */
load_constant_into_reg(DECRA(p->n_reg, 1) - R0R1,
p->n_lval & 0xffffffff);
load_constant_into_reg(DECRA(p->n_reg, 1) - R0R1 + 1,
(p->n_lval >> 32));
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);
}
/*
* 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))
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)
{
char *s;
int val = p->n_lval;
switch (p->n_op) {
case ICON:
#if 0
if (p->n_sp)
printf(" [class=%d,level=%d] ", p->n_sp->sclass, p->n_sp->slevel);
#endif
#ifdef notdef /* ICON cannot ever use sp here */
/* If it does, it's a giant bug */
if (p->n_sp == NULL || (p->n_sp->sclass == ILABEL ||
(p->n_sp->sclass == STATIC && p->n_sp->slevel > 0)))
s = p->n_name;
else
s = exname(p->n_name);
#else
s = p->n_name;
#endif
if (*s != '\0') {
fprintf(fp, "%s", s);
if (val > 0)
fprintf(fp, "+%d", val);
else if (val < 0)
fprintf(fp, "-%d", -val);
} else
fprintf(fp, CONFMT, (CONSZ)val);
return;
default:
comperr("illegal conput, p %p", p);
}
}
/*ARGSUSED*/
void
insput(NODE *p)
{
comperr("insput");
}
/*
* 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:
fprintf(stdout, "%s", rnames[p->n_rval-R0R1+1]);
break;
case NAME:
case OREG:
p->n_lval += size;
adrput(stdout, p);
p->n_lval -= size;
break;
case ICON:
fprintf(stdout, CONFMT, p->n_lval >> 32);
break;
default:
comperr("upput bad op %d size %d", p->n_op, size);
}
}
void
adrput(FILE *io, NODE *p)
{
int r;
/* 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 (p->n_lval != 0)
fprintf(io, "+%lld", p->n_lval);
} else
fprintf(io, CONFMT, p->n_lval);
return;
case OREG:
r = p->n_rval;
if (R2TEST(r))
fprintf(io, "[%s, %s, lsl #%d]",
rnames[R2UPK1(r)],
rnames[R2UPK2(r)],
R2UPK3(r));
else
fprintf(io, "[%s,#%d]", rnames[p->n_rval], (int)p->n_lval);
return;
case ICON:
/* addressable value of the constant */
conput(io, p);
return;
case REG:
switch (p->n_type) {
case DOUBLE:
case LDOUBLE:
if (features(FEATURE_HARDFLOAT)) {
fprintf(io, "%s", rnames[p->n_rval]);
break;
}
/* FALLTHROUGH */
case LONGLONG:
case ULONGLONG:
fprintf(stdout, "%s", rnames[p->n_rval-R0R1]);
break;
default:
fprintf(io, "%s", rnames[p->n_rval]);
}
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)
{
if (o < EQ || o > UGT)
comperr("bad conditional branch: %s", opst[o]);
printf("\t%s " LABFMT COM "conditional branch\n",
ccbranches[o-EQ], lab);
}
/*
* The arm can only address 4k to get a NAME, so there must be some
* rewriting here. Strategy:
* For first 1000 nodes found, print out the word directly.
* For the following 1000 nodes, group them together in asm statements
* and create a jump over.
* For the last <1000 statements, print out the words last.
*/
struct addrsymb {
SLIST_ENTRY(addrsymb) link;
char *name; /* symbol name */
int num; /* symbol offset */
char *str; /* replace label */
};
SLIST_HEAD(, addrsymb) aslist;
static struct interpass *ipbase;
static int prtnumber, nodcnt, notfirst;
#define PRTLAB ".LY%d" /* special for here */
static struct interpass *
anode(char *p)
{
extern int thisline;
struct interpass *ip = tmpalloc(sizeof(struct interpass));
ip->ip_asm = p;
ip->type = IP_ASM;
ip->lineno = thisline;
return ip;
}
static void
flshlab(void)
{
struct interpass *ip;
struct addrsymb *el;
int lab = prtnumber++;
char *c;
if (SLIST_FIRST(&aslist) == NULL)
return;
snprintf(c = tmpalloc(32), 32, "\tb " PRTLAB "\n", lab);
ip = anode(c);
DLIST_INSERT_BEFORE(ipbase, ip, qelem);
SLIST_FOREACH(el, &aslist, link) {
/* insert each node as asm */
int l = 32+strlen(el->name);
c = tmpalloc(l);
if (el->num)
snprintf(c, l, "%s:\n\t.word %s+%d\n",
el->str, el->name, el->num);
else
snprintf(c, l, "%s:\n\t.word %s\n", el->str, el->name);
ip = anode(c);
DLIST_INSERT_BEFORE(ipbase, ip, qelem);
}
/* generate asm label */
snprintf(c = tmpalloc(32), 32, PRTLAB ":\n", lab);
ip = anode(c);
DLIST_INSERT_BEFORE(ipbase, ip, qelem);
}
static void
prtaddr(NODE *p)
{
NODE *l = p->n_left;
struct addrsymb *el;
int found = 0;
int lab;
nodcnt++;
if (p->n_op == ASSIGN && p->n_right->n_op == ICON &&
p->n_right->n_name[0] != '\0') {
/* named constant */
p = p->n_right;
/* Restore addrof */
l = mklnode(NAME, p->n_lval, 0, 0);
l->n_name = p->n_name;
p->n_left = l;
p->n_op = ADDROF;
}
if (p->n_op != ADDROF || l->n_op != NAME)
return;
/* if we passed 1k nodes printout list */
if (nodcnt > 1000) {
if (notfirst)
flshlab();
SLIST_INIT(&aslist);
notfirst = 1;
nodcnt = 0;
}
/* write address to byte stream */
SLIST_FOREACH(el, &aslist, link) {
if (el->num == l->n_lval && el->name[0] == l->n_name[0] &&
strcmp(el->name, l->n_name) == 0) {
found = 1;
break;
}
}
if (!found) {
/* we know that this is text segment */
lab = prtnumber++;
if (nodcnt <= 1000 && notfirst == 0) {
if (l->n_lval)
printf(PRTLAB ":\n\t.word %s+%lld\n",
lab, l->n_name, l->n_lval);
else
printf(PRTLAB ":\n\t.word %s\n",
lab, l->n_name);
}
el = tmpalloc(sizeof(struct addrsymb));
el->num = l->n_lval;
el->name = l->n_name;
el->str = tmpalloc(32);
snprintf(el->str, 32, PRTLAB, lab);
SLIST_INSERT_LAST(&aslist, el, link);
}
nfree(l);
p->n_op = NAME;
p->n_lval = 0;
p->n_name = el->str;
}
void
myreader(struct interpass *ipole)
{
struct interpass *ip;
SLIST_INIT(&aslist);
notfirst = nodcnt = 0;
DLIST_FOREACH(ip, ipole, qelem) {
switch (ip->type) {
case IP_NODE:
lineno = ip->lineno;
ipbase = ip;
walkf(ip->ip_node, prtaddr);
break;
case IP_EPILOG:
ipbase = ip;
if (notfirst)
flshlab();
break;
default:
break;
}
}
if (x2debug)
printip(ipole);
}
/*
* Remove some PCONVs after OREGs are created.
*/
static void
pconv2(NODE *p)
{
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);
}
void
myoptim(struct interpass *ipp)
{
}
/*
* Register move: move contents of register 's' to register 'r'.
*/
void
rmove(int s, int d, TWORD t)
{
switch (t) {
case DOUBLE:
case LDOUBLE:
if (features(FEATURE_HARDFLOAT)) {
printf("\tfmr %s,%s" COM "rmove\n",
rnames[d], rnames[s]);
break;
}
/* FALLTHROUGH */
case LONGLONG:
case ULONGLONG:
#define LONGREG(x, y) rnames[(x)-(R0R1-(y))]
if (s == d+1) {
/* dh = sl, copy low word first */
printf("\tmov %s,%s" COM "rmove\n",
LONGREG(d,0), LONGREG(s,0));
printf("\tmov %s,%s\n",
LONGREG(d,1), LONGREG(s,1));
} else {
/* copy high word first */
printf("\tmov %s,%s" COM "rmove\n",
LONGREG(d,1), LONGREG(s,1));
printf("\tmov %s,%s\n",
LONGREG(d,0), LONGREG(s,0));
}
#undef LONGREG
break;
case FLOAT:
if (features(FEATURE_HARDFLOAT)) {
printf("\tmr %s,%s" COM "rmove\n",
rnames[d], rnames[s]);
break;
}
/* FALLTHROUGH */
default:
printf("\tmov %s,%s" COM "rmove\n", rnames[d], rnames[s]);
}
}
/*
* Can we assign a register from class 'c', given the set
* of number of assigned registers in each class 'r'.
*
* On ARM, we have:
* 11 CLASSA registers (32-bit hard registers)
* 10 CLASSB registers (64-bit composite registers)
* 8 or 32 CLASSC registers (floating-point)
*
* There is a problem calculating the available composite registers
* (ie CLASSB). The algorithm below assumes that given any two
* registers, we can make a composite register. But this isn't true
* here (or with other targets), since the number of combinations
* of register pairs could become very large. Additionally,
* having so many combinations really isn't so practical, since
* most register pairs cannot be used to pass function arguments.
* Consequently, when there is pressure composite registers,
* "beenhere" compilation failures are common.
*
* [We need to know which registers are allocated, not simply
* the number in each class]
*/
int
COLORMAP(int c, int *r)
{
int num = 0; /* number of registers used */
#if 0
static const char classes[] = { 'X', 'A', 'B', 'C', 'D' };
printf("COLORMAP: requested class %c\n", classes[c]);
printf("COLORMAP: class A: %d\n", r[CLASSA]);
printf("COLORMAP: class B: %d\n", r[CLASSB]);
#endif
switch (c) {
case CLASSA:
num += r[CLASSA];
num += 2*r[CLASSB];
return num < 11;
case CLASSB:
num += 2*r[CLASSB];
num += r[CLASSA];
return num < 6; /* XXX see comments above */
case CLASSC:
num += r[CLASSC];
if (features(FEATURE_FPA))
return num < 8;
else if (features(FEATURE_VFP))
return num < 8;
else
cerror("colormap 1");
}
cerror("colormap 2");
return 0; /* XXX gcc */
}
/*
* Return a class suitable for a specific type.
*/
int
gclass(TWORD t)
{
if (t == DOUBLE || t == LDOUBLE) {
if (features(FEATURE_HARDFLOAT))
return CLASSC;
else
return CLASSB;
}
if (t == FLOAT) {
if (features(FEATURE_HARDFLOAT))
return CLASSC;
else
return CLASSA;
}
if (DEUNSIGN(t) == LONGLONG)
return CLASSB;
return CLASSA;
}
int
retreg(int t)
{
int c = gclass(t);
if (c == CLASSB)
return R0R1;
else if (c == CLASSC)
return F0;
return R0;
}
/*
* Calculate argument sizes.
*/
void
lastcall(NODE *p)
{
NODE *op = p;
int size = 0;
p->n_qual = 0;
if (p->n_op != CALL && p->n_op != FORTCALL && p->n_op != STCALL)
return;
for (p = p->n_right; p->n_op == CM; p = p->n_left)
size += argsiz(p->n_right);
size += argsiz(p);
op->n_qual = size - 16; /* XXX */
}
/*
* Special shapes.
*/
int
special(NODE *p, int shape)
{
return SRNOPE;
}
/*
* default to ARMv2
*/
#ifdef TARGET_BIG_ENDIAN
#define DEFAULT_FEATURES FEATURE_BIGENDIAN | FEATURE_MUL
#else
#define DEFAULT_FEATURES FEATURE_MUL
#endif
static int fset = DEFAULT_FEATURES;
/*
* Target-dependent command-line options.
*/
void
mflags(char *str)
{
if (strcasecmp(str, "little-endian") == 0) {
fset &= ~FEATURE_BIGENDIAN;
} else if (strcasecmp(str, "big-endian") == 0) {
fset |= FEATURE_BIGENDIAN;
} else if (strcasecmp(str, "fpe=fpa") == 0) {
fset &= ~(FEATURE_VFP | FEATURE_FPA);
fset |= FEATURE_FPA;
} else if (strcasecmp(str, "fpe=vfp") == 0) {
fset &= ~(FEATURE_VFP | FEATURE_FPA);
fset |= FEATURE_VFP;
} else if (strcasecmp(str, "soft-float") == 0) {
fset &= ~(FEATURE_VFP | FEATURE_FPA);
} else if (strcasecmp(str, "arch=armv1") == 0) {
fset &= ~FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset &= ~FEATURE_MUL;
fset &= ~FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv2") == 0) {
fset &= ~FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset &= ~FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv2a") == 0) {
fset &= ~FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset &= ~FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv3") == 0) {
fset &= ~FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset &= ~FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv4") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv4t") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv4tej") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv5") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv5te") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv5tej") == 0) {
fset |= FEATURE_HALFWORDS;
fset &= ~FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv6") == 0) {
fset |= FEATURE_HALFWORDS;
fset |= FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv6t2") == 0) {
fset |= FEATURE_HALFWORDS;
fset |= FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv6kz") == 0) {
fset |= FEATURE_HALFWORDS;
fset |= FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv6k") == 0) {
fset |= FEATURE_HALFWORDS;
fset |= FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else if (strcasecmp(str, "arch=armv7") == 0) {
fset |= FEATURE_HALFWORDS;
fset |= FEATURE_EXTEND;
fset |= FEATURE_MUL;
fset |= FEATURE_MULL;
} else {
fprintf(stderr, "unknown m option '%s'\n", str);
exit(1);
}
}
int
features(int mask)
{
if (mask == FEATURE_HARDFLOAT)
return ((fset & mask) != 0);
return ((fset & mask) == mask);
}
/*
* Define the current location as an internal label.
*/
void
deflab(int label)
{
printf(LABFMT ":\n", label);
}