Merge pull request #14 from davidgiven/dtrg-experimental-mcgg
Merge mcg code generator to trunk
This commit is contained in:
commit
0b21f18d4c
|
@ -7,6 +7,7 @@ addons:
|
|||
apt:
|
||||
packages:
|
||||
- ed
|
||||
- openbios-ppc
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
|
11
build.lua
11
build.lua
|
@ -9,14 +9,22 @@ vars.plats = {
|
|||
"linux386",
|
||||
"linux68k",
|
||||
"linuxppc",
|
||||
"qemuppc",
|
||||
"pc86",
|
||||
"rpi",
|
||||
}
|
||||
vars.plats_with_tests = {
|
||||
"qemuppc",
|
||||
}
|
||||
|
||||
local plat_packages = {}
|
||||
local test_packages = {}
|
||||
for _, p in ipairs(vars.plats) do
|
||||
plat_packages[#plat_packages+1] = "plat/"..p.."+pkg"
|
||||
end
|
||||
for _, p in ipairs(vars.plats_with_tests) do
|
||||
test_packages[#test_packages+1] = "plat/"..p.."/tests+tests"
|
||||
end
|
||||
|
||||
installable {
|
||||
name = "ack",
|
||||
|
@ -34,6 +42,9 @@ installable {
|
|||
"util/opt+pkg",
|
||||
"examples+pkg",
|
||||
plat_packages
|
||||
},
|
||||
deps = {
|
||||
test_packages
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -451,7 +451,7 @@ loadtarget = function(targetname)
|
|||
target = targets[targetname]
|
||||
if not target then
|
||||
error(string.format("build file '%s' contains no target '%s'",
|
||||
filename, targetpart))
|
||||
filepart, targetpart))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -729,9 +729,10 @@ definerule("simplerule",
|
|||
definerule("installable",
|
||||
{
|
||||
map = { type="targets", default={} },
|
||||
deps = { type="targets", default={} },
|
||||
},
|
||||
function (e)
|
||||
local deps = {}
|
||||
local deps = filenamesof(e.deps)
|
||||
local commands = {}
|
||||
local srcs = {}
|
||||
local outs = {}
|
||||
|
|
|
@ -226,6 +226,7 @@ definerule("cprogram",
|
|||
},
|
||||
function (e)
|
||||
local libs = matching(filenamesof(e.deps), "%.a$")
|
||||
local srcs = {}
|
||||
if (#e.srcs > 0) then
|
||||
for _, f in pairs(
|
||||
matching(
|
||||
|
@ -240,7 +241,7 @@ definerule("cprogram",
|
|||
"%.a$"
|
||||
)
|
||||
) do
|
||||
libs[#libs+1] = f
|
||||
srcs[#srcs+1] = f
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -248,7 +249,7 @@ definerule("cprogram",
|
|||
name = e.name,
|
||||
cwd = e.cwd,
|
||||
deps = e.deps,
|
||||
ins = libs,
|
||||
ins = { srcs, libs },
|
||||
outleaves = { e.name },
|
||||
commands = e.commands,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int _errsym;
|
||||
int _erlsym;
|
||||
|
||||
/* error takes an error value in the range of 0-255 */
|
||||
/* and generates a trap */
|
||||
|
||||
|
@ -52,9 +55,6 @@ char *errortable[255]={
|
|||
error(index)
|
||||
int index;
|
||||
{
|
||||
extern int _errsym;
|
||||
extern int _erlsym;
|
||||
|
||||
_setline();
|
||||
if( index<0 || index >40 )
|
||||
printf("LINE %d:ERROR %d: Unprintable error\n",_erlsym,index);
|
||||
|
|
|
@ -617,11 +617,7 @@ prologcode()
|
|||
C_df_dnam("_iomode");
|
||||
C_rom_scon("O",(arith)2);
|
||||
C_exa_dnam("_errsym");
|
||||
C_df_dnam("_errsym");
|
||||
C_bss_cst((arith)BEMINTSIZE,(arith)0,1);
|
||||
C_exa_dnam("_erlsym");
|
||||
C_df_dnam("_erlsym");
|
||||
C_bss_cst((arith)BEMINTSIZE,(arith)0,1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#undef word_t
|
||||
#define word_t long
|
||||
|
||||
typedef uint32_t quad;
|
||||
|
||||
#undef ALIGNWORD
|
||||
#define ALIGNWORD 4
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
%token <y_word> OP_RS_RA_NB
|
||||
%token <y_word> OP_RS_RA_RB
|
||||
%token <y_word> OP_RS_RA_RB_C
|
||||
%token <y_word> OP_RS_RA_RA_C
|
||||
%token <y_word> OP_RS_RA_RB_MB5_ME5_C
|
||||
%token <y_word> OP_RS_RA_RB_MB6_C
|
||||
%token <y_word> OP_RS_RA_RB_ME6_C
|
||||
|
@ -80,6 +81,7 @@
|
|||
%token <y_word> OP_TO_RA_RB
|
||||
%token <y_word> OP_TO_RA_SI
|
||||
|
||||
%token <y_word> OP_LA
|
||||
%token <y_word> OP_LI32
|
||||
|
||||
/* Other token types */
|
||||
|
|
|
@ -99,6 +99,9 @@
|
|||
/* Special instructions */
|
||||
|
||||
0, OP_LI32, 0, "li32",
|
||||
0, OP_LA, 0, "la",
|
||||
0, OP_LA, 0, "li",
|
||||
0, OP_RS_RA_RA_C, 31<<26 | 444<<1, "mr",
|
||||
|
||||
/* Branch processor instructions (page 20) */
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ operation
|
|||
| OP_RS_RA_UI_CC C GPR ',' GPR ',' e16 { emit4($1 | ($5<<21) | ($3<<16) | $7); }
|
||||
| OP_RS_RA_RB GPR ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
|
||||
| OP_RS_RA_RB_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11)); }
|
||||
| OP_RS_RA_RA_C c GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($5<<11)); }
|
||||
| OP_RS_RA_RB_MB5_ME5_C c GPR ',' GPR ',' GPR ',' u5 ',' u5 { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11) | ($9<<6) | ($11<<1)); }
|
||||
| OP_RS_RA_RB_MB6_C c GPR ',' GPR ',' GPR ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11) | (($9&0x1F)<<6) | (($9&0x20)>>0)); }
|
||||
| OP_RS_RA_RB_ME6_C c GPR ',' GPR ',' GPR ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11) | (($9&0x1F)<<6) | (($9&0x20)>>0)); }
|
||||
|
@ -196,9 +197,16 @@ bda
|
|||
li32
|
||||
: GPR ',' expr
|
||||
{
|
||||
newrelo($3.typ, RELOPPC | FIXUPFLAGS);
|
||||
emit4((15<<26) | ($1<<21) | (0<<16) | ($3.val >> 16)); /* addis */
|
||||
emit4((24<<26) | ($1<<21) | ($1<<16) | ($3.val & 0xffff)); /* ori */
|
||||
quad type = $3.typ & S_TYP;
|
||||
quad val = $3.val;
|
||||
if ((type == S_ABS) && (val <= 0xffff))
|
||||
emit4((14<<26) | ($1<<21) | (0<<16) | val); /* addi */
|
||||
else
|
||||
{
|
||||
newrelo($3.typ, RELOPPC | FIXUPFLAGS);
|
||||
emit4((15<<26) | ($1<<21) | (0<<16) | (val >> 16)); /* addis */
|
||||
emit4((24<<26) | ($1<<21) | ($1<<16) | (val & 0xffff)); /* ori */
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
for _, plat in ipairs(vars.plats) do
|
||||
acklibrary {
|
||||
name = "headers_"..plat,
|
||||
hdrs = { "./*.h" }
|
||||
}
|
||||
|
||||
acklibrary {
|
||||
name = "lib_"..plat,
|
||||
srcs = {
|
||||
"./*.s",
|
||||
"./*.e",
|
||||
},
|
||||
vars = { plat = plat },
|
||||
deps = {
|
||||
"h+emheaders"
|
||||
"h+emheaders",
|
||||
"+headers_"..plat,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
! address and jumps to it.
|
||||
! traps if resulting address is zero
|
||||
!
|
||||
! On entry: r3 = address of CSA table
|
||||
! r4 = value
|
||||
! Stack: ( value tableaddr -- )
|
||||
|
||||
.define .csa
|
||||
.csa:
|
||||
lwz r3, 0(sp)
|
||||
lwz r4, 4(sp)
|
||||
addi sp, sp, 8
|
||||
|
||||
lwz r5, 0(r3) ! load default
|
||||
mtspr ctr, r5
|
||||
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
! address and jumps to it.
|
||||
! traps if resulting address is zero
|
||||
!
|
||||
! On entry: r3 = address of CSB table
|
||||
! r4 = value
|
||||
! Stack: ( value tableaddr -- )
|
||||
|
||||
.define .csb
|
||||
.csb:
|
||||
lwz r3, 0(sp)
|
||||
lwz r4, 4(sp)
|
||||
addi sp, sp, 8
|
||||
|
||||
lwz r5, 0(r3) ! load default
|
||||
mtspr ctr, r5
|
||||
|
||||
|
|
26
mach/powerpc/libem/inn.s
Normal file
26
mach/powerpc/libem/inn.s
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "powerpc.h"
|
||||
|
||||
.sect .text
|
||||
|
||||
/* Tests a bit in a bitset on the stack.
|
||||
*
|
||||
* Stack: ( bitset bitnum setsize -- bool )
|
||||
*/
|
||||
|
||||
.define .inn
|
||||
.inn:
|
||||
lwz r3, 0(sp) /* r3 = size (bytes) */
|
||||
lwz r4, 4(sp) /* r4 = bit number */
|
||||
addi r5, sp, 8 /* r5 = base address of bit set */
|
||||
|
||||
srawi r6, r4, 3 /* r6 = byte address into set */
|
||||
andi. r7, r4, 7 /* r7 = bit within byte */
|
||||
|
||||
lbzx r8, r5, r6 /* r8 = individual byte from set */
|
||||
sraw r8, r8, r7
|
||||
rlwinm r8, r8, 0, 31, 31
|
||||
|
||||
addi sp, sp, 8 /* retract over the two words */
|
||||
add sp, sp, r3 /* retract over bitfield */
|
||||
stwu r8, -4(sp) /* push result */
|
||||
bclr ALWAYS, 0, 0 /* return */
|
333
mach/powerpc/mcg/platform.c
Normal file
333
mach/powerpc/mcg/platform.c
Normal file
|
@ -0,0 +1,333 @@
|
|||
#include "mcg.h"
|
||||
|
||||
/* mcg stack frames are laid out as:
|
||||
*
|
||||
* | ...params...
|
||||
* | --------------- <- ab
|
||||
* | old FR
|
||||
* | old FP
|
||||
* | --------------- <- fp (a.k.a. lb)
|
||||
* | locals
|
||||
* | ---------------
|
||||
* | spills
|
||||
* | --------------- <- sb
|
||||
* | saved regs
|
||||
* | --------------- <- sp, rb
|
||||
* V ...user area...
|
||||
*
|
||||
* st indexes up; lb indexes down.
|
||||
*
|
||||
* Note that [fp] == old_fp and ab == fp + 8.
|
||||
*/
|
||||
|
||||
static ARRAYOF(struct hreg) saved_regs;
|
||||
|
||||
void platform_calculate_offsets(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
saved_regs.count = 0;
|
||||
for (i=0; i<current_proc->usedregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = current_proc->usedregs.item[i];
|
||||
|
||||
if (!(hreg->attrs & burm_volatile_ATTR) &&
|
||||
((hreg->attrs & burm_long_ATTR) || (hreg->attrs & burm_double_ATTR)))
|
||||
{
|
||||
hreg->offset = current_proc->saved_size;
|
||||
current_proc->saved_size += 8;
|
||||
array_append(&saved_regs, hreg);
|
||||
}
|
||||
}
|
||||
for (i=0; i<current_proc->usedregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = current_proc->usedregs.item[i];
|
||||
|
||||
if (!(hreg->attrs & burm_volatile_ATTR) &&
|
||||
((hreg->attrs & burm_int_ATTR) || (hreg->attrs & burm_float_ATTR)))
|
||||
{
|
||||
hreg->offset = current_proc->saved_size;
|
||||
current_proc->saved_size += 4;
|
||||
array_append(&saved_regs, hreg);
|
||||
}
|
||||
}
|
||||
|
||||
current_proc->fp_to_ab = 8;
|
||||
current_proc->fp_to_lb = 0;
|
||||
current_proc->fp_to_sb = -(current_proc->locals_size + current_proc->spills_size);
|
||||
current_proc->fp_to_rb = current_proc->fp_to_sb - current_proc->saved_size;
|
||||
}
|
||||
|
||||
struct hop* platform_prologue(void)
|
||||
{
|
||||
int i;
|
||||
int spoffset = current_proc->saved_size + current_proc->spills_size +
|
||||
current_proc->locals_size;
|
||||
struct hop* hop = new_hop(current_proc->entry, NULL);
|
||||
|
||||
hop_add_insel(hop, "! locals_size = %d", current_proc->locals_size);
|
||||
hop_add_insel(hop, "! spills_size = %d", current_proc->spills_size);
|
||||
hop_add_insel(hop, "! saved_size = %d", current_proc->saved_size);
|
||||
hop_add_insel(hop, "! params @ fp+%d", current_proc->fp_to_ab);
|
||||
hop_add_insel(hop, "! lr @ fp+4");
|
||||
hop_add_insel(hop, "! fp @ fp+0");
|
||||
hop_add_insel(hop, "! locals @ fp-%d to fp+0",
|
||||
current_proc->locals_size);
|
||||
hop_add_insel(hop, "! spills @ fp-%d to fp-%d",
|
||||
-current_proc->fp_to_sb, current_proc->locals_size);
|
||||
for (i=saved_regs.count-1; i>=0; i--)
|
||||
{
|
||||
struct hreg* hreg = saved_regs.item[i];
|
||||
hop_add_insel(hop, "! %s @ fp-%d",
|
||||
hreg->id, -(current_proc->fp_to_rb + hreg->offset));
|
||||
}
|
||||
|
||||
hop_add_insel(hop, "addi sp, sp, %d", -(spoffset + 8));
|
||||
hop_add_insel(hop, "mfspr r0, lr");
|
||||
hop_add_insel(hop, "stw fp, %d(sp)", spoffset + 0);
|
||||
hop_add_insel(hop, "stw r0, %d(sp)", spoffset + 4);
|
||||
hop_add_insel(hop, "addi fp, sp, %d", spoffset);
|
||||
|
||||
/* Saved reg offsets are negative. */
|
||||
for (i=0; i<saved_regs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = saved_regs.item[i];
|
||||
if (hreg->attrs & burm_int_ATTR)
|
||||
hop_add_insel(hop, "stw %H, %d(fp)",
|
||||
hreg, current_proc->fp_to_rb + hreg->offset);
|
||||
else if (hreg->attrs & burm_float_ATTR)
|
||||
hop_add_insel(hop, "stfs %H, %d(fp)",
|
||||
hreg, current_proc->fp_to_rb + hreg->offset);
|
||||
}
|
||||
return hop;
|
||||
}
|
||||
|
||||
struct hop* platform_epilogue(void)
|
||||
{
|
||||
struct hop* hop = new_hop(current_proc->exit, NULL);
|
||||
int i;
|
||||
|
||||
for (i=0; i<saved_regs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = saved_regs.item[i];
|
||||
if (hreg->attrs & burm_int_ATTR)
|
||||
hop_add_insel(hop, "lwz %H, %d(fp)",
|
||||
hreg, current_proc->fp_to_rb + hreg->offset);
|
||||
else if (hreg->attrs & burm_float_ATTR)
|
||||
hop_add_insel(hop, "lfs %H, %d(fp)",
|
||||
hreg, current_proc->fp_to_rb + hreg->offset);
|
||||
}
|
||||
|
||||
hop_add_insel(hop, "lwz r0, 4(fp)");
|
||||
hop_add_insel(hop, "mtspr lr, r0");
|
||||
hop_add_insel(hop, "lwz r0, 0(fp)"); /* load old fp */
|
||||
hop_add_insel(hop, "addi sp, fp, %d", current_proc->fp_to_ab);
|
||||
hop_add_insel(hop, "mr fp, r0");
|
||||
hop_add_insel(hop, "bclr 20, 0, 0");
|
||||
|
||||
return hop;
|
||||
}
|
||||
|
||||
struct hop* platform_move(struct basicblock* bb, struct hreg* src, struct hreg* dest)
|
||||
{
|
||||
struct hop* hop = new_hop(bb, NULL);
|
||||
|
||||
if ((src->attrs & TYPE_ATTRS) != (dest->attrs & TYPE_ATTRS))
|
||||
{
|
||||
assert(!src->is_stacked);
|
||||
assert(!dest->is_stacked);
|
||||
|
||||
switch (src->attrs & TYPE_ATTRS)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "stwu %H, -4(sp)", src);
|
||||
break;
|
||||
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "stwu %0H, -4(sp)", src);
|
||||
hop_add_insel(hop, "stwu %1H, -4(sp)", src);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
hop_add_insel(hop, "stfsu %H, -4(sp)", src);
|
||||
break;
|
||||
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "stfdu %H, -8(sp)", src);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
|
||||
switch (dest->attrs & TYPE_ATTRS)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "lwz %H, 0(sp)", dest);
|
||||
break;
|
||||
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "lwz %0H, 4(sp)", dest);
|
||||
hop_add_insel(hop, "lwz %1H, 0(sp)", dest);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
hop_add_insel(hop, "lfs %H, 0(sp)", dest);
|
||||
break;
|
||||
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "lfd %H, 0(sp)", dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
|
||||
switch (dest->attrs & TYPE_ATTRS)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
case burm_float_ATTR:
|
||||
hop_add_insel(hop, "addi sp, sp, 4");
|
||||
break;
|
||||
|
||||
case burm_double_ATTR:
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "addi sp, sp, 8");
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t type = src->attrs & TYPE_ATTRS;
|
||||
|
||||
if (!src->is_stacked && dest->is_stacked)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "stw %H, %S(fp) ! %H", src, dest, dest);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
hop_add_insel(hop, "stfs %H, %S(fp) ! %H", src, dest, dest);
|
||||
break;
|
||||
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "stw %0H, 4+%S(fp) ! %H", src, dest, dest);
|
||||
hop_add_insel(hop, "stw %1H, 0+%S(fp) ! %H", src, dest, dest);
|
||||
break;
|
||||
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "stfd %H, %S(fp) ! %H", src, dest, dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
}
|
||||
else if (src->is_stacked && !dest->is_stacked)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "lwz %H, %S(fp) ! %H", dest, src, src);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
hop_add_insel(hop, "lfs %H, %S(fp) ! %H", dest, src, src);
|
||||
break;
|
||||
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "lfd %H, %S(fp) ! %H", dest, src, src);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
}
|
||||
else if (!src->is_stacked && !dest->is_stacked)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "mr %H, %H", dest, src);
|
||||
break;
|
||||
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "mr %0H, %0H", dest, src);
|
||||
hop_add_insel(hop, "mr %1H, %1H", dest, src);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "fmr %H, %H", dest, src);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto nomove;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto nomove;
|
||||
}
|
||||
|
||||
return hop;
|
||||
|
||||
nomove:
|
||||
fatal("cannot move %s to %s", src->id, dest->id);
|
||||
}
|
||||
|
||||
struct hop* platform_swap(struct basicblock* bb, struct hreg* src, struct hreg* dest)
|
||||
{
|
||||
struct hop* hop = new_hop(bb, NULL);
|
||||
|
||||
assert(!src->is_stacked);
|
||||
assert(!dest->is_stacked);
|
||||
assert((src->attrs & TYPE_ATTRS) == (dest->attrs & TYPE_ATTRS));
|
||||
|
||||
switch (src->attrs & TYPE_ATTRS)
|
||||
{
|
||||
case burm_int_ATTR:
|
||||
hop_add_insel(hop, "mr r0, %H", src);
|
||||
hop_add_insel(hop, "mr %H, %H", src, dest);
|
||||
hop_add_insel(hop, "mr %H, r0", dest);
|
||||
break;
|
||||
|
||||
case burm_long_ATTR:
|
||||
hop_add_insel(hop, "mr r0, %0H", src);
|
||||
hop_add_insel(hop, "mr %0H, %0H", src, dest);
|
||||
hop_add_insel(hop, "mr %0H, r0", dest);
|
||||
|
||||
hop_add_insel(hop, "mr r0, %1H", src);
|
||||
hop_add_insel(hop, "mr %1H, %1H", src, dest);
|
||||
hop_add_insel(hop, "mr %1H, r0", dest);
|
||||
break;
|
||||
|
||||
case burm_float_ATTR:
|
||||
case burm_double_ATTR:
|
||||
hop_add_insel(hop, "fmr f0, %H", src);
|
||||
hop_add_insel(hop, "fmr %H, %H", src, dest);
|
||||
hop_add_insel(hop, "fmr %H, f0", dest);
|
||||
break;
|
||||
}
|
||||
|
||||
return hop;
|
||||
}
|
||||
|
||||
const char* platform_label(const char* label)
|
||||
{
|
||||
/* Labels starting with . are internal, not exported, and don't need mangling. */
|
||||
|
||||
if (label[0] == '.')
|
||||
return label;
|
||||
|
||||
/* Otherwise, mangle. */
|
||||
|
||||
return aprintf("_%s", label);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
794
mach/powerpc/mcg/table
Normal file
794
mach/powerpc/mcg/table
Normal file
|
@ -0,0 +1,794 @@
|
|||
REGISTERS
|
||||
|
||||
/* Registers are allocated top down; the order here is odd in order to make
|
||||
* sure that non-volatile registers get allocated from r31 (or f31) down.
|
||||
*
|
||||
* Attributes may have at most one of: int, float, long, double. These
|
||||
* indicate that the register is used to store a value of that type. If
|
||||
* your register can store more than one type, create an alias. Registers
|
||||
* with none of these cannot be copied by the code generator (and so cannot
|
||||
* be moved from register to register or spilt).
|
||||
*/
|
||||
|
||||
r12 int volatile;
|
||||
r11 int volatile;
|
||||
r10 int volatile;
|
||||
r9 int volatile;
|
||||
r8 int volatile;
|
||||
r7 int volatile;
|
||||
r6 int volatile;
|
||||
r5 int volatile;
|
||||
r4 int volatile;
|
||||
r3 int volatile iret;
|
||||
|
||||
r31 int;
|
||||
r30 int;
|
||||
r29 int;
|
||||
r28 int;
|
||||
r27 int;
|
||||
r26 int;
|
||||
r25 int;
|
||||
r24 int;
|
||||
r23 int;
|
||||
r22 int;
|
||||
r21 int;
|
||||
r20 int;
|
||||
r19 int;
|
||||
r18 int;
|
||||
r17 int;
|
||||
r16 int;
|
||||
r15 int;
|
||||
r14 int;
|
||||
r13 int;
|
||||
|
||||
r11r12 named("r11", "r12") aliases(r11, r12) long volatile;
|
||||
r9r10 named("r9", "r10") aliases(r9, r10) long volatile;
|
||||
r7r8 named("r7", "r8") aliases(r7, r8) long volatile;
|
||||
r5r6 named("r5", "r6") aliases(r6, r6) long volatile;
|
||||
r3r4 named("r3", "r4") aliases(r3, r4) long volatile lret;
|
||||
|
||||
r29r30 named("r29", "r30") aliases(r29, r30) long;
|
||||
r27r28 named("r27", "r28") aliases(r27, r28) long;
|
||||
r25r26 named("r25", "r26") aliases(r25, r26) long;
|
||||
r23r24 named("r23", "r24") aliases(r23, r24) long;
|
||||
r21r22 named("r21", "r22") aliases(r21, r22) long;
|
||||
r19r20 named("r19", "r20") aliases(r19, r20) long;
|
||||
r17r18 named("r17", "r18") aliases(r17, r18) long;
|
||||
r15r16 named("r15", "r16") aliases(r15, r16) long;
|
||||
r13r14 named("r13", "r14") aliases(r13, r14) long;
|
||||
|
||||
f14 float volatile;
|
||||
f13 float volatile;
|
||||
f12 float volatile;
|
||||
f11 float volatile;
|
||||
f10 float volatile;
|
||||
f9 float volatile;
|
||||
f8 float volatile;
|
||||
f7 float volatile;
|
||||
f6 float volatile;
|
||||
f5 float volatile;
|
||||
f4 float volatile;
|
||||
f3 float volatile fret;
|
||||
f2 float volatile;
|
||||
f1 float volatile;
|
||||
f0 float volatile;
|
||||
|
||||
f31 float;
|
||||
f30 float;
|
||||
f29 float;
|
||||
f28 float;
|
||||
f27 float;
|
||||
f26 float;
|
||||
f25 float;
|
||||
f24 float;
|
||||
f23 float;
|
||||
f22 float;
|
||||
f21 float;
|
||||
f20 float;
|
||||
f19 float;
|
||||
f18 float;
|
||||
f17 float;
|
||||
f16 float;
|
||||
f15 float;
|
||||
|
||||
d14 named("f14") aliases(f14) double volatile;
|
||||
d13 named("f13") aliases(f13) double volatile;
|
||||
d12 named("f12") aliases(f12) double volatile;
|
||||
d11 named("f11") aliases(f11) double volatile;
|
||||
d10 named("f10") aliases(f10) double volatile;
|
||||
d9 named("f9") aliases(f9) double volatile;
|
||||
d8 named("f8") aliases(f8) double volatile;
|
||||
d7 named("f7") aliases(f7) double volatile;
|
||||
d6 named("f6") aliases(f6) double volatile;
|
||||
d5 named("f5") aliases(f5) double volatile;
|
||||
d4 named("f4") aliases(f4) double volatile;
|
||||
d3 named("f3") aliases(f3) double volatile dret;
|
||||
d2 named("f2") aliases(f2) double volatile;
|
||||
d1 named("f1") aliases(f1) double volatile;
|
||||
d0 named("f0") aliases(f0) double volatile;
|
||||
|
||||
d31 named("f31") aliases(f31) double;
|
||||
d30 named("f30") aliases(f30) double;
|
||||
d29 named("f29") aliases(f29) double;
|
||||
d28 named("f28") aliases(f28) double;
|
||||
d27 named("f27") aliases(f27) double;
|
||||
d26 named("f26") aliases(f26) double;
|
||||
d25 named("f25") aliases(f25) double;
|
||||
d24 named("f24") aliases(f24) double;
|
||||
d23 named("f23") aliases(f23) double;
|
||||
d22 named("f22") aliases(f22) double;
|
||||
d21 named("f21") aliases(f21) double;
|
||||
d20 named("f20") aliases(f20) double;
|
||||
d19 named("f19") aliases(f19) double;
|
||||
d18 named("f18") aliases(f18) double;
|
||||
d17 named("f17") aliases(f17) double;
|
||||
d16 named("f16") aliases(f16) double;
|
||||
d15 named("f15") aliases(f15) double;
|
||||
|
||||
cr0 cr;
|
||||
|
||||
|
||||
|
||||
DECLARATIONS
|
||||
|
||||
cr;
|
||||
ubyteX; /* bottom 8 bits valid, the rest undefined */
|
||||
ubyte0; /* bottom 8 bits valid, the rest 0 */
|
||||
ushortX; /* bottom 16 bits valid, the rest undefined */
|
||||
ushort0; /* bottom 16 bits valid, the rest 0 */
|
||||
|
||||
address fragment;
|
||||
|
||||
|
||||
|
||||
PATTERNS
|
||||
|
||||
/* Special */
|
||||
|
||||
PAIR(BLOCK.I, BLOCK.I);
|
||||
|
||||
|
||||
|
||||
/* Miscellaneous special things */
|
||||
|
||||
PUSH.I(in:(int)reg)
|
||||
emit "stwu %in, -4(sp)"
|
||||
cost 4;
|
||||
|
||||
PUSH.L(in:(long)reg)
|
||||
emit "stwu %in.0, -4(sp)"
|
||||
emit "stwu %in.1, -4(sp)"
|
||||
cost 8;
|
||||
|
||||
PUSH.D(in:(double)reg)
|
||||
emit "stfdu %in, -8(sp)"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = POP.I
|
||||
emit "lwz %out, 0(sp)"
|
||||
emit "addi sp, sp, 4"
|
||||
cost 8;
|
||||
|
||||
out:(long)reg = POP.L
|
||||
emit "lwz %out.0, 4(sp)"
|
||||
emit "lwz %out.1, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 12;
|
||||
|
||||
out:(float)reg = POP.F
|
||||
emit "lfs %out, 0(sp)"
|
||||
emit "addi sp, sp, 4"
|
||||
cost 8;
|
||||
|
||||
out:(double)reg = POP.D
|
||||
emit "lfd %out, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 8;
|
||||
|
||||
SETRET.I(in:(iret)reg)
|
||||
emit "! setret4"
|
||||
cost 1;
|
||||
|
||||
SETRET.L(in:(lret)reg)
|
||||
emit "! setret8"
|
||||
cost 1;
|
||||
|
||||
STACKADJUST.I(delta:CONST.I)
|
||||
when signed_constant(%delta, 16)
|
||||
emit "addi sp, sp, $delta"
|
||||
cost 4;
|
||||
|
||||
STACKADJUST.I(in:(int)reg)
|
||||
emit "add sp, sp, %in"
|
||||
cost 4;
|
||||
|
||||
STACKADJUST.I(NEG.I(in:(int)reg))
|
||||
emit "subf sp, %in, sp"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = GETFP.I
|
||||
emit "mr %out, fp"
|
||||
cost 4;
|
||||
|
||||
SETFP.I(in:(int)reg)
|
||||
emit "mr fp, %in"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = CHAINFP.I(in:(int)reg)
|
||||
emit "lwz %out, 0(%in)"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FPTOAB.I(GETFP.I)
|
||||
emit "addi %out, fp, 8"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FPTOAB.I(in:(int)reg)
|
||||
emit "addi %out, %in, 8"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FPTOLB.I(in:(int)reg)
|
||||
with %out == %in
|
||||
cost 1;
|
||||
|
||||
out:(int)reg = GETSP.I
|
||||
emit "mr %out, sp"
|
||||
cost 4;
|
||||
|
||||
SETSP.I(in:(int)reg)
|
||||
emit "mr sp, %in"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = ANY.I
|
||||
cost 1;
|
||||
|
||||
out:(int)reg = COPYF.I(in:(float)reg)
|
||||
emit "stfsu %in, -4(sp)"
|
||||
emit "lwz %out, 0(sp)"
|
||||
emit "addi sp, sp, 4"
|
||||
cost 12;
|
||||
|
||||
out:(double)reg = COPYL.D(in:(long)reg)
|
||||
emit "stwu %in.0, -4(sp)"
|
||||
emit "stwu %in.1, -4(sp)"
|
||||
emit "lfd %out, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 16;
|
||||
|
||||
out:(long)reg = COPYD.L(in:(double)reg)
|
||||
emit "stfdu %in, -8(sp)"
|
||||
emit "lwz %out.0, 4(sp)"
|
||||
emit "lwz %out.1, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 16;
|
||||
|
||||
|
||||
|
||||
/* Memory operations */
|
||||
|
||||
/* Stores */
|
||||
|
||||
STORE.D(addr:address, value:(double)reg)
|
||||
emit "stfd %value, %addr"
|
||||
cost 4;
|
||||
|
||||
STORE.L(addr:address, value:(long)reg)
|
||||
emit "stw %value.0, 4+%addr"
|
||||
emit "stw %value.1, 0+%addr"
|
||||
cost 8;
|
||||
|
||||
STORE.I(addr:address, value:(int)reg)
|
||||
emit "stw %value, %addr"
|
||||
cost 4;
|
||||
|
||||
STOREH.I(addr:address, value:(int)ushortX)
|
||||
emit "sth %value, %addr"
|
||||
cost 4;
|
||||
|
||||
STOREH.I(ADD.I(left:(int)reg, right:(int)reg), value:(int)ushortX)
|
||||
emit "sthx %value, %left, %right"
|
||||
cost 4;
|
||||
|
||||
STOREB.I(addr:address, value:(int)ushortX)
|
||||
emit "sth %value, %addr"
|
||||
cost 4;
|
||||
|
||||
STOREB.I(addr:address, value:(int)ubyteX)
|
||||
emit "stb %value, %addr"
|
||||
cost 4;
|
||||
|
||||
STOREB.I(ADD.I(left:(int)reg, right:(int)reg), value:(int)ubyteX)
|
||||
emit "stbx %value, %left, %right"
|
||||
cost 4;
|
||||
|
||||
/* Loads */
|
||||
|
||||
out:(int)reg = LOAD.I(addr:address)
|
||||
emit "lwz %out, %addr"
|
||||
cost 4;
|
||||
|
||||
out:(long)reg = LOAD.L(addr:address)
|
||||
emit "lwz %out.0, 4+%addr"
|
||||
emit "lwz %out.1, 0+%addr"
|
||||
cost 8;
|
||||
|
||||
out:(int)ushort0 = LOADH.I(addr:address)
|
||||
emit "lhz %out, %addr"
|
||||
cost 4;
|
||||
|
||||
out:(int)ubyte0 = LOADB.I(addr:address)
|
||||
emit "lbz %out, %addr"
|
||||
cost 4;
|
||||
|
||||
/* ubyte intrinsics */
|
||||
|
||||
out:(int)ubyteX = in:(int)ubyte0
|
||||
with %out == %in
|
||||
emit "! ubyte0 -> ubyteX"
|
||||
cost 1;
|
||||
|
||||
out:(int)ubyte0 = in:(int)ubyteX
|
||||
emit "andi %out, %in, 0xff ! ubyteX -> ubyte0"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = in:(int)ubyte0
|
||||
with %out == %in
|
||||
emit "! ubyte0 -> reg"
|
||||
cost 4;
|
||||
|
||||
out:(int)ubyteX = in:(int)reg
|
||||
with %out == %in
|
||||
emit "! reg -> ubyteX"
|
||||
cost 1;
|
||||
|
||||
/* ushort intrinsics */
|
||||
|
||||
out:(int)ushortX = in:(int)ushort0
|
||||
with %out == %in
|
||||
emit "! ushort0 -> ushortX"
|
||||
cost 1;
|
||||
|
||||
out:(int)ushort0 = in:(int)ushortX
|
||||
emit "andi %out, %in, 0xff ! ushortX -> ushort0"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = in:(int)ushort0
|
||||
with %out == %in
|
||||
emit "! ushort0 -> reg"
|
||||
cost 4;
|
||||
|
||||
out:(int)ushortX = in:(int)reg
|
||||
with %out == %in
|
||||
emit "! reg -> ushortX"
|
||||
cost 1;
|
||||
|
||||
|
||||
/* Extensions and conversions */
|
||||
|
||||
out:(int)reg = EXTENDB.I(in:(int)reg)
|
||||
emit "extsb %out, %in"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = EXTENDH.I(in:(int)reg)
|
||||
emit "extsh %out, %in"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FROMSI.I(in:(int)reg)
|
||||
with %out == %in
|
||||
emit "! FROMSI.I(int) -> int"
|
||||
cost 1;
|
||||
|
||||
out:(int)reg = FROMUI.I(in:(int)reg)
|
||||
with %out == %in
|
||||
emit "! FROMUI.I(int) -> int"
|
||||
cost 1;
|
||||
|
||||
out:(long)reg = FROMSI.L(in:(int)reg)
|
||||
emit "mr %out.0, %in"
|
||||
emit "srawi %out.1, %out.0, 31"
|
||||
cost 8;
|
||||
|
||||
out:(long)reg = FROMUI.L(in:(int)reg)
|
||||
emit "mr %out.0, %in"
|
||||
emit "li32 %out.1, 0"
|
||||
cost 8;
|
||||
|
||||
out:(iret)reg = FROMSF.I(in:(dret)reg)
|
||||
with corrupted(volatile)
|
||||
emit "bl .fromf2i"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FROMSD.I(in:(double)reg)
|
||||
with preserved(%in)
|
||||
emit "fctiwz %in, %in"
|
||||
emit "stfdu %in, -8(sp)"
|
||||
emit "lwz %out, 4(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 16;
|
||||
|
||||
out:(int)reg = FROMUD.I(in:(double)reg)
|
||||
with corrupted(volatile)
|
||||
emit "stfdu %in, -8(sp)"
|
||||
emit "bl .cfu8"
|
||||
emit "lwz %out, 0(sp)"
|
||||
emit "addi sp, sp, 4"
|
||||
cost 16;
|
||||
|
||||
out:(lret)reg = FROMSF.L(in:(fret)reg)
|
||||
with corrupted(volatile)
|
||||
emit "bl .fromf2l"
|
||||
cost 4;
|
||||
|
||||
out:(lret)reg = FROMUF.I(in:(fret)reg)
|
||||
with corrupted(volatile)
|
||||
emit "bl .fromf2l"
|
||||
cost 4;
|
||||
|
||||
out:(double)reg = FROMSI.D(in:(int)reg)
|
||||
with corrupted(volatile)
|
||||
emit "stwu %in, -4(sp)"
|
||||
emit "bl .cif8"
|
||||
emit "lfd %out, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 4;
|
||||
|
||||
out:(fret)reg = FROMUI.F(in:(iret)reg)
|
||||
with corrupted(volatile)
|
||||
emit "bl .fromui2f"
|
||||
cost 4;
|
||||
|
||||
out:(double)reg = FROMUI.D(in:(int)reg)
|
||||
with corrupted(volatile)
|
||||
emit "stwu %in, -4(sp)"
|
||||
emit "bl .cuf8"
|
||||
emit "lfd %out, 0(sp)"
|
||||
emit "addi sp, sp, 8"
|
||||
cost 4;
|
||||
|
||||
out:(lret)reg = FROMIPAIR.L(in1:(int)reg, in2:(int)reg)
|
||||
emit "mr %out.0, %in1"
|
||||
emit "mr %out.1, %in2"
|
||||
cost 8;
|
||||
|
||||
out:(int)reg = FROML0.I(in:(long)reg)
|
||||
emit "mr %out, %in.0"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = FROML1.I(in:(long)reg)
|
||||
emit "mr %out, %in.1"
|
||||
cost 4;
|
||||
|
||||
|
||||
|
||||
/* Locals */
|
||||
|
||||
out:(int)reg = in:LOCAL.I
|
||||
emit "addi %out, fp, $in"
|
||||
cost 4;
|
||||
|
||||
address = in:LOCAL.I
|
||||
emit "$in(fp)";
|
||||
|
||||
|
||||
|
||||
|
||||
/* Memory addressing modes */
|
||||
|
||||
address = ADD.I(addr:(int)reg, offset:CONST.I)
|
||||
when signed_constant(%offset, 16)
|
||||
emit "$offset(%addr)";
|
||||
|
||||
address = addr:(int)reg
|
||||
emit "0(%addr)";
|
||||
|
||||
|
||||
|
||||
/* Branches */
|
||||
|
||||
JUMP(addr:BLOCK.I)
|
||||
emit "b $addr"
|
||||
cost 4;
|
||||
|
||||
FARJUMP(addr:LABEL.I)
|
||||
with corrupted(volatile)
|
||||
emit "b $addr"
|
||||
cost 4;
|
||||
|
||||
JUMP(dest:(int)reg)
|
||||
emit "mtspr ctr, %dest"
|
||||
emit "bcctrl 20, 0, 0"
|
||||
cost 8;
|
||||
|
||||
CJUMPEQ(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
|
||||
emit "bc 12, 2, $true" /* IFTRUE EQ */
|
||||
emit "b $false"
|
||||
cost 8;
|
||||
|
||||
CJUMPLE(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
|
||||
emit "bc 4, 1, $true" /* IFFALSE GT */
|
||||
emit "b $false"
|
||||
cost 8;
|
||||
|
||||
CJUMPLT(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
|
||||
emit "bc 12, 0, $true" /* IFTRUE LT */
|
||||
emit "b $false"
|
||||
cost 8;
|
||||
|
||||
#define CALLLABEL(insn) \
|
||||
insn (dest:LABEL.I) \
|
||||
with corrupted(volatile) \
|
||||
emit "bl $dest" \
|
||||
cost 4;
|
||||
|
||||
CALLLABEL(CALL)
|
||||
out:(iret)reg = CALLLABEL(CALL.I)
|
||||
out:(lret)reg = CALLLABEL(CALL.L)
|
||||
|
||||
#define CALLINDIRECT(insn) \
|
||||
insn (dest:(int)reg) \
|
||||
with corrupted(volatile) \
|
||||
emit "mtspr ctr, %dest" \
|
||||
emit "bcctrl 20, 0, 0" \
|
||||
cost 8;
|
||||
|
||||
CALLINDIRECT(CALL)
|
||||
out:(iret)reg = CALLINDIRECT(CALL.I)
|
||||
out:(lret)reg = CALLINDIRECT(CALL.L)
|
||||
|
||||
JUMP(dest:LABEL.I)
|
||||
emit "b $dest"
|
||||
cost 4;
|
||||
|
||||
|
||||
|
||||
/* Comparisons */
|
||||
|
||||
cr:(cr)cr = COMPARESI.I(left:(int)reg, right:(int)reg)
|
||||
emit "cmp %cr, 0, %left, %right"
|
||||
cost 4;
|
||||
|
||||
cr:(cr)cr = COMPARESI.I(left:(int)reg, right:CONST.I)
|
||||
when signed_constant(%right, 16)
|
||||
emit "cmpi %cr, 0, %left, $right"
|
||||
cost 4;
|
||||
|
||||
cr:(cr)cr = COMPAREUI.I(left:(int)reg, right:(int)reg)
|
||||
emit "cmpl %cr, 0, %left, %right"
|
||||
cost 4;
|
||||
|
||||
cr:(cr)cr = COMPAREUI.I(left:(int)reg, right:CONST.I)
|
||||
when signed_constant(%right, 16)
|
||||
emit "cmpli %cr, 0, %left, $right"
|
||||
cost 4;
|
||||
|
||||
out:(cr)cr = COMPARESI.I(in:(cr)cr, result:CONST.I)
|
||||
when specific_constant(%result, 0)
|
||||
with %out == %in
|
||||
emit "! COMPARESI.I(cr, 0)"
|
||||
cost 4;
|
||||
|
||||
|
||||
|
||||
/* Booleans */
|
||||
|
||||
out:(int)reg = IFEQ.I(in:(cr)cr)
|
||||
emit "mfcr %out" /* get cr0 */
|
||||
emit "rlwinm %out, %out, [32-2], 2, 31" /* extract just EQ */
|
||||
cost 8;
|
||||
|
||||
out:(int)reg = IFEQ.I(in:(int)reg)
|
||||
emit "cntlzw %out, %in" /* returns 0..32 */
|
||||
emit "rlwinm %out, %out, [32-5], 5, 31" /* if 32, return 1, otherwise 0 */
|
||||
cost 8;
|
||||
|
||||
out:(int)reg = IFLT.I(in:(cr)cr)
|
||||
emit "mfcr %out" /* get cr0 */
|
||||
emit "andi. %out, %out, 1" /* leave just LT */
|
||||
cost 8;
|
||||
|
||||
out:(int)reg = IFLE.I(in:(cr)cr)
|
||||
emit "mfcr %out" /* get cr0 */
|
||||
emit "andi. %out, %out, 5" /* leave just LT and EQ */
|
||||
emit "cntlzw %out, %out" /* returns 0..32 */
|
||||
emit "rlwinm %out, %out, [32-5], 5, 31" /* if 32, return 1, otherwise 0 */
|
||||
emit "xori %out, %out, 1" /* negate */
|
||||
cost 8;
|
||||
|
||||
|
||||
|
||||
/* Conversions */
|
||||
|
||||
#if 0
|
||||
out:(int)reg = CIU44(in:(int)reg)
|
||||
with %out == %in
|
||||
emit "! ciu44"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = CUI44(in:(int)reg)
|
||||
with %out == %in
|
||||
emit "! cui44"
|
||||
cost 4;
|
||||
#endif
|
||||
|
||||
/* ALU operations */
|
||||
|
||||
#define ALUR(name, instr) \
|
||||
out:(int)reg = name(left:(int)reg, right:(int)reg) \
|
||||
emit instr " %out, %left, %right" \
|
||||
cost 4; \
|
||||
|
||||
#define ALUC(name, instr) \
|
||||
out:(int)reg = name(left:(int)reg, right:CONST.I) \
|
||||
when signed_constant(%right, 16) \
|
||||
emit instr " %out, %left, $right" \
|
||||
cost 4; \
|
||||
|
||||
#define ALUC_reversed(name, instr) \
|
||||
out:(int)reg = name(left:CONST.I, right:(int)reg) \
|
||||
when signed_constant(%left, 16) \
|
||||
emit instr " %out, %right, $left" \
|
||||
cost 4; \
|
||||
|
||||
#define ALUCC(name, instr) \
|
||||
ALUC(name, instr) \
|
||||
ALUC_reversed(name, instr)
|
||||
|
||||
ALUR(ADD.I, "add")
|
||||
ALUCC(ADD.I, "addi")
|
||||
|
||||
out:(int)reg = SUB.I(left:(int)reg, right:(int)reg)
|
||||
emit "subf %out, %right, %left"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = SUB.I(left:(int)reg, right:CONST.I)
|
||||
emit "addi %out, %left, -[$right]"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = MOD.I(left:(int)reg, right:(int)reg)
|
||||
with preserved(%left), preserved(%right)
|
||||
emit "divw %out, %left, %right"
|
||||
emit "mullw %out, %out, %right"
|
||||
emit "subf %out, %out, %left"
|
||||
cost 12;
|
||||
|
||||
out:(int)reg = MODU.I(left:(int)reg, right:(int)reg)
|
||||
with preserved(%left), preserved(%right)
|
||||
emit "divwu %out, %left, %right"
|
||||
emit "mullw %out, %out, %right"
|
||||
emit "subf %out, %out, %left"
|
||||
cost 12;
|
||||
|
||||
ALUR(MUL.I, "mullw")
|
||||
ALUCC(MUL.I, "mulli")
|
||||
|
||||
ALUR(DIV.I, "divw")
|
||||
ALUR(DIVU.I, "divwu")
|
||||
|
||||
ALUR(ASL.I, "slw")
|
||||
ALUR(ASR.I, "sraw")
|
||||
|
||||
ALUR(LSL.I, "slw")
|
||||
ALUR(LSR.I, "srw")
|
||||
|
||||
out:(int)reg = NEG.I(left:(int)reg)
|
||||
emit "neg %out, %left"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = NOT.I(left:(int)reg)
|
||||
emit "cntlzw %out, %left"
|
||||
emit "rlwinm %out, %out, 32-5, 5, 31"
|
||||
cost 8;
|
||||
|
||||
ALUR(AND.I, "and")
|
||||
ALUCC(AND.I, "andi.")
|
||||
|
||||
ALUR(OR.I, "or")
|
||||
ALUCC(OR.I, "ori")
|
||||
|
||||
ALUR(EOR.I, "xor")
|
||||
ALUCC(EOR.I, "xori")
|
||||
|
||||
out:(int)reg = value:LABEL.I
|
||||
emit "li32 %out, $value"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = value:BLOCK.I
|
||||
emit "li32 %out, $value"
|
||||
cost 4;
|
||||
|
||||
out:(int)reg = value:CONST.I
|
||||
emit "li32 %out, $value"
|
||||
cost 8;
|
||||
|
||||
|
||||
/* FPU operations */
|
||||
|
||||
#define FPU4R(name, instr) \
|
||||
out:(float)reg = name(left:(float)reg, right:(float)reg) \
|
||||
emit instr " %out, %left, %right" \
|
||||
cost 4; \
|
||||
|
||||
#define FPU8R(name, instr) \
|
||||
out:(double)reg = name(left:(double)reg, right:(double)reg) \
|
||||
emit instr " %out, %left, %right" \
|
||||
cost 4; \
|
||||
|
||||
out:(float)reg = LOAD.F(addr:address)
|
||||
emit "lfs %out, %addr"
|
||||
cost 4;
|
||||
|
||||
out:(double)reg = LOAD.D(addr:address)
|
||||
emit "lfd %out, %addr"
|
||||
cost 4;
|
||||
|
||||
out:(float)reg = in:CONST.F
|
||||
when specific_constant(%in, 0)
|
||||
emit "li32 r0, .fd_00000000"
|
||||
emit "lfs %out, 0(r0)"
|
||||
cost 12;
|
||||
|
||||
FPU4R(ADDF.F, "fadds")
|
||||
FPU8R(ADDF.D, "fadd")
|
||||
|
||||
FPU4R(SUBF.F, "fsubs")
|
||||
FPU8R(SUBF.D, "fsub")
|
||||
|
||||
FPU4R(MULF.F, "fmuls")
|
||||
FPU8R(MULF.D, "fmul")
|
||||
|
||||
FPU4R(DIVF.F, "fdivs")
|
||||
FPU8R(DIVF.D, "fdiv")
|
||||
|
||||
#define FMALEFT(type, insn, add, mul) \
|
||||
out:(type)reg = add(mul(m1:(type)reg, m2:(type)reg), m3:(type)reg) \
|
||||
emit insn " %out, %m1, %m2, %m3" \
|
||||
cost 4; \
|
||||
|
||||
#define FMARIGHT(type, insn, add, mul) \
|
||||
out:(type)reg = add(m3:(type)reg, mul(m1:(type)reg, m2:(type)reg)) \
|
||||
emit insn " %out, %m1, %m2, %m3" \
|
||||
cost 4; \
|
||||
|
||||
FMALEFT( double, "fmadd", ADDF.D, MULF.D)
|
||||
FMARIGHT(double, "fmadd", ADDF.D, MULF.D)
|
||||
FMALEFT( float, "fmadds", ADDF.F, MULF.F)
|
||||
FMARIGHT(float, "fmadds", ADDF.F, MULF.F)
|
||||
|
||||
FMALEFT( double, "fmsub", SUBF.D, MULF.D)
|
||||
FMALEFT( float, "fmsubs", SUBF.F, MULF.F)
|
||||
|
||||
FMARIGHT(double, "fnmadd", SUBF.D, MULF.D)
|
||||
FMARIGHT(float, "fnmadds", SUBF.F, MULF.F)
|
||||
|
||||
#define FMANEGLEFT(type, insn, neg, add, mul) \
|
||||
out:(type)reg = neg(add(mul(m1:(type)reg, m2:(type)reg), m3:(type)reg)) \
|
||||
emit insn " %out, %m1, %m2, %m3" \
|
||||
cost 4; \
|
||||
|
||||
#define FMANEGRIGHT(type, insn, neg, add, mul) \
|
||||
out:(type)reg = neg(add(m3:(type)reg, mul(m1:(type)reg, m2:(type)reg))) \
|
||||
emit insn " %out, %m1, %m2, %m3" \
|
||||
cost 4; \
|
||||
|
||||
FMANEGLEFT( double, "fnmsub", NEGF.D, ADDF.D, MULF.D)
|
||||
FMANEGRIGHT(double, "fnmsub", NEGF.D, ADDF.D, MULF.D)
|
||||
FMANEGLEFT( float, "fnmsub", NEGF.F, ADDF.F, MULF.F)
|
||||
FMANEGRIGHT(float, "fnmsub", NEGF.F, ADDF.F, MULF.F)
|
||||
|
||||
out:(float)reg = NEGF.F(left:(float)reg)
|
||||
emit "fneg %out, %left"
|
||||
cost 4;
|
||||
|
||||
out:(double)reg = NEGF.D(left:(double)reg)
|
||||
emit "fneg %out, %left"
|
||||
cost 4;
|
||||
|
||||
cr:(cr)cr = COMPAREF.I(left:(float)reg, right:(float)reg)
|
||||
emit "fcmpu %cr, %left, %right"
|
||||
cost 4;
|
||||
|
||||
cr:(cr)cr = COMPARED.I(left:(double)reg, right:(double)reg)
|
||||
emit "fcmpu %cr, %left, %right"
|
||||
cost 4;
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
|
@ -328,6 +328,7 @@ INSTRUCTIONS
|
|||
lhax GPR:wo, GPR:ro, GPR:ro cost(4, 3).
|
||||
lhz GPR:wo, GPRINDIRECT:ro cost(4, 3).
|
||||
lhzx GPR:wo, GPR:ro, GPR:ro cost(4, 3).
|
||||
li32 GPR:wo, CONST:ro cost(8, 2).
|
||||
li32 GPR:wo, LABEL:ro cost(8, 2).
|
||||
lwzu GPR:wo, GPRINDIRECT:ro cost(4, 3).
|
||||
lwzx GPR:wo, GPR:ro, GPR:ro cost(4, 3).
|
||||
|
@ -1637,14 +1638,13 @@ PATTERNS
|
|||
gen
|
||||
bl {LABEL, ".set"}
|
||||
|
||||
pat inn defined($1) /* Test for set bit */
|
||||
leaving
|
||||
set INT32
|
||||
and INT32
|
||||
|
||||
pat inn !defined($1) /* Test for set bit (variable) */
|
||||
with GPR3 STACK
|
||||
pat inn /* Test for set bit */
|
||||
with STACK
|
||||
kills ALL
|
||||
uses REG
|
||||
gen
|
||||
li32 %a, {CONST, $1}
|
||||
stwu %a, {GPRINDIRECT, SP, 0-4}
|
||||
bl {LABEL, ".inn"}
|
||||
|
||||
|
||||
|
@ -1898,12 +1898,12 @@ PATTERNS
|
|||
addi SP, SP, {CONST, 12}
|
||||
|
||||
pat csa /* Array-lookup switch */
|
||||
with GPR3 GPR4 STACK
|
||||
with STACK
|
||||
gen
|
||||
b {LABEL, ".csa"}
|
||||
|
||||
pat csb /* Table-lookup switch */
|
||||
with GPR3 GPR4 STACK
|
||||
with STACK
|
||||
gen
|
||||
b {LABEL, ".csb"}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
/* 68020 desciptor table for ACK target optimizer */
|
||||
/* PowerPC desciptor table for ACK target optimizer */
|
||||
|
||||
MAXOP 3;
|
||||
LABEL_STARTER '.';
|
||||
|
||||
%%;
|
||||
|
||||
|
@ -15,6 +16,14 @@ X, Y, Z { TRUE };
|
|||
addi X, X, 0 -> ;
|
||||
addis X, X, 0 -> ;
|
||||
|
||||
mr X, X -> ;
|
||||
fmr X, X -> ;
|
||||
|
||||
or X, Y, Z : or. X, X, X -> or. X, Y, Z ;
|
||||
|
||||
b X : labdef X -> labdef X ;
|
||||
|
||||
/* IFFALSE=4, IFTRUE=12, ALWAYS=20 */
|
||||
/* LT=0, GT=1, EQ=2, OV=3 */
|
||||
|
||||
%%;
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* All preprocessor based options/constants/functions
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ========== ON/OFF options (use #define in mach0.c) ========== */
|
||||
|
||||
/*
|
||||
|
|
42
mach/proto/mcg/basicblock.c
Normal file
42
mach/proto/mcg/basicblock.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static void init_idf();
|
||||
static struct idf* str2idf(char* tg, int cp);
|
||||
|
||||
#define IDF_TYPE struct basicblock*
|
||||
#define IDF_NAME block
|
||||
#include <idf_pkg.spec>
|
||||
#include <idf_pkg.body>
|
||||
|
||||
static int next_id = 0;
|
||||
|
||||
void bb_init(void)
|
||||
{
|
||||
init_idf();
|
||||
}
|
||||
|
||||
struct basicblock* bb_get(const char* name)
|
||||
{
|
||||
struct idf* p;
|
||||
|
||||
if (!name)
|
||||
name = aprintf(".anon_bb_%d", next_id++);
|
||||
p = str2idf((char*) name, 0);
|
||||
if (!p->block)
|
||||
{
|
||||
p->block = calloc(1, sizeof(*p->block));
|
||||
p->block->name = name;
|
||||
}
|
||||
return p->block;
|
||||
}
|
||||
|
||||
void bb_alias(struct basicblock* block, const char* name)
|
||||
{
|
||||
struct idf* p = str2idf((char*) name, -1);
|
||||
assert(p == NULL);
|
||||
|
||||
p = str2idf((char*) name, 0);
|
||||
p->block = block;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
43
mach/proto/mcg/basicblock.h
Normal file
43
mach/proto/mcg/basicblock.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef BASICBLOCK_H
|
||||
#define BASICBLOCK_H
|
||||
|
||||
struct phi
|
||||
{
|
||||
struct basicblock* prev; /* Predecessor that this phi is referring to */
|
||||
struct ir* ir; /* IR of variable definition */
|
||||
};
|
||||
|
||||
struct basicblock
|
||||
{
|
||||
const char* name;
|
||||
ARRAYOF(struct em) ems;
|
||||
ARRAYOF(struct ir) irs;
|
||||
ARRAYOF(struct hop) hops;
|
||||
|
||||
ARRAYOF(struct basicblock) prevs;
|
||||
ARRAYOF(struct basicblock) nexts;
|
||||
int order; /* used by dominance graph code */
|
||||
|
||||
PMAPOF(struct vreg, struct phi) phis;
|
||||
|
||||
/* Used by liveness calculation. */
|
||||
ARRAYOF(struct vreg) liveins;
|
||||
ARRAYOF(struct vreg) liveouts;
|
||||
|
||||
/* Register assignments on entry and exit. */
|
||||
register_assignment_t regsin;
|
||||
register_assignment_t* regsout; /* points at regsout of the last insn. */
|
||||
|
||||
bool is_fake : 1;
|
||||
bool is_root : 1;
|
||||
bool is_terminated : 1;
|
||||
};
|
||||
|
||||
extern void bb_init(void);
|
||||
extern struct basicblock* bb_get(const char* name);
|
||||
extern void bb_alias(struct basicblock* block, const char* name);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
51
mach/proto/mcg/build.lua
Normal file
51
mach/proto/mcg/build.lua
Normal file
|
@ -0,0 +1,51 @@
|
|||
include("util/mcgg/build.lua")
|
||||
|
||||
definerule("build_mcg",
|
||||
{
|
||||
arch = { type="string" }
|
||||
},
|
||||
function(e)
|
||||
-- Remember this is executed from the caller's directory; local
|
||||
-- target names will resolve there
|
||||
local headers = clibrary {
|
||||
name = e.name.."/headers",
|
||||
srcs = {},
|
||||
hdrs = {
|
||||
"mach/proto/mcg/*.h",
|
||||
"mach/"..e.arch.."/mcg/*.h",
|
||||
}
|
||||
}
|
||||
|
||||
local tables = mcgg {
|
||||
name = e.name.."/tables",
|
||||
srcs = { "mach/"..e.arch.."/mcg/table" }
|
||||
}
|
||||
|
||||
return cprogram {
|
||||
name = e.name,
|
||||
srcs = {
|
||||
"mach/proto/mcg/*.c",
|
||||
"mach/"..e.arch.."/mcg/platform.c",
|
||||
matching(filenamesof(tables), "%.c$")
|
||||
},
|
||||
deps = {
|
||||
"h+emheaders",
|
||||
"modules+headers",
|
||||
"modules/src/alloc+lib",
|
||||
"modules/src/data+lib",
|
||||
"modules/src/em_code+lib_k",
|
||||
"modules/src/em_data+lib",
|
||||
"modules/src/flt_arith+lib",
|
||||
"modules/src/idf+lib",
|
||||
"modules/src/object+lib",
|
||||
"modules/src/read_em+lib_kv",
|
||||
"modules/src/string+lib",
|
||||
"modules/src/system+lib",
|
||||
"util/mcgg+lib",
|
||||
headers,
|
||||
tables, -- for .h file
|
||||
}
|
||||
}
|
||||
end
|
||||
)
|
||||
|
160
mach/proto/mcg/data.c
Normal file
160
mach/proto/mcg/data.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
#include "mcg.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#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"
|
||||
|
||||
static struct symbol* pending;
|
||||
|
||||
void data_label(const char* label)
|
||||
{
|
||||
if (pending)
|
||||
fatal("two consecutive data labels ('%s' and '%s')",
|
||||
pending->name, label);
|
||||
|
||||
pending = symbol_get(label);
|
||||
if (pending->is_defined)
|
||||
fatal("label '%s' defined twice", pending->name);
|
||||
pending->is_defined = true;
|
||||
}
|
||||
|
||||
static const char* section_to_str(int section)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_ROM: return ".rom";
|
||||
case SECTION_DATA: return ".data";
|
||||
case SECTION_BSS: return ".bss";
|
||||
case SECTION_TEXT: return ".text";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_header(int desired_section)
|
||||
{
|
||||
if (pending)
|
||||
{
|
||||
if (pending->section == SECTION_UNKNOWN)
|
||||
pending->section = desired_section;
|
||||
else if (pending->section != desired_section)
|
||||
fatal("label '%s' can't change sections", pending->name);
|
||||
|
||||
fprintf(outputfile, "\n.sect %s\n", section_to_str(pending->section));
|
||||
fprintf(outputfile, "%s:\n", platform_label(pending->name));
|
||||
pending = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void writehex(arith data, int size)
|
||||
{
|
||||
if (data < 0)
|
||||
fprintf(outputfile, "-0x%0*lx", size*2, -data);
|
||||
else
|
||||
fprintf(outputfile, "0x%0*lx", size*2, data);
|
||||
}
|
||||
|
||||
void data_int(arith data, size_t size, bool is_ro)
|
||||
{
|
||||
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
|
||||
assert((size == 1) || (size == 2) || (size == 4) || (size == 8));
|
||||
fprintf(outputfile, "\t.data%d ", size);
|
||||
writehex(data, size);
|
||||
fprintf(outputfile, "\n");
|
||||
}
|
||||
|
||||
|
||||
void data_float(const char* data, size_t size, bool is_ro)
|
||||
{
|
||||
unsigned char buffer[8];
|
||||
int i;
|
||||
|
||||
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
|
||||
assert((size == 4) || (size == 8));
|
||||
|
||||
i = float_cst(data, size, (char*) buffer);
|
||||
if ((i != 0) && (i != 2)) /* 2 == overflow */
|
||||
fatal("cannot parse floating point constant %s sz %d", data, size);
|
||||
|
||||
fprintf(outputfile, "\t!float %s sz %d\n", data, size);
|
||||
fprintf(outputfile, "\t.data1 ");
|
||||
writehex(buffer[0], 1);
|
||||
for (i=1; i<size; i++)
|
||||
{
|
||||
fprintf(outputfile, ", ");
|
||||
writehex(buffer[i], 1);
|
||||
}
|
||||
fprintf(outputfile, "\n");
|
||||
}
|
||||
|
||||
static bool istext(c)
|
||||
{
|
||||
return isprint(c) && (c != '"');
|
||||
}
|
||||
|
||||
void data_block(const uint8_t* data, size_t size, bool is_ro)
|
||||
{
|
||||
const uint8_t* start = data;
|
||||
const uint8_t* end = data + size;
|
||||
const uint8_t* p = data;
|
||||
|
||||
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
|
||||
|
||||
start = p = data;
|
||||
while (p < end)
|
||||
{
|
||||
while ((p < end) && istext(*p))
|
||||
p++;
|
||||
|
||||
if (start < p)
|
||||
{
|
||||
fprintf(outputfile, "\t.ascii \"");
|
||||
while (start < p)
|
||||
{
|
||||
fprintf(outputfile, "%c", *start);
|
||||
start++;
|
||||
}
|
||||
fprintf(outputfile, "\"\n");
|
||||
}
|
||||
|
||||
while ((p < end) && !istext(*p))
|
||||
p++;
|
||||
|
||||
if (start < p)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
fprintf(outputfile, "\t.data1 ");
|
||||
while (start < p)
|
||||
{
|
||||
if (!first)
|
||||
fprintf(outputfile, ", ");
|
||||
writehex(*start, 1);
|
||||
start++;
|
||||
first = false;
|
||||
}
|
||||
fprintf(outputfile, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void data_offset(const char* label, arith offset, bool is_ro)
|
||||
{
|
||||
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
|
||||
fprintf(outputfile, "\t.data%d %s+%lld\n",
|
||||
EM_pointersize, platform_label(label), offset);
|
||||
}
|
||||
|
||||
void data_bss(arith size, int init)
|
||||
{
|
||||
if (init != 0)
|
||||
fatal("non-zero-initialised bss not supported");
|
||||
|
||||
emit_header(SECTION_BSS);
|
||||
fprintf(outputfile, "\t.space %lld\n", size);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
210
mach/proto/mcg/graph.c
Normal file
210
mach/proto/mcg/graph.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
#include "mcg.h"
|
||||
|
||||
struct graph_data cfg;
|
||||
struct dominance_data dominance;
|
||||
|
||||
static ARRAYOF(struct basicblock) pending;
|
||||
|
||||
static bool collect_outputs_cb(struct ir* ir, void* user)
|
||||
{
|
||||
struct basicblock* caller = user;
|
||||
|
||||
if (ir->opcode == IR_BLOCK)
|
||||
{
|
||||
array_appendu(&caller->nexts, ir->u.bvalue);
|
||||
array_appendu(&ir->u.bvalue->prevs, caller);
|
||||
pmap_add(&cfg.graph, caller, ir->u.bvalue);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void update_block_pointers_from_ir(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
bb->prevs.count = bb->nexts.count = 0;
|
||||
}
|
||||
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
ir_walk(bb->irs.item[j], collect_outputs_cb, bb);
|
||||
}
|
||||
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
|
||||
for (j=0; j<bb->nexts.count; j++)
|
||||
{
|
||||
tracef('D', "D: cfg graph %s -> %s\n",
|
||||
bb->name,
|
||||
bb->nexts.item[j]->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void recursively_walk_cfg_graph(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (array_contains(&cfg.postorder, bb) || array_contains(&pending, bb))
|
||||
return;
|
||||
|
||||
array_appendu(&cfg.preorder, bb);
|
||||
array_appendu(&pending, bb);
|
||||
|
||||
for (i=0; i<bb->nexts.count; i++)
|
||||
recursively_walk_cfg_graph(bb->nexts.item[i]);
|
||||
|
||||
array_remove(&pending, bb);
|
||||
bb->order = cfg.postorder.count;
|
||||
array_appendu(&cfg.postorder, bb);
|
||||
}
|
||||
|
||||
static void walk_cfg_graph(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
cfg.preorder.count = 0;
|
||||
cfg.postorder.count = 0;
|
||||
pending.count = 0;
|
||||
recursively_walk_cfg_graph(cfg.entry);
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
tracef('G', "G: cfg preorder: %s\n",
|
||||
cfg.preorder.item[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
static struct basicblock* intersect(struct basicblock* p1, struct basicblock* p2)
|
||||
{
|
||||
while (p1 != p2)
|
||||
{
|
||||
while (p1->order < p2->order)
|
||||
p1 = pmap_findleft(&dominance.graph, p1);
|
||||
|
||||
while (p2->order < p1->order)
|
||||
p2 = pmap_findleft(&dominance.graph, p2);
|
||||
}
|
||||
|
||||
return p1;
|
||||
}
|
||||
|
||||
static void calculate_dominance_graph(void)
|
||||
{
|
||||
/* This is the algorithm described here:
|
||||
*
|
||||
* Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy.
|
||||
* "A simple, fast dominance algorithm."
|
||||
* Software Practice & Experience 4.1-10 (2001): 1-8.
|
||||
*
|
||||
* https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
*/
|
||||
|
||||
int i, j;
|
||||
bool changed;
|
||||
|
||||
dominance.graph.count = 0;
|
||||
|
||||
/* The entry block dominates itself. */
|
||||
|
||||
pmap_put(&dominance.graph, cfg.entry, cfg.entry);
|
||||
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (i = cfg.postorder.count-2; i >= 0; i--)
|
||||
{
|
||||
struct basicblock* b = cfg.postorder.item[i];
|
||||
struct basicblock* new_idom = NULL;
|
||||
for (j=0; j<b->prevs.count; j++)
|
||||
{
|
||||
struct basicblock* p = b->prevs.item[j];
|
||||
|
||||
/* Skip unprocessed blocks. */
|
||||
if (!pmap_findleft(&dominance.graph, p))
|
||||
continue;
|
||||
|
||||
if (!new_idom)
|
||||
new_idom = p;
|
||||
else if (pmap_findleft(&dominance.graph, p))
|
||||
new_idom = intersect(p, new_idom);
|
||||
}
|
||||
|
||||
if (pmap_findleft(&dominance.graph, b) != new_idom)
|
||||
{
|
||||
pmap_put(&dominance.graph, b, new_idom);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (changed);
|
||||
|
||||
for (i=0; i<dominance.graph.count; i++)
|
||||
{
|
||||
tracef('G', "G: dominance graph: %s -> %s\n",
|
||||
dominance.graph.item[i].right->name,
|
||||
dominance.graph.item[i].left->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void recursively_walk_dominance_graph(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (array_contains(&dominance.postorder, bb) || array_contains(&pending, bb))
|
||||
return;
|
||||
|
||||
array_appendu(&dominance.preorder, bb);
|
||||
array_appendu(&pending, bb);
|
||||
|
||||
for (i=0; i<dominance.graph.count; i++)
|
||||
if (dominance.graph.item[i].right == bb)
|
||||
recursively_walk_dominance_graph(dominance.graph.item[i].left);
|
||||
|
||||
array_remove(&pending, bb);
|
||||
array_appendu(&dominance.postorder, bb);
|
||||
}
|
||||
|
||||
static void walk_dominance_graph(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
dominance.preorder.count = 0;
|
||||
dominance.postorder.count = 0;
|
||||
pending.count = 0;
|
||||
recursively_walk_dominance_graph(cfg.entry);
|
||||
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
tracef('G', "G: dominance preorder: %s\n",
|
||||
dominance.preorder.item[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
void update_graph_data(void)
|
||||
{
|
||||
cfg.entry = current_proc->blocks.item[0];
|
||||
cfg.graph.count = 0;
|
||||
update_block_pointers_from_ir();
|
||||
|
||||
walk_cfg_graph();
|
||||
assert(cfg.postorder.count == current_proc->blocks.count);
|
||||
assert(cfg.preorder.count == current_proc->blocks.count);
|
||||
|
||||
calculate_dominance_graph();
|
||||
|
||||
walk_dominance_graph();
|
||||
assert(dominance.postorder.count == dominance.graph.count);
|
||||
assert(dominance.preorder.count == dominance.graph.count);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
27
mach/proto/mcg/graph.h
Normal file
27
mach/proto/mcg/graph.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef GRAPH_H
|
||||
#define GRAPH_H
|
||||
|
||||
struct graph_data
|
||||
{
|
||||
struct basicblock* entry;
|
||||
PMAPOF(struct basicblock, struct basicblock) graph;
|
||||
ARRAYOF(struct basicblock) preorder;
|
||||
ARRAYOF(struct basicblock) postorder;
|
||||
};
|
||||
|
||||
struct dominance_data
|
||||
{
|
||||
PMAPOF(struct basicblock, struct basicblock) graph;
|
||||
ARRAYOF(struct basicblock) preorder;
|
||||
ARRAYOF(struct basicblock) postorder;
|
||||
};
|
||||
|
||||
extern struct graph_data cfg;
|
||||
extern struct dominance_data dominance;
|
||||
|
||||
extern void update_graph_data(void);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
328
mach/proto/mcg/hop.c
Normal file
328
mach/proto/mcg/hop.c
Normal file
|
@ -0,0 +1,328 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static int hop_count = 1;
|
||||
static struct hop* current_hop;
|
||||
static char* buffer = NULL;
|
||||
static int bufferlen = 0;
|
||||
static int buffersize = 0;
|
||||
|
||||
static const struct burm_emitter_data emitter_data;
|
||||
|
||||
struct hop* new_hop(struct basicblock* bb, struct ir* ir)
|
||||
{
|
||||
struct hop* hop = calloc(1, sizeof *hop);
|
||||
hop->id = hop_count++;
|
||||
hop->bb = bb;
|
||||
hop->ir = ir;
|
||||
return hop;
|
||||
}
|
||||
|
||||
static struct insel* new_insel(enum insel_type type)
|
||||
{
|
||||
struct insel* insel = calloc(1, sizeof(*insel));
|
||||
insel->type = type;
|
||||
return insel;
|
||||
}
|
||||
|
||||
void hop_add_string_insel(struct hop* hop, const char* string)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_STRING);
|
||||
insel->u.string = string;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_hreg_insel(struct hop* hop, struct hreg* hreg, int index)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_HREG);
|
||||
insel->u.hreg = hreg;
|
||||
insel->index = index;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_vreg_insel(struct hop* hop, struct vreg* vreg, int index)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_VREG);
|
||||
insel->u.vreg = vreg;
|
||||
insel->index = index;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_value_insel(struct hop* hop, struct ir* ir)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_VALUE);
|
||||
insel->u.value = ir;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_st_offset_insel(struct hop* hop, struct hreg* hreg)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_ST_OFFSET);
|
||||
insel->u.hreg = hreg;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_ab_offset_insel(struct hop* hop, int offset)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_AB_OFFSET);
|
||||
insel->u.offset = offset;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_lb_offset_insel(struct hop* hop, int offset)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_LB_OFFSET);
|
||||
insel->u.offset = offset;
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_eoi_insel(struct hop* hop)
|
||||
{
|
||||
struct insel* insel = new_insel(INSEL_EOI);
|
||||
array_append(&hop->insels, insel);
|
||||
}
|
||||
|
||||
void hop_add_insel(struct hop* hop, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
if (*fmt == '%')
|
||||
{
|
||||
int index = 0;
|
||||
fmt += 2;
|
||||
again:
|
||||
switch (fmt[-1])
|
||||
{
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
index = fmt[-1] - '0';
|
||||
fmt++;
|
||||
goto again;
|
||||
|
||||
case 'd':
|
||||
hop_add_string_insel(hop, aprintf("%d", va_arg(ap, int)));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
hop_add_string_insel(hop, va_arg(ap, const char*));
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
hop_add_st_offset_insel(hop, va_arg(ap, struct hreg*));
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
hop_add_ab_offset_insel(hop, va_arg(ap, int));
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
hop_add_lb_offset_insel(hop, va_arg(ap, int));
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
hop_add_hreg_insel(hop, va_arg(ap, struct hreg*), index);
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
hop_add_vreg_insel(hop, va_arg(ap, struct vreg*), index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* end = strchr(fmt, '%');
|
||||
const char* s;
|
||||
if (end)
|
||||
{
|
||||
int len = end - fmt;
|
||||
s = strndup(fmt, len);
|
||||
fmt = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = fmt;
|
||||
fmt += strlen(fmt);
|
||||
}
|
||||
|
||||
hop_add_string_insel(hop, s);
|
||||
}
|
||||
}
|
||||
|
||||
hop_add_eoi_insel(hop);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void print_header(char k, struct hop* hop)
|
||||
{
|
||||
int i;
|
||||
|
||||
tracef(k, "%c: %d", k, hop->id);
|
||||
if (hop->ir)
|
||||
tracef(k, " from $%d", hop->ir->id);
|
||||
tracef(k, ":");
|
||||
|
||||
for (i=0; i<hop->ins.count; i++)
|
||||
tracef(k, " r%%%d", hop->ins.item[i]->id);
|
||||
for (i=0; i<hop->throughs.count; i++)
|
||||
tracef(k, " =%%%d", hop->throughs.item[i]->id);
|
||||
for (i=0; i<hop->outs.count; i++)
|
||||
tracef(k, " w%%%d", hop->outs.item[i]->id);
|
||||
tracef(k, " ");
|
||||
}
|
||||
|
||||
static char* appendf(const char* fmt, ...)
|
||||
{
|
||||
int n;
|
||||
char* p;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = bufferlen + vsnprintf(NULL, 0, fmt, ap) + 1;
|
||||
va_end(ap);
|
||||
|
||||
if (n > buffersize)
|
||||
{
|
||||
buffersize *= 2;
|
||||
if (buffersize < n)
|
||||
buffersize = n*2;
|
||||
buffer = realloc(buffer, buffersize);
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(buffer+bufferlen, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
bufferlen = n - 1; /* remember the \0 at the end */
|
||||
return p;
|
||||
}
|
||||
|
||||
char* hop_render(struct hop* hop)
|
||||
{
|
||||
int i;
|
||||
|
||||
appendf(""); /* ensure the buffer has been allocated */
|
||||
bufferlen = 0;
|
||||
buffer[0] = '\0';
|
||||
|
||||
for (i=0; i<hop->insels.count; i++)
|
||||
{
|
||||
struct insel* insel = hop->insels.item[i];
|
||||
|
||||
switch (insel->type)
|
||||
{
|
||||
case INSEL_EOI:
|
||||
appendf("\n");
|
||||
break;
|
||||
|
||||
case INSEL_HREG:
|
||||
{
|
||||
struct hreg* hreg = insel->u.hreg;
|
||||
if (hreg->brd)
|
||||
appendf("%s", hreg->brd->names[insel->index]);
|
||||
else
|
||||
appendf("%s.%d", hreg->id, insel->index);
|
||||
break;
|
||||
}
|
||||
|
||||
case INSEL_VREG:
|
||||
{
|
||||
struct vreg* vreg = insel->u.vreg;
|
||||
struct hreg* hreg = pmap_findright(&hop->regsin, vreg);
|
||||
if (!hreg)
|
||||
hreg = pmap_findright(&hop->regsout, vreg);
|
||||
if (hreg)
|
||||
appendf("%s", hreg->brd->names[insel->index]);
|
||||
else
|
||||
appendf("%%%d.%d", vreg->id, insel->index);
|
||||
break;
|
||||
}
|
||||
|
||||
case INSEL_STRING:
|
||||
appendf("%s", insel->u.string);
|
||||
break;
|
||||
|
||||
case INSEL_ST_OFFSET:
|
||||
appendf("%d", current_proc->fp_to_sb + insel->u.hreg->offset);
|
||||
break;
|
||||
|
||||
case INSEL_AB_OFFSET:
|
||||
appendf("%d", current_proc->fp_to_ab + insel->u.offset);
|
||||
break;
|
||||
|
||||
case INSEL_LB_OFFSET:
|
||||
appendf("%d", current_proc->fp_to_lb + insel->u.offset);
|
||||
break;
|
||||
|
||||
case INSEL_VALUE:
|
||||
{
|
||||
struct ir* ir = insel->u.value;
|
||||
switch (ir->opcode)
|
||||
{
|
||||
case IR_BLOCK:
|
||||
appendf("%s", platform_label(ir->u.bvalue->name));
|
||||
break;
|
||||
|
||||
case IR_LABEL:
|
||||
appendf("%s", platform_label(ir->u.lvalue));
|
||||
break;
|
||||
|
||||
case IR_LOCAL:
|
||||
if (ir->u.ivalue >= 0)
|
||||
appendf("%d", current_proc->fp_to_ab + ir->u.ivalue);
|
||||
else
|
||||
appendf("%d", current_proc->fp_to_lb + ir->u.ivalue);
|
||||
break;
|
||||
|
||||
case IR_CONST:
|
||||
appendf("%d", ir->u.ivalue);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void hop_print(char k, struct hop* hop)
|
||||
{
|
||||
int i;
|
||||
bool soi = false;
|
||||
char* p;
|
||||
|
||||
hop_render(hop);
|
||||
|
||||
p = strtok(buffer, "\n");
|
||||
print_header(k, hop);
|
||||
while (p)
|
||||
{
|
||||
tracef(k, "%s", p);
|
||||
p = strtok(NULL, "\n");
|
||||
if (p)
|
||||
{
|
||||
tracef(k, "\n");
|
||||
print_header(k, hop);
|
||||
}
|
||||
}
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
75
mach/proto/mcg/hop.h
Normal file
75
mach/proto/mcg/hop.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef HOP_H
|
||||
#define HOP_H
|
||||
|
||||
enum insel_type
|
||||
{
|
||||
INSEL_STRING,
|
||||
INSEL_HREG,
|
||||
INSEL_VREG,
|
||||
INSEL_VALUE,
|
||||
INSEL_ST_OFFSET,
|
||||
INSEL_AB_OFFSET,
|
||||
INSEL_LB_OFFSET,
|
||||
INSEL_EOI
|
||||
};
|
||||
|
||||
struct insel
|
||||
{
|
||||
enum insel_type type;
|
||||
int index;
|
||||
union
|
||||
{
|
||||
const char* string;
|
||||
struct hreg* hreg;
|
||||
struct vreg* vreg;
|
||||
struct ir* value;
|
||||
int offset;
|
||||
}
|
||||
u;
|
||||
};
|
||||
|
||||
struct constraint
|
||||
{
|
||||
uint32_t attrs;
|
||||
bool preserved;
|
||||
struct vreg* equals_to;
|
||||
};
|
||||
|
||||
struct hop
|
||||
{
|
||||
int id;
|
||||
struct basicblock* bb;
|
||||
struct ir* ir;
|
||||
const struct burm_instruction_data* insndata;
|
||||
ARRAYOF(struct insel) insels;
|
||||
struct vreg* output;
|
||||
|
||||
PMAPOF(struct vreg, struct constraint) constraints;
|
||||
|
||||
ARRAYOF(struct vreg) ins;
|
||||
ARRAYOF(struct vreg) outs;
|
||||
ARRAYOF(struct vreg) throughs;
|
||||
register_assignment_t regsin;
|
||||
register_assignment_t regsout;
|
||||
};
|
||||
|
||||
extern struct hop* new_hop(struct basicblock* bb, struct ir* ir);
|
||||
|
||||
extern void hop_add_string_insel(struct hop* hop, const char* string);
|
||||
extern void hop_add_hreg_insel(struct hop* hop, struct hreg* hreg, int index);
|
||||
extern void hop_add_vreg_insel(struct hop* hop, struct vreg* vreg, int index);
|
||||
extern void hop_add_value_insel(struct hop* hop, struct ir* ir);
|
||||
extern void hop_add_st_offset_insel(struct hop* hop, struct hreg* hreg);
|
||||
extern void hop_add_ab_offset_insel(struct hop* hop, int offset);
|
||||
extern void hop_add_lb_offset_insel(struct hop* hop, int offset);
|
||||
extern void hop_add_eoi_insel(struct hop* hop);
|
||||
|
||||
extern void hop_add_insel(struct hop* hop, const char* fmt, ...);
|
||||
|
||||
extern char* hop_render(struct hop* hop);
|
||||
extern void hop_print(char k, struct hop* hop);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
172
mach/proto/mcg/ir.c
Normal file
172
mach/proto/mcg/ir.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static int next_id = 0;
|
||||
|
||||
struct ir* new_ir0(int opcode, int size)
|
||||
{
|
||||
struct ir* ir = calloc(sizeof(struct ir), 1);
|
||||
ir->id = next_id++;
|
||||
ir->opcode = opcode;
|
||||
ir->size = size;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_ir1(int opcode, int size,
|
||||
struct ir* left)
|
||||
{
|
||||
struct ir* ir = new_ir0(opcode, size);
|
||||
ir->left = left;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_ir2(int opcode, int size,
|
||||
struct ir* left, struct ir* right)
|
||||
{
|
||||
struct ir* ir = new_ir0(opcode, size);
|
||||
ir->left = left;
|
||||
ir->right = right;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_labelir(const char* label)
|
||||
{
|
||||
struct ir* ir = new_ir0(IR_LABEL, EM_pointersize);
|
||||
ir->u.lvalue = label;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_constir(int size, arith value)
|
||||
{
|
||||
struct ir* ir = new_ir0(IR_CONST, size);
|
||||
ir->u.ivalue = value;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_wordir(arith value)
|
||||
{
|
||||
return new_constir(EM_wordsize, value);
|
||||
}
|
||||
|
||||
struct ir* new_bbir(struct basicblock* bb)
|
||||
{
|
||||
struct ir* ir = new_ir0(IR_BLOCK, EM_pointersize);
|
||||
ir->u.bvalue = bb;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* new_anyir(int size)
|
||||
{
|
||||
return new_ir0(IR_ANY, size);
|
||||
}
|
||||
|
||||
struct ir* new_localir(int offset)
|
||||
{
|
||||
struct ir* ir = new_ir0(IR_LOCAL, EM_pointersize);
|
||||
ir->u.ivalue = offset;
|
||||
return ir;
|
||||
}
|
||||
|
||||
struct ir* ir_walk(struct ir* ir, ir_walker_t* cb, void* user)
|
||||
{
|
||||
assert(ir->root);
|
||||
if (cb(ir, user))
|
||||
return ir;
|
||||
|
||||
if (ir->left && (ir->left->root == ir->root))
|
||||
{
|
||||
struct ir* irr = ir_walk(ir->left, cb, user);
|
||||
if (irr)
|
||||
return irr;
|
||||
}
|
||||
|
||||
if (ir->right && (ir->right->root == ir->root))
|
||||
{
|
||||
struct ir* irr = ir_walk(ir->right, cb, user);
|
||||
if (irr)
|
||||
return irr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool finder_cb(struct ir* ir, void* user)
|
||||
{
|
||||
int opcode = *(int*)user;
|
||||
if (ir->opcode == opcode)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ir* ir_find(struct ir* ir, int opcode)
|
||||
{
|
||||
return ir_walk(ir, finder_cb, &opcode);
|
||||
}
|
||||
|
||||
static void print_expr(char k, const struct ir* ir)
|
||||
{
|
||||
tracef(k, "%s", ir_data[ir->opcode].name);
|
||||
if (ir->type)
|
||||
tracef(k, ".%c", ir->type);
|
||||
else if (ir->size)
|
||||
tracef(k, "%d", ir->size);
|
||||
tracef(k, "(");
|
||||
|
||||
switch (ir->opcode)
|
||||
{
|
||||
case IR_CONST:
|
||||
case IR_LOCAL:
|
||||
tracef(k, "%d", ir->u.ivalue);
|
||||
break;
|
||||
|
||||
case IR_LABEL:
|
||||
tracef(k, "%s", ir->u.lvalue);
|
||||
break;
|
||||
|
||||
case IR_BLOCK:
|
||||
tracef(k, "%s", ir->u.bvalue->name);
|
||||
break;
|
||||
|
||||
case IR_PHI:
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<ir->u.phivalue.count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
tracef(k, ", ");
|
||||
tracef(k, "%s=>$%d",
|
||||
ir->u.phivalue.item[i].left->name,
|
||||
ir->u.phivalue.item[i].right->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (ir->left)
|
||||
{
|
||||
if (ir->left->root != ir->root)
|
||||
tracef(k, "$%d", ir->left->id);
|
||||
else
|
||||
print_expr(k, ir->left);
|
||||
}
|
||||
if (ir->right)
|
||||
{
|
||||
tracef(k, ", ");
|
||||
if (ir->right->root != ir->root)
|
||||
tracef(k, "$%d", ir->right->id);
|
||||
else
|
||||
print_expr(k, ir->right);
|
||||
}
|
||||
}
|
||||
|
||||
tracef(k, ")");
|
||||
}
|
||||
|
||||
void ir_print(char k, const struct ir* ir)
|
||||
{
|
||||
tracef(k, "%c: $%d = ", k, ir->id);
|
||||
print_expr(k, ir);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
52
mach/proto/mcg/ir.h
Normal file
52
mach/proto/mcg/ir.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef IR_H
|
||||
#define IR_H
|
||||
|
||||
#include "ircodes.h"
|
||||
|
||||
struct ir
|
||||
{
|
||||
int id;
|
||||
enum ir_opcode opcode;
|
||||
int size;
|
||||
char type;
|
||||
struct ir* left;
|
||||
struct ir* right;
|
||||
struct ir* root;
|
||||
union
|
||||
{
|
||||
arith ivalue;
|
||||
int rvalue;
|
||||
const char* lvalue;
|
||||
struct basicblock* bvalue;
|
||||
PMAPOF(struct basicblock, struct ir) phivalue;
|
||||
} u;
|
||||
|
||||
ARRAYOF(struct ir) uses; /* all places this IR is used */
|
||||
|
||||
struct vreg* result; /* vreg containing IR result */
|
||||
};
|
||||
|
||||
extern const char* ir_names[];
|
||||
|
||||
extern struct ir* new_ir0(int opcode, int size);
|
||||
extern struct ir* new_ir1(int opcode, int size,
|
||||
struct ir* c1);
|
||||
extern struct ir* new_ir2(int opcode, int size,
|
||||
struct ir* c1, struct ir* c2);
|
||||
|
||||
extern struct ir* new_labelir(const char* label);
|
||||
extern struct ir* new_wordir(arith value);
|
||||
extern struct ir* new_constir(int size, arith value);
|
||||
extern struct ir* new_bbir(struct basicblock* bb);
|
||||
extern struct ir* new_anyir(int size);
|
||||
extern struct ir* new_localir(int offset);
|
||||
|
||||
typedef bool ir_walker_t(struct ir* node, void* user);
|
||||
extern struct ir* ir_walk(struct ir* ir, ir_walker_t* callback, void* user);
|
||||
extern struct ir* ir_find(struct ir* ir, int opcode);
|
||||
|
||||
extern void ir_print(char k, const struct ir* ir);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
137
mach/proto/mcg/main.c
Normal file
137
mach/proto/mcg/main.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include "mcg.h"
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char* tracechars = NULL;
|
||||
|
||||
FILE* outputfile = NULL;
|
||||
FILE* dominance_dot_file = NULL;
|
||||
FILE* cfg_dot_file = NULL;
|
||||
|
||||
bool tracing(char k)
|
||||
{
|
||||
if (k == '!')
|
||||
return true;
|
||||
if (!tracechars)
|
||||
return false;
|
||||
|
||||
return index(tracechars, k);
|
||||
}
|
||||
|
||||
void tracef(char k, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (tracing(k))
|
||||
{
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static bool find_procedures_cb(struct symbol* symbol, void* user)
|
||||
{
|
||||
if (symbol->proc)
|
||||
procedure_compile(symbol->proc);
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[])
|
||||
{
|
||||
const char* inputfilename = NULL;
|
||||
const char* outputfilename = NULL;
|
||||
FILE* output;
|
||||
|
||||
program_name = argv[0];
|
||||
|
||||
opterr = 1;
|
||||
for (;;)
|
||||
{
|
||||
int c = getopt(argc, argv, "-d:D:C:o:");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'C':
|
||||
cfg_dot_file = fopen(optarg, "w");
|
||||
if (!cfg_dot_file)
|
||||
fatal("couldn't open output file '%s': %s",
|
||||
optarg, strerror(errno));
|
||||
fprintf(cfg_dot_file, "digraph {\n");
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
dominance_dot_file = fopen(optarg, "w");
|
||||
if (!dominance_dot_file)
|
||||
fatal("couldn't open output file '%s': %s",
|
||||
optarg, strerror(errno));
|
||||
fprintf(dominance_dot_file, "digraph {\n");
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
tracechars = optarg;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (outputfilename)
|
||||
fatal("already specified an output file");
|
||||
outputfilename = optarg;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (inputfilename)
|
||||
fatal("unexpected argument '%s'", optarg);
|
||||
inputfilename = optarg;
|
||||
}
|
||||
}
|
||||
|
||||
symbol_init();
|
||||
|
||||
if (!EM_open((char*) inputfilename))
|
||||
fatal("couldn't open input '%s': %s",
|
||||
inputfilename ? inputfilename : "<stdin>", EM_error);
|
||||
|
||||
if (outputfilename)
|
||||
{
|
||||
outputfile = fopen(outputfilename, "w");
|
||||
if (!outputfile)
|
||||
fatal("couldn't open output '%s': %s",
|
||||
outputfilename, strerror(errno));
|
||||
}
|
||||
else
|
||||
outputfile = stdout;
|
||||
|
||||
fprintf(outputfile, ".sect .text\n.sect .rom\n.sect .data\n.sect .bss\n");
|
||||
|
||||
/* Reads in the EM, outputs the data sections, parses any code and
|
||||
* generates IR trees. */
|
||||
|
||||
parse_em();
|
||||
|
||||
/* For every procedure, go ahead and do the compilation proper. We do this
|
||||
* now so that we know that all the data has been read correctly and our
|
||||
* symbol table is complete (we may need to refer to it). */
|
||||
|
||||
symbol_walk(find_procedures_cb, NULL);
|
||||
|
||||
if (outputfilename)
|
||||
fclose(outputfile);
|
||||
EM_close();
|
||||
|
||||
if (cfg_dot_file)
|
||||
{
|
||||
fprintf(cfg_dot_file, "}\n");
|
||||
fclose(cfg_dot_file);
|
||||
}
|
||||
if (dominance_dot_file)
|
||||
{
|
||||
fprintf(dominance_dot_file, "}\n");
|
||||
fclose(dominance_dot_file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
136
mach/proto/mcg/mcg.h
Normal file
136
mach/proto/mcg/mcg.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
#ifndef MCG_H
|
||||
#define MCG_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "flt_arith.h"
|
||||
#include "em_arith.h"
|
||||
#include "em_label.h"
|
||||
#include "em.h"
|
||||
#include "em_comp.h"
|
||||
#include "em_pseu.h"
|
||||
#include "em_mnem.h"
|
||||
#include "em_flag.h"
|
||||
#include "em_ptyp.h"
|
||||
#include "array.h"
|
||||
#include "imap.h"
|
||||
#include "pmap.h"
|
||||
#include "diagnostics.h"
|
||||
#include "astring.h"
|
||||
#include "ir.h"
|
||||
#include "mcgg.h"
|
||||
#include "reg.h"
|
||||
#include "hop.h"
|
||||
#include "basicblock.h"
|
||||
#include "procedure.h"
|
||||
#include "graph.h"
|
||||
#include "tables.h"
|
||||
|
||||
extern char em_pseu[][4];
|
||||
extern char em_mnem[][4];
|
||||
extern char em_flag[];
|
||||
|
||||
enum {
|
||||
SECTION_UNKNOWN = 0,
|
||||
SECTION_ROM,
|
||||
SECTION_DATA,
|
||||
SECTION_BSS,
|
||||
SECTION_TEXT
|
||||
};
|
||||
|
||||
struct symbol
|
||||
{
|
||||
const char* name;
|
||||
int section;
|
||||
struct procedure* proc;
|
||||
bool is_defined : 1;
|
||||
bool is_exported : 1;
|
||||
bool is_proc : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PARAM_NONE,
|
||||
PARAM_IVALUE,
|
||||
PARAM_LVALUE,
|
||||
PARAM_BVALUE,
|
||||
};
|
||||
|
||||
struct em
|
||||
{
|
||||
int opcode;
|
||||
int paramtype;
|
||||
union {
|
||||
arith ivalue;
|
||||
struct {
|
||||
const char* label;
|
||||
arith offset;
|
||||
} lvalue;
|
||||
struct {
|
||||
struct basicblock* left;
|
||||
struct basicblock* right;
|
||||
} bvalue;
|
||||
} u;
|
||||
};
|
||||
|
||||
extern const char* aprintf(const char* fmt, ...);
|
||||
extern void tracef(char k, const char* fmt, ...);
|
||||
extern bool tracing(char k);
|
||||
|
||||
extern void parse_em(void);
|
||||
|
||||
extern void symbol_init(void);
|
||||
extern bool symbol_exists(const char* name);
|
||||
extern struct symbol* symbol_get(const char* name);
|
||||
extern void symbol_declare(const char* name, bool is_exported, bool is_proc);
|
||||
|
||||
typedef bool symbol_walker_t(struct symbol* symbol, void* user);
|
||||
extern struct symbol* symbol_walk(symbol_walker_t* walker, void* user);
|
||||
|
||||
extern void data_label(const char* name);
|
||||
extern void data_int(arith data, size_t size, bool is_ro);
|
||||
extern void data_float(const char* data, size_t size, bool is_ro);
|
||||
extern void data_block(const uint8_t* data, size_t size, bool is_ro);
|
||||
extern void data_offset(const char* label, arith offset, bool is_ro);
|
||||
extern void data_bss(arith size, int init);
|
||||
|
||||
extern void tb_filestart(void);
|
||||
extern void tb_fileend(void);
|
||||
extern void tb_procedure(void);
|
||||
extern void tb_regvar(struct procedure* proc, arith offset, int size, int type, int priority);
|
||||
|
||||
extern void pass_convert_locals_to_ssa(void);
|
||||
extern void pass_convert_stack_ops(void);
|
||||
extern void pass_eliminate_trivial_blocks(void);
|
||||
extern void pass_find_phi_congruence_groups(void);
|
||||
extern void pass_group_irs(void);
|
||||
extern void pass_infer_types(void);
|
||||
extern void pass_insert_moves(void);
|
||||
extern void pass_instruction_selector(void);
|
||||
extern void pass_live_vreg_analysis(void);
|
||||
extern void pass_add_prologue_epilogue(void);
|
||||
extern void pass_register_allocator(void);
|
||||
extern void pass_remove_dead_blocks(void);
|
||||
extern void pass_remove_dead_phis(void);
|
||||
extern void pass_split_critical_edges(void);
|
||||
extern void pass_wire_up_return_values(void);
|
||||
|
||||
extern void platform_calculate_offsets(void);
|
||||
extern struct hop* platform_prologue(void);
|
||||
extern struct hop* platform_epilogue(void);
|
||||
extern struct hop* platform_move(struct basicblock* bb, struct hreg* src, struct hreg* dest);
|
||||
extern struct hop* platform_swap(struct basicblock* bb, struct hreg* src, struct hreg* dest);
|
||||
extern const char* platform_label(const char* label);
|
||||
|
||||
extern FILE* outputfile;
|
||||
extern FILE* dominance_dot_file;
|
||||
extern FILE* cfg_dot_file;
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
55
mach/proto/mcg/mcgg_generated_footer.h
Normal file
55
mach/proto/mcg/mcgg_generated_footer.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
static void dumpCover(NODEPTR_TYPE p, int goalnt, int indent) {
|
||||
#ifdef TRACE
|
||||
int eruleno = burm_rule(STATE_LABEL(p), goalnt);
|
||||
const short *nts = burm_nts[eruleno];
|
||||
NODEPTR_TYPE kids[10];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
fprintf(stderr, " ");
|
||||
fprintf(stderr, "%s\n", burm_string[eruleno]);
|
||||
burm_kids(p, eruleno, kids);
|
||||
for (i = 0; nts[i]; i++)
|
||||
{
|
||||
if (kids[i])
|
||||
dumpCover(kids[i], nts[i], indent + 1);
|
||||
else
|
||||
fprintf(stderr, "failed!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
static NODEPTR_TYPE tree(int op, NODEPTR_TYPE l, NODEPTR_TYPE r) {
|
||||
NODEPTR_TYPE p = malloc(sizeof *p);
|
||||
|
||||
assert(p);
|
||||
p->op = op;
|
||||
p->kids[0] = l; p->kids[1] = r;
|
||||
return p;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
NODEPTR_TYPE p;
|
||||
|
||||
p = tree(STORE4,
|
||||
tree(ADD4,
|
||||
tree(LABEL4, 0, 0),
|
||||
tree(CONST4, 0, 0)
|
||||
),
|
||||
tree(ADD4,
|
||||
tree(LOAD4,
|
||||
tree(LABEL4, 0, 0),
|
||||
0
|
||||
),
|
||||
tree(CONST4, 0, 0)
|
||||
)
|
||||
);
|
||||
burm_label(p);
|
||||
dumpCover(p, 1, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
15
mach/proto/mcg/mcgg_generated_header.h
Normal file
15
mach/proto/mcg/mcgg_generated_header.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "mcg.h"
|
||||
#include "mcgg.h"
|
||||
|
||||
#define PANIC printf
|
||||
|
||||
#define burm_assert(b, s) assert(b)
|
||||
|
||||
extern void burm_panic_cannot_match(NODEPTR_TYPE node);
|
||||
|
||||
extern bool burm_predicate_signed_constant(struct burm_node* node, arith size);
|
||||
extern bool burm_predicate_unsigned_constant(struct burm_node* node, arith size);
|
||||
extern bool burm_predicate_specific_constant(struct burm_node* node, arith value);
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
483
mach/proto/mcg/parse_em.c
Normal file
483
mach/proto/mcg/parse_em.c
Normal file
|
@ -0,0 +1,483 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static struct e_instr em;
|
||||
static struct basicblock* code_bb;
|
||||
static struct basicblock* data_bb;
|
||||
|
||||
static void queue_insn_label(int opcode, const char* label, arith offset);
|
||||
|
||||
static const char* type_to_str(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EM_MNEM: return "EM_MNEM";
|
||||
case EM_PSEU: return "EM_PSEU";
|
||||
case EM_STARTMES: return "EM_STARTMES";
|
||||
case EM_MESARG: return "EM_MESARG";
|
||||
case EM_ENDMES: return "EM_ENDMES";
|
||||
case EM_DEFILB: return "EM_DEFILB";
|
||||
case EM_DEFDLB: return "EM_DEFDLB";
|
||||
case EM_DEFDNAM: return "EM_DEFDNAM";
|
||||
case EM_ERROR: return "EM_ERROR";
|
||||
case EM_FATAL: return "EM_FATAL";
|
||||
case EM_EOF: return "EM_EOF";
|
||||
}
|
||||
|
||||
assert(0 && "invalid EM type");
|
||||
}
|
||||
|
||||
static const char* argtype_to_str(int type)
|
||||
{
|
||||
if (type == 0) return "...";
|
||||
if (type == ilb_ptyp) return "ilb";
|
||||
if (type == nof_ptyp) return "nof";
|
||||
if (type == sof_ptyp) return "sof";
|
||||
if (type == cst_ptyp) return "cst";
|
||||
if (type == pro_ptyp) return "pro";
|
||||
if (type == str_ptyp) return "str";
|
||||
if (type == ico_ptyp) return "ico";
|
||||
if (type == uco_ptyp) return "uco";
|
||||
if (type == fco_ptyp) return "fco";
|
||||
return "???";
|
||||
}
|
||||
|
||||
static void unknown_type(const char* s)
|
||||
{
|
||||
fatal("%s with unknown type '%s'",
|
||||
s,
|
||||
argtype_to_str(em.em_arg.ema_argtype));
|
||||
}
|
||||
|
||||
static const char* ilabel_to_str(label l)
|
||||
{
|
||||
assert(current_proc != NULL);
|
||||
return aprintf(".%s_I%d", current_proc->name, l);
|
||||
}
|
||||
|
||||
static const char* dlabel_to_str(label l)
|
||||
{
|
||||
return aprintf(".D%d", l);
|
||||
}
|
||||
|
||||
static void terminate_block(void)
|
||||
{
|
||||
code_bb->is_terminated = true;
|
||||
code_bb = NULL;
|
||||
}
|
||||
|
||||
static struct em* new_insn(int opcode)
|
||||
{
|
||||
struct em* em = calloc(sizeof(struct em), 1);
|
||||
em->opcode = opcode;
|
||||
return em;
|
||||
}
|
||||
|
||||
static void queue_insn_simple(int opcode)
|
||||
{
|
||||
struct em* em = new_insn(opcode);
|
||||
em->paramtype = PARAM_NONE;
|
||||
array_append(&code_bb->ems, em);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case op_bra:
|
||||
terminate_block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_insn_value(int opcode, arith value)
|
||||
{
|
||||
struct em* em = new_insn(opcode);
|
||||
em->paramtype = PARAM_IVALUE;
|
||||
em->u.ivalue = value;
|
||||
array_append(&code_bb->ems, em);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case op_csa:
|
||||
case op_csb:
|
||||
case op_ret:
|
||||
terminate_block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_insn_label(int opcode, const char* label, arith offset)
|
||||
{
|
||||
struct em* em = new_insn(opcode);
|
||||
em->paramtype = PARAM_LVALUE;
|
||||
em->u.lvalue.label = label;
|
||||
em->u.lvalue.offset = offset;
|
||||
array_append(&code_bb->ems, em);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case op_bra:
|
||||
terminate_block();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_insn_block(int opcode, struct basicblock* left, struct basicblock* right)
|
||||
{
|
||||
struct em* em = new_insn(opcode);
|
||||
em->paramtype = PARAM_BVALUE;
|
||||
em->u.bvalue.left = left;
|
||||
em->u.bvalue.right = right;
|
||||
array_append(&code_bb->ems, em);
|
||||
|
||||
terminate_block();
|
||||
}
|
||||
|
||||
static void change_basicblock(struct basicblock* newbb)
|
||||
{
|
||||
array_appendu(¤t_proc->blocks, newbb);
|
||||
|
||||
if (code_bb && !code_bb->is_terminated)
|
||||
queue_insn_block(op_bra, newbb, NULL);
|
||||
|
||||
code_bb = newbb;
|
||||
}
|
||||
|
||||
static void queue_insn_ilabel(int opcode, int label)
|
||||
{
|
||||
const char* name = ilabel_to_str(em.em_ilb);
|
||||
struct basicblock* left = bb_get(name);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case op_bra:
|
||||
queue_insn_block(em.em_opcode, left, NULL);
|
||||
break;
|
||||
|
||||
case op_zeq:
|
||||
case op_zne:
|
||||
case op_zlt:
|
||||
case op_zle:
|
||||
case op_zgt:
|
||||
case op_zge:
|
||||
case op_beq:
|
||||
case op_bne:
|
||||
case op_blt:
|
||||
case op_ble:
|
||||
case op_bgt:
|
||||
case op_bge:
|
||||
{
|
||||
struct basicblock* bb = bb_get(NULL);
|
||||
queue_insn_block(em.em_opcode, left, bb);
|
||||
change_basicblock(bb);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
fatal("parse_em: unhandled conditional '%s'",
|
||||
em_mnem[opcode - sp_fmnem]);
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_ilabel(arith label)
|
||||
{
|
||||
change_basicblock(bb_get(ilabel_to_str(label)));
|
||||
}
|
||||
|
||||
static void parse_pseu(void)
|
||||
{
|
||||
switch (em.em_opcode)
|
||||
{
|
||||
case ps_exp: /* external proc */
|
||||
case ps_exa: /* external array */
|
||||
case ps_inp: /* internal proc */
|
||||
case ps_ina: /* internal array */
|
||||
{
|
||||
bool export = (em.em_opcode == ps_exp) || (em.em_opcode == ps_exa);
|
||||
bool proc = (em.em_opcode == ps_exp) || (em.em_opcode == ps_inp);
|
||||
|
||||
switch (em.em_arg.ema_argtype)
|
||||
{
|
||||
case pro_ptyp:
|
||||
symbol_declare(strdup(em.em_pnam), export, proc);
|
||||
break;
|
||||
|
||||
case sof_ptyp:
|
||||
assert(em.em_off == 0);
|
||||
symbol_declare(strdup(em.em_dnam), export, proc);
|
||||
break;
|
||||
|
||||
case nof_ptyp:
|
||||
assert(em.em_off == 0);
|
||||
symbol_declare(dlabel_to_str(em.em_dlb), export, proc);
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown_type("exp, exa, inp, ina");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ps_con: /* .data */
|
||||
case ps_rom: /* .rom */
|
||||
{
|
||||
bool ro = (em.em_opcode == ps_rom);
|
||||
|
||||
switch (em.em_arg.ema_argtype)
|
||||
{
|
||||
case ico_ptyp:
|
||||
case uco_ptyp:
|
||||
{
|
||||
arith val = atol(em.em_string);
|
||||
data_int(val, em.em_size, ro);
|
||||
break;
|
||||
}
|
||||
|
||||
case fco_ptyp:
|
||||
{
|
||||
data_float(em.em_string, em.em_size, ro);
|
||||
break;
|
||||
}
|
||||
|
||||
case str_ptyp:
|
||||
data_block(strdup(em.em_string), em.em_size, ro);
|
||||
break;
|
||||
|
||||
case cst_ptyp:
|
||||
data_int(em.em_cst, EM_wordsize, ro);
|
||||
break;
|
||||
|
||||
case nof_ptyp:
|
||||
data_offset(dlabel_to_str(em.em_dlb), em.em_off, ro);
|
||||
break;
|
||||
|
||||
case sof_ptyp:
|
||||
case pro_ptyp:
|
||||
data_offset(strdup(em.em_dnam), em.em_off, ro);
|
||||
break;
|
||||
|
||||
case ilb_ptyp:
|
||||
{
|
||||
const char* label = ilabel_to_str(em.em_ilb);
|
||||
|
||||
/* This is really hacky; to handle basic block flow
|
||||
* descriptor blocks, we need to track which bbs a descriptor
|
||||
* can exit to. So we create fake bb objects for each
|
||||
* block, purely to track this.
|
||||
*/
|
||||
|
||||
if (data_bb)
|
||||
{
|
||||
struct em* em = new_insn(op_bra);
|
||||
em->paramtype = PARAM_BVALUE;
|
||||
em->u.bvalue.left = bb_get(label);
|
||||
array_append(&data_bb->ems, em);
|
||||
}
|
||||
|
||||
data_offset(label, 0, ro);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
unknown_type("con, rom");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ps_bss:
|
||||
{
|
||||
switch (em.em_arg.ema_argtype)
|
||||
{
|
||||
case cst_ptyp:
|
||||
data_bss(EM_bsssize, em.em_cst);
|
||||
break;
|
||||
|
||||
case ico_ptyp:
|
||||
case uco_ptyp:
|
||||
{
|
||||
arith val = atol(em.em_string);
|
||||
data_int(val, em.em_size, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case sof_ptyp:
|
||||
data_offset(strdup(em.em_dnam), em.em_off, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown_type("bss");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ps_pro: /* procedure start */
|
||||
{
|
||||
struct symbol* symbol;
|
||||
|
||||
current_proc = calloc(sizeof(struct procedure), 1);
|
||||
current_proc->name = strdup(em.em_pnam);
|
||||
current_proc->entry = bb_get(current_proc->name);
|
||||
current_proc->locals_size = em.em_nlocals;
|
||||
code_bb = current_proc->entry;
|
||||
code_bb->is_root = true;
|
||||
array_append(¤t_proc->blocks, code_bb);
|
||||
|
||||
symbol = symbol_get(current_proc->name);
|
||||
symbol->section = SECTION_TEXT;
|
||||
symbol->proc = current_proc;
|
||||
symbol->is_proc = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case ps_end: /* procedure end */
|
||||
tb_procedure();
|
||||
|
||||
current_proc = NULL;
|
||||
code_bb = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("unknown pseudo with opcode %d\n", em.em_opcode);
|
||||
}
|
||||
}
|
||||
|
||||
static arith mes_get_cst(void)
|
||||
{
|
||||
EM_getinstr(&em);
|
||||
if (em.em_type != EM_MESARG)
|
||||
fatal("malformed MES");
|
||||
return em.em_cst;
|
||||
}
|
||||
|
||||
static void parse_mes(void)
|
||||
{
|
||||
assert(em.em_arg.ema_argtype == cst_ptyp);
|
||||
switch (em.em_cst)
|
||||
{
|
||||
case 0: /* error */
|
||||
fatal("MES 0 received (explicit halt)");
|
||||
|
||||
case 3: /* register variable */
|
||||
{
|
||||
/* ego will sometimes generate 'mes 3' pseudos with no actual
|
||||
* parameters. Detect and ignore these. */
|
||||
|
||||
EM_getinstr(&em);
|
||||
if (em.em_type == EM_MESARG)
|
||||
{
|
||||
arith offset = em.em_cst;
|
||||
int size = mes_get_cst();
|
||||
int type = mes_get_cst();
|
||||
int priority = mes_get_cst();
|
||||
tb_regvar(current_proc, offset, size, type, priority);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while ((em.em_type == EM_STARTMES) || (em.em_type == EM_MESARG))
|
||||
EM_getinstr(&em);
|
||||
|
||||
if (em.em_type != EM_ENDMES)
|
||||
fatal("malformed MES");
|
||||
}
|
||||
|
||||
static void create_data_label(const char* label)
|
||||
{
|
||||
data_label(label);
|
||||
if (current_proc)
|
||||
{
|
||||
data_bb = bb_get(label);
|
||||
data_bb->is_fake = true;
|
||||
array_append(¤t_proc->blocks, data_bb);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_em(void)
|
||||
{
|
||||
EM_getinstr(&em);
|
||||
tb_filestart();
|
||||
|
||||
while (em.em_type != EM_EOF)
|
||||
{
|
||||
switch (em.em_type)
|
||||
{
|
||||
case EM_PSEU:
|
||||
parse_pseu();
|
||||
break;
|
||||
|
||||
case EM_DEFILB:
|
||||
queue_ilabel(em.em_ilb);
|
||||
break;
|
||||
|
||||
case EM_DEFDLB:
|
||||
create_data_label(dlabel_to_str(em.em_dlb));
|
||||
break;
|
||||
|
||||
case EM_DEFDNAM:
|
||||
create_data_label(strdup(em.em_dnam));
|
||||
break;
|
||||
|
||||
case EM_STARTMES:
|
||||
parse_mes();
|
||||
break;
|
||||
|
||||
case EM_MNEM:
|
||||
if (code_bb)
|
||||
{
|
||||
int flags = em_flag[em.em_opcode - sp_fmnem];
|
||||
|
||||
if (flags & EM_PAR)
|
||||
{
|
||||
switch (em.em_argtype)
|
||||
{
|
||||
case 0:
|
||||
/* This is an instruction which would normally
|
||||
* take a size, but the size is provided on the
|
||||
* stack. We hates them. */
|
||||
queue_insn_simple(em.em_opcode);
|
||||
break;
|
||||
|
||||
case ilb_ptyp:
|
||||
queue_insn_ilabel(em.em_opcode, em.em_ilb);
|
||||
break;
|
||||
|
||||
case nof_ptyp:
|
||||
queue_insn_label(em.em_opcode,
|
||||
dlabel_to_str(em.em_dlb), em.em_off);
|
||||
break;
|
||||
|
||||
case sof_ptyp:
|
||||
queue_insn_label(em.em_opcode,
|
||||
strdup(em.em_dnam), em.em_off);
|
||||
break;
|
||||
|
||||
case pro_ptyp:
|
||||
queue_insn_label(em.em_opcode,
|
||||
strdup(em.em_pnam), 0);
|
||||
break;
|
||||
|
||||
case cst_ptyp:
|
||||
if ((flags & EM_PAR) == PAR_B)
|
||||
queue_insn_ilabel(em.em_opcode, em.em_ilb);
|
||||
else
|
||||
queue_insn_value(em.em_opcode, em.em_cst);
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown_type("instruction");
|
||||
}
|
||||
}
|
||||
else
|
||||
queue_insn_simple(em.em_opcode);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("unrecognised instruction type '%d'", em.em_type);
|
||||
}
|
||||
|
||||
EM_getinstr(&em);
|
||||
}
|
||||
|
||||
tb_fileend();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
135
mach/proto/mcg/pass_convertstackops.c
Normal file
135
mach/proto/mcg/pass_convertstackops.c
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static PMAPOF(struct basicblock, struct ir) pops;
|
||||
static PMAPOF(struct basicblock, struct ir) pushes;
|
||||
|
||||
static struct ir* get_last_push(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=bb->irs.count-1; i>=0; i--)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
|
||||
if (ir->opcode == IR_PUSH)
|
||||
return ir;
|
||||
if (ir_find(ir, IR_POP))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ir* get_first_pop(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* irr;
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
|
||||
if (ir->opcode == IR_PUSH)
|
||||
return NULL;
|
||||
|
||||
irr = ir_find(ir, IR_POP);
|
||||
if (irr)
|
||||
return irr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void convert_block(struct basicblock* bb)
|
||||
{
|
||||
int i, j;
|
||||
struct ir* ir;
|
||||
|
||||
pushes.count = pops.count = 0;
|
||||
for (;;)
|
||||
{
|
||||
struct ir* lastpush = get_last_push(bb);
|
||||
if (!lastpush)
|
||||
return;
|
||||
|
||||
/* Abort unless *every* successor block of this one starts with a pop
|
||||
* of the same size... */
|
||||
|
||||
for (i=0; i<bb->nexts.count; i++)
|
||||
{
|
||||
struct basicblock* outbb = bb->nexts.item[i];
|
||||
|
||||
ir = get_first_pop(outbb);
|
||||
if (!ir || (ir->size != lastpush->size))
|
||||
return;
|
||||
pmap_add(&pops, outbb, ir);
|
||||
|
||||
/* Also abort unless *every* predecessor block of the one we've
|
||||
* just found *also* ends in a push of the same size. */
|
||||
|
||||
for (j=0; j<outbb->prevs.count; j++)
|
||||
{
|
||||
struct basicblock* inbb = outbb->prevs.item[j];
|
||||
|
||||
ir = get_last_push(inbb);
|
||||
if (!ir || (ir->size != lastpush->size))
|
||||
return;
|
||||
pmap_add(&pushes, inbb, ir);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't actually find anything, give up. */
|
||||
|
||||
if ((pushes.count == 0) || (pops.count == 0))
|
||||
return;
|
||||
|
||||
/* Okay, now we can wire them all up. */
|
||||
|
||||
for (i=0; i<pushes.count; i++)
|
||||
{
|
||||
struct ir* ir = pushes.item[i].right;
|
||||
ir->opcode = IR_NOP;
|
||||
}
|
||||
|
||||
for (i=0; i<pops.count; i++)
|
||||
{
|
||||
struct basicblock* outbb = pops.item[i].left;
|
||||
struct ir* ir = pops.item[i].right;
|
||||
|
||||
assert(pushes.count > 0);
|
||||
if (pushes.count == 1)
|
||||
{
|
||||
/* The push happened in exactly one place; that means we don't need a phi and can
|
||||
* just import the value directly. */
|
||||
|
||||
struct ir* src = pushes.item[0].right;
|
||||
ir->opcode = IR_NOP;
|
||||
ir->left = src;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The push could have happened in one of several places; we need a phi. */
|
||||
|
||||
struct ir* phi = new_ir0(IR_PHI, ir->size);
|
||||
for (j=0; j<pushes.count; j++)
|
||||
{
|
||||
pmap_add(&phi->u.phivalue,
|
||||
pushes.item[j].left,
|
||||
pushes.item[j].right);
|
||||
}
|
||||
phi->root = phi;
|
||||
*ir = *phi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pass_convert_stack_ops(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
convert_block(cfg.preorder.item[i]);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
42
mach/proto/mcg/pass_eliminatetrivialblocks.c
Normal file
42
mach/proto/mcg/pass_eliminatetrivialblocks.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static bool rewrite_jumps_cb(struct ir* ir, void* user)
|
||||
{
|
||||
if (ir->opcode == IR_BLOCK)
|
||||
{
|
||||
struct basicblock* bb = ir->u.bvalue;
|
||||
if (!bb->is_fake
|
||||
&& (bb->irs.count > 0)
|
||||
&& (bb->irs.item[0]->opcode == IR_JUMP)
|
||||
&& (bb->irs.item[0]->left->opcode == IR_BLOCK))
|
||||
{
|
||||
ir->u.bvalue = bb->irs.item[0]->left->u.bvalue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rewrite_jumps(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
ir_walk(ir, rewrite_jumps_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_eliminate_trivial_blocks(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
rewrite_jumps(bb);
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
95
mach/proto/mcg/pass_groupirs.c
Normal file
95
mach/proto/mcg/pass_groupirs.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static ARRAYOF(struct ir) allirs;
|
||||
static ARRAYOF(struct ir) rootirs;
|
||||
|
||||
static void addall(struct ir* ir)
|
||||
{
|
||||
if (array_appendu(&allirs, ir))
|
||||
return;
|
||||
|
||||
if (ir->left)
|
||||
addall(ir->left);
|
||||
if (ir->right)
|
||||
addall(ir->right);
|
||||
}
|
||||
|
||||
static void collect_irs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
allirs.count = rootirs.count = 0;
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
int j;
|
||||
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[j];
|
||||
addall(ir);
|
||||
array_append(&rootirs, ir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_roots(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<allirs.count; i++)
|
||||
{
|
||||
struct ir* ir = allirs.item[i];
|
||||
ir->root = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void find_roots(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<rootirs.count; i++)
|
||||
{
|
||||
struct ir* ir = rootirs.item[i];
|
||||
assert(!ir->root);
|
||||
ir->root = ir;
|
||||
}
|
||||
}
|
||||
|
||||
static void recursively_mark_root(struct ir* node, struct ir* root)
|
||||
{
|
||||
if (node != root)
|
||||
{
|
||||
if (node->root)
|
||||
return;
|
||||
node->root = root;
|
||||
}
|
||||
|
||||
if (node->left)
|
||||
recursively_mark_root(node->left, root);
|
||||
if (node->right)
|
||||
recursively_mark_root(node->right, root);
|
||||
}
|
||||
|
||||
static void find_non_roots(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<rootirs.count; i++)
|
||||
{
|
||||
struct ir* ir = rootirs.item[i];
|
||||
recursively_mark_root(ir, ir);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_group_irs(void)
|
||||
{
|
||||
collect_irs();
|
||||
clear_roots();
|
||||
find_roots();
|
||||
find_non_roots();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
338
mach/proto/mcg/pass_instructionselection.c
Normal file
338
mach/proto/mcg/pass_instructionselection.c
Normal file
|
@ -0,0 +1,338 @@
|
|||
#include "mcg.h"
|
||||
|
||||
#define MAX_CHILDREN 10
|
||||
|
||||
struct insn
|
||||
{
|
||||
struct ir* ir;
|
||||
struct hop* hop;
|
||||
const struct burm_instruction_data* insndata;
|
||||
int num_children;
|
||||
struct insn* children[MAX_CHILDREN];
|
||||
};
|
||||
|
||||
static struct basicblock* current_bb;
|
||||
static struct hop* current_hop;
|
||||
static struct ir* current_ir;
|
||||
static struct insn* current_insn;
|
||||
|
||||
static void emit(struct insn* insn);
|
||||
|
||||
void burm_trace(struct burm_node* p, int ruleno, int cost, int bestcost) {
|
||||
const struct burm_instruction_data* insndata = &burm_instruction_data[ruleno];
|
||||
//tracef('I', "I: 0x%p matched %s with cost %d vs. %d\n", p,
|
||||
// insndata->name, cost, bestcost);
|
||||
}
|
||||
|
||||
void burm_panic_cannot_match(struct burm_node* node)
|
||||
{
|
||||
fprintf(stderr, "could not find any patterns to match:\n");
|
||||
ir_print('!', node->ir);
|
||||
fprintf(stderr, "aborting!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void emit_return_reg(int index)
|
||||
{
|
||||
hop_add_vreg_insel(current_hop, current_hop->output, index);
|
||||
}
|
||||
|
||||
static struct vreg* find_vreg_of_child(int child)
|
||||
{
|
||||
struct insn* insn = current_insn->children[child];
|
||||
|
||||
if (insn->hop)
|
||||
return insn->hop->output;
|
||||
else
|
||||
return insn->ir->result;
|
||||
}
|
||||
|
||||
static void emit_reg(int child, int index)
|
||||
{
|
||||
struct vreg* vreg = find_vreg_of_child(child);
|
||||
|
||||
if (vreg)
|
||||
{
|
||||
hop_add_vreg_insel(current_hop, vreg, index);
|
||||
array_appendu(&vreg->used, current_hop);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_string(const char* data)
|
||||
{
|
||||
hop_add_string_insel(current_hop, data);
|
||||
}
|
||||
|
||||
static void emit_fragment(int child)
|
||||
{
|
||||
emit(current_insn->children[child]);
|
||||
}
|
||||
|
||||
static void emit_value(int child)
|
||||
{
|
||||
hop_add_value_insel(current_hop, current_insn->children[child]->ir);
|
||||
}
|
||||
|
||||
static void emit_eoi(void)
|
||||
{
|
||||
hop_add_eoi_insel(current_hop);
|
||||
}
|
||||
|
||||
static struct constraint* get_constraint(struct vreg* vreg)
|
||||
{
|
||||
struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||
if (!c)
|
||||
{
|
||||
c = calloc(1, sizeof(*c));
|
||||
pmap_put(¤t_hop->constraints, vreg, c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void constrain_input_reg(int child, uint32_t attr)
|
||||
{
|
||||
struct vreg* vreg = find_vreg_of_child(child);
|
||||
struct constraint* c;
|
||||
|
||||
assert(vreg);
|
||||
|
||||
array_appendu(¤t_hop->ins, vreg);
|
||||
get_constraint(vreg)->attrs = attr;
|
||||
}
|
||||
|
||||
static void constrain_input_reg_preserved(int child)
|
||||
{
|
||||
struct vreg* vreg = find_vreg_of_child(child);
|
||||
struct constraint* c;
|
||||
|
||||
assert(vreg);
|
||||
array_appendu(¤t_hop->throughs, vreg);
|
||||
get_constraint(vreg)->preserved = true;
|
||||
}
|
||||
|
||||
static uint32_t find_type_from_constraint(uint32_t attr)
|
||||
{
|
||||
/* Looks through the registers and finds a concrete register implementing
|
||||
* that attribute, and returns the type. We assume that all registers
|
||||
* implementing an attribute (which anyone is going to ask for, 'volatile'
|
||||
* doesn't count) will have the same type. TODO: mcgg should check for
|
||||
* this. */
|
||||
|
||||
const struct burm_register_data* brd = burm_register_data;
|
||||
while (brd->id)
|
||||
{
|
||||
if (brd->attrs & attr)
|
||||
{
|
||||
const uint32_t type_attrs =
|
||||
(burm_int_ATTR | burm_float_ATTR |
|
||||
burm_long_ATTR | burm_double_ATTR);
|
||||
|
||||
if (brd->attrs & type_attrs)
|
||||
return brd->attrs & type_attrs;
|
||||
return attr;
|
||||
}
|
||||
brd++;
|
||||
}
|
||||
|
||||
fatal("unable to find a register matching attribute 0x%x", attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void constrain_output_reg(uint32_t attr)
|
||||
{
|
||||
struct vreg* vreg = current_hop->output;
|
||||
|
||||
if (!vreg)
|
||||
current_hop->output = vreg = new_vreg();
|
||||
|
||||
array_appendu(¤t_hop->outs, vreg);
|
||||
vreg->defined = current_hop;
|
||||
vreg->type = find_type_from_constraint(attr);
|
||||
|
||||
get_constraint(vreg)->attrs = attr;
|
||||
}
|
||||
|
||||
static void constrain_output_reg_equal_to(int child)
|
||||
{
|
||||
struct vreg* vreg = find_vreg_of_child(child);
|
||||
|
||||
get_constraint(current_hop->output)->equals_to = vreg;
|
||||
}
|
||||
|
||||
static const struct burm_emitter_data emitter_data =
|
||||
{
|
||||
&emit_string,
|
||||
&emit_fragment,
|
||||
&emit_return_reg,
|
||||
&emit_reg,
|
||||
&emit_value,
|
||||
&emit_eoi,
|
||||
&constrain_input_reg,
|
||||
&constrain_input_reg_preserved,
|
||||
&constrain_output_reg,
|
||||
&constrain_output_reg_equal_to,
|
||||
};
|
||||
|
||||
static void emit(struct insn* insn)
|
||||
{
|
||||
struct insn* old = current_insn;
|
||||
current_insn = insn;
|
||||
|
||||
insn->insndata->emitter(&emitter_data);
|
||||
|
||||
current_insn = old;
|
||||
}
|
||||
|
||||
static struct insn* walk_instructions(struct burm_node* node, int goal)
|
||||
{
|
||||
struct insn* insn = calloc(1, sizeof(*insn));
|
||||
int i;
|
||||
|
||||
insn->ir = node->ir;
|
||||
insn->num_children = 0;
|
||||
|
||||
if (goal)
|
||||
{
|
||||
int insn_no = burm_rule(node->state_label, goal);
|
||||
const short* nts = burm_nts[insn_no];
|
||||
struct burm_node* children[MAX_CHILDREN] = {0};
|
||||
|
||||
insn->insndata = &burm_instruction_data[insn_no];
|
||||
|
||||
burm_kids(node, insn_no, children);
|
||||
|
||||
i = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (!children[i])
|
||||
break;
|
||||
|
||||
insn->children[i] = walk_instructions(children[i], nts[i]);
|
||||
insn->num_children++;
|
||||
i++;
|
||||
}
|
||||
|
||||
tracef('I', "I: $%d goal %d %s selected %d: %s\n",
|
||||
node->ir->id,
|
||||
goal,
|
||||
insn->insndata->is_fragment ? "fragment" : "instruction",
|
||||
insn_no,
|
||||
insn->insndata->name);
|
||||
|
||||
if (!insn->insndata->is_fragment)
|
||||
{
|
||||
insn->hop = current_hop = new_hop(current_bb, insn->ir);
|
||||
current_hop->insndata = insn->insndata;
|
||||
emit(insn);
|
||||
|
||||
if (!current_hop->output)
|
||||
{
|
||||
switch (node->label)
|
||||
{
|
||||
case ir_to_esn(IR_REG, 0):
|
||||
current_hop->output = node->ir->result;
|
||||
assert(current_hop->output != NULL);
|
||||
break;
|
||||
|
||||
case ir_to_esn(IR_NOP, 'I'):
|
||||
case ir_to_esn(IR_NOP, 'F'):
|
||||
case ir_to_esn(IR_NOP, 'L'):
|
||||
case ir_to_esn(IR_NOP, 'D'):
|
||||
current_hop->output = node->left->ir->result;
|
||||
assert(current_hop->output != NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hop_print('I', current_hop);
|
||||
array_append(¤t_bb->hops, current_hop);
|
||||
|
||||
if ((goal != burm_stmt_NT) && !insn->ir->result)
|
||||
insn->ir->result = insn->hop->output;
|
||||
}
|
||||
}
|
||||
|
||||
return insn;
|
||||
}
|
||||
|
||||
static struct burm_node* build_shadow_tree(struct ir* root, struct ir* ir)
|
||||
{
|
||||
struct burm_node* node = calloc(1, sizeof(*node));
|
||||
node->ir = ir;
|
||||
|
||||
if (ir->root == root)
|
||||
{
|
||||
node->label = ir_to_esn(ir->opcode, ir->type);
|
||||
|
||||
if (ir->left)
|
||||
node->left = build_shadow_tree(root, ir->left);
|
||||
|
||||
if (ir->right)
|
||||
node->right = build_shadow_tree(root, ir->right);
|
||||
}
|
||||
else
|
||||
node->label = ir_to_esn(IR_REG, 0);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void select_instructions(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
tracef('I', "I: BLOCK: %s\n", current_bb->name);
|
||||
|
||||
for (i=0; i<current_bb->irs.count; i++)
|
||||
{
|
||||
struct burm_node* shadow;
|
||||
int insnno;
|
||||
|
||||
current_ir = current_bb->irs.item[i];
|
||||
|
||||
if (current_ir->opcode == IR_PHI)
|
||||
{
|
||||
int j;
|
||||
|
||||
current_ir->result = new_vreg();
|
||||
tracef('I', "I: $%d is phi:", current_ir->result->id);
|
||||
for (j=0; j<current_ir->u.phivalue.count; j++)
|
||||
{
|
||||
struct basicblock* parentbb = current_ir->u.phivalue.item[j].left;
|
||||
struct ir* parentir = current_ir->u.phivalue.item[j].right;
|
||||
struct phi* phi = calloc(1, sizeof(*phi));
|
||||
tracef('I', " %s=>$%d", parentbb->name, parentir->id);
|
||||
|
||||
phi->prev = parentbb;
|
||||
phi->ir = parentir;
|
||||
pmap_add(¤t_bb->phis, current_ir->result, phi);
|
||||
}
|
||||
tracef('I', "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
ir_print('I', current_ir);
|
||||
shadow = build_shadow_tree(current_ir, current_ir);
|
||||
burm_label(shadow);
|
||||
|
||||
insnno = burm_rule(shadow->state_label, 1);
|
||||
if (!insnno)
|
||||
burm_panic_cannot_match(shadow);
|
||||
|
||||
walk_instructions(shadow, burm_stmt_NT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pass_instruction_selector(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
current_bb = dominance.preorder.item[i];
|
||||
select_instructions();
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
75
mach/proto/mcg/pass_livevreganalysis.c
Normal file
75
mach/proto/mcg/pass_livevreganalysis.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static bool finished;
|
||||
|
||||
static void preload_blocks(void)
|
||||
{
|
||||
/* Any variable referenced in a phi *must* be a liveout of one of our
|
||||
* predecessors. */
|
||||
|
||||
int i, j;
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
|
||||
for (j=0; j<bb->phis.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[j].left;
|
||||
struct phi* phi = bb->phis.item[j].right;
|
||||
|
||||
assert(array_contains(&bb->prevs, phi->prev));
|
||||
array_appendu(&phi->prev->liveouts, phi->ir->result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void propagate_liveness(struct basicblock* bb)
|
||||
{
|
||||
static ARRAYOF(struct vreg) current;
|
||||
int i;
|
||||
|
||||
current.count = 0;
|
||||
array_appendall(¤t, &bb->liveouts);
|
||||
|
||||
for (i=bb->hops.count-1; i>=0; i--)
|
||||
{
|
||||
struct hop* hop = bb->hops.item[i];
|
||||
|
||||
array_removeall(¤t, &hop->outs);
|
||||
finished &= array_appendallu(&hop->throughs, ¤t);
|
||||
array_appendallu(¤t, &hop->ins);
|
||||
}
|
||||
|
||||
for (i=0; i<bb->phis.count; i++)
|
||||
array_remove(¤t, bb->phis.item[i].left);
|
||||
|
||||
finished &= array_appendallu(&bb->liveins, ¤t);
|
||||
|
||||
for (i=0; i<bb->prevs.count; i++)
|
||||
{
|
||||
struct basicblock* prev = bb->prevs.item[i];
|
||||
finished &= array_appendallu(&prev->liveouts, ¤t);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_live_vreg_analysis(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
preload_blocks();
|
||||
|
||||
do
|
||||
{
|
||||
finished = true;
|
||||
|
||||
tracef('L', "L: beginning liveness pass\n");
|
||||
for (i=0; i<dominance.postorder.count; i++)
|
||||
propagate_liveness(dominance.postorder.item[i]);
|
||||
}
|
||||
while (!finished);
|
||||
|
||||
//assert(cfg.entry->liveins.count == 0);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
103
mach/proto/mcg/pass_phigroups.c
Normal file
103
mach/proto/mcg/pass_phigroups.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static PMAPOF(struct vreg, struct vreg) phimap;
|
||||
|
||||
static void make_phimap(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
phimap.count = 0;
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
|
||||
for (j=0; j<bb->phis.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[j].left;
|
||||
struct phi* phi = bb->phis.item[j].right;
|
||||
struct vreg* prevvreg = phi->ir->result;
|
||||
|
||||
pmap_add(&phimap, vreg, prevvreg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void recursively_associate_group(struct phicongruence* c, struct vreg* vreg)
|
||||
{
|
||||
int i;
|
||||
|
||||
vreg->congruence = c;
|
||||
array_appendu(&c->vregs, vreg);
|
||||
tracef('V', "V: %%%d is a member of congruence group %d\n",
|
||||
vreg->id, c->id);
|
||||
|
||||
if (vreg->defined)
|
||||
{
|
||||
struct constraint* constraint = pmap_findleft(&vreg->defined->constraints, vreg);
|
||||
if (c->type == 0)
|
||||
c->type = vreg->type;
|
||||
|
||||
assert(c->type == vreg->type);
|
||||
|
||||
array_appendu(&c->definitions, vreg->defined);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct vreg* child = pmap_findleft(&phimap, vreg);
|
||||
if (!child)
|
||||
break;
|
||||
|
||||
pmap_remove(&phimap, vreg, child);
|
||||
recursively_associate_group(c, child);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct vreg* child = pmap_findright(&phimap, vreg);
|
||||
if (!child)
|
||||
break;
|
||||
|
||||
pmap_remove(&phimap, child, vreg);
|
||||
recursively_associate_group(c, child);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_vreg_types(struct phicongruence* c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<c->vregs.count; i++)
|
||||
{
|
||||
struct vreg* vreg = c->vregs.item[i];
|
||||
|
||||
if (vreg->type == 0)
|
||||
vreg->type = c->type;
|
||||
assert(vreg->type == c->type);
|
||||
assert(vreg->type != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void associate_groups(void)
|
||||
{
|
||||
static int number = 0;
|
||||
|
||||
while (phimap.count > 0)
|
||||
{
|
||||
struct phicongruence* c = calloc(1, sizeof(*c));
|
||||
c->id = number++;
|
||||
recursively_associate_group(c, phimap.item[0].left);
|
||||
update_vreg_types(c);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_find_phi_congruence_groups(void)
|
||||
{
|
||||
make_phimap();
|
||||
associate_groups();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
||||
|
44
mach/proto/mcg/pass_prologueepilogue.c
Normal file
44
mach/proto/mcg/pass_prologueepilogue.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "mcg.h"
|
||||
|
||||
void pass_add_prologue_epilogue(void)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
current_proc->usedregs.count = 0;
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
|
||||
for (j=0; j<bb->hops.count; j++)
|
||||
{
|
||||
struct hop* hop = bb->hops.item[j];
|
||||
|
||||
for (k=0; k<hop->regsin.count; k++)
|
||||
{
|
||||
struct hreg* hreg = hop->regsin.item[k].left;
|
||||
if (!hreg->is_stacked)
|
||||
array_appendu(¤t_proc->usedregs, hreg);
|
||||
}
|
||||
|
||||
for (k=0; k<hop->regsout.count; k++)
|
||||
{
|
||||
struct hreg* hreg = hop->regsout.item[k].left;
|
||||
if (!hreg->is_stacked)
|
||||
array_appendu(¤t_proc->usedregs, hreg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
platform_calculate_offsets();
|
||||
|
||||
array_insert(¤t_proc->entry->hops, platform_prologue(), 0);
|
||||
|
||||
if (current_proc->exit)
|
||||
{
|
||||
current_proc->exit->hops.count = 0;
|
||||
array_append(¤t_proc->exit->hops, platform_epilogue());
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
771
mach/proto/mcg/pass_registerallocator.c
Normal file
771
mach/proto/mcg/pass_registerallocator.c
Normal file
|
@ -0,0 +1,771 @@
|
|||
#include "mcg.h"
|
||||
|
||||
struct assignment
|
||||
{
|
||||
struct vreg* in;
|
||||
struct vreg* out;
|
||||
};
|
||||
|
||||
static ARRAYOF(struct hreg) hregs;
|
||||
|
||||
static PMAPOF(struct vreg, struct hreg) evicted;
|
||||
static struct hop* current_hop;
|
||||
static register_assignment_t* current_ins;
|
||||
static register_assignment_t* current_outs;
|
||||
|
||||
static int insert_moves(struct basicblock* bb, int index,
|
||||
register_assignment_t* srcregs, register_assignment_t* destregs);
|
||||
|
||||
static bool type_match(struct hreg* hreg, struct vreg* vreg);
|
||||
|
||||
static void populate_hregs(void)
|
||||
{
|
||||
int i;
|
||||
const struct burm_register_data* brd = burm_register_data;
|
||||
|
||||
hregs.count = 0;
|
||||
while (brd->id)
|
||||
{
|
||||
array_append(&hregs, new_hreg(brd));
|
||||
brd++;
|
||||
}
|
||||
|
||||
/* Wire up the register aliases. */
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = hregs.item[i];
|
||||
const struct burm_register_data** alias = hreg->brd->aliases;
|
||||
|
||||
while (*alias)
|
||||
{
|
||||
int index = *alias - burm_register_data;
|
||||
array_append(&hreg->aliases, hregs.item[index]);
|
||||
alias++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wire_up_blocks_ins_outs(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = dominance.preorder.item[i];
|
||||
assert(bb->hops.count >= 1);
|
||||
bb->regsout = &bb->hops.item[bb->hops.count-1]->regsout;
|
||||
}
|
||||
}
|
||||
|
||||
static bool register_used(register_assignment_t* regs, struct hreg* hreg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<hreg->aliases.count; i++)
|
||||
{
|
||||
struct hreg* alias = hreg->aliases.item[i];
|
||||
if (pmap_findleft(regs, alias))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct hreg* allocate_phi_hreg(register_assignment_t* regs,
|
||||
struct vreg* vreg, uint32_t type)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* We need a new register at the beginning of the block to put a phi value
|
||||
* into. */
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = hregs.item[i];
|
||||
if (!register_used(regs, hreg) && (hreg->attrs & type))
|
||||
{
|
||||
/* This one is unused. Use it. */
|
||||
return hreg;
|
||||
}
|
||||
}
|
||||
|
||||
/* We'll need to allocate a new stack slot for it. */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static struct hreg* evict(struct vreg* vreg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Look for a through register which matches our requirements. We should be
|
||||
* doing some calculation here to figure out the cheapest register to
|
||||
* evict, but for now we're picking the first one. FIXME. */
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = hregs.item[i];
|
||||
struct vreg* candidatein = pmap_findleft(current_ins, hreg);
|
||||
struct vreg* candidateout = pmap_findleft(current_outs, hreg);
|
||||
|
||||
if (hreg->attrs & vreg->type)
|
||||
{
|
||||
if (!candidatein &&
|
||||
!candidateout &&
|
||||
!register_used(current_ins, hreg) &&
|
||||
!register_used(current_outs, hreg))
|
||||
{
|
||||
/* This hreg is unused, so we don't need to evict anything.
|
||||
* Shouldn't really happen in real life. */
|
||||
return hreg;
|
||||
}
|
||||
if (candidatein && candidateout && (candidatein == candidateout))
|
||||
{
|
||||
/* This is a through register. */
|
||||
tracef('R', "R: evicting %%%d from %s\n", candidatein->id, hreg->id);
|
||||
pmap_put(&evicted, candidatein, hreg);
|
||||
pmap_remove(current_ins, hreg, candidatein);
|
||||
pmap_remove(current_outs, hreg, candidatein);
|
||||
return hreg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find anything to evict */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static bool constraints_match(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||
if (c)
|
||||
return (hreg->attrs & c->attrs);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool allocatable_stackable_input(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
return !register_used(current_ins, hreg) &&
|
||||
(hreg->attrs & vreg->type);
|
||||
}
|
||||
|
||||
static bool allocatable_stackable_output(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
return !register_used(current_outs, hreg) &&
|
||||
(hreg->attrs & vreg->type);
|
||||
}
|
||||
|
||||
static bool allocatable_input(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
return allocatable_stackable_input(hreg, vreg) &&
|
||||
constraints_match(hreg, vreg) &&
|
||||
!hreg->is_stacked;
|
||||
}
|
||||
|
||||
static bool allocatable_output(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
return allocatable_stackable_output(hreg, vreg) &&
|
||||
constraints_match(hreg, vreg) &&
|
||||
!hreg->is_stacked;
|
||||
}
|
||||
|
||||
static bool allocatable_through(struct hreg* hreg, struct vreg* vreg)
|
||||
{
|
||||
return allocatable_stackable_input(hreg, vreg) &&
|
||||
allocatable_stackable_output(hreg, vreg) &&
|
||||
!(hreg->attrs & current_hop->insndata->corrupts);
|
||||
}
|
||||
|
||||
static struct hreg* find_input_reg(struct vreg* vreg)
|
||||
{
|
||||
int i;
|
||||
struct hreg* hreg = NULL;
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
hreg = hregs.item[i];
|
||||
if (allocatable_input(hreg, vreg))
|
||||
{
|
||||
return hreg;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hreg* find_output_reg(struct vreg* vreg)
|
||||
{
|
||||
int i;
|
||||
struct hreg* hreg = NULL;
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
hreg = hregs.item[i];
|
||||
if (allocatable_output(hreg, vreg))
|
||||
return hreg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hreg* find_through_reg(struct vreg* vreg)
|
||||
{
|
||||
int i;
|
||||
struct hreg* hreg = NULL;
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
hreg = hregs.item[i];
|
||||
if (allocatable_through(hreg, vreg))
|
||||
{
|
||||
return hreg;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_input_register(struct vreg* vreg, struct hreg* hreg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Register hint for an input? */
|
||||
|
||||
if (hreg)
|
||||
{
|
||||
if (hreg->is_stacked)
|
||||
{
|
||||
/* This vreg is stacked; we need to put it in a register. That's
|
||||
* slightly exciting because the vreg might be a through, which
|
||||
* means we need an output register too... which we might not be
|
||||
* able to allocate. */
|
||||
|
||||
if (array_contains(¤t_hop->throughs, vreg))
|
||||
{
|
||||
struct hreg* src = hreg;
|
||||
hreg = find_through_reg(vreg);
|
||||
assert(hreg);
|
||||
pmap_remove(current_ins, src, vreg);
|
||||
pmap_remove(current_outs, src, vreg);
|
||||
pmap_add(current_ins, hreg, vreg);
|
||||
pmap_add(current_outs, hreg, vreg);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not a through. */
|
||||
pmap_remove(current_ins, hreg, vreg);
|
||||
hreg = NULL;
|
||||
}
|
||||
}
|
||||
else if (pmap_findleft(current_ins, hreg) == vreg)
|
||||
{
|
||||
/* Yup, already there. */
|
||||
}
|
||||
else if (allocatable_input(hreg, vreg))
|
||||
{
|
||||
/* The register is free. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Can't honour the hint. */
|
||||
hreg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hreg)
|
||||
{
|
||||
/* Find an unused input register of the right class. */
|
||||
|
||||
hreg = find_input_reg(vreg);
|
||||
if (!hreg)
|
||||
hreg = evict(vreg);
|
||||
}
|
||||
|
||||
pmap_add(current_ins, hreg, vreg);
|
||||
}
|
||||
|
||||
static void add_output_register(struct vreg* vreg)
|
||||
{
|
||||
struct hreg* hreg;
|
||||
int i;
|
||||
struct constraint* c;
|
||||
|
||||
/* Is this register supposed to be the same as one of the input registers?
|
||||
* */
|
||||
|
||||
c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||
if (c->equals_to)
|
||||
{
|
||||
tracef('R', "R: output equality constraint of %%%d to %%%d\n",
|
||||
vreg->id, c->equals_to->id);
|
||||
|
||||
/* This output register is constrained to be in the same hreg as an
|
||||
* input register (most likely for a 2op instruction). */
|
||||
|
||||
hreg = pmap_findright(current_ins, c->equals_to);
|
||||
|
||||
/* If this register is currently unused as an output, use it. */
|
||||
|
||||
if (allocatable_output(hreg, c->equals_to))
|
||||
{
|
||||
pmap_add(current_outs, hreg, vreg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Okay, something's in it. Most likely it's a through being used as an
|
||||
* input register. Trying to evict it would be pointless as that would
|
||||
* also evict the input. So, we're going to have to do this the hard
|
||||
* way: we try to allocate a matched set of input and output registers.
|
||||
* */
|
||||
|
||||
hreg = find_through_reg(vreg);
|
||||
if (!hreg)
|
||||
hreg = evict(vreg);
|
||||
|
||||
pmap_add(current_outs, hreg, vreg);
|
||||
tracef('R', "R: output equality constraint requires extra move of %%%d => %s\n",
|
||||
c->equals_to->id, hreg->id);
|
||||
pmap_add(current_ins, hreg, c->equals_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an ordinary new register. */
|
||||
|
||||
hreg = find_output_reg(vreg);
|
||||
if (!hreg)
|
||||
hreg = evict(vreg);
|
||||
|
||||
pmap_add(current_outs, hreg, vreg);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_through_register(struct vreg* vreg, struct hreg* hreg)
|
||||
{
|
||||
/* Register hint for an input? */
|
||||
|
||||
if (hreg)
|
||||
{
|
||||
bool unused = allocatable_through(hreg, vreg);
|
||||
struct vreg* inuse = pmap_findleft(current_ins, hreg);
|
||||
struct vreg* outuse = pmap_findleft(current_outs, hreg);
|
||||
|
||||
if (unused || ((inuse == vreg) && (outuse == vreg)))
|
||||
{
|
||||
/* Input and output are either free or already assigned to this
|
||||
* vreg. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nope, can't honour the hint. Mark the register as evicted; we'll
|
||||
* put it in something later (probably a stack slot). */
|
||||
|
||||
tracef('R', "R: cannot place %%%d in %s, evicting\n", vreg->id, hreg->id);
|
||||
pmap_put(&evicted, vreg, hreg);
|
||||
pmap_remove(current_ins, hreg, vreg);
|
||||
pmap_remove(current_outs, hreg, vreg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(hreg);
|
||||
pmap_put(current_ins, hreg, vreg);
|
||||
pmap_put(current_outs, hreg, vreg);
|
||||
}
|
||||
|
||||
static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* src)
|
||||
{
|
||||
uint32_t srctype = vreg->type;
|
||||
struct hreg* hreg;
|
||||
int i;
|
||||
|
||||
/* Find an unused output register of the right class which is not also
|
||||
* being used as an input register. */
|
||||
|
||||
hreg = NULL;
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
hreg = hregs.item[i];
|
||||
if ((hreg->attrs & srctype) &&
|
||||
allocatable_through(hreg, vreg))
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
/* No more registers --- allocate a stack slot. */
|
||||
|
||||
hreg = new_stacked_hreg(srctype);
|
||||
array_append(&hregs, hreg);
|
||||
|
||||
found:
|
||||
tracef('R', "R: evicted %%%d moving to %s\n", vreg->id, hreg->id);
|
||||
pmap_add(current_ins, hreg, vreg);
|
||||
pmap_add(current_outs, hreg, vreg);
|
||||
}
|
||||
|
||||
static void select_registers(struct hop* hop,
|
||||
register_assignment_t* old, register_assignment_t* in, register_assignment_t* out)
|
||||
{
|
||||
int i;
|
||||
|
||||
current_hop = hop;
|
||||
current_ins = in;
|
||||
current_outs = out;
|
||||
evicted.count = 0;
|
||||
|
||||
/* First, any vregs passing through the instruction stay in the same
|
||||
* registers they are currently in. */
|
||||
|
||||
for (i=0; i<hop->throughs.count; i++)
|
||||
{
|
||||
struct vreg* vreg = hop->throughs.item[i];
|
||||
struct hreg* hreg = pmap_findright(old, vreg);
|
||||
add_through_register(vreg, hreg);
|
||||
}
|
||||
|
||||
/* Any registers being *read* by the instruction should also stay where
|
||||
* they are. (This is likely to duplicate some throughs.) */
|
||||
|
||||
for (i=0; i<hop->ins.count; i++)
|
||||
{
|
||||
struct vreg* vreg = hop->ins.item[i];
|
||||
struct hreg* hreg = pmap_findright(old, vreg);
|
||||
add_input_register(vreg, hreg);
|
||||
}
|
||||
|
||||
/* Any output registers will be *new* vregs (because SSA). So, allocate
|
||||
* new hregs for them. */
|
||||
|
||||
for (i=0; i<hop->outs.count; i++)
|
||||
{
|
||||
struct vreg* vreg = hop->outs.item[i];
|
||||
add_output_register(vreg);
|
||||
}
|
||||
|
||||
/* Any evicted registers now need to go somewhere (potentially, the stack).
|
||||
* */
|
||||
|
||||
for (i=0; i<evicted.count; i++)
|
||||
{
|
||||
struct vreg* vreg = evicted.item[i].left;
|
||||
struct hreg* src = evicted.item[i].right;
|
||||
find_new_home_for_evicted_register(vreg, src);
|
||||
}
|
||||
}
|
||||
|
||||
static void assign_hregs_to_vregs(void)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = dominance.preorder.item[i];
|
||||
register_assignment_t* old = &bb->regsin;
|
||||
|
||||
tracef('R', "R: considering block %s\n", bb->name);
|
||||
|
||||
/* Attempt to import any block input registers from a predecessor. At
|
||||
* least one predecessor should export it; our graph traversal order
|
||||
* guarantees it. */
|
||||
|
||||
for (j=0; j<bb->liveins.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->liveins.item[j];
|
||||
for (k=0; k<bb->prevs.count; k++)
|
||||
{
|
||||
struct basicblock* prevbb = bb->prevs.item[k];
|
||||
struct hreg* hreg = pmap_findright(prevbb->regsout, vreg);
|
||||
if (hreg)
|
||||
{
|
||||
tracef('R', "R: import hreg %s for input %%%d from %s\n",
|
||||
hreg->id, vreg->id, prevbb->name);
|
||||
assert(!pmap_findleft(old, hreg));
|
||||
pmap_put(old, hreg, vreg);
|
||||
goto nextvreg;
|
||||
}
|
||||
}
|
||||
|
||||
fatal("couldn't find a register assignment for $%d", vreg->id);
|
||||
nextvreg:;
|
||||
}
|
||||
|
||||
/* And now do the same for the phis. Unlike input vregs, a phi can
|
||||
* import a vreg from multiple locations. However, we don't care which
|
||||
* one we find, as this is just a hint. Picking the same register as a
|
||||
* predecessor helps reduce the number of copies the SSA deconstruction
|
||||
* pass will need to insert. */
|
||||
|
||||
for (j=0; j<bb->phis.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[j].left;
|
||||
struct phi* phi = bb->phis.item[j].right;
|
||||
if (!pmap_findright(old, vreg))
|
||||
{
|
||||
/* This variable isn't allocated yet. */
|
||||
|
||||
struct hreg* hreg = pmap_findright(
|
||||
phi->prev->regsout, phi->ir->result);
|
||||
if (hreg && !pmap_findleft(old, hreg))
|
||||
{
|
||||
tracef('R', "R: import hreg %s for %%%d, imported from %s %%%d\n",
|
||||
hreg->id, vreg->id,
|
||||
phi->prev->name, phi->ir->id);
|
||||
pmap_put(old, hreg, vreg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* It's possible for the previous stage to fail because in in has
|
||||
* clobbered the physical register we were wanting. So we need to
|
||||
* allocate a new register for that phi value.
|
||||
*/
|
||||
|
||||
for (j=0; j<bb->phis.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[j].left;
|
||||
struct phi* phi = bb->phis.item[j].right;
|
||||
if (!pmap_findright(old, vreg))
|
||||
{
|
||||
struct phicongruence* c = vreg->congruence;
|
||||
struct hreg* hreg = allocate_phi_hreg(old, vreg, c->type);
|
||||
|
||||
tracef('R', "R: import fallback hreg %s for %%%d, imported from %s %%%d\n",
|
||||
hreg->id, vreg->id,
|
||||
phi->prev->name, phi->ir->id);
|
||||
pmap_add(old, hreg, vreg);
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0; j<bb->hops.count; j++)
|
||||
{
|
||||
struct hop* hop = bb->hops.item[j];
|
||||
register_assignment_t* in = &hop->regsin;
|
||||
register_assignment_t* out = &hop->regsout;;
|
||||
|
||||
hop_print('R', hop);
|
||||
|
||||
select_registers(hop, old, in, out);
|
||||
|
||||
tracef('R', "R: %d from $%d: [", hop->id, hop->ir->id);
|
||||
for (k=0; k<hop->regsin.count; k++)
|
||||
{
|
||||
struct hreg* hreg = hop->regsin.item[k].left;
|
||||
struct vreg* vreg = hop->regsin.item[k].right;
|
||||
if (k != 0)
|
||||
tracef('R', " ");
|
||||
tracef('R', "%%%d=>%s", vreg->id, hreg->id);
|
||||
}
|
||||
tracef('R', "] [");
|
||||
for (k=0; k<hop->regsout.count; k++)
|
||||
{
|
||||
struct hreg* hreg = hop->regsout.item[k].left;
|
||||
struct vreg* vreg = hop->regsout.item[k].right;
|
||||
if (k != 0)
|
||||
tracef('R', " ");
|
||||
tracef('R', "%%%d=>%s", vreg->id, hreg->id);
|
||||
}
|
||||
tracef('R', "]\n");
|
||||
|
||||
j += insert_moves(bb, j, old, in);
|
||||
|
||||
old = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* returns the number of instructions inserted */
|
||||
static int insert_moves(struct basicblock* bb, int index,
|
||||
register_assignment_t* srcregs, register_assignment_t* destregs)
|
||||
{
|
||||
int i;
|
||||
int inserted = 0;
|
||||
static PMAPOF(struct hreg, struct hreg) copies;
|
||||
|
||||
copies.count = 0;
|
||||
for (i=0; i<destregs->count; i++)
|
||||
{
|
||||
struct hreg* dest = destregs->item[i].left;
|
||||
struct vreg* vreg = destregs->item[i].right;
|
||||
struct hreg* src = pmap_findright(srcregs, vreg);
|
||||
assert(src != NULL);
|
||||
|
||||
pmap_add(&copies, src, dest);
|
||||
}
|
||||
|
||||
while (copies.count > 0)
|
||||
{
|
||||
struct hreg* src;
|
||||
struct hreg* dest;
|
||||
struct hreg* other;
|
||||
struct hop* hop;
|
||||
|
||||
/* Try and find a destination which isn't a source. */
|
||||
|
||||
src = NULL;
|
||||
for (i=0; i<copies.count; i++)
|
||||
{
|
||||
dest = copies.item[i].right;
|
||||
if (!pmap_findleft(&copies, dest))
|
||||
{
|
||||
src = copies.item[i].left;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (src)
|
||||
{
|
||||
/* Copy. */
|
||||
|
||||
hop = platform_move(bb, src, dest);
|
||||
pmap_remove(&copies, src, dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Okay, so there's nowhere to free to move src to. This could be
|
||||
* because it's already in the right place. */
|
||||
|
||||
src = copies.item[0].left;
|
||||
dest = pmap_findleft(&copies, src);
|
||||
|
||||
if (src == dest)
|
||||
{
|
||||
/* This register is already in the right place! */
|
||||
|
||||
pmap_remove(&copies, src, dest);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's not in the right place. That means we have a cycle, and need to do
|
||||
* a swap. */
|
||||
|
||||
/* (src->dest, other->src) */
|
||||
hop = platform_swap(bb, src, dest);
|
||||
pmap_remove(&copies, src, dest);
|
||||
|
||||
/* Now src and dest are swapped. We know that the old src is in the right place
|
||||
* and now contains dest. Any copies from the old dest (now containing src) must
|
||||
* be patched to point at the old src. */
|
||||
|
||||
for (i=0; i<copies.count; i++)
|
||||
{
|
||||
if (copies.item[i].right == src)
|
||||
copies.item[i].right = dest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array_insert(&bb->hops, hop, index + inserted);
|
||||
inserted++;
|
||||
}
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
static void insert_phi_copies(void)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
/* If we're importing an hreg from a parent block via a phi, insert a move
|
||||
* at the end of the parent block to put the result into the right
|
||||
* register. */
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
|
||||
/* Group together copies from each predecessor, so we can generate the
|
||||
* appropriate parallel move. */
|
||||
|
||||
for (j=0; j<bb->prevs.count; j++)
|
||||
{
|
||||
struct basicblock* prevbb = bb->prevs.item[j];
|
||||
static register_assignment_t destregs;
|
||||
|
||||
tracef('R', "R: inserting phis for %s -> %s\n",
|
||||
prevbb->name, bb->name);
|
||||
destregs.count = 0;
|
||||
for (k=0; k<bb->phis.count; k++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[k].left;
|
||||
struct phi* phi = bb->phis.item[k].right;
|
||||
struct hreg* src = pmap_findright(prevbb->regsout, phi->ir->result);
|
||||
struct hreg* dest = pmap_findright(&bb->regsin, vreg);
|
||||
|
||||
if ((phi->prev == prevbb) && dest)
|
||||
{
|
||||
/* We inserted critical edges to guarantee this. */
|
||||
assert(prevbb->nexts.count == 1);
|
||||
|
||||
tracef('R', "R: phi map %%%d (%s) -> %%%d (%s)\n",
|
||||
phi->ir->result->id, src->id,
|
||||
vreg->id, dest->id);
|
||||
|
||||
pmap_put(&destregs, dest, phi->ir->result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add any non-phi inputs. */
|
||||
|
||||
for (k=0; k<bb->regsin.count; k++)
|
||||
{
|
||||
struct hreg*hreg = bb->regsin.item[k].left;
|
||||
struct vreg* vreg = bb->regsin.item[k].right;
|
||||
struct hreg* src = pmap_findright(prevbb->regsout, vreg);
|
||||
if (!pmap_findleft(&bb->phis, vreg))
|
||||
{
|
||||
tracef('R', "R: input map %%%d (%s) -> (%s)\n",
|
||||
vreg->id, src->id, hreg->id);
|
||||
|
||||
pmap_add(&destregs, hreg, vreg);
|
||||
}
|
||||
}
|
||||
|
||||
/* The last instruction of a block should be the jump that sends us
|
||||
* to the next block. Insert the moves before then. */
|
||||
|
||||
insert_moves(prevbb, prevbb->hops.count-1, prevbb->regsout, &destregs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pack_stackframe(int stacksize, int size, uint32_t attr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<hregs.count; i++)
|
||||
{
|
||||
struct hreg* hreg = hregs.item[i];
|
||||
if (hreg->is_stacked && (hreg->attrs & attr))
|
||||
{
|
||||
hreg->offset = stacksize;
|
||||
stacksize += size;
|
||||
}
|
||||
}
|
||||
|
||||
return stacksize;
|
||||
}
|
||||
|
||||
static void layout_stack_frame(void)
|
||||
{
|
||||
int stacksize = 0;
|
||||
stacksize = pack_stackframe(stacksize, EM_wordsize*2, burm_double_ATTR);
|
||||
stacksize = pack_stackframe(stacksize, EM_wordsize*2, burm_long_ATTR);
|
||||
stacksize = pack_stackframe(stacksize, EM_wordsize*1, burm_float_ATTR);
|
||||
stacksize = pack_stackframe(stacksize, EM_wordsize*1, burm_int_ATTR);
|
||||
current_proc->spills_size = stacksize;
|
||||
}
|
||||
|
||||
void pass_register_allocator(void)
|
||||
{
|
||||
populate_hregs();
|
||||
wire_up_blocks_ins_outs();
|
||||
|
||||
assign_hregs_to_vregs();
|
||||
insert_phi_copies();
|
||||
layout_stack_frame();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
40
mach/proto/mcg/pass_removedeadblocks.c
Normal file
40
mach/proto/mcg/pass_removedeadblocks.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static ARRAYOF(struct basicblock) used;
|
||||
|
||||
static void walk_blocks(struct basicblock* bb);
|
||||
|
||||
static bool walk_blocks_cb(struct ir* ir, void* user)
|
||||
{
|
||||
if (ir->opcode == IR_BLOCK)
|
||||
walk_blocks(ir->u.bvalue);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void walk_blocks(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!array_contains(&used, bb))
|
||||
{
|
||||
array_append(&used, bb);
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
ir_walk(bb->irs.item[i], walk_blocks_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_remove_dead_blocks(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
used.count = 0;
|
||||
walk_blocks(current_proc->blocks.item[0]);
|
||||
|
||||
current_proc->blocks.count = 0;
|
||||
for (i=0; i<used.count; i++)
|
||||
array_append(¤t_proc->blocks, used.item[i]);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
88
mach/proto/mcg/pass_removedeadphis.c
Normal file
88
mach/proto/mcg/pass_removedeadphis.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static ARRAYOF(struct ir) phis;
|
||||
static bool changed;
|
||||
|
||||
static void collect_phis(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
if (ir->opcode == IR_PHI)
|
||||
array_append(&phis, ir);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ir_walker_cb(struct ir* ir, void* user)
|
||||
{
|
||||
if (ir->left)
|
||||
array_remove(&phis, ir->left);
|
||||
if (ir->right)
|
||||
array_remove(&phis, ir->right);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void remove_referenced_phis(struct basicblock* bb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
switch (ir->opcode)
|
||||
{
|
||||
case IR_PHI:
|
||||
for (j=0; j<ir->u.phivalue.count; j++)
|
||||
array_remove(&phis, ir->u.phivalue.item[j].right);
|
||||
break;
|
||||
|
||||
default:
|
||||
ir_walk(ir, ir_walker_cb, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void purge_unused_phis(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
if ((ir->opcode == IR_PHI) && (array_contains(&phis, ir)))
|
||||
{
|
||||
array_remove(&bb->irs, ir);
|
||||
i--;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pass_remove_dead_phis(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
|
||||
phis.count = 0;
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
collect_phis(cfg.preorder.item[i]);
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
remove_referenced_phis(cfg.preorder.item[i]);
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
purge_unused_phis(cfg.preorder.item[i]);
|
||||
}
|
||||
while (changed);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
123
mach/proto/mcg/pass_returnvalues.c
Normal file
123
mach/proto/mcg/pass_returnvalues.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "mcg.h"
|
||||
|
||||
/* The ACK returns values from functions not on the stack but in a special
|
||||
* 'register' which are read with lfr. This register is defined to survive
|
||||
* asp, bra and gto. The way it's intended to work is that value just gets put
|
||||
* in a particular hreg and stays there until lfr brings it to the attention of
|
||||
* the code generator.
|
||||
*
|
||||
* Trouble is, while that worked on ncg, it doesn't work here because the
|
||||
* register allocator may decide to insert moves arbitrarily. So we need to
|
||||
* somehow turn this special register into a real register so that it can be
|
||||
* kept alive.
|
||||
*
|
||||
* The easiest thing to do is to just push the result of call onto the stack...
|
||||
* but that doesn't work either, because if someone does a call without an lfr,
|
||||
* we don't want to be left with an unpopped value.
|
||||
*
|
||||
* So what we do is we find lfrs, and then we search for the call which
|
||||
* generated the value, and then we hook up the IRs so there's a connection
|
||||
* between the two. But beware! The lfr value survives bra! Which means a
|
||||
* single lfr may actually read the value produced by *several* call
|
||||
* instructions. You know what that means? Phis.
|
||||
*
|
||||
* (Luckily a single call instruction can't be read by multiple lfrs, because
|
||||
* conditional branches trash the lfr value.)
|
||||
*/
|
||||
|
||||
static void find_call(struct basicblock* bb, int index, struct ir* lfr,
|
||||
struct basicblock** callbb, struct ir** callir)
|
||||
{
|
||||
if (index == -1)
|
||||
index = bb->irs.count - 1;
|
||||
|
||||
while (index >= 0)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[index];
|
||||
switch (ir->opcode)
|
||||
{
|
||||
case IR_CALL:
|
||||
ir->size = lfr->size;
|
||||
*callbb = bb;
|
||||
*callir = ir;
|
||||
return;
|
||||
|
||||
case IR_STACKADJUST:
|
||||
case IR_GETRET:
|
||||
case IR_JUMP:
|
||||
/* lfr value preserved */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* lfr value has been corrupted. */
|
||||
fatal("lfr reading corrupted value in %s", bb->name);
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
/* Our search hit the top of the block; we need to import the
|
||||
* lfr value from a previous block. */
|
||||
|
||||
if (bb->prevs.count == 1)
|
||||
{
|
||||
/* Only a single predecessor, so no phi is necessary. */
|
||||
|
||||
find_call(bb->prevs.item[0], -1, lfr, callbb, callir);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have multiple predecessors. This means that the lfr value may
|
||||
* come from any of these blocks. We need a phi. */
|
||||
|
||||
int i;
|
||||
struct ir* phi = new_ir0(IR_PHI, lfr->size);
|
||||
|
||||
phi->root = phi;
|
||||
array_insert(&bb->irs, phi, 0);
|
||||
|
||||
for (i=0; i<bb->prevs.count; i++)
|
||||
{
|
||||
struct basicblock* prev = bb->prevs.item[i];
|
||||
struct basicblock* parentbb;
|
||||
struct ir* parentir;
|
||||
|
||||
find_call(prev, -1, phi, &parentbb, &parentir);
|
||||
|
||||
pmap_add(&phi->u.phivalue, parentbb, parentir);
|
||||
}
|
||||
|
||||
*callbb = bb;
|
||||
*callir = phi;
|
||||
}
|
||||
}
|
||||
|
||||
static void wire_up_ir(struct basicblock* bb, int index)
|
||||
{
|
||||
struct ir* lfr = bb->irs.item[index];
|
||||
struct basicblock* callbb;
|
||||
struct ir* callir;
|
||||
|
||||
find_call(bb, index, lfr, &callbb, &callir);
|
||||
|
||||
lfr->left = callir;
|
||||
lfr->opcode = IR_NOP;
|
||||
}
|
||||
|
||||
void pass_wire_up_return_values(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[j];
|
||||
if (ir->opcode == IR_GETRET)
|
||||
wire_up_ir(bb, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
103
mach/proto/mcg/pass_splitcriticaledges.c
Normal file
103
mach/proto/mcg/pass_splitcriticaledges.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include "mcg.h"
|
||||
|
||||
/* Insert empty nodes at certain places in the basic block graph so that when
|
||||
* we convert out of SSA form, we have somewhere to insert copies. This is
|
||||
* necessary for correctness in certain circumstances. The best explanation of
|
||||
* why I've found is here, starting at the bottom of page 23.
|
||||
*
|
||||
* Briggs, Preston, et al.
|
||||
* "Practical improvements to the construction and destruction of static single assignment form."
|
||||
* Software-Practice and experience 28.8 (1998): 859-882.
|
||||
*
|
||||
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.25.749
|
||||
*/
|
||||
|
||||
struct rewrite_params
|
||||
{
|
||||
struct basicblock* find;
|
||||
struct basicblock* replace;
|
||||
};
|
||||
|
||||
static bool find_replace_cb(struct ir* ir, void* user)
|
||||
{
|
||||
struct rewrite_params* rwp = user;
|
||||
|
||||
if ((ir->opcode == IR_BLOCK) && (ir->u.bvalue == rwp->find))
|
||||
ir->u.bvalue = rwp->replace;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void split_edge(struct basicblock* source, struct basicblock* sink)
|
||||
{
|
||||
int i;
|
||||
struct rewrite_params rwp;
|
||||
struct basicblock* bb = bb_get(NULL);
|
||||
|
||||
struct ir* jump =
|
||||
new_ir1(
|
||||
IR_JUMP, 0,
|
||||
new_bbir(sink)
|
||||
);
|
||||
|
||||
jump->root = jump->left->root = jump;
|
||||
array_append(&bb->irs, jump);
|
||||
|
||||
rwp.find = sink;
|
||||
rwp.replace = bb;
|
||||
|
||||
for (i=0; i<source->irs.count; i++)
|
||||
ir_walk(source->irs.item[i], find_replace_cb, &rwp);
|
||||
|
||||
array_remove(&source->nexts, sink);
|
||||
array_append(&source->nexts, bb);
|
||||
|
||||
array_append(&bb->prevs, source);
|
||||
array_append(&bb->nexts, sink);
|
||||
|
||||
array_remove(&sink->prevs, source);
|
||||
array_append(&sink->prevs, bb);
|
||||
|
||||
array_append(¤t_proc->blocks, bb);
|
||||
}
|
||||
|
||||
static bool consider_edges_leading_from(struct basicblock* bb)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (bb->nexts.count > 1)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<bb->nexts.count; i++)
|
||||
{
|
||||
struct basicblock* next = bb->nexts.item[i];
|
||||
if (next->prevs.count > 1)
|
||||
{
|
||||
split_edge(bb, next);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void pass_split_critical_edges(void)
|
||||
{
|
||||
int i;
|
||||
bool changed;
|
||||
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
changed |= consider_edges_leading_from(current_proc->blocks.item[i]);
|
||||
|
||||
}
|
||||
while (changed);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
234
mach/proto/mcg/pass_ssa.c
Normal file
234
mach/proto/mcg/pass_ssa.c
Normal file
|
@ -0,0 +1,234 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static PMAPOF(struct basicblock, struct basicblock) dominancefrontiers;
|
||||
|
||||
static struct local* current_local;
|
||||
static ARRAYOF(struct basicblock) defining;
|
||||
static ARRAYOF(struct basicblock) needsphis;
|
||||
static ARRAYOF(struct ir) definitions;
|
||||
static ARRAYOF(struct basicblock) rewritten;
|
||||
|
||||
static void calculate_dominance_frontier_graph(void)
|
||||
{
|
||||
/* This is the algorithm described here:
|
||||
*
|
||||
* Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy.
|
||||
* "A simple, fast dominance algorithm."
|
||||
* Software Practice & Experience 4.1-10 (2001): 1-8.
|
||||
*
|
||||
* https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
*/
|
||||
|
||||
int i, j;
|
||||
|
||||
dominancefrontiers.count = 0;
|
||||
|
||||
for (i=0; i<cfg.postorder.count; i++)
|
||||
{
|
||||
struct basicblock* b = cfg.postorder.item[i];
|
||||
struct basicblock* dominator = pmap_findleft(&dominance.graph, b);
|
||||
if (b->prevs.count >= 2)
|
||||
{
|
||||
for (j=0; j<b->prevs.count; j++)
|
||||
{
|
||||
struct basicblock* runner = b->prevs.item[j];
|
||||
while (runner != dominator)
|
||||
{
|
||||
tracef('S', "S: %s is in %s's dominance frontier\n",
|
||||
b->name, runner->name);
|
||||
pmap_add(&dominancefrontiers, runner, b);
|
||||
runner = pmap_findleft(&dominance.graph, runner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool is_local(struct ir* ir)
|
||||
{
|
||||
return ((ir->opcode == IR_LOAD) &&
|
||||
(ir->left->opcode == IR_LOCAL) &&
|
||||
(ir->left->u.ivalue == current_local->offset));
|
||||
}
|
||||
|
||||
static bool rewrite_loads_cb(struct ir* ir, void* user)
|
||||
{
|
||||
struct ir* definition = user;
|
||||
|
||||
/* Rewrite in place where possible. */
|
||||
|
||||
if (ir->left && is_local(ir->left))
|
||||
ir->left = definition;
|
||||
if (ir->right && is_local(ir->right))
|
||||
ir->right = definition;
|
||||
|
||||
/* Otherwise, go via a IR_NOP (which should, with luck, turn into no code). */
|
||||
if (is_local(ir))
|
||||
{
|
||||
ir->opcode = IR_NOP;
|
||||
ir->left = definition;
|
||||
ir->right = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Walks the tree, rewriting IRs to push new definitions downwards. */
|
||||
|
||||
static void recursively_rewrite_tree(struct basicblock* bb)
|
||||
{
|
||||
int i;
|
||||
int defcount = definitions.count;
|
||||
|
||||
if (array_contains(&rewritten, bb))
|
||||
return;
|
||||
array_appendu(&rewritten, bb);
|
||||
|
||||
for (i=0; i<bb->irs.count; i++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[i];
|
||||
|
||||
if (definitions.count > 0)
|
||||
{
|
||||
ir_walk(ir, rewrite_loads_cb, definitions.item[definitions.count-1]);
|
||||
}
|
||||
|
||||
if (((ir->opcode == IR_STORE) &&
|
||||
(ir->left->opcode == IR_LOCAL) &&
|
||||
(ir->left->u.ivalue == current_local->offset)
|
||||
) ||
|
||||
((i == 0) &&
|
||||
(ir->opcode == IR_PHI) &&
|
||||
array_contains(&needsphis, bb)))
|
||||
{
|
||||
/* This is a definition. */
|
||||
|
||||
if (ir->opcode == IR_STORE)
|
||||
{
|
||||
ir->opcode = IR_NOP;
|
||||
ir->left = ir->right;
|
||||
ir->right = NULL;
|
||||
}
|
||||
array_push(&definitions, ir);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<bb->nexts.count; i++)
|
||||
{
|
||||
struct basicblock* nextbb = bb->nexts.item[i];
|
||||
struct ir* ir = nextbb->irs.item[0];
|
||||
|
||||
if ((definitions.count > 0) &&
|
||||
(ir->opcode == IR_PHI) &&
|
||||
array_contains(&needsphis, nextbb))
|
||||
{
|
||||
pmap_add(&ir->u.phivalue,
|
||||
bb, definitions.item[definitions.count-1]);
|
||||
}
|
||||
|
||||
recursively_rewrite_tree(nextbb);
|
||||
}
|
||||
|
||||
definitions.count = defcount;
|
||||
}
|
||||
|
||||
static void ssa_convert(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* If this is a parameter, synthesise a load/store at the beginning of the
|
||||
* program to force it into a register. (Unless it's written to it'll
|
||||
* always be read from the frame.) */
|
||||
|
||||
if (current_local->offset >= 0)
|
||||
{
|
||||
struct ir* ir = new_ir2(
|
||||
IR_STORE, current_local->size,
|
||||
new_localir(current_local->offset),
|
||||
new_ir1(
|
||||
IR_LOAD, current_local->size,
|
||||
new_localir(current_local->offset)
|
||||
)
|
||||
);
|
||||
|
||||
ir->root = ir;
|
||||
ir->left->root = ir;
|
||||
ir->right->root = ir;
|
||||
ir->right->left->root = ir;
|
||||
array_insert(&cfg.entry->irs, ir, 0);
|
||||
}
|
||||
|
||||
defining.count = 0;
|
||||
needsphis.count = 0;
|
||||
|
||||
/* Find everwhere where the variable is *defined*. */
|
||||
|
||||
for (i=0; i<cfg.postorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.postorder.item[i];
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
{
|
||||
struct ir* ir = bb->irs.item[j];
|
||||
if ((ir->opcode == IR_STORE) &&
|
||||
(ir->left->opcode == IR_LOCAL) &&
|
||||
(ir->left->u.ivalue == current_local->offset))
|
||||
{
|
||||
array_appendu(&defining, bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Every block which is in one of the defining block's dominance frontiers
|
||||
* requires a phi. Remember that adding a phi also adds a definition. */
|
||||
|
||||
for (i=0; i<defining.count; i++)
|
||||
{
|
||||
struct basicblock* bb = defining.item[i];
|
||||
tracef('S', "S: local %d in defined in block %s\n", current_local->offset, bb->name);
|
||||
for (j=0; j<dominancefrontiers.count; j++)
|
||||
{
|
||||
if (dominancefrontiers.item[j].left == bb)
|
||||
{
|
||||
struct basicblock* dominates = dominancefrontiers.item[j].right;
|
||||
array_appendu(&needsphis, dominates);
|
||||
array_appendu(&defining, dominates);
|
||||
tracef('S', "S: local %d needs phi in block %s\n", current_local->offset, dominates->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add empty phi nodes. */
|
||||
|
||||
for (i=0; i<needsphis.count; i++)
|
||||
{
|
||||
struct basicblock* bb = needsphis.item[i];
|
||||
struct ir* ir = new_ir0(IR_PHI, current_local->size);
|
||||
ir->root = ir;
|
||||
array_insert(&bb->irs, ir, 0);
|
||||
}
|
||||
|
||||
/* Now do the rewriting by walking the tree, pushing definitions down the tree. */
|
||||
|
||||
definitions.count = 0;
|
||||
rewritten.count = 0;
|
||||
recursively_rewrite_tree(cfg.entry);
|
||||
}
|
||||
|
||||
void pass_convert_locals_to_ssa(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
calculate_dominance_frontier_graph();
|
||||
|
||||
for (i=0; i<current_proc->locals.count; i++)
|
||||
{
|
||||
current_local = current_proc->locals.item[i].right;
|
||||
if (current_local->is_register)
|
||||
ssa_convert();
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
321
mach/proto/mcg/pass_typeinference.c
Normal file
321
mach/proto/mcg/pass_typeinference.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static bool changed;
|
||||
static ARRAYOF(struct ir) irs;
|
||||
|
||||
static void addall(struct ir* ir)
|
||||
{
|
||||
if (array_appendu(&irs, ir))
|
||||
return;
|
||||
|
||||
if (ir->left)
|
||||
addall(ir->left);
|
||||
if (ir->right)
|
||||
addall(ir->right);
|
||||
}
|
||||
|
||||
static void collect_irs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
irs.count = 0;
|
||||
for (i=0; i<cfg.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = cfg.preorder.item[i];
|
||||
int j;
|
||||
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
addall(bb->irs.item[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static char effective_type(struct ir* ir, char type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 'I':
|
||||
case 'F':
|
||||
case 'L':
|
||||
case 'D':
|
||||
return type;
|
||||
|
||||
case 'i':
|
||||
if (ir->size == EM_wordsize)
|
||||
return 'I';
|
||||
else if (ir->size == (EM_wordsize*2))
|
||||
return 'L';
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (ir->size == EM_wordsize)
|
||||
return 'F';
|
||||
else if (ir->size == (EM_wordsize*2))
|
||||
return 'D';
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scan_irs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<irs.count; i++)
|
||||
{
|
||||
struct ir* ir = irs.item[i];
|
||||
|
||||
if (ir->opcode == IR_PHI)
|
||||
{
|
||||
int j;
|
||||
|
||||
/* Phis are special. We treat them as ?=? for every import value.
|
||||
* */
|
||||
|
||||
if (ir->type)
|
||||
{
|
||||
/* Push this type to all our children. */
|
||||
|
||||
for (j=0; j<ir->u.phivalue.count; j++)
|
||||
{
|
||||
struct ir* child = ir->u.phivalue.item[j].right;
|
||||
if (!child->type)
|
||||
{
|
||||
child->type = ir->type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pull our type from the first child with a set type; we
|
||||
* ignore the rest, as the next iteration will push our type to
|
||||
* them. It's possible for our children to have conflicting
|
||||
* types. There's not much we can do about that so we just have
|
||||
* to live with it and intelligently insert casts. */
|
||||
|
||||
for (j=0; j<ir->u.phivalue.count; j++)
|
||||
{
|
||||
struct ir* child = ir->u.phivalue.item[j].right;
|
||||
if (child->type)
|
||||
{
|
||||
/* Found one! */
|
||||
ir->type = child->type;
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const struct ir_data* ird = &ir_data[ir->opcode];
|
||||
|
||||
if (!ir->type)
|
||||
{
|
||||
char etype = effective_type(ir, ird->returntype);
|
||||
if (etype)
|
||||
{
|
||||
ir->type = etype;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ir->left && !ir->left->type)
|
||||
{
|
||||
const struct ir_data* leftird = &ir_data[ir->left->opcode];
|
||||
if (leftird->returntype == '?')
|
||||
{
|
||||
char etype = effective_type(ir, ird->lefttype);
|
||||
if (etype)
|
||||
{
|
||||
ir->left->type = etype;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ir->right && !ir->right->type)
|
||||
{
|
||||
const struct ir_data* rightird = &ir_data[ir->right->opcode];
|
||||
if (rightird->returntype == '?')
|
||||
{
|
||||
char etype = effective_type(ir, ird->righttype);
|
||||
if (etype)
|
||||
{
|
||||
ir->right->type = etype;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ir->type && (ird->returntype == '?'))
|
||||
{
|
||||
if ((ird->lefttype == '?') && ir->left->type)
|
||||
{
|
||||
ir->type = ir->left->type;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ((ird->righttype == '?') && ir->right->type)
|
||||
{
|
||||
ir->type = ir->right->type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ir->type && (ird->lefttype == '?') && !ir->left->type)
|
||||
{
|
||||
ir->left->type = ir->type;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (ir->type && (ird->righttype == '?') && !ir->right->type)
|
||||
{
|
||||
ir->right->type = ir->type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void propagate_types(void)
|
||||
{
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
|
||||
scan_irs();
|
||||
}
|
||||
while (changed);
|
||||
}
|
||||
|
||||
static void assign_fallback_types(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<irs.count; i++)
|
||||
{
|
||||
struct ir* ir = irs.item[i];
|
||||
const struct ir_data* ird = &ir_data[ir->opcode];
|
||||
|
||||
if (!ir->type && (ird->returntype == '?'))
|
||||
ir->type = effective_type(ir, 'i');
|
||||
}
|
||||
}
|
||||
|
||||
static struct ir* new_copy(char wanted, char real, struct ir* ir)
|
||||
{
|
||||
struct ir* copy;
|
||||
int opcode;
|
||||
|
||||
if ((wanted == 'F') && (real == 'I'))
|
||||
opcode = IR_COPYI;
|
||||
else if ((wanted == 'D') && (real == 'L'))
|
||||
opcode = IR_COPYL;
|
||||
else if ((wanted == 'I') && (real == 'F'))
|
||||
opcode = IR_COPYF;
|
||||
else if ((wanted == 'L') && (real == 'D'))
|
||||
opcode = IR_COPYD;
|
||||
else
|
||||
fatal("type mismatch: parent IR $%d wanted %c, child IR provided %c",
|
||||
ir->id, wanted, real);
|
||||
|
||||
copy = new_ir1(opcode, ir->size, ir);
|
||||
copy->type = wanted;
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void insert_copy(struct ir* ir, struct ir** child, char returntype, char childtype)
|
||||
{
|
||||
if (*child)
|
||||
{
|
||||
char wanted;
|
||||
char real;
|
||||
|
||||
if ((returntype == '?') && (childtype == '?'))
|
||||
{
|
||||
wanted = ir->type;
|
||||
real = (*child)->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
wanted = effective_type(ir, childtype);
|
||||
real = (*child)->type;
|
||||
}
|
||||
|
||||
if (wanted)
|
||||
{
|
||||
if (wanted != real)
|
||||
{
|
||||
struct ir* copy = new_copy(wanted, real, *child);
|
||||
copy->root = ir->root;
|
||||
*child = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_ir_copies(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Insert copies for normal IR nodes. */
|
||||
|
||||
for (i=0; i<irs.count; i++)
|
||||
{
|
||||
struct ir* ir = irs.item[i];
|
||||
const struct ir_data* ird = &ir_data[ir->opcode];
|
||||
|
||||
insert_copy(ir, &ir->left, ird->returntype, ird->lefttype);
|
||||
insert_copy(ir, &ir->right, ird->returntype, ird->righttype);
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_phi_copies(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* If the child of a phi isn't the same type as the phi itself, we need to
|
||||
* insert the copy at the end of the block that exported the value. */
|
||||
|
||||
for (i=0; i<irs.count; i++)
|
||||
{
|
||||
struct ir* ir = irs.item[i];
|
||||
|
||||
for (j=0; j<ir->u.phivalue.count; j++)
|
||||
{
|
||||
struct ir* childir = ir->u.phivalue.item[j].right;
|
||||
int wanted = ir->type;
|
||||
int real = childir->type;
|
||||
if (wanted != real)
|
||||
{
|
||||
struct basicblock* childbb = ir->u.phivalue.item[j].left;
|
||||
struct ir* copy = new_copy(wanted, real, childir);
|
||||
copy->root = copy;
|
||||
|
||||
/* The copy gets inserted as the second last item of the child
|
||||
* basic block. That way it'll happen before the final jump
|
||||
* that exits the block. */
|
||||
|
||||
array_insert(&childbb->irs, copy, childbb->irs.count-1);
|
||||
|
||||
/* And replace the value in the phi with our copy. */
|
||||
|
||||
ir->u.phivalue.item[j].right = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pass_infer_types(void)
|
||||
{
|
||||
collect_irs();
|
||||
propagate_types();
|
||||
assign_fallback_types();
|
||||
insert_ir_copies();
|
||||
insert_phi_copies();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
32
mach/proto/mcg/predicates.c
Normal file
32
mach/proto/mcg/predicates.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "mcg.h"
|
||||
|
||||
bool burm_predicate_signed_constant(struct burm_node* node, arith size)
|
||||
{
|
||||
struct ir* ir = node->ir;
|
||||
arith pivot = 1<<(size-1);
|
||||
arith mask = ~((1<<size) - 1);
|
||||
assert(ir->opcode == IR_CONST);
|
||||
|
||||
return ((ir->u.ivalue + pivot) & mask) == 0;
|
||||
}
|
||||
|
||||
bool burm_predicate_unsigned_constant(struct burm_node* node, arith size)
|
||||
{
|
||||
struct ir* ir = node->ir;
|
||||
arith mask = ~((1<<size) - 1);
|
||||
assert(ir->opcode == IR_CONST);
|
||||
|
||||
return (ir->u.ivalue & mask) == 0;
|
||||
}
|
||||
|
||||
bool burm_predicate_specific_constant(struct burm_node* node, arith val)
|
||||
{
|
||||
struct ir* ir = node->ir;
|
||||
assert(ir->opcode == IR_CONST);
|
||||
|
||||
return ir->u.ivalue == val;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
209
mach/proto/mcg/procedure.c
Normal file
209
mach/proto/mcg/procedure.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
#include "mcg.h"
|
||||
|
||||
struct procedure* current_proc;
|
||||
|
||||
static void print_blocks(char k)
|
||||
{
|
||||
int i;
|
||||
|
||||
tracef(k, "%c: procedure %s\n", k, current_proc->name);
|
||||
for (i=0; i<current_proc->blocks.count; i++)
|
||||
{
|
||||
struct basicblock* bb = current_proc->blocks.item[i];
|
||||
int j;
|
||||
|
||||
tracef(k, "%c:\n", k);
|
||||
tracef(k, "%c: %sBLOCK: %s\n", k,
|
||||
bb->is_fake ? "FAKE " : "",
|
||||
bb->name);
|
||||
|
||||
if (bb->prevs.count > 0)
|
||||
{
|
||||
tracef(k, "%c: FROM:", k);
|
||||
for (j=0; j<bb->prevs.count; j++)
|
||||
tracef(k, " %s", bb->prevs.item[j]->name);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
if (bb->nexts.count > 0)
|
||||
{
|
||||
tracef(k, "%c: TO:", k);
|
||||
for (j=0; j<bb->nexts.count; j++)
|
||||
tracef(k, " %s", bb->nexts.item[j]->name);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
for (j=0; j<bb->irs.count; j++)
|
||||
ir_print(k, bb->irs.item[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_hops(char k)
|
||||
{
|
||||
int i;
|
||||
|
||||
tracef(k, "%c: procedure %s\n", k, current_proc->name);
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = dominance.preorder.item[i];
|
||||
int j;
|
||||
|
||||
tracef(k, "%c:\n", k);
|
||||
tracef(k, "%c: %sBLOCK: %s\n", k,
|
||||
bb->is_fake ? "FAKE " : "",
|
||||
bb->name);
|
||||
|
||||
if (bb->prevs.count > 0)
|
||||
{
|
||||
tracef(k, "%c: FROM:", k);
|
||||
for (j=0; j<bb->prevs.count; j++)
|
||||
tracef(k, " %s", bb->prevs.item[j]->name);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
if (bb->nexts.count > 0)
|
||||
{
|
||||
tracef(k, "%c: TO:", k);
|
||||
for (j=0; j<bb->nexts.count; j++)
|
||||
tracef(k, " %s", bb->nexts.item[j]->name);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
if (bb->liveins.count > 0)
|
||||
{
|
||||
tracef(k, "%c: INS:", k);
|
||||
for (j=0; j<bb->liveins.count; j++)
|
||||
tracef(k, " %%%d", bb->liveins.item[j]->id);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
if (bb->liveouts.count > 0)
|
||||
{
|
||||
tracef(k, "%c: OUTS:", k);
|
||||
for (j=0; j<bb->liveouts.count; j++)
|
||||
tracef(k, " %%%d", bb->liveouts.item[j]->id);
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
if (bb->phis.count > 0)
|
||||
{
|
||||
tracef(k, "%c: PHIS:", k);
|
||||
for (j=0; j<bb->phis.count; j++)
|
||||
{
|
||||
struct vreg* vreg = bb->phis.item[j].left;
|
||||
struct phi* phi = bb->phis.item[j].right;
|
||||
|
||||
tracef(k, " %%%d(via %s)=>%%%d",
|
||||
phi->ir->result->id,
|
||||
phi->prev->name,
|
||||
vreg->id);
|
||||
}
|
||||
tracef(k, "\n");
|
||||
}
|
||||
|
||||
for (j=0; j<bb->hops.count; j++)
|
||||
hop_print(k, bb->hops.item[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_procedure(struct procedure* proc)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
fprintf(outputfile, "\n.sect .text\n");
|
||||
for (i=0; i<dominance.preorder.count; i++)
|
||||
{
|
||||
struct basicblock* bb = dominance.preorder.item[i];
|
||||
|
||||
fprintf(outputfile, "%s:\n", platform_label(bb->name));
|
||||
for (j=0; j<bb->hops.count; j++)
|
||||
{
|
||||
struct hop* hop = bb->hops.item[j];
|
||||
fprintf(outputfile, "%s", hop_render(hop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_cfg_graph(const char* name)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(cfg_dot_file, "subgraph \"%s\" {\n", name);
|
||||
fprintf(cfg_dot_file, "\t\"%s\" [color=red];\n", cfg.entry->name);
|
||||
|
||||
for (i=0; i<cfg.graph.count; i++)
|
||||
{
|
||||
fprintf(cfg_dot_file, "\t\"%s\" -> \"%s\";\n",
|
||||
cfg.graph.item[i].left->name,
|
||||
cfg.graph.item[i].right->name);
|
||||
}
|
||||
|
||||
fprintf(cfg_dot_file, "}\n");
|
||||
}
|
||||
|
||||
static void write_dominance_graph(const char* name)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(dominance_dot_file, "subgraph \"%s\" {\n", name);
|
||||
fprintf(dominance_dot_file, "\t\"%s\" [color=green];\n", cfg.entry->name);
|
||||
|
||||
for (i=0; i<dominance.graph.count; i++)
|
||||
{
|
||||
fprintf(dominance_dot_file, "\t\"%s\" -> \"%s\";\n",
|
||||
dominance.graph.item[i].right->name,
|
||||
dominance.graph.item[i].left->name);
|
||||
}
|
||||
|
||||
fprintf(dominance_dot_file, "}\n");
|
||||
}
|
||||
|
||||
void procedure_compile(struct procedure* proc)
|
||||
{
|
||||
current_proc = proc;
|
||||
|
||||
pass_group_irs();
|
||||
print_blocks('1');
|
||||
|
||||
/* Passes from here on must preserve IR grouping */
|
||||
|
||||
pass_eliminate_trivial_blocks();
|
||||
pass_remove_dead_blocks();
|
||||
|
||||
print_blocks('2');
|
||||
update_graph_data();
|
||||
pass_split_critical_edges();
|
||||
update_graph_data();
|
||||
|
||||
/* Passes from here on can't alter the BB graph without also updating prevs
|
||||
* and nexts (and then calling update_graph_data()). */
|
||||
|
||||
print_blocks('3');
|
||||
pass_wire_up_return_values();
|
||||
pass_convert_stack_ops();
|
||||
print_blocks('4');
|
||||
pass_convert_locals_to_ssa();
|
||||
print_blocks('5');
|
||||
pass_remove_dead_phis();
|
||||
pass_infer_types();
|
||||
print_blocks('6');
|
||||
|
||||
pass_instruction_selector();
|
||||
print_hops('7');
|
||||
pass_find_phi_congruence_groups();
|
||||
pass_live_vreg_analysis();
|
||||
print_hops('8');
|
||||
pass_register_allocator();
|
||||
pass_add_prologue_epilogue();
|
||||
print_hops('9');
|
||||
|
||||
emit_procedure(proc);
|
||||
|
||||
if (cfg_dot_file)
|
||||
write_cfg_graph(proc->name);
|
||||
if (dominance_dot_file)
|
||||
write_dominance_graph(proc->name);
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
35
mach/proto/mcg/procedure.h
Normal file
35
mach/proto/mcg/procedure.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef PROCEDURE_H
|
||||
#define PROCEDURE_H
|
||||
|
||||
struct local
|
||||
{
|
||||
int size;
|
||||
int offset;
|
||||
bool is_register;
|
||||
};
|
||||
|
||||
struct procedure
|
||||
{
|
||||
const char* name;
|
||||
struct basicblock* entry;
|
||||
struct basicblock* exit;
|
||||
int locals_size;
|
||||
int spills_size;
|
||||
int saved_size;
|
||||
int fp_to_ab; /* argument base (indexes up) */
|
||||
int fp_to_lb; /* locals base (indexes down) */
|
||||
int fp_to_sb; /* spill base (indexes up) */
|
||||
int fp_to_rb; /* saved registers base (indexes up) */
|
||||
ARRAYOF(struct basicblock) blocks;
|
||||
IMAPOF(struct local) locals;
|
||||
ARRAYOF(struct hreg) usedregs;
|
||||
};
|
||||
|
||||
extern void procedure_compile(struct procedure* proc);
|
||||
extern void procedure_update_bb_graph(struct procedure* proc);
|
||||
|
||||
extern struct procedure* current_proc;
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
36
mach/proto/mcg/reg.c
Normal file
36
mach/proto/mcg/reg.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static int vreg_count = 1;
|
||||
|
||||
struct vreg* new_vreg(void)
|
||||
{
|
||||
struct vreg* vreg = calloc(1, sizeof *vreg);
|
||||
vreg->id = vreg_count++;
|
||||
return vreg;
|
||||
}
|
||||
|
||||
struct hreg* new_hreg(const struct burm_register_data* brd)
|
||||
{
|
||||
struct hreg* hreg = calloc(1, sizeof *hreg);
|
||||
hreg->id = brd->id;
|
||||
hreg->brd = brd;
|
||||
hreg->attrs = brd->attrs;
|
||||
hreg->is_stacked = false;
|
||||
/* The aliases array needs to be initialised later. */
|
||||
return hreg;
|
||||
}
|
||||
|
||||
struct hreg* new_stacked_hreg(uint32_t attrs)
|
||||
{
|
||||
static int hreg_count = 1;
|
||||
struct hreg* hreg = calloc(1, sizeof *hreg);
|
||||
hreg->id = aprintf("stacked_%d_id_%d", attrs, hreg_count++);
|
||||
hreg->attrs = attrs;
|
||||
hreg->is_stacked = true;
|
||||
hreg->offset = -1;
|
||||
array_append(&hreg->aliases, hreg);
|
||||
return hreg;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
42
mach/proto/mcg/reg.h
Normal file
42
mach/proto/mcg/reg.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef REG_H
|
||||
#define REG_H
|
||||
|
||||
#define WITH_ATTR(a) (1<<(a))
|
||||
|
||||
struct phicongruence
|
||||
{
|
||||
int id;
|
||||
ARRAYOF(struct vreg) vregs;
|
||||
ARRAYOF(struct hop) definitions;
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
struct hreg
|
||||
{
|
||||
const char* id;
|
||||
const struct burm_register_data* brd;
|
||||
uint32_t attrs;
|
||||
bool is_stacked;
|
||||
int offset;
|
||||
ARRAYOF(struct hreg) aliases;
|
||||
};
|
||||
|
||||
struct vreg
|
||||
{
|
||||
int id;
|
||||
uint32_t type;
|
||||
struct phicongruence* congruence;
|
||||
struct hop* defined;
|
||||
ARRAYOF(struct hop) used;
|
||||
};
|
||||
|
||||
typedef PMAPOF(struct hreg, struct vreg) register_assignment_t;
|
||||
|
||||
extern struct vreg* new_vreg(void);
|
||||
|
||||
extern struct hreg* new_hreg(const struct burm_register_data* brd);
|
||||
extern struct hreg* new_stacked_hreg(uint32_t type);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
65
mach/proto/mcg/symbol.c
Normal file
65
mach/proto/mcg/symbol.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "mcg.h"
|
||||
|
||||
static void init_idf();
|
||||
static struct idf* str2idf(char* tg, int cp);
|
||||
|
||||
#define IDF_TYPE struct symbol
|
||||
#define IDF_NAME symbol
|
||||
#include <idf_pkg.spec>
|
||||
#include <idf_pkg.body>
|
||||
|
||||
void symbol_init(void)
|
||||
{
|
||||
init_idf();
|
||||
}
|
||||
|
||||
bool symbol_exists(const char* name)
|
||||
{
|
||||
return !!findidf((char*) name);
|
||||
}
|
||||
|
||||
struct symbol* symbol_get(const char* name)
|
||||
{
|
||||
struct idf* p = str2idf((char*) name, 0);
|
||||
p->symbol.name = p->id_text;
|
||||
return &p->symbol;
|
||||
}
|
||||
|
||||
void symbol_declare(const char* name, bool is_exported, bool is_proc)
|
||||
{
|
||||
struct symbol* s = symbol_get(name);
|
||||
s->is_exported = is_exported;
|
||||
|
||||
if (is_proc)
|
||||
{
|
||||
if (s->section == SECTION_UNKNOWN)
|
||||
s->section = SECTION_TEXT;
|
||||
else if (s->section != SECTION_TEXT)
|
||||
fatal("section mismatch for '%s'", name);
|
||||
}
|
||||
|
||||
if (is_exported)
|
||||
fprintf(outputfile, ".extern %s\n", platform_label(name));
|
||||
}
|
||||
|
||||
struct symbol* symbol_walk(symbol_walker_t* cb, void* user)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<IDF_HASHSIZE; i++)
|
||||
{
|
||||
struct idf* idf = IDF_hashtable[i];
|
||||
while (idf)
|
||||
{
|
||||
struct symbol* symbol = &idf->symbol;
|
||||
if (cb(symbol, user))
|
||||
return symbol;
|
||||
idf = idf->id_next;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
1681
mach/proto/mcg/treebuilder.c
Normal file
1681
mach/proto/mcg/treebuilder.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -330,7 +330,7 @@ labeldef(ip)
|
|||
int oplen;
|
||||
|
||||
p = ip->rest_line;
|
||||
while( *p != LABEL_TERMINATOR) p++;
|
||||
while(*p && (*p != LABEL_TERMINATOR)) p++;
|
||||
oplen = p - ip->rest_line;
|
||||
if (oplen == 0 || oplen > MAXOPLEN) return;
|
||||
strncpy(ip->op[0],ip->rest_line,oplen);
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#define label unsigned int
|
||||
typedef unsigned int label;
|
||||
|
||||
|
|
127
modules/src/data/array.c
Normal file
127
modules/src/data/array.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "array.h"
|
||||
|
||||
static void extend(struct array* array)
|
||||
{
|
||||
if (array->count == array->max)
|
||||
{
|
||||
int newmax = (array->max == 0) ? 8 : (array->max * 2);
|
||||
struct array* newarray = realloc(array->item, newmax * sizeof(*newarray));
|
||||
|
||||
array->max = newmax;
|
||||
array->item = newarray;
|
||||
}
|
||||
}
|
||||
|
||||
void array_append(void* arrayp, void* value)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
|
||||
extend(array);
|
||||
array->item[array->count] = value;
|
||||
array->count++;
|
||||
}
|
||||
|
||||
int array_indexof(void* arrayp, void* value)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<array->count; i++)
|
||||
if (array->item[i] == value)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool array_contains(void* arrayp, void* value)
|
||||
{
|
||||
return (array_indexof(arrayp, value) != -1);
|
||||
}
|
||||
|
||||
bool array_appendu(void* arrayp, void* value)
|
||||
{
|
||||
if (array_contains(arrayp, value))
|
||||
return true;
|
||||
|
||||
array_append(arrayp, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
void array_insert(void* arrayp, void* value, int before)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
|
||||
extend(array);
|
||||
memmove(&array->item[before+1], &array->item[before],
|
||||
(array->count-before) * sizeof(*array));
|
||||
array->item[before] = value;
|
||||
array->count++;
|
||||
}
|
||||
|
||||
void array_remove(void* arrayp, void* value)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<array->count; i++)
|
||||
{
|
||||
if (array->item[i] == value)
|
||||
{
|
||||
while (i < (array->count-1))
|
||||
{
|
||||
array->item[i] = array->item[i+1];
|
||||
i++;
|
||||
}
|
||||
array->count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* array_pop(void* arrayp)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
|
||||
assert(array->count > 0);
|
||||
return array->item[array->count--];
|
||||
}
|
||||
|
||||
void array_appendall(void* arrayp, void* srcp)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
struct array* src = srcp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<src->count; i++)
|
||||
array_append(array, src->item[i]);
|
||||
}
|
||||
|
||||
void array_removeall(void* arrayp, void* srcp)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
struct array* src = srcp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<src->count; i++)
|
||||
array_remove(array, src->item[i]);
|
||||
}
|
||||
|
||||
bool array_appendallu(void* arrayp, void* srcp)
|
||||
{
|
||||
struct array* array = arrayp;
|
||||
struct array* src = srcp;
|
||||
bool unchanged = true;
|
||||
int i;
|
||||
|
||||
for (i=0; i<src->count; i++)
|
||||
unchanged &= array_appendu(array, src->item[i]);
|
||||
|
||||
return unchanged;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
37
modules/src/data/array.h
Normal file
37
modules/src/data/array.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef ARRAY_H
|
||||
#define ARRAY_H
|
||||
|
||||
/* Danger, Will Robinson! The type and the macro must be compatible. */
|
||||
|
||||
struct array
|
||||
{
|
||||
void** item;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
#define ARRAYOF(TYPE) \
|
||||
struct { \
|
||||
TYPE** item; \
|
||||
int count; \
|
||||
int max; \
|
||||
}
|
||||
|
||||
extern void array_append(void* array, void* value);
|
||||
extern bool array_appendu(void* array, void* value);
|
||||
extern void array_insert(void* array, void* value, int before);
|
||||
extern void array_remove(void* array, void* value);
|
||||
extern bool array_contains(void* array, void* value);
|
||||
extern int array_indexof(void* array, void* value);
|
||||
|
||||
#define array_push(a, v) array_append(a, v)
|
||||
extern void* array_pop(void* array);
|
||||
|
||||
extern void array_appendall(void* dest, void* src);
|
||||
extern void array_removeall(void* dest, void* src);
|
||||
|
||||
/* Returns false if *any* items were added. */
|
||||
extern bool array_appendallu(void* dest, void* src);
|
||||
|
||||
#endif
|
||||
|
27
modules/src/data/astring.c
Normal file
27
modules/src/data/astring.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
const char* aprintf(const char* fmt, ...)
|
||||
{
|
||||
int n;
|
||||
char* p;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(NULL, 0, fmt, ap) + 1;
|
||||
va_end(ap);
|
||||
|
||||
p = malloc(n);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(p, n, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
9
modules/src/data/astring.h
Normal file
9
modules/src/data/astring.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef ASTRING_H
|
||||
#define ASTRING_H
|
||||
|
||||
extern const char* aprintf(const char* fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
7
modules/src/data/build.lua
Normal file
7
modules/src/data/build.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
clibrary {
|
||||
name = "lib",
|
||||
srcs = { "./*.c" },
|
||||
hdrs = { "./*.h" },
|
||||
deps = { "./*.h" },
|
||||
}
|
||||
|
38
modules/src/data/diagnostics.c
Normal file
38
modules/src/data/diagnostics.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "diagnostics.h"
|
||||
|
||||
const char* program_name = NULL;
|
||||
|
||||
void warning(const char* msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
|
||||
if (program_name)
|
||||
fprintf(stderr, "%s: ", program_name);
|
||||
fprintf(stderr, "warning: ");
|
||||
vfprintf(stderr, msg, ap);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void fatal(const char* msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
|
||||
if (program_name)
|
||||
fprintf(stderr, "%s: ", program_name);
|
||||
fprintf(stderr, "fatal: ");
|
||||
vfprintf(stderr, msg, ap);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
va_end(ap);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
12
modules/src/data/diagnostics.h
Normal file
12
modules/src/data/diagnostics.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef DIAGNOSTICS_H
|
||||
#define DIAGNOSTICS_H
|
||||
|
||||
extern const char* program_name;
|
||||
|
||||
extern void warning(const char* fmt, ...);
|
||||
extern void fatal(const char* fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
74
modules/src/data/imap.c
Normal file
74
modules/src/data/imap.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "imap.h"
|
||||
|
||||
static void append(void* mapp, int left, void* right)
|
||||
{
|
||||
struct imap* map = mapp;
|
||||
struct imap_node* node;
|
||||
|
||||
if (map->count == map->max)
|
||||
{
|
||||
int newmax = (map->max == 0) ? 8 : (map->max * 2);
|
||||
struct imap_node* newarray = realloc(map->item, newmax * sizeof(*newarray));
|
||||
|
||||
map->max = newmax;
|
||||
map->item = newarray;
|
||||
}
|
||||
|
||||
node = &map->item[map->count];
|
||||
map->count++;
|
||||
|
||||
node->left = left;
|
||||
node->right = right;
|
||||
}
|
||||
|
||||
void imap_put(void* mapp, int left, void* right)
|
||||
{
|
||||
struct imap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct imap_node* node = &map->item[i];
|
||||
if (node->left == left)
|
||||
{
|
||||
node->right = right;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void imap_add(void* mapp, int left, void* right)
|
||||
{
|
||||
struct imap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct imap_node* node = &map->item[i];
|
||||
if ((node->left == left) && (node->right == right))
|
||||
return;
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void* imap_get(void* mapp, int left)
|
||||
{
|
||||
struct imap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct imap_node* node = &map->item[i];
|
||||
if (node->left == left)
|
||||
return node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
31
modules/src/data/imap.h
Normal file
31
modules/src/data/imap.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef IMAP_H
|
||||
#define IMAP_H
|
||||
|
||||
struct imap_node
|
||||
{
|
||||
int left;
|
||||
void* right;
|
||||
};
|
||||
|
||||
/* Danger, Will Robinson! The type and the macro must be compatible. */
|
||||
|
||||
struct imap
|
||||
{
|
||||
struct imap_node* item;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
#define IMAPOF(RIGHT) \
|
||||
struct { \
|
||||
struct { int left; RIGHT* right; }* item; \
|
||||
int count; \
|
||||
int max; \
|
||||
}
|
||||
|
||||
extern void imap_put(void* map, int left, void* right);
|
||||
extern void imap_add(void* map, int left, void* right);
|
||||
extern void* imap_get(void* map, int left);
|
||||
|
||||
#endif
|
||||
|
107
modules/src/data/pmap.c
Normal file
107
modules/src/data/pmap.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "pmap.h"
|
||||
|
||||
static void append(void* mapp, void* left, void* right)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
struct pmap_node* node;
|
||||
|
||||
if (map->count == map->max)
|
||||
{
|
||||
int newmax = (map->max == 0) ? 8 : (map->max * 2);
|
||||
struct pmap_node* newarray = realloc(map->item, newmax * sizeof(*newarray));
|
||||
|
||||
map->max = newmax;
|
||||
map->item = newarray;
|
||||
}
|
||||
|
||||
node = &map->item[map->count];
|
||||
map->count++;
|
||||
|
||||
node->left = left;
|
||||
node->right = right;
|
||||
}
|
||||
|
||||
void pmap_put(void* mapp, void* left, void* right)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct pmap_node* node = &map->item[i];
|
||||
if (node->left == left)
|
||||
{
|
||||
node->right = right;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void pmap_add(void* mapp, void* left, void* right)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct pmap_node* node = &map->item[i];
|
||||
if ((node->left == left) && (node->right == right))
|
||||
return;
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void pmap_remove(void* mapp, void* left, void* right)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct pmap_node* node = &map->item[i];
|
||||
if ((node->left == left) && (node->right == right))
|
||||
{
|
||||
memmove(node, node+1, sizeof(*node) * (map->count - i - 1));
|
||||
map->count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* pmap_findleft(void* mapp, void* left)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct pmap_node* node = &map->item[i];
|
||||
if (node->left == left)
|
||||
return node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* pmap_findright(void* mapp, void* right)
|
||||
{
|
||||
struct pmap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct pmap_node* node = &map->item[i];
|
||||
if (node->right == right)
|
||||
return node->left;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
33
modules/src/data/pmap.h
Normal file
33
modules/src/data/pmap.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef PMAP_H
|
||||
#define PMAP_H
|
||||
|
||||
struct pmap_node
|
||||
{
|
||||
void* left;
|
||||
void* right;
|
||||
};
|
||||
|
||||
/* Danger, Will Robinson! The type and the macro must be compatible. */
|
||||
|
||||
struct pmap
|
||||
{
|
||||
struct pmap_node* item;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
#define PMAPOF(LEFT, RIGHT) \
|
||||
struct { \
|
||||
struct { LEFT* left; RIGHT* right; }* item; \
|
||||
int count; \
|
||||
int max; \
|
||||
}
|
||||
|
||||
extern void pmap_put(void* map, void* left, void* right);
|
||||
extern void pmap_add(void* map, void* left, void* right);
|
||||
extern void pmap_remove(void* map, void* left, void* right);
|
||||
extern void* pmap_findleft(void* map, void* left);
|
||||
extern void* pmap_findright(void* map, void* right);
|
||||
|
||||
#endif
|
||||
|
74
modules/src/data/smap.c
Normal file
74
modules/src/data/smap.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "smap.h"
|
||||
|
||||
static void append(void* mapp, const char* left, void* right)
|
||||
{
|
||||
struct smap* map = mapp;
|
||||
struct smap_node* node;
|
||||
|
||||
if (map->count == map->max)
|
||||
{
|
||||
int newmax = (map->max == 0) ? 8 : (map->max * 2);
|
||||
struct smap_node* newarray = realloc(map->item, newmax * sizeof(*newarray));
|
||||
|
||||
map->max = newmax;
|
||||
map->item = newarray;
|
||||
}
|
||||
|
||||
node = &map->item[map->count];
|
||||
map->count++;
|
||||
|
||||
node->left = left;
|
||||
node->right = right;
|
||||
}
|
||||
|
||||
void smap_put(void* mapp, const char* left, void* right)
|
||||
{
|
||||
struct smap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct smap_node* node = &map->item[i];
|
||||
if (strcmp(node->left, left) == 0)
|
||||
{
|
||||
node->right = right;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void smap_add(void* mapp, const char* left, void* right)
|
||||
{
|
||||
struct smap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct smap_node* node = &map->item[i];
|
||||
if ((strcmp(node->left, left) == 0) && (node->right == right))
|
||||
return;
|
||||
}
|
||||
|
||||
append(map, left, right);
|
||||
}
|
||||
|
||||
void* smap_get(void* mapp, const char* left)
|
||||
{
|
||||
struct smap* map = mapp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map->count; i++)
|
||||
{
|
||||
struct smap_node* node = &map->item[i];
|
||||
if (strcmp(node->left, left) == 0)
|
||||
return node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
31
modules/src/data/smap.h
Normal file
31
modules/src/data/smap.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef SMAP_H
|
||||
#define SMAP_H
|
||||
|
||||
struct smap_node
|
||||
{
|
||||
const char* left;
|
||||
void* right;
|
||||
};
|
||||
|
||||
/* Danger, Will Robinson! The type and the macro must be compatible. */
|
||||
|
||||
struct smap
|
||||
{
|
||||
struct smap_node* item;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
#define SMAPOF(RIGHT) \
|
||||
struct { \
|
||||
struct { const char* left; RIGHT* right; }* item; \
|
||||
int count; \
|
||||
int max; \
|
||||
}
|
||||
|
||||
extern void smap_put(void* map, const char* left, void* right);
|
||||
extern void smap_add(void* map, const char* left, void* right);
|
||||
extern void* smap_get(void* map, const char* left);
|
||||
|
||||
#endif
|
||||
|
43
modules/src/data/stringlist.c
Normal file
43
modules/src/data/stringlist.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <stdlib.h>
|
||||
#include "stringlist.h"
|
||||
|
||||
void stringlist_add(struct stringlist* list, const char* data)
|
||||
{
|
||||
struct stringfragment* f = calloc(1, sizeof *f);
|
||||
f->data = data;
|
||||
|
||||
if (!list->last)
|
||||
list->first = list->last = f;
|
||||
else
|
||||
{
|
||||
list->last->next = f;
|
||||
list->last = f;
|
||||
}
|
||||
}
|
||||
|
||||
void stringlist_addall(struct stringlist* list, struct stringlist* src)
|
||||
{
|
||||
struct stringfragment* f = src->first;
|
||||
|
||||
while (f)
|
||||
{
|
||||
stringlist_add(list, f->data);
|
||||
f = f->next;
|
||||
}
|
||||
}
|
||||
|
||||
void stringlist_free(struct stringlist* list)
|
||||
{
|
||||
struct stringfragment* f = list->first;
|
||||
|
||||
while (f)
|
||||
{
|
||||
struct stringfragment* next = f->next;
|
||||
free(f);
|
||||
f = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
||||
|
23
modules/src/data/stringlist.h
Normal file
23
modules/src/data/stringlist.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef STRINGLIST_H
|
||||
#define STRINGLIST_H
|
||||
|
||||
struct stringfragment
|
||||
{
|
||||
const char* data;
|
||||
struct stringfragment* next;
|
||||
};
|
||||
|
||||
struct stringlist
|
||||
{
|
||||
struct stringfragment* first;
|
||||
struct stringfragment* last;
|
||||
};
|
||||
|
||||
extern void stringlist_add(struct stringlist* list, const char* data);
|
||||
extern void stringlist_addall(struct stringlist* list, struct stringlist* src);
|
||||
extern void stringlist_free(struct stringlist* list);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set sw=4 ts=4 expandtab : */
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
/* SYMBOL TABLE HANDLING */
|
||||
|
||||
#include <alloc.h>
|
||||
#include <alloc.h>
|
||||
|
||||
#define IDF_HASHSIZE 307 /* size of hashtable, must be odd */
|
||||
#define IDF_HASHSIZE 307 /* size of hashtable, must be odd */
|
||||
|
||||
#define IDF_STARTHASH(hs) (hs = 0)
|
||||
#define IDF_ENHASH(hs,ch) (hs = (hs << 2) + ch)
|
||||
#define IDF_STOPHASH(hs) (hs = hs % IDF_HASHSIZE)
|
||||
#define IDF_STARTHASH(hs) (hs = 0)
|
||||
#define IDF_ENHASH(hs, ch) (hs = (hs << 2) + ch)
|
||||
#define IDF_STOPHASH(hs) (hs = hs % IDF_HASHSIZE)
|
||||
|
||||
static struct idf *IDF_hashtable[IDF_HASHSIZE];
|
||||
/* All identifiers can in principle be reached through
|
||||
static struct idf* IDF_hashtable[IDF_HASHSIZE];
|
||||
/* All identifiers can in principle be reached through
|
||||
IDF_hashtable; IDF_hashtable[hc] is the start of a chain of
|
||||
idf's whose tags all hash to hc.
|
||||
Any identifier is entered into this
|
||||
|
@ -17,69 +17,71 @@ static struct idf *IDF_hashtable[IDF_HASHSIZE];
|
|||
(variable, selector, structure tag, etc.).
|
||||
*/
|
||||
|
||||
_PROTOTYPE(static struct idf *IDF_new, (char *, int, int));
|
||||
static struct idf* IDF_new(char* tg, int size, int cpy);
|
||||
|
||||
void
|
||||
init_idf()
|
||||
void init_idf()
|
||||
{
|
||||
}
|
||||
|
||||
static struct idf *
|
||||
IDF_new(tg, size, cpy)
|
||||
register char *tg;
|
||||
register int size;
|
||||
static struct idf* IDF_new(char* tg, int size, int cpy)
|
||||
{
|
||||
static int nidf;
|
||||
static struct idf *pidf;
|
||||
static struct idf* pidf;
|
||||
static struct idf null_idf;
|
||||
register struct idf *id;
|
||||
register struct idf* id;
|
||||
#define NIDS 50
|
||||
#define IBUFSIZ 2048
|
||||
#define IBUFSIZ 2048
|
||||
static unsigned int icnt;
|
||||
static char *ip;
|
||||
register char *p;
|
||||
static char* ip;
|
||||
register char* p;
|
||||
|
||||
|
||||
if (! nidf--) {
|
||||
if (!nidf--)
|
||||
{
|
||||
nidf += NIDS;
|
||||
pidf = (struct idf *) Malloc(NIDS * sizeof (struct idf));
|
||||
pidf = (struct idf*)Malloc(NIDS * sizeof(struct idf));
|
||||
}
|
||||
|
||||
id = pidf;
|
||||
pidf++;
|
||||
*id = null_idf;
|
||||
|
||||
if (cpy) {
|
||||
if (size > icnt) {
|
||||
icnt = size > IBUFSIZ ? size : IBUFSIZ;
|
||||
if (cpy)
|
||||
{
|
||||
if (size > icnt)
|
||||
{
|
||||
icnt = size > IBUFSIZ ? size : IBUFSIZ;
|
||||
p = Malloc(icnt);
|
||||
}
|
||||
else p = ip;
|
||||
else
|
||||
p = ip;
|
||||
icnt -= size;
|
||||
id->id_text = p;
|
||||
while (size--) {
|
||||
while (size--)
|
||||
{
|
||||
*p++ = *tg++;
|
||||
}
|
||||
ip = p;
|
||||
}
|
||||
else id->id_text = tg;
|
||||
else
|
||||
id->id_text = tg;
|
||||
return id;
|
||||
}
|
||||
|
||||
#ifdef IDF_DEBUG
|
||||
void
|
||||
hash_stat()
|
||||
#ifdef IDF_DEBUG
|
||||
void hash_stat(void)
|
||||
{
|
||||
register int i;
|
||||
int total_count = 0;
|
||||
|
||||
print("Hash table tally:\n");
|
||||
for (i = 0; i < IDF_HASHSIZE; i++) {
|
||||
register struct idf *notch = IDF_hashtable[i];
|
||||
for (i = 0; i < IDF_HASHSIZE; i++)
|
||||
{
|
||||
register struct idf* notch = IDF_hashtable[i];
|
||||
register int cnt = 0;
|
||||
|
||||
print ("%d ", i);
|
||||
while (notch) {
|
||||
print("%d ", i);
|
||||
while (notch)
|
||||
{
|
||||
cnt++;
|
||||
print("'%s' ", notch->id_text);
|
||||
notch = notch->id_next;
|
||||
|
@ -91,40 +93,38 @@ hash_stat()
|
|||
print("End hash table tally\n");
|
||||
}
|
||||
|
||||
void
|
||||
idfappfun(fun, opt)
|
||||
int (*fun)();
|
||||
int opt;
|
||||
void idfappfun(int (*fun)(), int opt)
|
||||
{
|
||||
register int i;
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < IDF_HASHSIZE; i++) {
|
||||
register struct idf *notch = IDF_hashtable[i];
|
||||
for (i = 0; i < IDF_HASHSIZE; i++)
|
||||
{
|
||||
register struct idf* notch = IDF_hashtable[i];
|
||||
|
||||
while (notch) {
|
||||
while (notch)
|
||||
{
|
||||
(*fun)(notch, opt);
|
||||
notch = notch->id_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* IDF_DEBUG */
|
||||
#endif /* IDF_DEBUG */
|
||||
|
||||
struct idf *
|
||||
str2idf(tg, cpy)
|
||||
char tg[];
|
||||
struct idf* str2idf(char tg[], int cpy)
|
||||
{
|
||||
/* str2idf() returns an entry in the symbol table for the
|
||||
identifier tg. If necessary, an entry is created.
|
||||
*/
|
||||
register char *cp = tg;
|
||||
struct idf **hook;
|
||||
register struct idf *notch;
|
||||
register char* cp = tg;
|
||||
struct idf** hook;
|
||||
register struct idf* notch;
|
||||
register unsigned int hash;
|
||||
register int c;
|
||||
int size;
|
||||
|
||||
IDF_STARTHASH(hash);
|
||||
while (c = *cp++) {
|
||||
while (c = *cp++)
|
||||
{
|
||||
IDF_ENHASH(hash, c);
|
||||
}
|
||||
IDF_STOPHASH(hash);
|
||||
|
@ -137,25 +137,31 @@ str2idf(tg, cpy)
|
|||
*/
|
||||
hook = &IDF_hashtable[hash];
|
||||
|
||||
while ((notch = *hook)) {
|
||||
register char *s1 = tg;
|
||||
while ((notch = *hook))
|
||||
{
|
||||
register char* s1 = tg;
|
||||
|
||||
cp = notch->id_text;
|
||||
|
||||
while (!(c = (*s1 - *cp++))) {
|
||||
if (*s1++ == '\0') {
|
||||
while (!(c = (*s1 - *cp++)))
|
||||
{
|
||||
if (*s1++ == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == 0) return notch;
|
||||
if (c < 0) break;
|
||||
if (c == 0)
|
||||
return notch;
|
||||
if (c < 0)
|
||||
break;
|
||||
hook = ¬ch->id_next;
|
||||
}
|
||||
/* a new struct idf must be inserted at the hook */
|
||||
if (cpy < 0) return 0;
|
||||
if (cpy < 0)
|
||||
return 0;
|
||||
notch = IDF_new(tg, size, cpy);
|
||||
notch->id_next = *hook;
|
||||
*hook = notch; /* hooked in */
|
||||
*hook = notch; /* hooked in */
|
||||
return notch;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ struct idf {
|
|||
|
||||
Initializes the namelist.
|
||||
*/
|
||||
_PROTOTYPE(void init_idf, (void));
|
||||
extern void init_idf(void);
|
||||
|
||||
/* struct idf * str2idf(tg, cp)
|
||||
char *tg;
|
||||
|
@ -40,6 +40,6 @@ _PROTOTYPE(void init_idf, (void));
|
|||
If cp < 0, the string is not entered, but only looked for.
|
||||
*/
|
||||
|
||||
_PROTOTYPE(struct idf *str2idf, (char *, int));
|
||||
struct idf *str2idf(char* tg, int cp);
|
||||
|
||||
#define findidf(tg) str2idf(tg, -1)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
include("mach/proto/as/build.lua")
|
||||
include("mach/proto/ncg/build.lua")
|
||||
include("mach/proto/mcg/build.lua")
|
||||
include("mach/proto/top/build.lua")
|
||||
|
||||
definerule("ackfile",
|
||||
|
@ -61,11 +62,23 @@ definerule("ackprogram",
|
|||
deps = { type="targets", default={} },
|
||||
},
|
||||
function (e)
|
||||
-- This bit is a hack. We *don't* want to link the language libraries here,
|
||||
-- because the ack driver is going to pick the appropriate library itself and
|
||||
-- we don't want more than one. But we still need to depend on them, so we use
|
||||
-- a nasty hack.
|
||||
|
||||
local platstamp = normalrule {
|
||||
name = e.name.."/platstamp",
|
||||
ins = { "plat/"..e.vars.plat.."+pkg" },
|
||||
outleaves = { "stamp" },
|
||||
commands = { "touch %{outs}" }
|
||||
}
|
||||
|
||||
return cprogram {
|
||||
name = e.name,
|
||||
srcs = e.srcs,
|
||||
deps = {
|
||||
"plat/"..e.vars.plat.."+pkg",
|
||||
platstamp,
|
||||
"util/ack+pkg",
|
||||
"util/led+pkg",
|
||||
e.deps
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
.sect .data
|
||||
.sect .bss
|
||||
|
||||
.sect .text
|
||||
|
||||
!
|
||||
! This data block is used to store information about the current line number
|
||||
! and file.
|
||||
|
||||
.define hol0
|
||||
.comm hol0, 8
|
||||
.define .hol0
|
||||
.sect .bss
|
||||
hol0:
|
||||
.hol0:
|
||||
.space 8
|
||||
|
|
|
@ -5,16 +5,28 @@ build_as {
|
|||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_mcg {
|
||||
name = "mcg",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_ncg {
|
||||
name = "ncg",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_top {
|
||||
name = "top",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
return installable {
|
||||
name = "tools",
|
||||
map = {
|
||||
["$(PLATDEP)/linuxppc/as"] = "+as",
|
||||
["$(PLATDEP)/linuxppc/ncg"] = "+ncg",
|
||||
["$(PLATDEP)/linuxppc/mcg"] = "+mcg",
|
||||
["$(PLATDEP)/linuxppc/top"] = "+top",
|
||||
["$(PLATIND)/descr/linuxppc"] = "./descr",
|
||||
"util/opt+pkg",
|
||||
}
|
||||
|
|
|
@ -39,16 +39,15 @@ name be
|
|||
stdout
|
||||
need .e
|
||||
end
|
||||
# FIXME(dtrg): not working yet
|
||||
#name asopt
|
||||
# from .s
|
||||
# to .so
|
||||
# program {EM}/lib/ack/{PLATFORM}/top
|
||||
# args
|
||||
# optimizer
|
||||
# stdin
|
||||
# stdout
|
||||
#end
|
||||
name asopt
|
||||
from .s
|
||||
to .so
|
||||
program {EM}/lib/ack/{PLATFORM}/top
|
||||
args
|
||||
optimizer
|
||||
stdin
|
||||
stdout
|
||||
end
|
||||
name as
|
||||
from .s.so
|
||||
to .o
|
||||
|
|
42
plat/qemuppc/README
Normal file
42
plat/qemuppc/README
Normal file
|
@ -0,0 +1,42 @@
|
|||
# $Source: /cvsroot/tack/Ack/plat/linux386/README,v $
|
||||
# $State: Exp $
|
||||
# $Revision: 1.2 $
|
||||
|
||||
|
||||
The linux386 platform
|
||||
=====================
|
||||
|
||||
linux386 is an i386-based BSP that produces Linux ELF executables.
|
||||
|
||||
This port only implements a very limited number of system calls; basically,
|
||||
just enough to make the demo apps run. Adding more is easy, but there are some
|
||||
subtleties that require more thought. The port should be considered only in
|
||||
proof-of-concept stage right now.
|
||||
|
||||
Important note: you *can't* link access ELF shared libraries from these
|
||||
executables. In other words, you have to all your work from inside ACK.
|
||||
|
||||
IEEE floating point is available, but requires an FPU.
|
||||
|
||||
The executables are generated with aelfslod and are extremely simple; there's
|
||||
one rwx ELF section which contains all the application's code and data. This
|
||||
is not optimal, but it does work.
|
||||
|
||||
|
||||
Bugs
|
||||
====
|
||||
|
||||
isatty() is a stub and always returns 0.
|
||||
|
||||
|
||||
Example command line
|
||||
====================
|
||||
|
||||
ack -mlinux386 -O -o linux386.exe examples/paranoia.c
|
||||
|
||||
The file linux386.exe can then be run on a i386 Linux machine (or on an
|
||||
emulation thereof).
|
||||
|
||||
|
||||
David Given
|
||||
dg@cowlark.com
|
88
plat/qemuppc/boot.s
Normal file
88
plat/qemuppc/boot.s
Normal file
|
@ -0,0 +1,88 @@
|
|||
#
|
||||
! $Source: /cvsroot/tack/Ack/plat/linux386/boot.s,v $
|
||||
! $State: Exp $
|
||||
! $Revision: 1.3 $
|
||||
|
||||
! Declare segments (the order is important).
|
||||
|
||||
.sect .text
|
||||
.sect .rom
|
||||
.sect .data
|
||||
.sect .bss
|
||||
|
||||
.sect .text
|
||||
|
||||
begtext:
|
||||
! This code is placed at the beginning of the ELF executable and is the
|
||||
! first thing that runs.
|
||||
!
|
||||
! On entry, the stack looks like this:
|
||||
!
|
||||
! sp+... NULL
|
||||
! sp+8+(4*argc) env (X quads)
|
||||
! sp+4+(4*argc) NULL
|
||||
! sp+4 argv (argc quads)
|
||||
! sp argc
|
||||
!
|
||||
! The ACK actually expects:
|
||||
!
|
||||
! sp+8 argc
|
||||
! sp+4 ptr to argv
|
||||
! sp ptr to env
|
||||
|
||||
li32 r3, __openfirmware_ptr
|
||||
stw r5, 0(r3)
|
||||
|
||||
li32 r3, envp
|
||||
stwu r3, -4(sp)
|
||||
|
||||
li32 r3, argv
|
||||
stwu r3, -4(sp)
|
||||
|
||||
li32 r3, 1 ! argc
|
||||
stwu r3, -4(sp)
|
||||
|
||||
bl _openfirmware_init
|
||||
bl __m_a_i_n
|
||||
! falls through
|
||||
|
||||
.define __exit
|
||||
.extern __exit
|
||||
.define EXIT
|
||||
.extern EXIT
|
||||
__exit:
|
||||
EXIT:
|
||||
b EXIT
|
||||
|
||||
.define _openfirmware_call
|
||||
.extern _openfirmware_call
|
||||
_openfirmware_call:
|
||||
lwz r3, 0(sp)
|
||||
li32 r4, __openfirmware_ptr
|
||||
lwz r4, 0(r4)
|
||||
mtspr ctr, r4
|
||||
bcctr 20, 0, 0
|
||||
|
||||
! Define symbols at the beginning of our various segments, so that we can find
|
||||
! them. (Except .text, which has already been done.)
|
||||
|
||||
.sect .data; begdata:
|
||||
.sect .rom; begrom:
|
||||
.sect .bss; begbss:
|
||||
|
||||
! Some magic data. All EM systems need these.
|
||||
|
||||
.define _errno
|
||||
.comm _errno, 4 ! Posix errno storage
|
||||
|
||||
! The argv and env arrays.
|
||||
|
||||
.sect .rom
|
||||
argv: .data4 exename, 0
|
||||
envp: .data4 0
|
||||
exename: .asciz 'qemuppc.img'
|
||||
|
||||
.define .trppc, .ignmask
|
||||
.comm .trppc, 4 ! ptr to user trap handler
|
||||
.comm .ignmask, 4 ! user trap ignore mask
|
||||
.comm __openfirmware_ptr, 4 ! OpenFirmware entry point
|
26
plat/qemuppc/build-pkg.lua
Normal file
26
plat/qemuppc/build-pkg.lua
Normal file
|
@ -0,0 +1,26 @@
|
|||
include("plat/build.lua")
|
||||
|
||||
ackfile {
|
||||
name = "boot",
|
||||
srcs = { "./boot.s" },
|
||||
vars = { plat = "qemuppc" }
|
||||
}
|
||||
|
||||
build_plat_libs {
|
||||
name = "libs",
|
||||
arch = "powerpc",
|
||||
plat = "qemuppc",
|
||||
}
|
||||
|
||||
installable {
|
||||
name = "pkg",
|
||||
map = {
|
||||
"+tools",
|
||||
"+libs",
|
||||
"./include+pkg",
|
||||
"util/amisc+aslod-pkg",
|
||||
["$(PLATIND)/qemuppc/boot.o"] = "+boot",
|
||||
["$(PLATIND)/qemuppc/libsys.a"] = "./libsys+lib",
|
||||
}
|
||||
}
|
||||
|
33
plat/qemuppc/build-tools.lua
Normal file
33
plat/qemuppc/build-tools.lua
Normal file
|
@ -0,0 +1,33 @@
|
|||
include("plat/build.lua")
|
||||
|
||||
build_as {
|
||||
name = "as",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_mcg {
|
||||
name = "mcg",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_ncg {
|
||||
name = "ncg",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
build_top {
|
||||
name = "top",
|
||||
arch = "powerpc",
|
||||
}
|
||||
|
||||
return installable {
|
||||
name = "tools",
|
||||
map = {
|
||||
["$(PLATDEP)/qemuppc/as"] = "+as",
|
||||
["$(PLATDEP)/qemuppc/ncg"] = "+ncg",
|
||||
["$(PLATDEP)/qemuppc/mcg"] = "+mcg",
|
||||
["$(PLATDEP)/qemuppc/top"] = "+top",
|
||||
["$(PLATIND)/descr/qemuppc"] = "./descr",
|
||||
"util/opt+pkg",
|
||||
}
|
||||
}
|
88
plat/qemuppc/descr
Normal file
88
plat/qemuppc/descr
Normal file
|
@ -0,0 +1,88 @@
|
|||
# plat/linuxppc/descr
|
||||
|
||||
var w=4
|
||||
var wa=4
|
||||
var p={w}
|
||||
var pa={w}
|
||||
var s=2
|
||||
var sa={s}
|
||||
var l={w}
|
||||
var la={w}
|
||||
var f={w}
|
||||
var fa={w}
|
||||
var d=8
|
||||
var da={d}
|
||||
var x=8
|
||||
var xa={x}
|
||||
var ARCH=powerpc
|
||||
var PLATFORM=qemuppc
|
||||
var PLATFORMDIR={EM}/share/ack/{PLATFORM}
|
||||
var CPP_F=-D__unix
|
||||
var ALIGN=-a0:4 -a1:4 -a2:4 -a3:4 -b0:0x01000000
|
||||
var C_LIB={PLATFORMDIR}/libc-ansi.a
|
||||
# bitfields reversed for compatibility with (g)cc.
|
||||
var CC_ALIGN=-Vr
|
||||
var OLD_C_LIB={C_LIB}
|
||||
var MACHOPT_F=
|
||||
|
||||
# Override the setting in fe so that files compiled for qemuppc can see
|
||||
# the platform-specific headers.
|
||||
|
||||
var C_INCLUDES=-I{PLATFORMDIR}/include -I{EM}/share/ack/include/ansi
|
||||
|
||||
name be
|
||||
from .m.g
|
||||
to .s
|
||||
program {EM}/lib/ack/{PLATFORM}/mcg
|
||||
mapflag -gdb GF=-gdb
|
||||
args {GF?} <
|
||||
stdout
|
||||
need .e
|
||||
end
|
||||
name asopt
|
||||
from .s
|
||||
to .so
|
||||
program {EM}/lib/ack/{PLATFORM}/top
|
||||
args
|
||||
optimizer
|
||||
stdin
|
||||
stdout
|
||||
end
|
||||
name as
|
||||
from .s.so
|
||||
to .o
|
||||
program {EM}/lib/ack/{PLATFORM}/as
|
||||
args - -o > <
|
||||
prep cond
|
||||
end
|
||||
name led
|
||||
from .o.a
|
||||
to .out
|
||||
program {EM}/lib/ack/em_led
|
||||
mapflag -l* LNAME={PLATFORMDIR}/lib*
|
||||
mapflag -fp FLOATS={EM}/{LIB}fp
|
||||
args {ALIGN} {SEPID?} \
|
||||
(.e:{HEAD}={PLATFORMDIR}/boot.o) \
|
||||
({RTS}:.ocm.b={PLATFORMDIR}/c-ansi.o) \
|
||||
({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
|
||||
({RTS}:.mod={PLATFORMDIR}/modula2.o) \
|
||||
({RTS}:.p={PLATFORMDIR}/pascal.o) \
|
||||
-o > < \
|
||||
(.p:{TAIL}={PLATFORMDIR}/libpascal.a) \
|
||||
(.b:{TAIL}={PLATFORMDIR}/libbasic.a) \
|
||||
(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
|
||||
(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
|
||||
(.ocm.b.mod.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
|
||||
{FLOATS?} \
|
||||
(.e:{TAIL}={PLATFORMDIR}/libem.a \
|
||||
{PLATFORMDIR}/libsys.a \
|
||||
{PLATFORMDIR}/libend.a)
|
||||
linker
|
||||
end
|
||||
name cv
|
||||
from .out
|
||||
to .exe
|
||||
program {EM}/bin/aslod
|
||||
args < >
|
||||
outfile qemuppc.img
|
||||
end
|
14
plat/qemuppc/include/ack/config.h
Normal file
14
plat/qemuppc/include/ack/config.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $Source: /cvsroot/tack/Ack/plat/linux386/include/ack/config.h,v $
|
||||
* $State: Exp $
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
|
||||
#ifndef _ACK_CONFIG_H
|
||||
#define _ACK_CONFIG_H
|
||||
|
||||
/* We're providing a time() system call rather than wanting a wrapper around
|
||||
* gettimeofday() in the libc. */
|
||||
|
||||
#define ACKCONF_TIME_IS_A_SYSCALL
|
||||
|
||||
#endif
|
24
plat/qemuppc/include/build.lua
Normal file
24
plat/qemuppc/include/build.lua
Normal file
|
@ -0,0 +1,24 @@
|
|||
include("plat/build.lua")
|
||||
|
||||
headermap = {}
|
||||
packagemap = {}
|
||||
|
||||
local function addheader(h)
|
||||
headermap[h] = "./"..h
|
||||
packagemap["$(PLATIND)/qemuppc/include/"..h] = "./"..h
|
||||
end
|
||||
|
||||
addheader("ack/config.h")
|
||||
addheader("sys/ioctl.h")
|
||||
addheader("unistd.h")
|
||||
|
||||
acklibrary {
|
||||
name = "headers",
|
||||
hdrs = headermap
|
||||
}
|
||||
|
||||
installable {
|
||||
name = "pkg",
|
||||
map = packagemap
|
||||
}
|
||||
|
76
plat/qemuppc/include/sys/ioctl.h
Normal file
76
plat/qemuppc/include/sys/ioctl.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* $Source: /cvsroot/tack/Ack/plat/linux386/include/sys/ioctl.h,v $
|
||||
* $State: Exp $
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
|
||||
#ifndef _SYS_IOCTL_H
|
||||
#define _SYS_IOCTL_H
|
||||
|
||||
/* These are copied from the ioctl_list(2) man page. */
|
||||
|
||||
/* <include/asm-i386/socket.h> */
|
||||
|
||||
#define FIOSETOWN 0x00008901
|
||||
#define SIOCSPGRP 0x00008902
|
||||
#define FIOGETOWN 0x00008903
|
||||
#define SIOCGPGRP 0x00008904
|
||||
#define SIOCATMARK 0x00008905
|
||||
#define SIOCGSTAMP 0x00008906
|
||||
|
||||
/* <include/asm-i386/termios.h> */
|
||||
|
||||
#define TCGETS 0x00005401
|
||||
#define TCSETS 0x00005402
|
||||
#define TCSETSW 0x00005403
|
||||
#define TCSETSF 0x00005404
|
||||
#define TCGETA 0x00005405
|
||||
#define TCSETA 0x00005406
|
||||
#define TCSETAW 0x00005407
|
||||
#define TCSETAF 0x00005408
|
||||
#define TCSBRK 0x00005409
|
||||
#define TCXONC 0x0000540A
|
||||
#define TCFLSH 0x0000540B
|
||||
#define TIOCEXCL 0x0000540C
|
||||
#define TIOCNXCL 0x0000540D
|
||||
#define TIOCSCTTY 0x0000540E
|
||||
#define TIOCGPGRP 0x0000540F
|
||||
#define TIOCSPGRP 0x00005410
|
||||
#define TIOCOUTQ 0x00005411
|
||||
#define TIOCSTI 0x00005412
|
||||
#define TIOCGWINSZ 0x00005413
|
||||
#define TIOCSWINSZ 0x00005414
|
||||
#define TIOCMGET 0x00005415
|
||||
#define TIOCMBIS 0x00005416
|
||||
#define TIOCMBIC 0x00005417
|
||||
#define TIOCMSET 0x00005418
|
||||
#define TIOCGSOFTCAR 0x00005419
|
||||
#define TIOCSSOFTCAR 0x0000541A
|
||||
#define FIONREAD 0x0000541B
|
||||
#define TIOCINQ 0x0000541B
|
||||
#define TIOCLINUX 0x0000541C
|
||||
#define TIOCCONS 0x0000541D
|
||||
#define TIOCGSERIAL 0x0000541E
|
||||
#define TIOCSSERIAL 0x0000541F
|
||||
#define TIOCPKT 0x00005420
|
||||
#define FIONBIO 0x00005421
|
||||
#define TIOCNOTTY 0x00005422
|
||||
#define TIOCSETD 0x00005423
|
||||
#define TIOCGETD 0x00005424
|
||||
#define TCSBRKP 0x00005425
|
||||
#define TIOCTTYGSTRUCT 0x00005426
|
||||
#define FIONCLEX 0x00005450
|
||||
#define FIOCLEX 0x00005451
|
||||
#define FIOASYNC 0x00005452
|
||||
#define TIOCSERCONFIG 0x00005453
|
||||
#define TIOCSERGWILD 0x00005454
|
||||
#define TIOCSERSWILD 0x00005455
|
||||
#define TIOCGLCKTRMIOS 0x00005456
|
||||
#define TIOCSLCKTRMIOS 0x00005457
|
||||
#define TIOCSERGSTRUCT 0x00005458
|
||||
#define TIOCSERGETLSR 0x00005459
|
||||
#define TIOCSERGETMULTI 0x0000545A
|
||||
#define TIOCSERSETMULTI 0x0000545B
|
||||
|
||||
|
||||
|
||||
#endif
|
123
plat/qemuppc/include/unistd.h
Normal file
123
plat/qemuppc/include/unistd.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* unistd.h - standard system calls
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef _UNISTD_H
|
||||
#define _UNISTD_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Types */
|
||||
|
||||
typedef int pid_t;
|
||||
typedef int mode_t;
|
||||
|
||||
typedef long suseconds_t;
|
||||
|
||||
/* Time handling. */
|
||||
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
|
||||
struct timezone
|
||||
{
|
||||
int tz_minuteswest;
|
||||
int tz_dsttime;
|
||||
}; /* obsolete, unused */
|
||||
|
||||
extern int gettimeofday(struct timeval* tv, struct timezone* tz);
|
||||
extern int settimeofday(const struct timeval* tv, const struct timezone* tz);
|
||||
|
||||
/* File access. */
|
||||
|
||||
enum
|
||||
{
|
||||
O_ACCMODE = 0x3,
|
||||
|
||||
O_RDONLY = 0,
|
||||
O_WRONLY = 1,
|
||||
O_RDWR = 2,
|
||||
|
||||
O_CREAT = 0x10,
|
||||
O_TRUNC = 0x20,
|
||||
O_APPEND = 0x40
|
||||
};
|
||||
|
||||
extern int open(const char* path, int access, ...);
|
||||
extern int creat(const char* path, mode_t mode);
|
||||
extern int close(int d);
|
||||
extern int read(int fd, void* buffer, size_t count);
|
||||
extern int write(int fd, void* buffer, size_t count);
|
||||
extern off_t lseek(int fildes, off_t offset, int whence);
|
||||
extern int fcntl(int fd, int op, ...);
|
||||
|
||||
/* Special variables */
|
||||
|
||||
extern char** environ;
|
||||
|
||||
/* Implemented system calls */
|
||||
|
||||
extern void _exit(int);
|
||||
extern pid_t getpid(void);
|
||||
extern int brk(void* ptr);
|
||||
extern void* sbrk(intptr_t increment);
|
||||
extern int isatty(int d);
|
||||
|
||||
/* Signal handling */
|
||||
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
#define SIG_ERR ((sighandler_t) -1) /* Error return. */
|
||||
#define SIG_DFL ((sighandler_t) 0) /* Default action. */
|
||||
#define SIG_IGN ((sighandler_t) 1) /* Ignore signal. */
|
||||
|
||||
#define SIGHUP 1 /* Hangup (POSIX). */
|
||||
#define SIGINT 2 /* Interrupt (ANSI). */
|
||||
#define SIGQUIT 3 /* Quit (POSIX). */
|
||||
#define SIGILL 4 /* Illegal instruction (ANSI). */
|
||||
#define SIGTRAP 5 /* Trace trap (POSIX). */
|
||||
#define SIGABRT 6 /* Abort (ANSI). */
|
||||
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
|
||||
#define SIGBUS 7 /* BUS error (4.2 BSD). */
|
||||
#define SIGFPE 8 /* Floating-point exception (ANSI). */
|
||||
#define SIGKILL 9 /* Kill, unblockable (POSIX). */
|
||||
#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */
|
||||
#define SIGSEGV 11 /* Segmentation violation (ANSI). */
|
||||
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */
|
||||
#define SIGPIPE 13 /* Broken pipe (POSIX). */
|
||||
#define SIGALRM 14 /* Alarm clock (POSIX). */
|
||||
#define SIGTERM 15 /* Termination (ANSI). */
|
||||
#define SIGSTKFLT 16 /* Stack fault. */
|
||||
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */
|
||||
#define SIGCHLD 17 /* Child status has changed (POSIX). */
|
||||
#define SIGCONT 18 /* Continue (POSIX). */
|
||||
#define SIGSTOP 19 /* Stop, unblockable (POSIX). */
|
||||
#define SIGTSTP 20 /* Keyboard stop (POSIX). */
|
||||
#define SIGTTIN 21 /* Background read from tty (POSIX). */
|
||||
#define SIGTTOU 22 /* Background write to tty (POSIX). */
|
||||
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
|
||||
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */
|
||||
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */
|
||||
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
|
||||
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
|
||||
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */
|
||||
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
|
||||
#define SIGIO 29 /* I/O now possible (4.2 BSD). */
|
||||
#define SIGPWR 30 /* Power failure restart (System V). */
|
||||
#define SIGSYS 31 /* Bad system call. */
|
||||
#define SIGUNUSED 31
|
||||
|
||||
#define _NSIG 32 /* Biggest signal number + 1
|
||||
(not including real-time signals). */
|
||||
typedef void (*sighandler_t)(int);
|
||||
extern sighandler_t signal(int signum, sighandler_t handler);
|
||||
extern int raise(int signum);
|
||||
|
||||
|
||||
|
||||
#endif
|
21
plat/qemuppc/libsys/_hol0.s
Normal file
21
plat/qemuppc/libsys/_hol0.s
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
! $Source: /cvsroot/tack/Ack/plat/linux386/libsys/_hol0.s,v $
|
||||
! $State: Exp $
|
||||
! $Revision: 1.1 $
|
||||
|
||||
! Declare segments (the order is important).
|
||||
|
||||
.sect .text
|
||||
.sect .rom
|
||||
.sect .data
|
||||
.sect .bss
|
||||
|
||||
! This data block is used to store information about the current line number
|
||||
! and file.
|
||||
|
||||
.define hol0
|
||||
.define .hol0
|
||||
.sect .bss
|
||||
hol0:
|
||||
.hol0:
|
||||
.space 8
|
43
plat/qemuppc/libsys/brk.c
Normal file
43
plat/qemuppc/libsys/brk.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */
|
||||
#define STACK_BUFFER 128 /* number of bytes to leave for stack */
|
||||
|
||||
extern char _end[1];
|
||||
static char* current = _end;
|
||||
|
||||
int brk(void* newend)
|
||||
{
|
||||
/* This variable is used to figure out the current stack pointer,
|
||||
* by taking its address. */
|
||||
char dummy;
|
||||
char* p = newend;
|
||||
|
||||
if ((p > (&dummy - STACK_BUFFER)) ||
|
||||
(p < _end))
|
||||
return -1;
|
||||
|
||||
current = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* sbrk(intptr_t increment)
|
||||
{
|
||||
char* old;
|
||||
|
||||
if (increment == 0)
|
||||
return current;
|
||||
|
||||
old = current;
|
||||
if (brk(old + increment) < 0)
|
||||
return OUT_OF_MEMORY;
|
||||
|
||||
return old;
|
||||
}
|
16
plat/qemuppc/libsys/build.lua
Normal file
16
plat/qemuppc/libsys/build.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
acklibrary {
|
||||
name = "lib",
|
||||
srcs = {
|
||||
"./*.s",
|
||||
"./*.c",
|
||||
},
|
||||
deps = {
|
||||
"lang/cem/libcc.ansi/headers+headers",
|
||||
"plat/qemuppc/include+headers",
|
||||
"mach/powerpc/libem+headers_qemuppc",
|
||||
},
|
||||
vars = {
|
||||
plat = "qemuppc"
|
||||
}
|
||||
}
|
||||
|
14
plat/qemuppc/libsys/close.c
Normal file
14
plat/qemuppc/libsys/close.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int close(int fd)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
15
plat/qemuppc/libsys/creat.c
Normal file
15
plat/qemuppc/libsys/creat.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "libsys.h"
|
||||
|
||||
int open(const char* path, int access, ...)
|
||||
{
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
13
plat/qemuppc/libsys/getpid.c
Normal file
13
plat/qemuppc/libsys/getpid.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
pid_t getpid(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
8
plat/qemuppc/libsys/isatty.c
Normal file
8
plat/qemuppc/libsys/isatty.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int isatty(int fd)
|
||||
{
|
||||
return 1;
|
||||
}
|
14
plat/qemuppc/libsys/kill.c
Normal file
14
plat/qemuppc/libsys/kill.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int kill(pid_t pid, int sig)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
13
plat/qemuppc/libsys/libsys.h
Normal file
13
plat/qemuppc/libsys/libsys.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef LIBSYS_H
|
||||
#define LIBSYS_H
|
||||
|
||||
extern uint32_t openfirmware_call(void* array);
|
||||
|
||||
extern void _sys_rawwrite(unsigned char b);
|
||||
extern unsigned char _sys_rawread(void);
|
||||
|
||||
extern void _sys_write_tty(char c);
|
||||
|
||||
/* extern int _sys_ttyflags; */
|
||||
|
||||
#endif
|
14
plat/qemuppc/libsys/lseek.c
Normal file
14
plat/qemuppc/libsys/lseek.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
off_t lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
14
plat/qemuppc/libsys/open.c
Normal file
14
plat/qemuppc/libsys/open.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $Source$
|
||||
* $State$
|
||||
* $Revision$
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "libsys.h"
|
||||
|
||||
int creat(const char* path, int mode)
|
||||
{
|
||||
return open(path, O_CREAT|O_WRONLY|O_TRUNC, mode);
|
||||
}
|
80
plat/qemuppc/libsys/openfirmware.c
Normal file
80
plat/qemuppc/libsys/openfirmware.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "libsys.h"
|
||||
|
||||
static void* stdout_handle;
|
||||
|
||||
static void* of_finddevice(const char* name)
|
||||
{
|
||||
struct
|
||||
{
|
||||
const char* method;
|
||||
int ins, outs;
|
||||
const char* name;
|
||||
void* phandle;
|
||||
} args;
|
||||
|
||||
args.method = "finddevice";
|
||||
args.ins = 1;
|
||||
args.outs = 1;
|
||||
args.name = name;
|
||||
openfirmware_call(&args);
|
||||
return args.phandle;
|
||||
}
|
||||
|
||||
static int of_getprop(void* phandle, const char* name, void* dest, int destlen)
|
||||
{
|
||||
struct
|
||||
{
|
||||
const char* method;
|
||||
int ins, outs;
|
||||
void* phandle;
|
||||
const char* name;
|
||||
void* dest;
|
||||
int destlen;
|
||||
int flag;
|
||||
} args;
|
||||
|
||||
args.method = "getprop";
|
||||
args.ins = 4;
|
||||
args.outs = 1;
|
||||
args.phandle = phandle;
|
||||
args.name = name;
|
||||
args.dest = dest;
|
||||
args.destlen = destlen;
|
||||
openfirmware_call(&args);
|
||||
return args.flag;
|
||||
}
|
||||
|
||||
void openfirmware_init(void)
|
||||
{
|
||||
void* chosen = of_finddevice("/chosen");
|
||||
of_getprop(chosen, "stdout", &stdout_handle, sizeof(stdout_handle));
|
||||
}
|
||||
|
||||
unsigned char _sys_rawread(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _sys_rawwrite(unsigned char c)
|
||||
{
|
||||
struct
|
||||
{
|
||||
const char* method;
|
||||
int ins, outs;
|
||||
void* ihandle;
|
||||
void* address;
|
||||
int len;
|
||||
int actual;
|
||||
} args;
|
||||
|
||||
args.method = "write";
|
||||
args.ins = 3;
|
||||
args.outs = 1;
|
||||
args.ihandle = stdout_handle;
|
||||
args.address = &c;
|
||||
args.len = 1;
|
||||
openfirmware_call(&args);
|
||||
}
|
38
plat/qemuppc/libsys/read.c
Normal file
38
plat/qemuppc/libsys/read.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "libsys.h"
|
||||
|
||||
int read(int fd, void* buffer, size_t count)
|
||||
{
|
||||
char i;
|
||||
|
||||
/* We're only allowed to read from fd 0, 1 or 2. */
|
||||
|
||||
if ((fd < 0) || (fd > 2))
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Empty buffer? */
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
/* Read one byte. */
|
||||
|
||||
i = _sys_rawread();
|
||||
#if 0
|
||||
if ((i == '\r') && !(_sys_ttyflags & RAW))
|
||||
i = '\n';
|
||||
if (_sys_ttyflags & ECHO)
|
||||
_sys_write_tty(i);
|
||||
#endif
|
||||
if (i == '\r')
|
||||
i = '\n';
|
||||
_sys_write_tty(i);
|
||||
|
||||
*(char*)buffer = i;
|
||||
return 1;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue