1faff418ec
This turns EM `con 5000000000I8` into assembly `.data8 5000000000` for machines i386, i80, i86, m68020, powerpc, vc4. These are the only ncg machines in our build. i80 and i86 get con_mult(sz) for sz == 4 and sz == 8. The other machines only get sz == 8, because they have 4-byte words, and ncg only calls con_mult(sz) when sz is greater than the word size. The tab "\t" after .data4 or .data8 is like the tabs in the con_*() macros of mach/*/ncg/mach.h. i86 now uses .data4, like i80. Also, i86 and i386 now use the numeric string without converting it to an integer and back to a string.
313 lines
6.3 KiB
C
313 lines
6.3 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* machine dependent back end routines for the PowerPC
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stb.h>
|
|
|
|
static int writing_stabs = 0;
|
|
|
|
#ifdef REGVARS
|
|
static long framesize;
|
|
#endif
|
|
|
|
void
|
|
con_part(int sz, word w)
|
|
{
|
|
while (part_size % sz)
|
|
part_size++;
|
|
if (part_size == TEM_WSIZE)
|
|
part_flush();
|
|
if (sz == 1) {
|
|
w &= 0xFF;
|
|
w <<= 8 * (3 - part_size);
|
|
part_word |= w;
|
|
} else if (sz == 2) {
|
|
w &= 0xFFFF;
|
|
w <<= 8 * (2 - part_size);
|
|
part_word |= w;
|
|
} else {
|
|
assert(sz == 4);
|
|
part_word = w;
|
|
}
|
|
part_size += sz;
|
|
}
|
|
|
|
void
|
|
con_mult(word sz) {
|
|
|
|
if (sz != 8)
|
|
fatal("bad icon/ucon size");
|
|
fprintf(codefile,".data8\t%s\n", str);
|
|
}
|
|
|
|
#define CODE_GENERATOR
|
|
#define IEEEFLOAT
|
|
#define FL_MSL_AT_LOW_ADDRESS 1
|
|
#define FL_MSW_AT_LOW_ADDRESS 1
|
|
#define FL_MSB_AT_LOW_ADDRESS 1
|
|
#include <con_float>
|
|
|
|
void
|
|
prolog(full nlocals)
|
|
{
|
|
/*
|
|
* For N_LSYM and N_PSYM stabs, we want gdb to use fp, not sp.
|
|
* The trick is to use "stwu sp, _(sp)" then "addi fp, sp, 0"
|
|
* before we save lr with "stw r0, _(sp)".
|
|
*
|
|
* Tried with Apple's gdb-696. Refer to
|
|
* - gdb-696/src/gdb/rs6000-tdep.c, skip_prologue(), line 1101
|
|
* - gdb-696/src/gdb/macosx/ppc-macosx-frameinfo.c,
|
|
* ppc_parse_instructions(), line 717
|
|
* https://opensource.apple.com/release/developer-tools-25.html
|
|
*/
|
|
fprintf(codefile, "mfspr r0, lr\n");
|
|
if (writing_stabs) {
|
|
fprintf(codefile, "stwu sp, -8(sp)\n"); /* for gdb */
|
|
fprintf(codefile, "stw fp, 0(sp)\n");
|
|
} else
|
|
fprintf(codefile, "stwu fp, -8(sp)\n");
|
|
fprintf(codefile, "addi fp, sp, 0\n"); /* for gdb */
|
|
fprintf(codefile, "stw r0, 4(sp)\n");
|
|
|
|
#ifdef REGVARS
|
|
framesize = nlocals;
|
|
/* regsave() increases framesize; f_regsave() adjusts sp. */
|
|
#else
|
|
if (nlocals)
|
|
fprintf(codefile, "addi sp, sp, %ld\n", -nlocals);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
mes(word type)
|
|
{
|
|
int argt, a1, a2 ;
|
|
|
|
switch ( (int)type ) {
|
|
case ms_ext :
|
|
for (;;) {
|
|
switch ( argt=getarg(
|
|
ptyp(sp_cend)|ptyp(sp_pnam)|sym_ptyp) ) {
|
|
case sp_cend :
|
|
return ;
|
|
default:
|
|
strarg(argt) ;
|
|
fprintf(codefile,".define %s\n",argstr) ;
|
|
break ;
|
|
}
|
|
}
|
|
case ms_stb:
|
|
argt = getarg(str_ptyp | cst_ptyp);
|
|
if (argt == sp_cstx)
|
|
fputs(".symb \"\", ", codefile);
|
|
else {
|
|
fprintf(codefile, ".symb \"%s\", ", str);
|
|
argt = getarg(cst_ptyp);
|
|
}
|
|
a1 = argval;
|
|
argt = getarg(cst_ptyp);
|
|
a2 = argval;
|
|
argt = getarg(cst_ptyp|nof_ptyp|sof_ptyp|ilb_ptyp|pro_ptyp);
|
|
if (a1 == N_PSYM) {
|
|
/* Change offset from AB into offset from
|
|
the frame pointer.
|
|
*/
|
|
argval += 8;
|
|
}
|
|
fprintf(codefile, "%s, 0x%x, %d\n", strarg(argt), a1, a2);
|
|
argt = getarg(end_ptyp);
|
|
break;
|
|
case ms_std:
|
|
writing_stabs = 1; /* set by first "mes 13,...,100,0" */
|
|
argt = getarg(str_ptyp | cst_ptyp);
|
|
if (argt == sp_cstx)
|
|
str[0] = '\0';
|
|
else {
|
|
argt = getarg(cst_ptyp);
|
|
}
|
|
swtxt();
|
|
fprintf(codefile, ".symd \"%s\", 0x%x,", str, (int) argval);
|
|
argt = getarg(cst_ptyp);
|
|
fprintf(codefile, "%d\n", (int) argval);
|
|
argt = getarg(end_ptyp);
|
|
break;
|
|
default :
|
|
while ( getarg(any_ptyp) != sp_cend ) ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
char *segname[] = {
|
|
".sect .text",
|
|
".sect .data",
|
|
".sect .rom",
|
|
".sect .bss"
|
|
};
|
|
|
|
#ifdef REGVARS
|
|
|
|
static long savedf[32];
|
|
static long savedi[32];
|
|
static int savedtop;
|
|
static uint32_t lfs_set;
|
|
|
|
/* Calculate the register score of a local variable. */
|
|
int
|
|
regscore(long offset, int size, int type, int frequency, int totype)
|
|
{
|
|
int score;
|
|
|
|
switch (type) {
|
|
case reg_float:
|
|
/* Don't put reg_float in reg_any. */
|
|
if (totype != reg_float)
|
|
return -1;
|
|
assert(size == 4 || size == 8);
|
|
break;
|
|
default:
|
|
assert(size == 4);
|
|
break;
|
|
}
|
|
|
|
/* Clamp to avoid overflowing 16-bit int score. */
|
|
if (frequency > 8000)
|
|
frequency = 8000;
|
|
|
|
/*
|
|
* Each occurence of a regvar saves about 4 bytes by not
|
|
* emitting a load or store instruction. The overhead is
|
|
* about 8 bytes to save and restore the register, plus
|
|
* 4 bytes if the local is a parameter.
|
|
*/
|
|
score = 4 * frequency - 8 - ((offset >= 0) ? 4 : 0);
|
|
#if 0
|
|
fprintf(codefile, "! local %ld score %d\n", offset, score);
|
|
#endif
|
|
return score;
|
|
}
|
|
|
|
/* Initialise regvar system for one function. */
|
|
|
|
void
|
|
i_regsave(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<32; i++) {
|
|
savedf[i] = LONG_MIN;
|
|
savedi[i] = LONG_MIN;
|
|
}
|
|
|
|
/* Set top of register save area, relative to fp. */
|
|
savedtop = -framesize;
|
|
|
|
lfs_set = 0; /* empty set */
|
|
}
|
|
|
|
/* Mark a register as being saved. */
|
|
|
|
void
|
|
regsave(const char* regname, long offset, int size)
|
|
{
|
|
int regnum = atoi(regname + 1);
|
|
|
|
assert(regnum >= 0 && regnum <= 31);
|
|
switch (regname[0]) {
|
|
case 'f':
|
|
savedf[regnum] = offset;
|
|
framesize += 8;
|
|
if (size == 4)
|
|
lfs_set |= ((uint32_t)1<<regnum);
|
|
break;
|
|
case 'r':
|
|
savedi[regnum] = offset;
|
|
framesize += 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
saveloadregs(const char* ops, const char* opm, const char *opf)
|
|
{
|
|
long offset = savedtop;
|
|
int reg;
|
|
|
|
/* Do floating-point registers. */
|
|
for (reg = 31; reg >= 0; reg--) {
|
|
if (savedf[reg] != LONG_MIN) {
|
|
offset -= 8;
|
|
fprintf(codefile, "%s f%d,%ld(fp)\n",
|
|
opf, reg, offset);
|
|
}
|
|
}
|
|
|
|
if (savedi[31] != LONG_MIN && savedi[30] != LONG_MIN) {
|
|
/*
|
|
* Do multiple registers from reg to r31.
|
|
*
|
|
* Using stmw or lmw reduces code size, but in some
|
|
* processors, runs slower than the equivalent pile of
|
|
* stw or lwz instructions.
|
|
*/
|
|
reg = 30;
|
|
while (reg > 0 && savedi[reg - 1] != LONG_MIN)
|
|
reg--;
|
|
offset -= (32 - reg) * 4;
|
|
fprintf(codefile, "%s r%d,%ld(fp)\n", opm, reg, offset);
|
|
} else
|
|
reg = 32;
|
|
|
|
/* Do single general-purpose registers. */
|
|
for (reg--; reg >= 0; reg--) {
|
|
if (savedi[reg] != LONG_MIN) {
|
|
offset -= 4;
|
|
fprintf(codefile, "%s r%d,%ld(fp)\n",
|
|
ops, reg, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
f_regsave(void)
|
|
{
|
|
int reg;
|
|
|
|
if (framesize)
|
|
fprintf(codefile, "addi sp, sp, %ld\n", -framesize);
|
|
saveloadregs("stw", "stmw", "stfd");
|
|
|
|
/*
|
|
* Register variables with offset >= 0 must load an argument
|
|
* from that offset.
|
|
*/
|
|
for (reg = 31; reg >= 0; reg--)
|
|
if (savedf[reg] >= 0)
|
|
fprintf(codefile, "%s f%d, %ld(fp)\n",
|
|
(lfs_set & ((uint32_t)1<<reg)) ? "lfs" : "lfd",
|
|
reg, savedf[reg]);
|
|
|
|
for (reg = 31; reg >= 0; reg--)
|
|
if (savedi[reg] >= 0)
|
|
fprintf(codefile, "lwz r%d, %ld(fp)\n",
|
|
reg, savedi[reg]);
|
|
}
|
|
|
|
/* Restore all saved registers. */
|
|
|
|
void
|
|
regreturn(void)
|
|
{
|
|
saveloadregs("lwz", "lmw", "lfd");
|
|
}
|
|
|
|
#endif
|