Merge pull request #14 from davidgiven/dtrg-experimental-mcgg

Merge mcg code generator to trunk
This commit is contained in:
David Given 2016-11-23 21:58:31 +01:00 committed by GitHub
commit 0b21f18d4c
140 changed files with 12538 additions and 119 deletions

View file

@ -7,6 +7,7 @@ addons:
apt:
packages:
- ed
- openbios-ppc
git:
depth: 10

View file

@ -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
}
}

View file

@ -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 = {}

View file

@ -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,
}

View file

@ -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);

View file

@ -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);
}

View file

@ -19,6 +19,8 @@
#undef word_t
#define word_t long
typedef uint32_t quad;
#undef ALIGNWORD
#define ALIGNWORD 4

View file

@ -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 */

View file

@ -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) */

View file

@ -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 */
}
}
;

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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
View 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 : */

View file

@ -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"}

View file

@ -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 */
%%;

View file

@ -8,6 +8,8 @@
* All preprocessor based options/constants/functions
*/
#include <stdint.h>
/* ========== ON/OFF options (use #define in mach0.c) ========== */
/*

View 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 : */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 : */

View 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 : */

View 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
View 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(&current_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(&current_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(&current_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 : */

View 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 : */

View 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 : */

View 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 : */

View 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(&current_hop->constraints, vreg);
if (!c)
{
c = calloc(1, sizeof(*c));
pmap_put(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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 : */

View 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(&current, &bb->liveouts);
for (i=bb->hops.count-1; i>=0; i--)
{
struct hop* hop = bb->hops.item[i];
array_removeall(&current, &hop->outs);
finished &= array_appendallu(&hop->throughs, &current);
array_appendallu(&current, &hop->ins);
}
for (i=0; i<bb->phis.count; i++)
array_remove(&current, bb->phis.item[i].left);
finished &= array_appendallu(&bb->liveins, &current);
for (i=0; i<bb->prevs.count; i++)
{
struct basicblock* prev = bb->prevs.item[i];
finished &= array_appendallu(&prev->liveouts, &current);
}
}
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 : */

View 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 : */

View 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(&current_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(&current_proc->usedregs, hreg);
}
}
}
platform_calculate_offsets();
array_insert(&current_proc->entry->hops, platform_prologue(), 0);
if (current_proc->exit)
{
current_proc->exit->hops.count = 0;
array_append(&current_proc->exit->hops, platform_epilogue());
}
}
/* vim: set sw=4 ts=4 expandtab : */

View 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(&current_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(&current_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(&current_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 : */

View 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(&current_proc->blocks, used.item[i]);
}
/* vim: set sw=4 ts=4 expandtab : */

View 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 : */

View 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 : */

View 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(&current_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
View 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 : */

View 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 : */

View 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
View 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 : */

View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -4,4 +4,5 @@
*/
/* $Id$ */
#define label unsigned int
typedef unsigned int label;

127
modules/src/data/array.c Normal file
View 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
View 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

View 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 : */

View 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 : */

View file

@ -0,0 +1,7 @@
clibrary {
name = "lib",
srcs = { "./*.c" },
hdrs = { "./*.h" },
deps = { "./*.h" },
}

View 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 : */

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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 : */

View 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 : */

View file

@ -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 = &notch->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;
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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",
}

View file

@ -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
View 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
View 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

View 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",
}
}

View 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
View 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

View 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

View 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
}

View 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

View 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

View 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
View 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;
}

View 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"
}
}

View 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;
}

View 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;
}

View file

@ -0,0 +1,13 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
pid_t getpid(void)
{
return 0;
}

View file

@ -0,0 +1,8 @@
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
int isatty(int fd)
{
return 1;
}

View 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;
}

View 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

View 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;
}

View 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);
}

View 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);
}

View 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