diff --git a/.travis.yml b/.travis.yml index 408e5d59b..c6e2f5913 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ addons: apt: packages: - ed + - openbios-ppc git: depth: 10 diff --git a/build.lua b/build.lua index 05a4eafcd..cc550cb9e 100644 --- a/build.lua +++ b/build.lua @@ -9,14 +9,22 @@ vars.plats = { "linux386", "linux68k", "linuxppc", + "qemuppc", "pc86", "rpi", } +vars.plats_with_tests = { + "qemuppc", +} local plat_packages = {} +local test_packages = {} for _, p in ipairs(vars.plats) do plat_packages[#plat_packages+1] = "plat/"..p.."+pkg" end +for _, p in ipairs(vars.plats_with_tests) do + test_packages[#test_packages+1] = "plat/"..p.."/tests+tests" +end installable { name = "ack", @@ -34,6 +42,9 @@ installable { "util/opt+pkg", "examples+pkg", plat_packages + }, + deps = { + test_packages } } diff --git a/first/ackbuilder.lua b/first/ackbuilder.lua index f240f232c..81e86b528 100644 --- a/first/ackbuilder.lua +++ b/first/ackbuilder.lua @@ -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 = {} diff --git a/first/build.lua b/first/build.lua index 3240f9f1a..85df519a8 100644 --- a/first/build.lua +++ b/first/build.lua @@ -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, } diff --git a/lang/basic/lib/error.c b/lang/basic/lib/error.c index b2d893adc..becc69d7a 100644 --- a/lang/basic/lib/error.c +++ b/lang/basic/lib/error.c @@ -1,6 +1,9 @@ #include #include +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); diff --git a/lang/basic/src/gencode.c b/lang/basic/src/gencode.c index 8c7a50e95..8fb611dc0 100644 --- a/lang/basic/src/gencode.c +++ b/lang/basic/src/gencode.c @@ -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); } diff --git a/mach/powerpc/as/mach0.c b/mach/powerpc/as/mach0.c index 3a42f1dd8..325c08910 100644 --- a/mach/powerpc/as/mach0.c +++ b/mach/powerpc/as/mach0.c @@ -19,6 +19,8 @@ #undef word_t #define word_t long +typedef uint32_t quad; + #undef ALIGNWORD #define ALIGNWORD 4 diff --git a/mach/powerpc/as/mach2.c b/mach/powerpc/as/mach2.c index 7cd4a4ff4..96d6690df 100644 --- a/mach/powerpc/as/mach2.c +++ b/mach/powerpc/as/mach2.c @@ -49,6 +49,7 @@ %token OP_RS_RA_NB %token OP_RS_RA_RB %token OP_RS_RA_RB_C +%token OP_RS_RA_RA_C %token OP_RS_RA_RB_MB5_ME5_C %token OP_RS_RA_RB_MB6_C %token OP_RS_RA_RB_ME6_C @@ -80,6 +81,7 @@ %token OP_TO_RA_RB %token OP_TO_RA_SI +%token OP_LA %token OP_LI32 /* Other token types */ diff --git a/mach/powerpc/as/mach3.c b/mach/powerpc/as/mach3.c index 153e5d4d2..0f0bfdae7 100644 --- a/mach/powerpc/as/mach3.c +++ b/mach/powerpc/as/mach3.c @@ -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) */ diff --git a/mach/powerpc/as/mach4.c b/mach/powerpc/as/mach4.c index 2fe584992..3f79ca86c 100644 --- a/mach/powerpc/as/mach4.c +++ b/mach/powerpc/as/mach4.c @@ -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 */ + } } ; diff --git a/mach/powerpc/libem/build.lua b/mach/powerpc/libem/build.lua index 56278aa55..318be381d 100644 --- a/mach/powerpc/libem/build.lua +++ b/mach/powerpc/libem/build.lua @@ -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 diff --git a/mach/powerpc/libem/csa.s b/mach/powerpc/libem/csa.s index 64954ff4e..88e6e176a 100644 --- a/mach/powerpc/libem/csa.s +++ b/mach/powerpc/libem/csa.s @@ -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 diff --git a/mach/powerpc/libem/csb.s b/mach/powerpc/libem/csb.s index cbedc8c11..a8df85d7f 100644 --- a/mach/powerpc/libem/csb.s +++ b/mach/powerpc/libem/csb.s @@ -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 diff --git a/mach/powerpc/libem/inn.s b/mach/powerpc/libem/inn.s new file mode 100644 index 000000000..f5ae4c63e --- /dev/null +++ b/mach/powerpc/libem/inn.s @@ -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 */ diff --git a/mach/powerpc/mcg/platform.c b/mach/powerpc/mcg/platform.c new file mode 100644 index 000000000..65b158cac --- /dev/null +++ b/mach/powerpc/mcg/platform.c @@ -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; iusedregs.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; iusedregs.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; iattrs & 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; iattrs & 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 : */ + diff --git a/mach/powerpc/mcg/table b/mach/powerpc/mcg/table new file mode 100644 index 000000000..8511dbb5b --- /dev/null +++ b/mach/powerpc/mcg/table @@ -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 : */ + diff --git a/mach/powerpc/ncg/table b/mach/powerpc/ncg/table index ed107aceb..77fdaedf1 100644 --- a/mach/powerpc/ncg/table +++ b/mach/powerpc/ncg/table @@ -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"} diff --git a/mach/powerpc/top/table b/mach/powerpc/top/table index e735afee6..acbe543a7 100644 --- a/mach/powerpc/top/table +++ b/mach/powerpc/top/table @@ -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 */ + %%; diff --git a/mach/proto/as/comm0.h b/mach/proto/as/comm0.h index 4996350ac..dedafa4c7 100644 --- a/mach/proto/as/comm0.h +++ b/mach/proto/as/comm0.h @@ -8,6 +8,8 @@ * All preprocessor based options/constants/functions */ +#include + /* ========== ON/OFF options (use #define in mach0.c) ========== */ /* diff --git a/mach/proto/mcg/basicblock.c b/mach/proto/mcg/basicblock.c new file mode 100644 index 000000000..aa0a729e0 --- /dev/null +++ b/mach/proto/mcg/basicblock.c @@ -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 +#include + +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 : */ diff --git a/mach/proto/mcg/basicblock.h b/mach/proto/mcg/basicblock.h new file mode 100644 index 000000000..362a05f7f --- /dev/null +++ b/mach/proto/mcg/basicblock.h @@ -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 : */ + diff --git a/mach/proto/mcg/build.lua b/mach/proto/mcg/build.lua new file mode 100644 index 000000000..387d14d26 --- /dev/null +++ b/mach/proto/mcg/build.lua @@ -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 +) + diff --git a/mach/proto/mcg/data.c b/mach/proto/mcg/data.c new file mode 100644 index 000000000..13352ed37 --- /dev/null +++ b/mach/proto/mcg/data.c @@ -0,0 +1,160 @@ +#include "mcg.h" +#include + +#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; iopcode == 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; iblocks.count; i++) + { + struct basicblock* bb = current_proc->blocks.item[i]; + bb->prevs.count = bb->nexts.count = 0; + } + + for (i=0; iblocks.count; i++) + { + struct basicblock* bb = current_proc->blocks.item[i]; + for (j=0; jirs.count; j++) + ir_walk(bb->irs.item[j], collect_outputs_cb, bb); + } + + for (i=0; iblocks.count; i++) + { + struct basicblock* bb = current_proc->blocks.item[i]; + + for (j=0; jnexts.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; inexts.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; iname); + } +} + +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; jprevs.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 %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; iname); + } +} + +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 : */ + diff --git a/mach/proto/mcg/graph.h b/mach/proto/mcg/graph.h new file mode 100644 index 000000000..ed8e8ba21 --- /dev/null +++ b/mach/proto/mcg/graph.h @@ -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 : */ + diff --git a/mach/proto/mcg/hop.c b/mach/proto/mcg/hop.c new file mode 100644 index 000000000..77c6dc260 --- /dev/null +++ b/mach/proto/mcg/hop.c @@ -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; iins.count; i++) + tracef(k, " r%%%d", hop->ins.item[i]->id); + for (i=0; ithroughs.count; i++) + tracef(k, " =%%%d", hop->throughs.item[i]->id); + for (i=0; iouts.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; iinsels.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 : */ + diff --git a/mach/proto/mcg/hop.h b/mach/proto/mcg/hop.h new file mode 100644 index 000000000..21c7e9293 --- /dev/null +++ b/mach/proto/mcg/hop.h @@ -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 : */ + diff --git a/mach/proto/mcg/ir.c b/mach/proto/mcg/ir.c new file mode 100644 index 000000000..e30e82a2b --- /dev/null +++ b/mach/proto/mcg/ir.c @@ -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; iu.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 : */ diff --git a/mach/proto/mcg/ir.h b/mach/proto/mcg/ir.h new file mode 100644 index 000000000..10fa83a48 --- /dev/null +++ b/mach/proto/mcg/ir.h @@ -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 : */ diff --git a/mach/proto/mcg/main.c b/mach/proto/mcg/main.c new file mode 100644 index 000000000..cf8a4435f --- /dev/null +++ b/mach/proto/mcg/main.c @@ -0,0 +1,137 @@ +#include "mcg.h" +#include +#include + +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 : "", 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 : */ diff --git a/mach/proto/mcg/mcg.h b/mach/proto/mcg/mcg.h new file mode 100644 index 000000000..540f5799c --- /dev/null +++ b/mach/proto/mcg/mcg.h @@ -0,0 +1,136 @@ +#ifndef MCG_H +#define MCG_H + +#include +#include +#include +#include +#include +#include +#include +#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 : */ diff --git a/mach/proto/mcg/mcgg_generated_footer.h b/mach/proto/mcg/mcgg_generated_footer.h new file mode 100644 index 000000000..6de0c5277 --- /dev/null +++ b/mach/proto/mcg/mcgg_generated_footer.h @@ -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 : */ + diff --git a/mach/proto/mcg/mcgg_generated_header.h b/mach/proto/mcg/mcgg_generated_header.h new file mode 100644 index 000000000..f0bfa99a0 --- /dev/null +++ b/mach/proto/mcg/mcgg_generated_header.h @@ -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 : */ + diff --git a/mach/proto/mcg/parse_em.c b/mach/proto/mcg/parse_em.c new file mode 100644 index 000000000..b66b177c4 --- /dev/null +++ b/mach/proto/mcg/parse_em.c @@ -0,0 +1,483 @@ +#include "mcg.h" + +static struct e_instr em; +static struct basicblock* code_bb; +static struct basicblock* data_bb; + +static void queue_insn_label(int opcode, const char* label, arith offset); + +static const char* type_to_str(int type) +{ + switch (type) + { + case EM_MNEM: return "EM_MNEM"; + case EM_PSEU: return "EM_PSEU"; + case EM_STARTMES: return "EM_STARTMES"; + case EM_MESARG: return "EM_MESARG"; + case EM_ENDMES: return "EM_ENDMES"; + case EM_DEFILB: return "EM_DEFILB"; + case EM_DEFDLB: return "EM_DEFDLB"; + case EM_DEFDNAM: return "EM_DEFDNAM"; + case EM_ERROR: return "EM_ERROR"; + case EM_FATAL: return "EM_FATAL"; + case EM_EOF: return "EM_EOF"; + } + + assert(0 && "invalid EM type"); +} + +static const char* argtype_to_str(int type) +{ + if (type == 0) return "..."; + if (type == ilb_ptyp) return "ilb"; + if (type == nof_ptyp) return "nof"; + if (type == sof_ptyp) return "sof"; + if (type == cst_ptyp) return "cst"; + if (type == pro_ptyp) return "pro"; + if (type == str_ptyp) return "str"; + if (type == ico_ptyp) return "ico"; + if (type == uco_ptyp) return "uco"; + if (type == fco_ptyp) return "fco"; + return "???"; +} + +static void unknown_type(const char* s) +{ + fatal("%s with unknown type '%s'", + s, + argtype_to_str(em.em_arg.ema_argtype)); +} + +static const char* ilabel_to_str(label l) +{ + assert(current_proc != NULL); + return aprintf(".%s_I%d", current_proc->name, l); +} + +static const char* dlabel_to_str(label l) +{ + return aprintf(".D%d", l); +} + +static void terminate_block(void) +{ + code_bb->is_terminated = true; + code_bb = NULL; +} + +static struct em* new_insn(int opcode) +{ + struct em* em = calloc(sizeof(struct em), 1); + em->opcode = opcode; + return em; +} + +static void queue_insn_simple(int opcode) +{ + struct em* em = new_insn(opcode); + em->paramtype = PARAM_NONE; + array_append(&code_bb->ems, em); + + switch (opcode) + { + case op_bra: + terminate_block(); + break; + } +} + +static void queue_insn_value(int opcode, arith value) +{ + struct em* em = new_insn(opcode); + em->paramtype = PARAM_IVALUE; + em->u.ivalue = value; + array_append(&code_bb->ems, em); + + switch (opcode) + { + case op_csa: + case op_csb: + case op_ret: + terminate_block(); + break; + } +} + +static void queue_insn_label(int opcode, const char* label, arith offset) +{ + struct em* em = new_insn(opcode); + em->paramtype = PARAM_LVALUE; + em->u.lvalue.label = label; + em->u.lvalue.offset = offset; + array_append(&code_bb->ems, em); + + switch (opcode) + { + case op_bra: + terminate_block(); + break; + } +} + +static void queue_insn_block(int opcode, struct basicblock* left, struct basicblock* right) +{ + struct em* em = new_insn(opcode); + em->paramtype = PARAM_BVALUE; + em->u.bvalue.left = left; + em->u.bvalue.right = right; + array_append(&code_bb->ems, em); + + terminate_block(); +} + +static void change_basicblock(struct basicblock* newbb) +{ + array_appendu(¤t_proc->blocks, newbb); + + if (code_bb && !code_bb->is_terminated) + queue_insn_block(op_bra, newbb, NULL); + + code_bb = newbb; +} + +static void queue_insn_ilabel(int opcode, int label) +{ + const char* name = ilabel_to_str(em.em_ilb); + struct basicblock* left = bb_get(name); + + switch (opcode) + { + case op_bra: + queue_insn_block(em.em_opcode, left, NULL); + break; + + case op_zeq: + case op_zne: + case op_zlt: + case op_zle: + case op_zgt: + case op_zge: + case op_beq: + case op_bne: + case op_blt: + case op_ble: + case op_bgt: + case op_bge: + { + struct basicblock* bb = bb_get(NULL); + queue_insn_block(em.em_opcode, left, bb); + change_basicblock(bb); + break; + } + + default: + fatal("parse_em: unhandled conditional '%s'", + em_mnem[opcode - sp_fmnem]); + } +} + +static void queue_ilabel(arith label) +{ + change_basicblock(bb_get(ilabel_to_str(label))); +} + +static void parse_pseu(void) +{ + switch (em.em_opcode) + { + case ps_exp: /* external proc */ + case ps_exa: /* external array */ + case ps_inp: /* internal proc */ + case ps_ina: /* internal array */ + { + bool export = (em.em_opcode == ps_exp) || (em.em_opcode == ps_exa); + bool proc = (em.em_opcode == ps_exp) || (em.em_opcode == ps_inp); + + switch (em.em_arg.ema_argtype) + { + case pro_ptyp: + symbol_declare(strdup(em.em_pnam), export, proc); + break; + + case sof_ptyp: + assert(em.em_off == 0); + symbol_declare(strdup(em.em_dnam), export, proc); + break; + + case nof_ptyp: + assert(em.em_off == 0); + symbol_declare(dlabel_to_str(em.em_dlb), export, proc); + break; + + default: + unknown_type("exp, exa, inp, ina"); + } + break; + } + + case ps_con: /* .data */ + case ps_rom: /* .rom */ + { + bool ro = (em.em_opcode == ps_rom); + + switch (em.em_arg.ema_argtype) + { + case ico_ptyp: + case uco_ptyp: + { + arith val = atol(em.em_string); + data_int(val, em.em_size, ro); + break; + } + + case fco_ptyp: + { + data_float(em.em_string, em.em_size, ro); + break; + } + + case str_ptyp: + data_block(strdup(em.em_string), em.em_size, ro); + break; + + case cst_ptyp: + data_int(em.em_cst, EM_wordsize, ro); + break; + + case nof_ptyp: + data_offset(dlabel_to_str(em.em_dlb), em.em_off, ro); + break; + + case sof_ptyp: + case pro_ptyp: + data_offset(strdup(em.em_dnam), em.em_off, ro); + break; + + case ilb_ptyp: + { + const char* label = ilabel_to_str(em.em_ilb); + + /* This is really hacky; to handle basic block flow + * descriptor blocks, we need to track which bbs a descriptor + * can exit to. So we create fake bb objects for each + * block, purely to track this. + */ + + if (data_bb) + { + struct em* em = new_insn(op_bra); + em->paramtype = PARAM_BVALUE; + em->u.bvalue.left = bb_get(label); + array_append(&data_bb->ems, em); + } + + data_offset(label, 0, ro); + break; + } + + default: + unknown_type("con, rom"); + } + break; + } + + case ps_bss: + { + switch (em.em_arg.ema_argtype) + { + case cst_ptyp: + data_bss(EM_bsssize, em.em_cst); + break; + + case ico_ptyp: + case uco_ptyp: + { + arith val = atol(em.em_string); + data_int(val, em.em_size, false); + break; + } + + case sof_ptyp: + data_offset(strdup(em.em_dnam), em.em_off, false); + break; + + default: + unknown_type("bss"); + } + break; + } + + case ps_pro: /* procedure start */ + { + struct symbol* symbol; + + current_proc = calloc(sizeof(struct procedure), 1); + current_proc->name = strdup(em.em_pnam); + current_proc->entry = bb_get(current_proc->name); + current_proc->locals_size = em.em_nlocals; + code_bb = current_proc->entry; + code_bb->is_root = true; + array_append(¤t_proc->blocks, code_bb); + + symbol = symbol_get(current_proc->name); + symbol->section = SECTION_TEXT; + symbol->proc = current_proc; + symbol->is_proc = true; + break; + } + + case ps_end: /* procedure end */ + tb_procedure(); + + current_proc = NULL; + code_bb = NULL; + break; + + default: + fatal("unknown pseudo with opcode %d\n", em.em_opcode); + } +} + +static arith mes_get_cst(void) +{ + EM_getinstr(&em); + if (em.em_type != EM_MESARG) + fatal("malformed MES"); + return em.em_cst; +} + +static void parse_mes(void) +{ + assert(em.em_arg.ema_argtype == cst_ptyp); + switch (em.em_cst) + { + case 0: /* error */ + fatal("MES 0 received (explicit halt)"); + + case 3: /* register variable */ + { + /* ego will sometimes generate 'mes 3' pseudos with no actual + * parameters. Detect and ignore these. */ + + EM_getinstr(&em); + if (em.em_type == EM_MESARG) + { + arith offset = em.em_cst; + int size = mes_get_cst(); + int type = mes_get_cst(); + int priority = mes_get_cst(); + tb_regvar(current_proc, offset, size, type, priority); + } + break; + } + } + + while ((em.em_type == EM_STARTMES) || (em.em_type == EM_MESARG)) + EM_getinstr(&em); + + if (em.em_type != EM_ENDMES) + fatal("malformed MES"); +} + +static void create_data_label(const char* label) +{ + data_label(label); + if (current_proc) + { + data_bb = bb_get(label); + data_bb->is_fake = true; + array_append(¤t_proc->blocks, data_bb); + } +} + +void parse_em(void) +{ + EM_getinstr(&em); + tb_filestart(); + + while (em.em_type != EM_EOF) + { + switch (em.em_type) + { + case EM_PSEU: + parse_pseu(); + break; + + case EM_DEFILB: + queue_ilabel(em.em_ilb); + break; + + case EM_DEFDLB: + create_data_label(dlabel_to_str(em.em_dlb)); + break; + + case EM_DEFDNAM: + create_data_label(strdup(em.em_dnam)); + break; + + case EM_STARTMES: + parse_mes(); + break; + + case EM_MNEM: + if (code_bb) + { + int flags = em_flag[em.em_opcode - sp_fmnem]; + + if (flags & EM_PAR) + { + switch (em.em_argtype) + { + case 0: + /* This is an instruction which would normally + * take a size, but the size is provided on the + * stack. We hates them. */ + queue_insn_simple(em.em_opcode); + break; + + case ilb_ptyp: + queue_insn_ilabel(em.em_opcode, em.em_ilb); + break; + + case nof_ptyp: + queue_insn_label(em.em_opcode, + dlabel_to_str(em.em_dlb), em.em_off); + break; + + case sof_ptyp: + queue_insn_label(em.em_opcode, + strdup(em.em_dnam), em.em_off); + break; + + case pro_ptyp: + queue_insn_label(em.em_opcode, + strdup(em.em_pnam), 0); + break; + + case cst_ptyp: + if ((flags & EM_PAR) == PAR_B) + queue_insn_ilabel(em.em_opcode, em.em_ilb); + else + queue_insn_value(em.em_opcode, em.em_cst); + break; + + default: + unknown_type("instruction"); + } + } + else + queue_insn_simple(em.em_opcode); + } + break; + + default: + fatal("unrecognised instruction type '%d'", em.em_type); + } + + EM_getinstr(&em); + } + + tb_fileend(); +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/pass_convertstackops.c b/mach/proto/mcg/pass_convertstackops.c new file mode 100644 index 000000000..49da42226 --- /dev/null +++ b/mach/proto/mcg/pass_convertstackops.c @@ -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; iirs.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; inexts.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; jprevs.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; iopcode = IR_NOP; + } + + for (i=0; i 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; ju.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; iopcode == 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; iirs.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; iblocks.count; i++) + { + struct basicblock* bb = current_proc->blocks.item[i]; + rewrite_jumps(bb); + } +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/pass_groupirs.c b/mach/proto/mcg/pass_groupirs.c new file mode 100644 index 000000000..57b850a9b --- /dev/null +++ b/mach/proto/mcg/pass_groupirs.c @@ -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; iblocks.count; i++) + { + struct basicblock* bb = current_proc->blocks.item[i]; + int j; + + for (j=0; jirs.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; iroot = NULL; + } +} + +static void find_roots(void) +{ + int i; + + for (i=0; iroot); + 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; iname, cost, bestcost); +} + +void burm_panic_cannot_match(struct burm_node* node) +{ + fprintf(stderr, "could not find any patterns to match:\n"); + ir_print('!', node->ir); + fprintf(stderr, "aborting!\n"); + exit(1); +} + +static void emit_return_reg(int index) +{ + hop_add_vreg_insel(current_hop, current_hop->output, index); +} + +static struct vreg* find_vreg_of_child(int child) +{ + struct insn* insn = current_insn->children[child]; + + if (insn->hop) + return insn->hop->output; + else + return insn->ir->result; +} + +static void emit_reg(int child, int index) +{ + struct vreg* vreg = find_vreg_of_child(child); + + if (vreg) + { + hop_add_vreg_insel(current_hop, vreg, index); + array_appendu(&vreg->used, current_hop); + } +} + +static void emit_string(const char* data) +{ + hop_add_string_insel(current_hop, data); +} + +static void emit_fragment(int child) +{ + emit(current_insn->children[child]); +} + +static void emit_value(int child) +{ + hop_add_value_insel(current_hop, current_insn->children[child]->ir); +} + +static void emit_eoi(void) +{ + hop_add_eoi_insel(current_hop); +} + +static struct constraint* get_constraint(struct vreg* vreg) +{ + struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg); + if (!c) + { + c = calloc(1, sizeof(*c)); + pmap_put(¤t_hop->constraints, vreg, c); + } + return c; +} + +static void constrain_input_reg(int child, uint32_t attr) +{ + struct vreg* vreg = find_vreg_of_child(child); + struct constraint* c; + + assert(vreg); + + array_appendu(¤t_hop->ins, vreg); + get_constraint(vreg)->attrs = attr; +} + +static void constrain_input_reg_preserved(int child) +{ + struct vreg* vreg = find_vreg_of_child(child); + struct constraint* c; + + assert(vreg); + array_appendu(¤t_hop->throughs, vreg); + get_constraint(vreg)->preserved = true; +} + +static uint32_t find_type_from_constraint(uint32_t attr) +{ + /* Looks through the registers and finds a concrete register implementing + * that attribute, and returns the type. We assume that all registers + * implementing an attribute (which anyone is going to ask for, 'volatile' + * doesn't count) will have the same type. TODO: mcgg should check for + * this. */ + + const struct burm_register_data* brd = burm_register_data; + while (brd->id) + { + if (brd->attrs & attr) + { + const uint32_t type_attrs = + (burm_int_ATTR | burm_float_ATTR | + burm_long_ATTR | burm_double_ATTR); + + if (brd->attrs & type_attrs) + return brd->attrs & type_attrs; + return attr; + } + brd++; + } + + fatal("unable to find a register matching attribute 0x%x", attr); + return 0; +} + +static void constrain_output_reg(uint32_t attr) +{ + struct vreg* vreg = current_hop->output; + + if (!vreg) + current_hop->output = vreg = new_vreg(); + + array_appendu(¤t_hop->outs, vreg); + vreg->defined = current_hop; + vreg->type = find_type_from_constraint(attr); + + get_constraint(vreg)->attrs = attr; +} + +static void constrain_output_reg_equal_to(int child) +{ + struct vreg* vreg = find_vreg_of_child(child); + + get_constraint(current_hop->output)->equals_to = vreg; +} + +static const struct burm_emitter_data emitter_data = +{ + &emit_string, + &emit_fragment, + &emit_return_reg, + &emit_reg, + &emit_value, + &emit_eoi, + &constrain_input_reg, + &constrain_input_reg_preserved, + &constrain_output_reg, + &constrain_output_reg_equal_to, +}; + +static void emit(struct insn* insn) +{ + struct insn* old = current_insn; + current_insn = insn; + + insn->insndata->emitter(&emitter_data); + + current_insn = old; +} + +static struct insn* walk_instructions(struct burm_node* node, int goal) +{ + struct insn* insn = calloc(1, sizeof(*insn)); + int i; + + insn->ir = node->ir; + insn->num_children = 0; + + if (goal) + { + int insn_no = burm_rule(node->state_label, goal); + const short* nts = burm_nts[insn_no]; + struct burm_node* children[MAX_CHILDREN] = {0}; + + insn->insndata = &burm_instruction_data[insn_no]; + + burm_kids(node, insn_no, children); + + i = 0; + for (;;) + { + if (!children[i]) + break; + + insn->children[i] = walk_instructions(children[i], nts[i]); + insn->num_children++; + i++; + } + + tracef('I', "I: $%d goal %d %s selected %d: %s\n", + node->ir->id, + goal, + insn->insndata->is_fragment ? "fragment" : "instruction", + insn_no, + insn->insndata->name); + + if (!insn->insndata->is_fragment) + { + insn->hop = current_hop = new_hop(current_bb, insn->ir); + current_hop->insndata = insn->insndata; + emit(insn); + + if (!current_hop->output) + { + switch (node->label) + { + case ir_to_esn(IR_REG, 0): + current_hop->output = node->ir->result; + assert(current_hop->output != NULL); + break; + + case ir_to_esn(IR_NOP, 'I'): + case ir_to_esn(IR_NOP, 'F'): + case ir_to_esn(IR_NOP, 'L'): + case ir_to_esn(IR_NOP, 'D'): + current_hop->output = node->left->ir->result; + assert(current_hop->output != NULL); + break; + } + } + + hop_print('I', current_hop); + array_append(¤t_bb->hops, current_hop); + + if ((goal != burm_stmt_NT) && !insn->ir->result) + insn->ir->result = insn->hop->output; + } + } + + return insn; +} + +static struct burm_node* build_shadow_tree(struct ir* root, struct ir* ir) +{ + struct burm_node* node = calloc(1, sizeof(*node)); + node->ir = ir; + + if (ir->root == root) + { + node->label = ir_to_esn(ir->opcode, ir->type); + + if (ir->left) + node->left = build_shadow_tree(root, ir->left); + + if (ir->right) + node->right = build_shadow_tree(root, ir->right); + } + else + node->label = ir_to_esn(IR_REG, 0); + + return node; +} + +static void select_instructions(void) +{ + int i; + + tracef('I', "I: BLOCK: %s\n", current_bb->name); + + for (i=0; iirs.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; ju.phivalue.count; j++) + { + struct basicblock* parentbb = current_ir->u.phivalue.item[j].left; + struct ir* parentir = current_ir->u.phivalue.item[j].right; + struct phi* phi = calloc(1, sizeof(*phi)); + tracef('I', " %s=>$%d", parentbb->name, parentir->id); + + phi->prev = parentbb; + phi->ir = parentir; + pmap_add(¤t_bb->phis, current_ir->result, phi); + } + tracef('I', "\n"); + } + else + { + ir_print('I', current_ir); + shadow = build_shadow_tree(current_ir, current_ir); + burm_label(shadow); + + insnno = burm_rule(shadow->state_label, 1); + if (!insnno) + burm_panic_cannot_match(shadow); + + walk_instructions(shadow, burm_stmt_NT); + } + } +} + +void pass_instruction_selector(void) +{ + int i; + + for (i=0; iphis.count; j++) + { + struct vreg* vreg = bb->phis.item[j].left; + struct phi* phi = bb->phis.item[j].right; + + assert(array_contains(&bb->prevs, phi->prev)); + array_appendu(&phi->prev->liveouts, phi->ir->result); + } + } +} + +static void propagate_liveness(struct basicblock* bb) +{ + static ARRAYOF(struct vreg) current; + int i; + + current.count = 0; + array_appendall(¤t, &bb->liveouts); + + for (i=bb->hops.count-1; i>=0; i--) + { + struct hop* hop = bb->hops.item[i]; + + array_removeall(¤t, &hop->outs); + finished &= array_appendallu(&hop->throughs, ¤t); + array_appendallu(¤t, &hop->ins); + } + + for (i=0; iphis.count; i++) + array_remove(¤t, bb->phis.item[i].left); + + finished &= array_appendallu(&bb->liveins, ¤t); + + for (i=0; iprevs.count; i++) + { + struct basicblock* prev = bb->prevs.item[i]; + finished &= array_appendallu(&prev->liveouts, ¤t); + } +} + +void pass_live_vreg_analysis(void) +{ + int i; + + preload_blocks(); + + do + { + finished = true; + + tracef('L', "L: beginning liveness pass\n"); + for (i=0; iliveins.count == 0); +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/mach/proto/mcg/pass_phigroups.c b/mach/proto/mcg/pass_phigroups.c new file mode 100644 index 000000000..dfff73faa --- /dev/null +++ b/mach/proto/mcg/pass_phigroups.c @@ -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; iphis.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; ivregs.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 : */ + + + diff --git a/mach/proto/mcg/pass_prologueepilogue.c b/mach/proto/mcg/pass_prologueepilogue.c new file mode 100644 index 000000000..3b0c517cb --- /dev/null +++ b/mach/proto/mcg/pass_prologueepilogue.c @@ -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; ihops.count; j++) + { + struct hop* hop = bb->hops.item[j]; + + for (k=0; kregsin.count; k++) + { + struct hreg* hreg = hop->regsin.item[k].left; + if (!hreg->is_stacked) + array_appendu(¤t_proc->usedregs, hreg); + } + + for (k=0; kregsout.count; k++) + { + struct hreg* hreg = hop->regsout.item[k].left; + if (!hreg->is_stacked) + array_appendu(¤t_proc->usedregs, hreg); + } + } + } + + platform_calculate_offsets(); + + array_insert(¤t_proc->entry->hops, platform_prologue(), 0); + + if (current_proc->exit) + { + current_proc->exit->hops.count = 0; + array_append(¤t_proc->exit->hops, platform_epilogue()); + } +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/mach/proto/mcg/pass_registerallocator.c b/mach/proto/mcg/pass_registerallocator.c new file mode 100644 index 000000000..a5436bc73 --- /dev/null +++ b/mach/proto/mcg/pass_registerallocator.c @@ -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; ibrd->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; ihops.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; ialiases.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; iattrs & 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; iattrs & vreg->type) + { + if (!candidatein && + !candidateout && + !register_used(current_ins, hreg) && + !register_used(current_outs, hreg)) + { + /* This hreg is unused, so we don't need to evict anything. + * Shouldn't really happen in real life. */ + return hreg; + } + if (candidatein && candidateout && (candidatein == candidateout)) + { + /* This is a through register. */ + tracef('R', "R: evicting %%%d from %s\n", candidatein->id, hreg->id); + pmap_put(&evicted, candidatein, hreg); + pmap_remove(current_ins, hreg, candidatein); + pmap_remove(current_outs, hreg, candidatein); + return hreg; + } + } + } + + /* Couldn't find anything to evict */ + assert(false); +} + +static bool constraints_match(struct hreg* hreg, struct vreg* vreg) +{ + struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg); + if (c) + return (hreg->attrs & c->attrs); + return true; +} + +static bool allocatable_stackable_input(struct hreg* hreg, struct vreg* vreg) +{ + return !register_used(current_ins, hreg) && + (hreg->attrs & vreg->type); +} + +static bool allocatable_stackable_output(struct hreg* hreg, struct vreg* vreg) +{ + return !register_used(current_outs, hreg) && + (hreg->attrs & vreg->type); +} + +static bool allocatable_input(struct hreg* hreg, struct vreg* vreg) +{ + return allocatable_stackable_input(hreg, vreg) && + constraints_match(hreg, vreg) && + !hreg->is_stacked; +} + +static bool allocatable_output(struct hreg* hreg, struct vreg* vreg) +{ + return allocatable_stackable_output(hreg, vreg) && + constraints_match(hreg, vreg) && + !hreg->is_stacked; +} + +static bool allocatable_through(struct hreg* hreg, struct vreg* vreg) +{ + return allocatable_stackable_input(hreg, vreg) && + allocatable_stackable_output(hreg, vreg) && + !(hreg->attrs & current_hop->insndata->corrupts); +} + +static struct hreg* find_input_reg(struct vreg* vreg) +{ + int i; + struct hreg* hreg = NULL; + + for (i=0; iis_stacked) + { + /* This vreg is stacked; we need to put it in a register. That's + * slightly exciting because the vreg might be a through, which + * means we need an output register too... which we might not be + * able to allocate. */ + + if (array_contains(¤t_hop->throughs, vreg)) + { + struct hreg* src = hreg; + hreg = find_through_reg(vreg); + assert(hreg); + pmap_remove(current_ins, src, vreg); + pmap_remove(current_outs, src, vreg); + pmap_add(current_ins, hreg, vreg); + pmap_add(current_outs, hreg, vreg); + return; + } + else + { + /* Not a through. */ + pmap_remove(current_ins, hreg, vreg); + hreg = NULL; + } + } + else if (pmap_findleft(current_ins, hreg) == vreg) + { + /* Yup, already there. */ + } + else if (allocatable_input(hreg, vreg)) + { + /* The register is free. */ + } + else + { + /* Can't honour the hint. */ + hreg = NULL; + } + } + + if (!hreg) + { + /* Find an unused input register of the right class. */ + + hreg = find_input_reg(vreg); + if (!hreg) + hreg = evict(vreg); + } + + pmap_add(current_ins, hreg, vreg); +} + +static void add_output_register(struct vreg* vreg) +{ + struct hreg* hreg; + int i; + struct constraint* c; + + /* Is this register supposed to be the same as one of the input registers? + * */ + + c = pmap_findleft(¤t_hop->constraints, vreg); + if (c->equals_to) + { + tracef('R', "R: output equality constraint of %%%d to %%%d\n", + vreg->id, c->equals_to->id); + + /* This output register is constrained to be in the same hreg as an + * input register (most likely for a 2op instruction). */ + + hreg = pmap_findright(current_ins, c->equals_to); + + /* If this register is currently unused as an output, use it. */ + + if (allocatable_output(hreg, c->equals_to)) + { + pmap_add(current_outs, hreg, vreg); + return; + } + + /* Okay, something's in it. Most likely it's a through being used as an + * input register. Trying to evict it would be pointless as that would + * also evict the input. So, we're going to have to do this the hard + * way: we try to allocate a matched set of input and output registers. + * */ + + hreg = find_through_reg(vreg); + if (!hreg) + hreg = evict(vreg); + + pmap_add(current_outs, hreg, vreg); + tracef('R', "R: output equality constraint requires extra move of %%%d => %s\n", + c->equals_to->id, hreg->id); + pmap_add(current_ins, hreg, c->equals_to); + } + else + { + /* This is an ordinary new register. */ + + hreg = find_output_reg(vreg); + if (!hreg) + hreg = evict(vreg); + + pmap_add(current_outs, hreg, vreg); + } +} + +static void add_through_register(struct vreg* vreg, struct hreg* hreg) +{ + /* Register hint for an input? */ + + if (hreg) + { + bool unused = allocatable_through(hreg, vreg); + struct vreg* inuse = pmap_findleft(current_ins, hreg); + struct vreg* outuse = pmap_findleft(current_outs, hreg); + + if (unused || ((inuse == vreg) && (outuse == vreg))) + { + /* Input and output are either free or already assigned to this + * vreg. */ + } + else + { + /* Nope, can't honour the hint. Mark the register as evicted; we'll + * put it in something later (probably a stack slot). */ + + tracef('R', "R: cannot place %%%d in %s, evicting\n", vreg->id, hreg->id); + pmap_put(&evicted, vreg, hreg); + pmap_remove(current_ins, hreg, vreg); + pmap_remove(current_outs, hreg, vreg); + return; + } + } + + assert(hreg); + pmap_put(current_ins, hreg, vreg); + pmap_put(current_outs, hreg, vreg); +} + +static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* src) +{ + uint32_t srctype = vreg->type; + struct hreg* hreg; + int i; + + /* Find an unused output register of the right class which is not also + * being used as an input register. */ + + hreg = NULL; + for (i=0; iattrs & 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; ithroughs.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; iins.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; iouts.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; iregsin; + + 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; jliveins.count; j++) + { + struct vreg* vreg = bb->liveins.item[j]; + for (k=0; kprevs.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; jphis.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; jphis.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; jhops.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; kregsin.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; kregsout.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; icount; 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; idest, 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; ihops, 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; iprevs.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; kphis.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; kregsin.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; iis_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 : */ + diff --git a/mach/proto/mcg/pass_removedeadblocks.c b/mach/proto/mcg/pass_removedeadblocks.c new file mode 100644 index 000000000..8a9a07fb4 --- /dev/null +++ b/mach/proto/mcg/pass_removedeadblocks.c @@ -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; iirs.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; iblocks, used.item[i]); +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/mach/proto/mcg/pass_removedeadphis.c b/mach/proto/mcg/pass_removedeadphis.c new file mode 100644 index 000000000..b8b61fbbd --- /dev/null +++ b/mach/proto/mcg/pass_removedeadphis.c @@ -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; iirs.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; iirs.count; i++) + { + struct ir* ir = bb->irs.item[i]; + switch (ir->opcode) + { + case IR_PHI: + for (j=0; ju.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; iirs.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; iirs.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; iprevs.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; iirs.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 : */ diff --git a/mach/proto/mcg/pass_splitcriticaledges.c b/mach/proto/mcg/pass_splitcriticaledges.c new file mode 100644 index 000000000..27e832e44 --- /dev/null +++ b/mach/proto/mcg/pass_splitcriticaledges.c @@ -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; iirs.count; i++) + ir_walk(source->irs.item[i], find_replace_cb, &rwp); + + array_remove(&source->nexts, sink); + array_append(&source->nexts, bb); + + array_append(&bb->prevs, source); + array_append(&bb->nexts, sink); + + array_remove(&sink->prevs, source); + array_append(&sink->prevs, bb); + + array_append(¤t_proc->blocks, bb); +} + +static bool consider_edges_leading_from(struct basicblock* bb) +{ + bool changed = false; + + if (bb->nexts.count > 1) + { + int i; + + for (i=0; inexts.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; iblocks.count; i++) + changed |= consider_edges_leading_from(current_proc->blocks.item[i]); + + } + while (changed); +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/mach/proto/mcg/pass_ssa.c b/mach/proto/mcg/pass_ssa.c new file mode 100644 index 000000000..e0f835f17 --- /dev/null +++ b/mach/proto/mcg/pass_ssa.c @@ -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; iprevs.count >= 2) + { + for (j=0; jprevs.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; iirs.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; inexts.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; iirs.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; ioffset, bb->name); + for (j=0; joffset, dominates->name); + } + } + } + + /* Add empty phi nodes. */ + + for (i=0; isize); + 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; ilocals.count; i++) + { + current_local = current_proc->locals.item[i].right; + if (current_local->is_register) + ssa_convert(); + } +} + +/* vim: set sw=4 ts=4 expandtab : */ + + diff --git a/mach/proto/mcg/pass_typeinference.c b/mach/proto/mcg/pass_typeinference.c new file mode 100644 index 000000000..f3b123e25 --- /dev/null +++ b/mach/proto/mcg/pass_typeinference.c @@ -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; iirs.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; iopcode == 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; ju.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; ju.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; iopcode]; + + 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; iopcode]; + + 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; iu.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 : */ + diff --git a/mach/proto/mcg/predicates.c b/mach/proto/mcg/predicates.c new file mode 100644 index 000000000..427a59836 --- /dev/null +++ b/mach/proto/mcg/predicates.c @@ -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<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<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 : */ + + diff --git a/mach/proto/mcg/procedure.c b/mach/proto/mcg/procedure.c new file mode 100644 index 000000000..f819c56e3 --- /dev/null +++ b/mach/proto/mcg/procedure.c @@ -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; iblocks.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; jprevs.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; jnexts.count; j++) + tracef(k, " %s", bb->nexts.item[j]->name); + tracef(k, "\n"); + } + + for (j=0; jirs.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; iis_fake ? "FAKE " : "", + bb->name); + + if (bb->prevs.count > 0) + { + tracef(k, "%c: FROM:", k); + for (j=0; jprevs.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; jnexts.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; jliveins.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; jliveouts.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; jphis.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; jhops.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; iname)); + for (j=0; jhops.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 \"%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 \"%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 : */ + diff --git a/mach/proto/mcg/procedure.h b/mach/proto/mcg/procedure.h new file mode 100644 index 000000000..226db804c --- /dev/null +++ b/mach/proto/mcg/procedure.h @@ -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 : */ diff --git a/mach/proto/mcg/reg.c b/mach/proto/mcg/reg.c new file mode 100644 index 000000000..d40deb171 --- /dev/null +++ b/mach/proto/mcg/reg.c @@ -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 : */ + diff --git a/mach/proto/mcg/reg.h b/mach/proto/mcg/reg.h new file mode 100644 index 000000000..00c8220b5 --- /dev/null +++ b/mach/proto/mcg/reg.h @@ -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 : */ diff --git a/mach/proto/mcg/symbol.c b/mach/proto/mcg/symbol.c new file mode 100644 index 000000000..ac5e78ce4 --- /dev/null +++ b/mach/proto/mcg/symbol.c @@ -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 +#include + +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; isymbol; + if (cb(symbol, user)) + return symbol; + idf = idf->id_next; + } + } + + return NULL; +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/mach/proto/mcg/treebuilder.c b/mach/proto/mcg/treebuilder.c new file mode 100644 index 000000000..2ead527fc --- /dev/null +++ b/mach/proto/mcg/treebuilder.c @@ -0,0 +1,1681 @@ +#include "mcg.h" + +static struct basicblock* current_bb; + +static int stackptr; +static struct ir* stack[64]; + +static struct ir* convert(struct ir* src, int srcsize, int destsize, int opcode); +static struct ir* appendir(struct ir* ir); +static void insn_ivalue(int opcode, arith value); + +static void reset_stack(void) +{ + stackptr = 0; +} + +static void push(struct ir* ir) +{ + if (stackptr == sizeof(stack)/sizeof(*stack)) + fatal("stack overflow"); + +#if 0 + /* If we try to push something which is too small, convert it to a word + * first. */ + + if (ir->size < EM_wordsize) + ir = convertu(ir, EM_wordsize); +#endif + + stack[stackptr++] = ir; +} + +static struct ir* pop(int size) +{ + if (size < EM_wordsize) + size = EM_wordsize; + + if (stackptr == 0) + { + /* Nothing in our fake stack, so we have to read from the real stack. */ + + if (size < EM_wordsize) + size = EM_wordsize; + return + appendir( + new_ir0( + IR_POP, size + ) + ); + } + else + { + struct ir* ir = stack[--stackptr]; + +#if 0 + /* If we try to pop something which is smaller than a word, convert it first. */ + + if (size < EM_wordsize) + ir = convertu(ir, size); +#endif + + if (ir->size != size) + { + if ((size == (EM_wordsize*2)) && (ir->size == EM_wordsize)) + { + /* Tried to read a long, but we got an int. Assemble the long + * out of two ints. Note that EM doesn't specify an order. */ + return + new_ir2( + IR_FROMIPAIR, size, + ir, + pop(EM_wordsize) + ); + } + else if ((size == EM_wordsize) && (ir->size == (EM_wordsize*2))) + { + /* Tried to read an int, but we got a long. */ + push( + new_ir1( + IR_FROML1, EM_wordsize, + ir + ) + ); + + return + new_ir1( + IR_FROML0, EM_wordsize, + ir + ); + } + else + fatal("expected an item on stack of size %d, but got %d\n", size, ir->size); + } + return ir; + } +} + +static void print_stack(void) +{ + int i; + + tracef('E', "E: stack:"); + for (i=0; iid, ir->size); + } + tracef('E', " (top)\n"); +} + +static struct ir* appendir(struct ir* ir) +{ + int i; + + assert(current_bb != NULL); + array_appendu(¤t_bb->irs, ir); + + ir_print('0', ir); + return ir; +} + +static void materialise_stack(void) +{ + int i; + + for (i=0; isize, + ir + ) + ); + } + + reset_stack(); +} + +void tb_filestart(void) +{ +} + +void tb_fileend(void) +{ +} + +void tb_regvar(struct procedure* procedure, arith offset, int size, int type, int priority) +{ + struct local* local = calloc(1, sizeof(*local)); + local->size = size; + local->offset = offset; + local->is_register = true; + imap_put(&procedure->locals, offset, local); +} + +static struct ir* address_of_external(const char* label, arith offset) +{ + if (offset != 0) + return + new_ir2( + IR_ADD, EM_pointersize, + new_labelir(label), + new_wordir(offset) + ); + else + return + new_labelir(label); +} + +static struct ir* convert(struct ir* src, int srcsize, int destsize, int opcode) +{ + if (srcsize == 1) + { + if ((opcode == IR_FROMSI) || (opcode == IR_FROMSL)) + { + src = new_ir1( + IR_EXTENDB, EM_wordsize, + src + ); + } + srcsize = EM_wordsize; + } + + if ((srcsize == 2) && (srcsize != EM_wordsize)) + { + if ((opcode == IR_FROMSI) || (opcode == IR_FROMSL)) + { + src = new_ir1( + IR_EXTENDH, EM_wordsize, + src + ); + } + srcsize = EM_wordsize; + } + + if (src->size == EM_wordsize) + {} + else if (src->size == (2*EM_wordsize)) + opcode++; + else + fatal("can't convert from %d to %d", src->size, destsize); + + return + new_ir1( + opcode, destsize, + src + ); +} + +static struct ir* compare(struct ir* left, struct ir* right, + int size, int opcode) +{ + if (size == EM_wordsize) + {} + else if (size == (2*EM_wordsize)) + opcode++; + else + fatal("can't compare things of size %d", size); + + return + new_ir2( + opcode, EM_wordsize, + left, right + ); +} + +static struct ir* store(int size, struct ir* address, int offset, struct ir* value) +{ + int opcode; + + if (size == 1) + { + opcode = IR_STOREB; + size = EM_wordsize; + } + else if ((size < EM_wordsize) && (size == 2)) + { + opcode = IR_STOREH; + size = EM_wordsize; + } + else + opcode = IR_STORE; + + if (offset > 0) + address = new_ir2( + IR_ADD, EM_pointersize, + address, new_wordir(offset) + ); + + return + new_ir2( + opcode, size, + address, value + ); +} + +static struct ir* load(int size, struct ir* address, int offset) +{ + int opcode; + + if (size == 1) + { + opcode = IR_LOADB; + size = EM_wordsize; + } + else if ((size < EM_wordsize) && (size == 2)) + { + opcode = IR_LOADH; + size = EM_wordsize; + } + else + opcode = IR_LOAD; + + if (offset > 0) + address = new_ir2( + IR_ADD, EM_pointersize, + address, new_wordir(offset) + ); + + return + new_ir1( + opcode, size, + address + ); +} + +static struct ir* tristate_compare(int size, int opcode) +{ + struct ir* right = pop(size); + struct ir* left = pop(size); + + return compare(left, right, size, opcode); +} + +static struct ir* tristate_compare0(int size, int opcode) +{ + struct ir* right = new_wordir(0); + struct ir* left = pop(size); + + return compare(left, right, size, opcode); +} + +static void simple_convert(int opcode) +{ + struct ir* destsize = pop(EM_wordsize); + struct ir* srcsize = pop(EM_wordsize); + struct ir* value; + + assert(srcsize->opcode == IR_CONST); + assert(destsize->opcode == IR_CONST); + + value = pop(srcsize->u.ivalue); + push( + convert(value, srcsize->u.ivalue, destsize->u.ivalue, opcode) + ); +} + +static void simple_branch2(int opcode, int size, + struct basicblock* truebb, struct basicblock* falsebb, + int irop) +{ + struct ir* right = pop(size); + struct ir* left = pop(size); + + materialise_stack(); + appendir( + new_ir2( + irop, 0, + compare(left, right, size, IR_COMPARESI), + new_ir2( + IR_PAIR, 0, + new_bbir(truebb), + new_bbir(falsebb) + ) + ) + ); +} + +static void compare0_branch2(int opcode, + struct basicblock* truebb, struct basicblock* falsebb, + int irop) +{ + push( + new_wordir(0) + ); + + simple_branch2(opcode, EM_wordsize, truebb, falsebb, irop); +} + +static void simple_test(int size, int irop) +{ + push( + new_ir1( + irop, EM_wordsize, + tristate_compare0(size, IR_COMPARESI) + ) + ); +} + +static void simple_test_neg(int size, int irop) +{ + simple_test(size, irop); + + push( + new_ir1( + IR_NOT, EM_wordsize, + pop(EM_wordsize) + ) + ); +} + +static void helper_function(const char* name) +{ + /* Delegates to a helper function; these leave their result on the stack + * rather than returning values through lfr. */ + + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir(name) + ) + ); +} + +static void insn_simple(int opcode) +{ + switch (opcode) + { + case op_bra: + { + struct ir* dest = pop(EM_pointersize); + + materialise_stack(); + appendir( + new_ir1( + IR_JUMP, 0, + dest + ) + ); + break; + } + + case op_cii: simple_convert(IR_FROMSI); break; + case op_ciu: simple_convert(IR_FROMSI); break; + case op_cui: simple_convert(IR_FROMUI); break; + case op_cfu: simple_convert(IR_FROMUF); break; + case op_cfi: simple_convert(IR_FROMSF); break; + case op_cif: simple_convert(IR_FROMSI); break; + case op_cuf: simple_convert(IR_FROMUI); break; + case op_cff: simple_convert(IR_FROMSF); break; + + case op_cmp: + push( + tristate_compare(EM_pointersize, IR_COMPAREUI) + ); + break; + + case op_teq: simple_test( EM_wordsize, IR_IFEQ); break; + case op_tne: simple_test_neg(EM_wordsize, IR_IFEQ); break; + case op_tlt: simple_test( EM_wordsize, IR_IFLT); break; + case op_tge: simple_test_neg(EM_wordsize, IR_IFLT); break; + case op_tle: simple_test( EM_wordsize, IR_IFLE); break; + case op_tgt: simple_test_neg(EM_wordsize, IR_IFLE); break; + + case op_cai: + { + struct ir* dest = pop(EM_pointersize); + + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + dest + ) + ); + break; + } + + case op_inc: + { + push( + new_ir2( + IR_ADD, EM_wordsize, + pop(EM_wordsize), + new_wordir(1) + ) + ); + break; + } + + case op_dec: + { + push( + new_ir2( + IR_SUB, EM_wordsize, + pop(EM_wordsize), + new_wordir(1) + ) + ); + break; + } + + case op_lim: + { + push( + new_ir1( + (EM_wordsize == 2) ? IR_LOAD : IR_LOADH, EM_wordsize, + new_labelir(".ignmask") + ) + ); + break; + } + + case op_sim: + { + appendir( + new_ir2( + (EM_wordsize == 2) ? IR_STORE : IR_STOREH, EM_wordsize, + new_labelir(".ignmask"), + pop(EM_wordsize) + ) + ); + break; + } + + case op_trp: helper_function(".trp"); break; + case op_sig: helper_function(".sig"); break; + + case op_rtt: + { + insn_ivalue(op_ret, 0); + break; + } + + /* FIXME: These instructions are really complex and barely used + * (Modula-2 bitset support, I believe). Leave them until later. */ + case op_set: helper_function(".unimplemented_set"); break; + case op_ior: helper_function(".unimplemented_ior"); break; + + case op_dch: + push( + new_ir1( + IR_CHAINFP, EM_pointersize, + pop(EM_pointersize) + ) + ); + break; + + case op_lpb: + push( + new_ir1( + IR_FPTOAB, EM_pointersize, + pop(EM_pointersize) + ) + ); + break; + + case op_lni: + { + /* Increment line number --- ignore. */ + break; + } + + default: + fatal("treebuilder: unknown simple instruction '%s'", + em_mnem[opcode - sp_fmnem]); + } +} + +static void insn_bvalue(int opcode, struct basicblock* leftbb, struct basicblock* rightbb) +{ + switch (opcode) + { + case op_zeq: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPEQ); break; + case op_zlt: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPLT); break; + case op_zle: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPLE); break; + + case op_zne: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPEQ); break; + case op_zge: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPLT); break; + case op_zgt: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPLE); break; + + case op_beq: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPEQ); break; + case op_blt: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPLT); break; + case op_ble: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPLE); break; + + case op_bne: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPEQ); break; + case op_bge: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPLT); break; + case op_bgt: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPLE); break; + + case op_bra: + { + materialise_stack(); + + appendir( + new_ir1( + IR_JUMP, 0, + new_bbir(leftbb) + ) + ); + break; + } + + case op_lae: + push( + new_bbir(leftbb) + ); + break; + + default: + fatal("treebuilder: unknown bvalue instruction '%s'", + em_mnem[opcode - sp_fmnem]); + } +} + +static void simple_alu1(int opcode, int size, int irop) +{ + struct ir* val = pop(size); + + push( + new_ir1( + irop, size, + val + ) + ); +} + +static void simple_alu2(int opcode, int size, int irop) +{ + struct ir* right = pop(size); + struct ir* left = pop(size); + + push( + new_ir2( + irop, size, + left, right + ) + ); +} + +static struct ir* extract_block_refs(struct basicblock* bb) +{ + struct ir* outir = NULL; + int i; + + for (i=0; iems.count; i++) + { + struct em* em = bb->ems.item[i]; + assert(em->opcode == op_bra); + assert(em->paramtype == PARAM_BVALUE); + + outir = new_ir2( + IR_PAIR, 0, + new_bbir(em->u.bvalue.left), + outir + ); + } + + return outir; +} + +static void change_by(struct ir* address, int amount) +{ + appendir( + store( + EM_wordsize, address, 0, + new_ir2( + IR_ADD, EM_wordsize, + load( + EM_wordsize, address, 0 + ), + new_wordir(amount) + ) + ) + ); +} + +static struct ir* ptradd(struct ir* address, int offset) +{ + if (offset == 0) + return address; + + return + new_ir2( + IR_ADD, EM_pointersize, + address, + new_wordir(offset) + ); +} + +static void blockmove(struct ir* dest, struct ir* src, struct ir* size) +{ + /* memmove stack: ( size src dest -- ) */ + push(size); + push(src); + push(dest); + + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir("memmove") + ) + ); + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + new_wordir(EM_pointersize*2 + EM_wordsize) + ) + ); +} + +static void insn_ivalue(int opcode, arith value) +{ + switch (opcode) + { + case op_adi: simple_alu2(opcode, value, IR_ADD); break; + case op_sbi: simple_alu2(opcode, value, IR_SUB); break; + case op_mli: simple_alu2(opcode, value, IR_MUL); break; + case op_dvi: simple_alu2(opcode, value, IR_DIV); break; + case op_rmi: simple_alu2(opcode, value, IR_MOD); break; + case op_sli: simple_alu2(opcode, value, IR_ASL); break; + case op_sri: simple_alu2(opcode, value, IR_ASR); break; + case op_ngi: simple_alu1(opcode, value, IR_NEG); break; + + case op_adu: simple_alu2(opcode, value, IR_ADD); break; + case op_sbu: simple_alu2(opcode, value, IR_SUB); break; + case op_mlu: simple_alu2(opcode, value, IR_MUL); break; + case op_slu: simple_alu2(opcode, value, IR_LSL); break; + case op_sru: simple_alu2(opcode, value, IR_LSR); break; + case op_rmu: simple_alu2(opcode, value, IR_MODU); break; + case op_dvu: simple_alu2(opcode, value, IR_DIVU); break; + + case op_and: simple_alu2(opcode, value, IR_AND); break; + case op_ior: simple_alu2(opcode, value, IR_OR); break; + case op_xor: simple_alu2(opcode, value, IR_EOR); break; + case op_com: simple_alu1(opcode, value, IR_NOT); break; + + case op_adf: simple_alu2(opcode, value, IR_ADDF); break; + case op_sbf: simple_alu2(opcode, value, IR_SUBF); break; + case op_mlf: simple_alu2(opcode, value, IR_MULF); break; + case op_dvf: simple_alu2(opcode, value, IR_DIVF); break; + case op_ngf: simple_alu1(opcode, value, IR_NEGF); break; + + case op_cmu: /* fall through */ + case op_cms: push(tristate_compare(value, IR_COMPAREUI)); break; + case op_cmi: push(tristate_compare(value, IR_COMPARESI)); break; + case op_cmf: push(tristate_compare(value, IR_COMPAREF)); break; + + case op_lol: + push( + load( + EM_wordsize, + new_localir(value), 0 + ) + ); + break; + + case op_ldl: + push( + load( + EM_wordsize*2, + new_localir(value), 0 + ) + ); + break; + + case op_stl: + appendir( + store( + EM_wordsize, + new_localir(value), 0, + pop(EM_wordsize) + ) + ); + break; + + case op_sdl: + appendir( + store( + EM_wordsize*2, + new_localir(value), 0, + pop(EM_wordsize*2) + ) + ); + break; + + case op_lal: + push( + new_localir(value) + ); + break; + + case op_lil: + push( + load( + EM_wordsize, + load( + EM_pointersize, + new_localir(value), 0 + ), 0 + ) + ); + break; + + case op_sil: + appendir( + store( + EM_wordsize, + load( + EM_pointersize, + new_localir(value), 0 + ), 0, + pop(EM_wordsize) + ) + ); + break; + + case op_inl: + change_by(new_localir(value), 1); + break; + + case op_del: + change_by(new_localir(value), -1); + break; + + case op_zrl: + appendir( + store( + EM_wordsize, + new_localir(value), 0, + new_wordir(0) + ) + ); + break; + + case op_zrf: + { + struct ir* ir = new_constir(value, 0); + ir->opcode = IR_CONST; + push(ir); + break; + } + + case op_loe: + push( + load( + EM_wordsize, + new_labelir(".hol0"), value + ) + ); + break; + + case op_lae: + push( + new_ir2( + IR_ADD, EM_pointersize, + new_labelir(".hol0"), + new_wordir(value) + ) + ); + break; + + case op_ste: + appendir( + store( + EM_wordsize, + new_labelir(".hol0"), value, + pop(EM_wordsize) + ) + ); + break; + + case op_zre: + appendir( + store( + EM_wordsize, + new_labelir(".hol0"), value, + new_wordir(0) + ) + ); + break; + + case op_loc: + push( + new_wordir(value) + ); + break; + + case op_loi: + { + struct ir* ptr = pop(EM_pointersize); + int offset = 0; + + /* FIXME: this is awful; need a better way of dealing with + * non-standard EM sizes. */ + if (value > (EM_wordsize*2)) + appendir(ptr); + + while (value > 0) + { + int s; + if (value > (EM_wordsize*2)) + s = EM_wordsize*2; + else + s = value; + + push( + load( + s, + ptr, offset + ) + ); + + value -= s; + offset += s; + } + + assert(value == 0); + break; + } + + case op_lof: + { + struct ir* ptr = pop(EM_pointersize); + + push( + load( + EM_wordsize, + ptr, value + ) + ); + break; + } + + case op_ldf: + { + struct ir* ptr = pop(EM_pointersize); + + push( + load( + EM_wordsize*2, + ptr, value + ) + ); + break; + } + + case op_sti: + { + struct ir* ptr = pop(EM_pointersize); + int offset = 0; + + /* FIXME: this is awful; need a better way of dealing with + * non-standard EM sizes. */ + if (value > (EM_wordsize*2)) + appendir(ptr); + + while (value > 0) + { + int s; + if (value > (EM_wordsize*2)) + s = EM_wordsize*2; + else + s = value; + + appendir( + store( + s, + ptr, offset, + pop(s) + ) + ); + + value -= s; + offset += s; + } + + assert(value == 0); + break; + } + + case op_stf: + { + struct ir* ptr = pop(EM_pointersize); + struct ir* val = pop(EM_wordsize); + + appendir( + store( + EM_wordsize, + ptr, value, + val + ) + ); + break; + } + + case op_sdf: + { + struct ir* ptr = pop(EM_pointersize); + struct ir* val = pop(EM_wordsize*2); + + appendir( + store( + EM_wordsize*2, + ptr, value, + val + ) + ); + break; + } + + case op_ads: + { + struct ir* off = pop(value); + struct ir* ptr = pop(EM_pointersize); + + if (value != EM_pointersize) + off = convert(off, value, EM_pointersize, IR_FROMUI); + + push( + new_ir2( + IR_ADD, EM_pointersize, + ptr, off + ) + ); + break; + } + + case op_adp: + { + struct ir* ptr = pop(EM_pointersize); + + push( + new_ir2( + IR_ADD, EM_pointersize, + ptr, + new_wordir(value) + ) + ); + break; + } + + case op_sbs: + { + struct ir* right = pop(EM_pointersize); + struct ir* left = pop(EM_pointersize); + + struct ir* delta = + new_ir2( + IR_SUB, EM_pointersize, + left, right + ); + + if (value != EM_pointersize) + delta = convert(delta, EM_pointersize, value, IR_FROMUI); + + push(delta); + break; + } + + case op_dup: + { + struct ir* v = pop(value); + appendir(v); + push(v); + push(v); + break; + } + + case op_exg: + { + struct ir* v1 = pop(value); + struct ir* v2 = pop(value); + push(v1); + push(v2); + break; + } + + case op_asp: + { + switch (value) + { + case 0: + break; + + case -1: + case -2: + case -4: + case -8: + push(new_anyir(-value)); + break; + + default: + while ((value > 0) && (stackptr > 0)) + { + struct ir* ir = pop(stack[stackptr-1]->size); + value -= ir->size; + } + + if (value != 0) + { + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + new_wordir(value) + ) + ); + } + break; + } + break; + } + + case op_ass: + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + pop(value) + ) + ); + break; + + case op_ret: + { + if (value > 0) + { + struct ir* retval = pop(value); + materialise_stack(); + appendir( + new_ir1( + IR_SETRET, value, + retval + ) + ); + } + + if (!current_proc->exit) + { + current_proc->exit = bb_get(NULL); + array_append(¤t_proc->blocks, current_proc->exit); + + /* This is actually ignored --- the entire block gets special + * treatment. But a lot of the rest of the code assumes that + * all basic blocks have one instruction, so we insert one. */ + + array_append(¤t_proc->exit->irs, + new_ir0( + IR_RET, 0 + ) + ); + } + + appendir( + new_ir1( + IR_JUMP, 0, + new_bbir(current_proc->exit) + ) + ); + break; + } + + case op_lfr: + { + push( + appendir( + new_ir0( + IR_GETRET, value + ) + ) + ); + break; + } + + case op_csa: + case op_csb: + { + const char* helper = aprintf(".%s", + (opcode == op_csa) ? "csa" : "csb"); + struct ir* descriptor = pop(EM_pointersize); + + if (descriptor->opcode != IR_LABEL) + fatal("csa/csb are only supported if they refer " + "directly to a descriptor block"); + + push(descriptor); + materialise_stack(); + appendir( + new_ir2( + IR_FARJUMP, 0, + new_labelir(helper), + extract_block_refs(bb_get(descriptor->u.lvalue)) + ) + ); + break; + } + + case op_sar: + case op_lar: + case op_aar: + { + const char* helper; + if (value != EM_wordsize) + fatal("sar/lar/aar are only supported when using " + "word-size descriptors"); + + switch (opcode) + { + case op_sar: helper = ".sar4"; break; + case op_lar: helper = ".lar4"; break; + case op_aar: helper = ".aar4"; break; + } + + materialise_stack(); + /* No push here, because the helper function leaves the result on + * the physical stack (which is very dubious). */ + appendir( + new_ir1( + IR_CALL, 0, + new_labelir(helper) + ) + ); + break; + } + + case op_lxl: + { + struct ir* ir; + + /* Walk the static chain. */ + + ir = new_ir0( + IR_GETFP, EM_pointersize + ); + + while (value--) + { + ir = new_ir1( + IR_CHAINFP, EM_pointersize, + ir + ); + } + + push(ir); + break; + } + + case op_lxa: + { + struct ir* ir; + + /* Walk the static chain. */ + + ir = new_ir0( + IR_GETFP, EM_pointersize + ); + + while (value--) + { + ir = new_ir1( + IR_CHAINFP, EM_pointersize, + ir + ); + } + + push( + new_ir1( + IR_FPTOAB, EM_pointersize, + ir + ) + ); + break; + } + + case op_fef: + { + struct ir* f = pop(value); + + /* fef is implemented by calling a helper function which then mutates + * the stack. We read the return values off the stack when retracting + * the stack pointer. */ + + push(f); + push(new_wordir(0)); + + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir((value == 4) ? ".fef4" : ".fef8") + ) + ); + + /* exit, leaving an int and then a float (or double) on the stack. */ + break; + } + + case op_fif: + { + /* fif is implemented by calling a helper function which then mutates + * the stack. We read the return values off the stack when retracting + * the stack pointer. */ + + /* We start with two floats on the stack. */ + + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir((value == 4) ? ".fif4" : ".fif8") + ) + ); + + /* exit, leaving two floats (or doubles) on the stack. */ + break; + } + + case op_lor: + { + switch (value) + { + case 0: + push( + appendir( + new_ir1( + IR_FPTOLB, EM_pointersize, + new_ir0( + IR_GETFP, EM_pointersize + ) + ) + ) + ); + break; + + case 1: + push( + appendir( + new_ir0( + IR_GETSP, EM_pointersize + ) + ) + ); + break; + + case 2: + helper_function(".unimplemented_lor_2"); + break; + + default: + fatal("'lor %d' not supported", value); + } + + break; + } + + case op_str: + { + switch (value) + { + case 0: + appendir( + new_ir1( + IR_SETFP, EM_pointersize, + pop(EM_pointersize) + ) + ); + break; + + case 1: + appendir( + new_ir1( + IR_SETSP, EM_pointersize, + pop(EM_pointersize) + ) + ); + break; + + case 2: + helper_function(".unimplemented_str_2"); + break; + + default: + fatal("'str %d' not supported", value); + } + + break; + } + + case op_blm: + { + /* Input stack: ( src dest -- ) */ + struct ir* dest = pop(EM_pointersize); + struct ir* src = pop(EM_pointersize); + blockmove(dest, src, new_wordir(value)); + break; + } + + case op_bls: + { + /* Input stack: ( src dest size -- ) */ + struct ir* dest = pop(EM_pointersize); + struct ir* src = pop(EM_pointersize); + struct ir* size = pop(EM_wordsize); + blockmove(dest, src, size); + break; + } + + case op_los: + { + /* Copy an arbitrary amount to the stack. */ + struct ir* bytes = pop(EM_wordsize); + struct ir* address = pop(EM_pointersize); + + materialise_stack(); + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + new_ir1( + IR_NEG, EM_wordsize, + bytes + ) + ) + ); + + push( + new_ir0( + IR_GETSP, EM_pointersize + ) + ); + push(address); + push(bytes); + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir("memcpy") + ) + ); + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + new_wordir(EM_pointersize*2 + EM_wordsize) + ) + ); + break; + } + + case op_sts: + { + /* Copy an arbitrary amount from the stack. */ + struct ir* bytes = pop(EM_wordsize); + struct ir* dest = pop(EM_pointersize); + struct ir* src; + + materialise_stack(); + src = appendir( + new_ir0( + IR_GETSP, EM_pointersize + ) + ); + + push(dest); + push(src); + push(bytes); + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir("memcpy") + ) + ); + appendir( + new_ir1( + IR_STACKADJUST, EM_pointersize, + new_ir2( + IR_ADD, EM_wordsize, + new_wordir(EM_pointersize*2 + EM_wordsize), + bytes + ) + ) + ); + break; + } + + /* FIXME: These instructions are really complex and barely used + * (Modula-2 bitset support, I believe). Leave them until leter. */ + case op_inn: + { + push( + new_wordir(value) + ); + + helper_function(".inn"); + break; + } + + case op_lin: + { + /* Set line number --- ignore. */ + break; + } + + default: + fatal("treebuilder: unknown ivalue instruction '%s'", + em_mnem[opcode - sp_fmnem]); + } +} + +static void insn_lvalue(int opcode, const char* label, arith offset) +{ + switch (opcode) + { + case op_lpi: + case op_lae: + push( + address_of_external(label, offset) + ); + break; + + case op_loe: + push( + new_ir1( + IR_LOAD, EM_wordsize, + address_of_external(label, offset) + ) + ); + break; + + case op_lde: + push( + new_ir1( + IR_LOAD, EM_wordsize*2, + address_of_external(label, offset) + ) + ); + break; + + case op_ste: + appendir( + new_ir2( + IR_STORE, EM_wordsize, + address_of_external(label, offset), + pop(EM_wordsize) + ) + ); + break; + + case op_sde: + appendir( + new_ir2( + IR_STORE, EM_wordsize*2, + address_of_external(label, offset), + pop(EM_wordsize*2) + ) + ); + break; + + case op_zre: + appendir( + new_ir2( + IR_STORE, EM_wordsize, + address_of_external(label, offset), + new_wordir(0) + ) + ); + break; + + case op_ine: + appendir( + new_ir2( + IR_STORE, EM_wordsize, + address_of_external(label, offset), + new_ir2( + IR_ADD, EM_wordsize, + new_ir1( + IR_LOAD, EM_wordsize, + address_of_external(label, offset) + ), + new_wordir(1) + ) + ) + ); + break; + + case op_dee: + appendir( + new_ir2( + IR_STORE, EM_wordsize, + address_of_external(label, offset), + new_ir2( + IR_ADD, EM_wordsize, + new_ir1( + IR_LOAD, EM_wordsize, + address_of_external(label, offset) + ), + new_wordir(-1) + ) + ) + ); + break; + + case op_cal: + assert(offset == 0); + materialise_stack(); + appendir( + new_ir1( + IR_CALL, 0, + new_labelir(label) + ) + ); + break; + + case op_bra: + assert(offset == 0); + materialise_stack(); + appendir( + new_ir1( + IR_JUMP, 0, + new_labelir(label) + ) + ); + break; + + case op_gto: + { + struct ir* descriptor = pop(EM_pointersize); + + appendir( + new_ir1( + IR_SETSP, EM_pointersize, + load(EM_pointersize, descriptor, EM_pointersize*2) + ) + ); + appendir( + new_ir1( + IR_SETFP, EM_pointersize, + load(EM_pointersize, descriptor, EM_pointersize*1) + ) + ); + appendir( + new_ir1( + IR_JUMP, 0, + load(EM_pointersize, descriptor, EM_pointersize*0) + ) + ); + break; + } + + case op_fil: + { + /* Set filename --- ignore. */ + break; + } + + default: + fatal("treebuilder: unknown lvalue instruction '%s'", + em_mnem[opcode - sp_fmnem]); + } +} + +static void generate_tree(struct basicblock* bb) +{ + int i; + + tracef('0', "0: block %s\n", bb->name); + + current_bb = bb; + reset_stack(); + + for (i=0; iems.count; i++) + { + struct em* em = bb->ems.item[i]; + tracef('E', "E: read %s ", em_mnem[em->opcode - sp_fmnem]); + switch (em->paramtype) + { + case PARAM_NONE: + tracef('E', "\n"); + insn_simple(em->opcode); + break; + + case PARAM_IVALUE: + tracef('E', "value=%d\n", em->u.ivalue); + insn_ivalue(em->opcode, em->u.ivalue); + break; + + case PARAM_LVALUE: + tracef('E', "label=%s offset=%d\n", + em->u.lvalue.label, em->u.lvalue.offset); + insn_lvalue(em->opcode, em->u.lvalue.label, em->u.lvalue.offset); + break; + + case PARAM_BVALUE: + tracef('E', "true=%s", em->u.bvalue.left->name); + if (em->u.bvalue.right) + tracef('E', " false=%s", em->u.bvalue.right->name); + tracef('E', "\n"); + insn_bvalue(em->opcode, em->u.bvalue.left, em->u.bvalue.right); + break; + + default: + assert(0); + } + + if (tracing('E')) + print_stack(); + } + + /* Yes, we are allowed to leave stuff on the stack at the end of the procedure. + * It's discarded as part of the function return. */ +} + +void tb_procedure(void) +{ + int i; + + for (i=0; iblocks.count; i++) + generate_tree(current_proc->blocks.item[i]); +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/top/top.c b/mach/proto/top/top.c index 178dd450e..5d57fa1de 100644 --- a/mach/proto/top/top.c +++ b/mach/proto/top/top.c @@ -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); diff --git a/modules/h/em_label.h b/modules/h/em_label.h index f267364a3..018b0d345 100644 --- a/modules/h/em_label.h +++ b/modules/h/em_label.h @@ -4,4 +4,5 @@ */ /* $Id$ */ -#define label unsigned int +typedef unsigned int label; + diff --git a/modules/src/data/array.c b/modules/src/data/array.c new file mode 100644 index 000000000..2847bbdd4 --- /dev/null +++ b/modules/src/data/array.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#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; icount; 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; icount; 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; icount; 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; icount; 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; icount; i++) + unchanged &= array_appendu(array, src->item[i]); + + return unchanged; +} + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/modules/src/data/array.h b/modules/src/data/array.h new file mode 100644 index 000000000..6ec497566 --- /dev/null +++ b/modules/src/data/array.h @@ -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 + diff --git a/modules/src/data/astring.c b/modules/src/data/astring.c new file mode 100644 index 000000000..7da06e9fa --- /dev/null +++ b/modules/src/data/astring.c @@ -0,0 +1,27 @@ +#include +#include +#include + +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 : */ + diff --git a/modules/src/data/astring.h b/modules/src/data/astring.h new file mode 100644 index 000000000..483c0287d --- /dev/null +++ b/modules/src/data/astring.h @@ -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 : */ + diff --git a/modules/src/data/build.lua b/modules/src/data/build.lua new file mode 100644 index 000000000..9f5941808 --- /dev/null +++ b/modules/src/data/build.lua @@ -0,0 +1,7 @@ +clibrary { + name = "lib", + srcs = { "./*.c" }, + hdrs = { "./*.h" }, + deps = { "./*.h" }, +} + diff --git a/modules/src/data/diagnostics.c b/modules/src/data/diagnostics.c new file mode 100644 index 000000000..32ac6a0ee --- /dev/null +++ b/modules/src/data/diagnostics.c @@ -0,0 +1,38 @@ +#include +#include +#include +#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 : */ + diff --git a/modules/src/data/diagnostics.h b/modules/src/data/diagnostics.h new file mode 100644 index 000000000..7c2908fa0 --- /dev/null +++ b/modules/src/data/diagnostics.h @@ -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 : */ + diff --git a/modules/src/data/imap.c b/modules/src/data/imap.c new file mode 100644 index 000000000..81982a704 --- /dev/null +++ b/modules/src/data/imap.c @@ -0,0 +1,74 @@ +#include +#include +#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; icount; 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; icount; 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; icount; i++) + { + struct imap_node* node = &map->item[i]; + if (node->left == left) + return node->right; + } + + return NULL; +} + + diff --git a/modules/src/data/imap.h b/modules/src/data/imap.h new file mode 100644 index 000000000..cb312e16c --- /dev/null +++ b/modules/src/data/imap.h @@ -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 + diff --git a/modules/src/data/pmap.c b/modules/src/data/pmap.c new file mode 100644 index 000000000..fffba7e90 --- /dev/null +++ b/modules/src/data/pmap.c @@ -0,0 +1,107 @@ +#include +#include +#include +#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; icount; 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; icount; 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; icount; 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; icount; 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; icount; i++) + { + struct pmap_node* node = &map->item[i]; + if (node->right == right) + return node->left; + } + + return NULL; +} + + diff --git a/modules/src/data/pmap.h b/modules/src/data/pmap.h new file mode 100644 index 000000000..a7980a3a3 --- /dev/null +++ b/modules/src/data/pmap.h @@ -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 + diff --git a/modules/src/data/smap.c b/modules/src/data/smap.c new file mode 100644 index 000000000..312241a20 --- /dev/null +++ b/modules/src/data/smap.c @@ -0,0 +1,74 @@ +#include +#include +#include +#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; icount; 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; icount; 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; icount; i++) + { + struct smap_node* node = &map->item[i]; + if (strcmp(node->left, left) == 0) + return node->right; + } + + return NULL; +} + diff --git a/modules/src/data/smap.h b/modules/src/data/smap.h new file mode 100644 index 000000000..7ccdb51de --- /dev/null +++ b/modules/src/data/smap.h @@ -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 + diff --git a/modules/src/data/stringlist.c b/modules/src/data/stringlist.c new file mode 100644 index 000000000..28322bf98 --- /dev/null +++ b/modules/src/data/stringlist.c @@ -0,0 +1,43 @@ +#include +#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 : */ + + diff --git a/modules/src/data/stringlist.h b/modules/src/data/stringlist.h new file mode 100644 index 000000000..493d7ff1e --- /dev/null +++ b/modules/src/data/stringlist.h @@ -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 : */ + diff --git a/modules/src/idf/idf_pkg.body b/modules/src/idf/idf_pkg.body index 4b71bfaaa..8f970aef7 100644 --- a/modules/src/idf/idf_pkg.body +++ b/modules/src/idf/idf_pkg.body @@ -1,15 +1,15 @@ /* SYMBOL TABLE HANDLING */ -#include +#include -#define IDF_HASHSIZE 307 /* size of hashtable, must be odd */ +#define IDF_HASHSIZE 307 /* size of hashtable, must be odd */ -#define IDF_STARTHASH(hs) (hs = 0) -#define IDF_ENHASH(hs,ch) (hs = (hs << 2) + ch) -#define IDF_STOPHASH(hs) (hs = hs % IDF_HASHSIZE) +#define IDF_STARTHASH(hs) (hs = 0) +#define IDF_ENHASH(hs, ch) (hs = (hs << 2) + ch) +#define IDF_STOPHASH(hs) (hs = hs % IDF_HASHSIZE) -static struct idf *IDF_hashtable[IDF_HASHSIZE]; - /* All identifiers can in principle be reached through +static struct idf* IDF_hashtable[IDF_HASHSIZE]; +/* All identifiers can in principle be reached through IDF_hashtable; IDF_hashtable[hc] is the start of a chain of idf's whose tags all hash to hc. Any identifier is entered into this @@ -17,69 +17,71 @@ static struct idf *IDF_hashtable[IDF_HASHSIZE]; (variable, selector, structure tag, etc.). */ -_PROTOTYPE(static struct idf *IDF_new, (char *, int, int)); +static struct idf* IDF_new(char* tg, int size, int cpy); -void -init_idf() +void init_idf() { } -static struct idf * -IDF_new(tg, size, cpy) - register char *tg; - register int size; +static struct idf* IDF_new(char* tg, int size, int cpy) { static int nidf; - static struct idf *pidf; + static struct idf* pidf; static struct idf null_idf; - register struct idf *id; + register struct idf* id; #define NIDS 50 -#define IBUFSIZ 2048 +#define IBUFSIZ 2048 static unsigned int icnt; - static char *ip; - register char *p; + static char* ip; + register char* p; - - if (! nidf--) { + if (!nidf--) + { nidf += NIDS; - pidf = (struct idf *) Malloc(NIDS * sizeof (struct idf)); + pidf = (struct idf*)Malloc(NIDS * sizeof(struct idf)); } id = pidf; pidf++; *id = null_idf; - if (cpy) { - if (size > icnt) { - icnt = size > IBUFSIZ ? size : IBUFSIZ; + if (cpy) + { + if (size > icnt) + { + icnt = size > IBUFSIZ ? size : IBUFSIZ; p = Malloc(icnt); } - else p = ip; + else + p = ip; icnt -= size; id->id_text = p; - while (size--) { + while (size--) + { *p++ = *tg++; } ip = p; } - else id->id_text = tg; + else + id->id_text = tg; return id; } -#ifdef IDF_DEBUG -void -hash_stat() +#ifdef IDF_DEBUG +void hash_stat(void) { register int i; int total_count = 0; print("Hash table tally:\n"); - for (i = 0; i < IDF_HASHSIZE; i++) { - register struct idf *notch = IDF_hashtable[i]; + for (i = 0; i < IDF_HASHSIZE; i++) + { + register struct idf* notch = IDF_hashtable[i]; register int cnt = 0; - print ("%d ", i); - while (notch) { + print("%d ", i); + while (notch) + { cnt++; print("'%s' ", notch->id_text); notch = notch->id_next; @@ -91,40 +93,38 @@ hash_stat() print("End hash table tally\n"); } -void -idfappfun(fun, opt) - int (*fun)(); - int opt; +void idfappfun(int (*fun)(), int opt) { - register int i; + register int i; - for (i = 0; i < IDF_HASHSIZE; i++) { - register struct idf *notch = IDF_hashtable[i]; + for (i = 0; i < IDF_HASHSIZE; i++) + { + register struct idf* notch = IDF_hashtable[i]; - while (notch) { + while (notch) + { (*fun)(notch, opt); notch = notch->id_next; } } } -#endif /* IDF_DEBUG */ +#endif /* IDF_DEBUG */ -struct idf * -str2idf(tg, cpy) - char tg[]; +struct idf* str2idf(char tg[], int cpy) { /* str2idf() returns an entry in the symbol table for the identifier tg. If necessary, an entry is created. */ - register char *cp = tg; - struct idf **hook; - register struct idf *notch; + register char* cp = tg; + struct idf** hook; + register struct idf* notch; register unsigned int hash; register int c; int size; IDF_STARTHASH(hash); - while (c = *cp++) { + while (c = *cp++) + { IDF_ENHASH(hash, c); } IDF_STOPHASH(hash); @@ -137,25 +137,31 @@ str2idf(tg, cpy) */ hook = &IDF_hashtable[hash]; - while ((notch = *hook)) { - register char *s1 = tg; + while ((notch = *hook)) + { + register char* s1 = tg; cp = notch->id_text; - while (!(c = (*s1 - *cp++))) { - if (*s1++ == '\0') { + while (!(c = (*s1 - *cp++))) + { + if (*s1++ == '\0') + { break; } } - if (c == 0) return notch; - if (c < 0) break; + if (c == 0) + return notch; + if (c < 0) + break; hook = ¬ch->id_next; } /* a new struct idf must be inserted at the hook */ - if (cpy < 0) return 0; + if (cpy < 0) + return 0; notch = IDF_new(tg, size, cpy); notch->id_next = *hook; - *hook = notch; /* hooked in */ + *hook = notch; /* hooked in */ return notch; } diff --git a/modules/src/idf/idf_pkg.spec b/modules/src/idf/idf_pkg.spec index 42d65d25f..3db6b9d5f 100644 --- a/modules/src/idf/idf_pkg.spec +++ b/modules/src/idf/idf_pkg.spec @@ -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) diff --git a/plat/build.lua b/plat/build.lua index dc0e87c5e..85ba61bc9 100644 --- a/plat/build.lua +++ b/plat/build.lua @@ -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 diff --git a/plat/linux/libsys/_hol0.s b/plat/linux/libsys/_hol0.s index fcb58727b..d3d16db72 100644 --- a/plat/linux/libsys/_hol0.s +++ b/plat/linux/libsys/_hol0.s @@ -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 diff --git a/plat/linuxppc/build-tools.lua b/plat/linuxppc/build-tools.lua index 4d0f0048d..ce1a163d8 100644 --- a/plat/linuxppc/build-tools.lua +++ b/plat/linuxppc/build-tools.lua @@ -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", } diff --git a/plat/linuxppc/descr b/plat/linuxppc/descr index 3086a826c..12bece6e9 100644 --- a/plat/linuxppc/descr +++ b/plat/linuxppc/descr @@ -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 diff --git a/plat/qemuppc/README b/plat/qemuppc/README new file mode 100644 index 000000000..412e33ad1 --- /dev/null +++ b/plat/qemuppc/README @@ -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 diff --git a/plat/qemuppc/boot.s b/plat/qemuppc/boot.s new file mode 100644 index 000000000..2dd9a4c5c --- /dev/null +++ b/plat/qemuppc/boot.s @@ -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 diff --git a/plat/qemuppc/build-pkg.lua b/plat/qemuppc/build-pkg.lua new file mode 100644 index 000000000..0478bbd4d --- /dev/null +++ b/plat/qemuppc/build-pkg.lua @@ -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", + } +} + diff --git a/plat/qemuppc/build-tools.lua b/plat/qemuppc/build-tools.lua new file mode 100644 index 000000000..e4f3f534b --- /dev/null +++ b/plat/qemuppc/build-tools.lua @@ -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", + } +} diff --git a/plat/qemuppc/descr b/plat/qemuppc/descr new file mode 100644 index 000000000..c0bc1eab0 --- /dev/null +++ b/plat/qemuppc/descr @@ -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 diff --git a/plat/qemuppc/include/ack/config.h b/plat/qemuppc/include/ack/config.h new file mode 100644 index 000000000..f58ee3a43 --- /dev/null +++ b/plat/qemuppc/include/ack/config.h @@ -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 diff --git a/plat/qemuppc/include/build.lua b/plat/qemuppc/include/build.lua new file mode 100644 index 000000000..dc1b0bb43 --- /dev/null +++ b/plat/qemuppc/include/build.lua @@ -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 +} + diff --git a/plat/qemuppc/include/sys/ioctl.h b/plat/qemuppc/include/sys/ioctl.h new file mode 100644 index 000000000..af41165d7 --- /dev/null +++ b/plat/qemuppc/include/sys/ioctl.h @@ -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. */ + +/* */ + +#define FIOSETOWN 0x00008901 +#define SIOCSPGRP 0x00008902 +#define FIOGETOWN 0x00008903 +#define SIOCGPGRP 0x00008904 +#define SIOCATMARK 0x00008905 +#define SIOCGSTAMP 0x00008906 + +/* */ + +#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 diff --git a/plat/qemuppc/include/unistd.h b/plat/qemuppc/include/unistd.h new file mode 100644 index 000000000..5cbdc1b5d --- /dev/null +++ b/plat/qemuppc/include/unistd.h @@ -0,0 +1,123 @@ +/* + * unistd.h - standard system calls + */ +/* $Id$ */ + +#ifndef _UNISTD_H +#define _UNISTD_H + +#include +#include + +/* 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 diff --git a/plat/qemuppc/libsys/_hol0.s b/plat/qemuppc/libsys/_hol0.s new file mode 100644 index 000000000..d3d16db72 --- /dev/null +++ b/plat/qemuppc/libsys/_hol0.s @@ -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 diff --git a/plat/qemuppc/libsys/brk.c b/plat/qemuppc/libsys/brk.c new file mode 100644 index 000000000..02e213399 --- /dev/null +++ b/plat/qemuppc/libsys/brk.c @@ -0,0 +1,43 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +#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; +} diff --git a/plat/qemuppc/libsys/build.lua b/plat/qemuppc/libsys/build.lua new file mode 100644 index 000000000..7281bbc61 --- /dev/null +++ b/plat/qemuppc/libsys/build.lua @@ -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" + } +} + \ No newline at end of file diff --git a/plat/qemuppc/libsys/close.c b/plat/qemuppc/libsys/close.c new file mode 100644 index 000000000..1c570029b --- /dev/null +++ b/plat/qemuppc/libsys/close.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +int close(int fd) +{ + errno = EBADF; + return -1; +} diff --git a/plat/qemuppc/libsys/creat.c b/plat/qemuppc/libsys/creat.c new file mode 100644 index 000000000..34ace747c --- /dev/null +++ b/plat/qemuppc/libsys/creat.c @@ -0,0 +1,15 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +int open(const char* path, int access, ...) +{ + errno = EACCES; + return -1; +} diff --git a/plat/qemuppc/libsys/getpid.c b/plat/qemuppc/libsys/getpid.c new file mode 100644 index 000000000..5e6eb003e --- /dev/null +++ b/plat/qemuppc/libsys/getpid.c @@ -0,0 +1,13 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +pid_t getpid(void) +{ + return 0; +} diff --git a/plat/qemuppc/libsys/isatty.c b/plat/qemuppc/libsys/isatty.c new file mode 100644 index 000000000..8bee360e0 --- /dev/null +++ b/plat/qemuppc/libsys/isatty.c @@ -0,0 +1,8 @@ +#include +#include +#include + +int isatty(int fd) +{ + return 1; +} diff --git a/plat/qemuppc/libsys/kill.c b/plat/qemuppc/libsys/kill.c new file mode 100644 index 000000000..4a179c47c --- /dev/null +++ b/plat/qemuppc/libsys/kill.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +int kill(pid_t pid, int sig) +{ + errno = EINVAL; + return -1; +} diff --git a/plat/qemuppc/libsys/libsys.h b/plat/qemuppc/libsys/libsys.h new file mode 100644 index 000000000..cf9878b87 --- /dev/null +++ b/plat/qemuppc/libsys/libsys.h @@ -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 diff --git a/plat/qemuppc/libsys/lseek.c b/plat/qemuppc/libsys/lseek.c new file mode 100644 index 000000000..ecbc4b520 --- /dev/null +++ b/plat/qemuppc/libsys/lseek.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include + +off_t lseek(int fd, off_t offset, int whence) +{ + errno = EINVAL; + return -1; +} diff --git a/plat/qemuppc/libsys/open.c b/plat/qemuppc/libsys/open.c new file mode 100644 index 000000000..8f18317c5 --- /dev/null +++ b/plat/qemuppc/libsys/open.c @@ -0,0 +1,14 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +int creat(const char* path, int mode) +{ + return open(path, O_CREAT|O_WRONLY|O_TRUNC, mode); +} diff --git a/plat/qemuppc/libsys/openfirmware.c b/plat/qemuppc/libsys/openfirmware.c new file mode 100644 index 000000000..ad0c282ee --- /dev/null +++ b/plat/qemuppc/libsys/openfirmware.c @@ -0,0 +1,80 @@ +#include +#include +#include +#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); +} diff --git a/plat/qemuppc/libsys/read.c b/plat/qemuppc/libsys/read.c new file mode 100644 index 000000000..21fe90ebb --- /dev/null +++ b/plat/qemuppc/libsys/read.c @@ -0,0 +1,38 @@ +#include +#include +#include +#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; +} diff --git a/plat/qemuppc/libsys/signal.c b/plat/qemuppc/libsys/signal.c new file mode 100644 index 000000000..a87b9ced2 --- /dev/null +++ b/plat/qemuppc/libsys/signal.c @@ -0,0 +1,15 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include +#include "libsys.h" + +sighandler_t signal(int signum, sighandler_t handler) +{ + return SIG_DFL; +} diff --git a/plat/qemuppc/libsys/time.c b/plat/qemuppc/libsys/time.c new file mode 100644 index 000000000..5ef17b841 --- /dev/null +++ b/plat/qemuppc/libsys/time.c @@ -0,0 +1,17 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include +#include "libsys.h" + +time_t time(time_t* t) +{ + if (t) + *t = 0; + return 0; +} diff --git a/plat/qemuppc/libsys/trap.s b/plat/qemuppc/libsys/trap.s new file mode 100644 index 000000000..f05b907d0 --- /dev/null +++ b/plat/qemuppc/libsys/trap.s @@ -0,0 +1,69 @@ +# +! $Source: /cvsroot/tack/Ack/plat/linux386/libsys/_syscall.s,v $ +! $State: Exp $ +! $Revision: 1.1 $ + +! Declare segments (the order is important). + +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +#define IFFALSE 4 +#define IFTRUE 12 +#define ALWAYS 20 + +#define LT 0 +#define GT 1 +#define EQ 2 +#define OV 3 + +EARRAY = 0 +ERANGE = 1 +ESET = 2 +EIOVFL = 3 +EFOVFL = 4 +EFUNFL = 5 +EIDIVZ = 6 +EFDIVZ = 7 +EIUND = 8 +EFUND = 9 +ECONV = 10 +ESTACK = 16 +EHEAP = 17 +EILLINS = 18 +EODDZ = 19 +ECASE = 20 +EMEMFLT = 21 +EBADPTR = 22 +EBADPC = 23 +EBADLAE = 24 +EBADMON = 25 +EBADLIN = 26 +EBADGTO = 27 +EUNIMPL = 63 ! unimplemented em-instruction called + +.define .trap_ecase +.trap_ecase: + b .trp + +.define .trap_earray +.trap_earray: + b .trp + +.define .trp +.define .trap +.trp: +.trap: + b .trp ! spin forever + +.define .sig +.sig: + lwz r3, 0(sp) + li32 r4, .trppc + stw r3, 0(r4) + bclr ALWAYS, 0, 0 ! return + \ No newline at end of file diff --git a/plat/qemuppc/libsys/write.c b/plat/qemuppc/libsys/write.c new file mode 100644 index 000000000..8d2dd0d74 --- /dev/null +++ b/plat/qemuppc/libsys/write.c @@ -0,0 +1,48 @@ +/* $Source$ + * $State$ + * $Revision$ + */ + +#include +#include +#include +#include "libsys.h" + +void _sys_write_tty(char c) +{ + _sys_rawwrite(c); +#if 0 + if ((c == '\n') && !(_sys_ttyflags & RAW)) + _sys_rawwrite('\r'); +#endif + if (c == '\n') + _sys_rawwrite('\r'); +} + +int write(int fd, void* buffer, size_t count) +{ + int i; + char* p = buffer; + + /* We're only allowed to write to fd 0, 1 or 2. */ + + if ((fd < 0) || (fd > 2)) + { + errno = EBADF; + return -1; + } + + /* Write all data. */ + + i = 0; + while (i < count) + { + _sys_write_tty(*p++); + + i++; + } + + /* No failures. */ + + return count; +} diff --git a/plat/qemuppc/tests/_dummy.c b/plat/qemuppc/tests/_dummy.c new file mode 100644 index 000000000..48104b5aa --- /dev/null +++ b/plat/qemuppc/tests/_dummy.c @@ -0,0 +1,8 @@ +#include "test.h" + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT(0 == 0); + finished(); +} diff --git a/plat/qemuppc/tests/build.lua b/plat/qemuppc/tests/build.lua new file mode 100644 index 000000000..024961fda --- /dev/null +++ b/plat/qemuppc/tests/build.lua @@ -0,0 +1,49 @@ +include("plat/build.lua") + +local qemu = "qemu-system-ppc" +local tests = {} + +if os.execute("which "..qemu.." > /dev/null") ~= 0 then + print("warning: skipping tests which require ", qemu) +else + local testcases = filenamesof("./*.c", "./*.s", "./*.e") + + for _, f in ipairs(testcases) do + local fs = replace(basename(f), "%..$", "") + local _, _, lang = fs:find("_(.)$") + if not lang then + lang = "e" + end + + local bin = ackprogram { + name = fs.."_bin", + srcs = { f }, + deps = { "plat/qemuppc/tests/lib+lib" }, + vars = { + plat = "qemuppc", + lang = lang, + ackcflags = "-O0" + } + } + + tests[#tests+1] = normalrule { + name = fs, + outleaves = { "stamp" }, + ins = { + bin, + "./testdriver.sh" + }, + commands = { + "%{ins[2]} "..qemu.." %{ins[1]} 5", + "touch %{outs}" + } + } + end +end + +normalrule { + name = "tests", + outleaves = { "stamp" }, + ins = tests, + commands = { "touch %{outs}" } +} \ No newline at end of file diff --git a/plat/qemuppc/tests/csa_e.c b/plat/qemuppc/tests/csa_e.c new file mode 100644 index 000000000..355b75ee7 --- /dev/null +++ b/plat/qemuppc/tests/csa_e.c @@ -0,0 +1,26 @@ +#include "test.h" + +int csa(int i) +{ + switch (i) + { + case 2: return 2; + case 3: return 3; + case 4: return 4; + default: return 0; + } +} + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT(csa(0) == 0); + ASSERT(csa(1) == 0); + ASSERT(csa(2) == 2); + ASSERT(csa(3) == 3); + ASSERT(csa(4) == 4); + ASSERT(csa(5) == 0); + ASSERT(csa(6) == 0); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/csb_e.c b/plat/qemuppc/tests/csb_e.c new file mode 100644 index 000000000..c86d31fa6 --- /dev/null +++ b/plat/qemuppc/tests/csb_e.c @@ -0,0 +1,26 @@ +#include "test.h" + +int csa(int i) +{ + switch (i) + { + case 200: return 200; + case 300: return 300; + case 400: return 400; + default: return 0; + } +} + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT(csa(0) == 0); + ASSERT(csa(100) == 0); + ASSERT(csa(200) == 200); + ASSERT(csa(300) == 300); + ASSERT(csa(400) == 400); + ASSERT(csa(500) == 0); + ASSERT(csa(600) == 0); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/doublecmp_e.c b/plat/qemuppc/tests/doublecmp_e.c new file mode 100644 index 000000000..f6c1582dc --- /dev/null +++ b/plat/qemuppc/tests/doublecmp_e.c @@ -0,0 +1,20 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +double one = 1.0; +double zero = 0.0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT(zero == zero); + ASSERT(one != zero); + ASSERT(zero < one); + ASSERT(zero <= one); + ASSERT(zero <= zero); + ASSERT(one > zero); + ASSERT(one >= zero); + ASSERT(one >= one); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/from_d_to_si_e.c b/plat/qemuppc/tests/from_d_to_si_e.c new file mode 100644 index 000000000..8c7e31c3e --- /dev/null +++ b/plat/qemuppc/tests/from_d_to_si_e.c @@ -0,0 +1,20 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +double one = 1.0; +double zero = 0.0; +double minusone = -1.0; +double big = 2147483647.0; +double minusbig = -2147483648.0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((int)zero == 0); + ASSERT((int)one == 1); + ASSERT((int)minusone == -1); + ASSERT((int)big == 2147483647); + ASSERT((int)minusbig == -2147483648); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/from_d_to_ui_e.c b/plat/qemuppc/tests/from_d_to_ui_e.c new file mode 100644 index 000000000..b16667502 --- /dev/null +++ b/plat/qemuppc/tests/from_d_to_ui_e.c @@ -0,0 +1,16 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +double one = 1.0; +double zero = 0.0; +double big = 4294967295.0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((unsigned int)zero == 0); + ASSERT((unsigned int)one == 1); + ASSERT((unsigned int)big == 4294967295); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/from_si_to_d_e.c b/plat/qemuppc/tests/from_si_to_d_e.c new file mode 100644 index 000000000..e81c2f7c2 --- /dev/null +++ b/plat/qemuppc/tests/from_si_to_d_e.c @@ -0,0 +1,20 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int one = 1; +int zero = 0; +int minusone = -1; +int big = 0x7fffffff; +int minusbig = -0x8000000; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((double)zero == 0.0); + ASSERT((double)one == 1.0); + ASSERT((double)minusone == -1.0); + ASSERT((double)big == 2147483647.0); + /* ASSERT((double)minusbig == -2147483648.0); FIXME: fails for now */ + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/from_ui_to_d_e.c b/plat/qemuppc/tests/from_ui_to_d_e.c new file mode 100644 index 000000000..a3517b24c --- /dev/null +++ b/plat/qemuppc/tests/from_ui_to_d_e.c @@ -0,0 +1,16 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +unsigned int one_u = 1; +unsigned int zero_u = 0; +unsigned int big_u = 0xffffffff; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((double)zero_u == 0.0); + ASSERT((double)one_u == 1.0); + ASSERT((double)big_u == 4294967295.0); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/inn_e.e b/plat/qemuppc/tests/inn_e.e new file mode 100644 index 000000000..4e53b35c5 --- /dev/null +++ b/plat/qemuppc/tests/inn_e.e @@ -0,0 +1,76 @@ +# + mes 2, 4, 4 + + exp $_m_a_i_n + pro $_m_a_i_n, 0 + + /* Test non-existent bit */ + +.1 + rom 0I1, 0I1, 0I1, 0I1 + loe .1 + loc 1 /* bit number */ + inn 4 + zeq *1 + + loc __LINE__ + cal $fail + ass 4 +1 + + /* Test existent bit */ + +.2 + rom 2I1, 0I1, 0I1, 0I1 + loe .2 + loc 1 /* bit number */ + inn 4 + zne *2 + + loc __LINE__ + cal $fail + ass 4 +2 + + /* Test non-existent high bit */ + +.3 + rom 0I1, 0I1, 0I1, 0I1 + rom 0I1, 0I1, 0I1, 0I1 +.31 + rom 33 /* to defeat constant folding */ + + lae .3 + loi 8 + loe .31 /* bit number */ + inn 8 + zeq *3 + + loc __LINE__ + cal $fail + ass 4 +3 + + /* Test existent high bit */ + +.4 + rom 0I1, 0I1, 0I1, 0I1 + rom 2I1, 0I1, 0I1, 0I1 +.41 + rom 33 /* to defeat constant folding */ + + lae .4 + loi 8 + loe .41 /* bit number */ + inn 8 + zne *4 + + loc __LINE__ + cal $fail + ass 4 +4 + + cal $finished + ret 0 + + end diff --git a/plat/qemuppc/tests/intadd_e.c b/plat/qemuppc/tests/intadd_e.c new file mode 100644 index 000000000..8e4868a62 --- /dev/null +++ b/plat/qemuppc/tests/intadd_e.c @@ -0,0 +1,31 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int two = 2; +int one = 1; +int zero = 0; +int minusone = -1; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((two + one) == 3); + ASSERT((two + minusone) == 1); + + ASSERT((two + 1) == 3); + ASSERT((two + -1) == 1); + + ASSERT((1 + two) == 3); + ASSERT((-1 + two) == 1); + + ASSERT(((unsigned int)two + (unsigned int)one) == 3); + ASSERT(((unsigned int)two + (unsigned int)minusone) == 1); + + ASSERT(((unsigned int)two + (unsigned int) 1) == 3); + ASSERT(((unsigned int)two + (unsigned int)-1) == 1); + + ASSERT(((unsigned int)1 + (unsigned int)two) == 3); + ASSERT(((unsigned int)-1 + (unsigned int)two) == 1); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/intcmp_e.c b/plat/qemuppc/tests/intcmp_e.c new file mode 100644 index 000000000..dd7f1da75 --- /dev/null +++ b/plat/qemuppc/tests/intcmp_e.c @@ -0,0 +1,65 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int one = 1; +int zero = 0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT(zero == zero); + ASSERT(one != zero); + ASSERT(zero < one); + ASSERT(zero <= one); + ASSERT(zero <= zero); + ASSERT(one > zero); + ASSERT(one >= zero); + ASSERT(one >= one); + + ASSERT(zero == 0); + ASSERT(one != 0); + ASSERT(zero < 1); + ASSERT(zero <= 1); + ASSERT(zero <= 0); + ASSERT(one > 0); + ASSERT(one >= 0); + ASSERT(one >= 1); + + ASSERT(0 == zero); + ASSERT(1 != zero); + ASSERT(0 < one); + ASSERT(0 <= one); + ASSERT(0 <= zero); + ASSERT(1 > zero); + ASSERT(1 >= zero); + ASSERT(1 >= one); + + ASSERT((unsigned int)zero == (unsigned int)zero); + ASSERT((unsigned int)one != (unsigned int)zero); + ASSERT((unsigned int)zero < (unsigned int)one); + ASSERT((unsigned int)zero <= (unsigned int)one); + ASSERT((unsigned int)zero <= (unsigned int)zero); + ASSERT((unsigned int)one > (unsigned int)zero); + ASSERT((unsigned int)one >= (unsigned int)zero); + ASSERT((unsigned int)one >= (unsigned int)one); + + ASSERT((unsigned int)zero == (unsigned int)0); + ASSERT((unsigned int)one != (unsigned int)0); + ASSERT((unsigned int)zero < (unsigned int)1); + ASSERT((unsigned int)zero <= (unsigned int)1); + ASSERT((unsigned int)zero <= (unsigned int)0); + ASSERT((unsigned int)one > (unsigned int)0); + ASSERT((unsigned int)one >= (unsigned int)0); + ASSERT((unsigned int)one >= (unsigned int)1); + + ASSERT((unsigned int)0 == (unsigned int)zero); + ASSERT((unsigned int)1 != (unsigned int)zero); + ASSERT((unsigned int)0 < (unsigned int)one); + ASSERT((unsigned int)0 <= (unsigned int)one); + ASSERT((unsigned int)0 <= (unsigned int)zero); + ASSERT((unsigned int)1 > (unsigned int)zero); + ASSERT((unsigned int)1 >= (unsigned int)zero); + ASSERT((unsigned int)1 >= (unsigned int)one); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/intdiv_e.c b/plat/qemuppc/tests/intdiv_e.c new file mode 100644 index 000000000..c90964ced --- /dev/null +++ b/plat/qemuppc/tests/intdiv_e.c @@ -0,0 +1,28 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int three = 3; +int two = 2; +int one = 1; +int zero = 0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((three / two) == 1); + ASSERT((-three / two) == -1); + ASSERT((-three / -two) == 1); + ASSERT((three / -two) == -1); + + ASSERT((three / 2) == 1); + ASSERT((-three / 2) == -1); + ASSERT((-three / -2) == 1); + ASSERT((three / -2) == -1); + + ASSERT((3 / two) == 1); + ASSERT((-3 / two) == -1); + ASSERT((-3 / -two) == 1); + ASSERT((3 / -two) == -1); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/intrem_e.c b/plat/qemuppc/tests/intrem_e.c new file mode 100644 index 000000000..40f68d654 --- /dev/null +++ b/plat/qemuppc/tests/intrem_e.c @@ -0,0 +1,28 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int three = 3; +int two = 2; +int one = 1; +int zero = 0; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((three % two) == 1); + ASSERT((-three % two) == -1); + ASSERT((-three % -two) == -1); + ASSERT((three % -two) == 1); + + ASSERT((three % 2) == 1); + ASSERT((-three % 2) == -1); + ASSERT((-three % -2) == -1); + ASSERT((three % -2) == 1); + + ASSERT((3 % two) == 1); + ASSERT((-3 % two) == -1); + ASSERT((-3 % -two) == -1); + ASSERT((3 % -two) == 1); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/intshift_e.c b/plat/qemuppc/tests/intshift_e.c new file mode 100644 index 000000000..dd280142c --- /dev/null +++ b/plat/qemuppc/tests/intshift_e.c @@ -0,0 +1,52 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int one = 1; +int zero = 0; +int minusone = -1; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((one <>zero) == 1); + ASSERT((one >>one) == 0); + ASSERT((minusone>>zero) == -1); + ASSERT((minusone>>one) == -1); + + ASSERT(((unsigned int)one >>(unsigned int)zero) == 1); + ASSERT(((unsigned int)one >>(unsigned int)one) == 0); + ASSERT(((unsigned int)minusone>>(unsigned int)zero) == 0xffffffff); + ASSERT(((unsigned int)minusone>>(unsigned int)one) == 0x7fffffff); + + ASSERT((one <<0) == 1); + ASSERT((one <<1) == 2); + ASSERT((minusone<<0) == -1); + ASSERT((minusone<<1) == -2); + + ASSERT(((unsigned int)one <<(unsigned int)0) == 1); + ASSERT(((unsigned int)one <<(unsigned int)1) == 2); + ASSERT(((unsigned int)minusone<<(unsigned int)0) == -1); + ASSERT(((unsigned int)minusone<<(unsigned int)1) == -2); + + ASSERT((one >>0) == 1); + ASSERT((one >>1) == 0); + ASSERT((minusone>>0) == -1); + ASSERT((minusone>>1) == -1); + + ASSERT(((unsigned int)one >>(unsigned int)0) == 1); + ASSERT(((unsigned int)one >>(unsigned int)1) == 0); + ASSERT(((unsigned int)minusone>>(unsigned int)0) == 0xffffffff); + ASSERT(((unsigned int)minusone>>(unsigned int)1) == 0x7fffffff); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/intsub_e.c b/plat/qemuppc/tests/intsub_e.c new file mode 100644 index 000000000..72ba0ff08 --- /dev/null +++ b/plat/qemuppc/tests/intsub_e.c @@ -0,0 +1,31 @@ +#include "test.h" + +/* Constants in globals to defeat constant folding. */ +int two = 2; +int one = 1; +int zero = 0; +int minusone = -1; + +/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ +void _m_a_i_n(void) +{ + ASSERT((two - one) == 1); + ASSERT((one - two) == -1); + + ASSERT((two - 1) == 1); + ASSERT((one - 2) == -1); + + ASSERT((2 - one) == 1); + ASSERT((1 - two) == -1); + + ASSERT(((unsigned int)two - (unsigned int)one) == 1); + ASSERT(((unsigned int)one - (unsigned int)two) == 0xffffffff); + + ASSERT(((unsigned int)two - (unsigned int)1) == 1); + ASSERT(((unsigned int)one - (unsigned int)2) == 0xffffffff); + + ASSERT(((unsigned int)2 - (unsigned int)one) == 1); + ASSERT(((unsigned int)1 - (unsigned int)two) == 0xffffffff); + + finished(); +} \ No newline at end of file diff --git a/plat/qemuppc/tests/lib/build.lua b/plat/qemuppc/tests/lib/build.lua new file mode 100644 index 000000000..cb6b5cbea --- /dev/null +++ b/plat/qemuppc/tests/lib/build.lua @@ -0,0 +1,8 @@ +include("plat/build.lua") + +acklibrary { + name = "lib", + srcs = { "./test.c" }, + hdrs = { "./test.h" }, + vars = { plat = "qemuppc" } +} diff --git a/plat/qemuppc/tests/lib/test.c b/plat/qemuppc/tests/lib/test.c new file mode 100644 index 000000000..33d72f3ce --- /dev/null +++ b/plat/qemuppc/tests/lib/test.c @@ -0,0 +1,31 @@ +#include "test.h" + +/* No CRT in this file (this includes stdio and stdlib!). */ + +void finished(void) +{ + static const char s[] = "@@FINISHED\n"; + write(1, s, sizeof(s)); +} + +void writehex(uint32_t code) +{ + char buf[8]; + char* p = &buf[sizeof(buf)]; + + do + { + *--p = "0123456789abcdef"[code & 0xf]; + code >>= 4; + } + while (code > 0); + + write(1, p, buf + sizeof(buf) - p); +} + +void fail(uint32_t code) +{ + write(1, "@@FAIL on line 0x", 7); + writehex(code); + write(1, "\n", 1); +} diff --git a/plat/qemuppc/tests/lib/test.h b/plat/qemuppc/tests/lib/test.h new file mode 100644 index 000000000..96537a2cb --- /dev/null +++ b/plat/qemuppc/tests/lib/test.h @@ -0,0 +1,14 @@ +#ifndef TEST_H +#define TEST_H + +#include +#include + +extern void finished(void); +extern void writehex(uint32_t code); +extern void fail(uint32_t code); + +#define ASSERT(condition) \ + if (!(condition)) fail(__LINE__) + +#endif diff --git a/plat/qemuppc/tests/testdriver.sh b/plat/qemuppc/tests/testdriver.sh new file mode 100755 index 000000000..3424e9626 --- /dev/null +++ b/plat/qemuppc/tests/testdriver.sh @@ -0,0 +1,22 @@ +#!/bin/sh +qemu=$1 +img=$2 +timeout=$3 + +pipe=/tmp/$$.testdriver.pipe +mknod $pipe p +trap "rm -f $pipe" EXIT + +result=/tmp/$$.testdriver.result +trap "rm -f $result" EXIT + +pidfile=/tmp/$$.testdriver.pid +trap "rm -f $pidfile" EXIT + +( $qemu -nographic -kernel $img 2>&1 & echo $! > $pidfile ) \ + | tee $result \ + | ( timeout $timeout grep -l -q @@FINISHED ; echo ) \ + | ( read dummy && kill $(cat $pidfile) ) + +( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1 +exit 0 \ No newline at end of file diff --git a/util/amisc/aelflod.1 b/util/amisc/aelflod.1 index 6e22d53d2..24001ba2d 100644 --- a/util/amisc/aelflod.1 +++ b/util/amisc/aelflod.1 @@ -2,7 +2,7 @@ .SH NAME aelflod \- ACK ELF loader .SH SYNOPSIS -aelflod [-h] inputfile outputfile +aelflod [-h] [-v] inputfile outputfile .SH DESCRIPTION .I aelflod converts an absolute ack.out file into a simple binary memory diff --git a/util/amisc/aelflod.c b/util/amisc/aelflod.c index 398b223b2..e3692ac01 100644 --- a/util/amisc/aelflod.c +++ b/util/amisc/aelflod.c @@ -1,7 +1,4 @@ /* - * $Source$ - * $State$ - * * Simple tool to produce an utterly basic ELF executable * from an absolute ack.out file. Suitable for operating * systems like Linux. @@ -24,6 +21,7 @@ #include #include #include +#include #include #include #include "out.h" @@ -68,6 +66,8 @@ const char elf_le_ident_string[] = { char hdr[HDR_LENGTH] ; +bool verbose = false; + /* Segment numbers understood by aelflod. */ enum { @@ -299,6 +299,10 @@ int main(int argc, char* argv[]) elfmachine = atoi(&argv[1][2]); break; + case 'v': + verbose = true; + break; + default: syntaxerror: fatal("syntax error --- try -h for help"); @@ -451,8 +455,9 @@ int main(int argc, char* argv[]) /* Summarise what we've done. */ + if (verbose) { - long ss = 0; + uint32_t ss = 0; printf(" address length\n"); printf(" ehdr : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base & ~0x1FFF, codeoffset); printf(" text : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base, outsect[TEXT].os_size); @@ -463,7 +468,7 @@ int main(int argc, char* argv[]) ss += outsect[ROM].os_size; ss += outsect[DATA].os_size; ss += outsect[BSS].os_size; - printf("TOTAL : %08lX\n", ss); + printf("TOTAL : %08"PRIx32"\n", ss); } return 0; diff --git a/util/amisc/aslod.1 b/util/amisc/aslod.1 index 7e291838f..a786b13e6 100644 --- a/util/amisc/aslod.1 +++ b/util/amisc/aslod.1 @@ -2,7 +2,7 @@ .SH NAME aslod \- ACK simple loader .SH SYNOPSIS -aslod [-h] inputfile outputfile +aslod [-h] [-v] inputfile outputfile .SH DESCRIPTION .I aslod converts an absolute ack.out file into a simple binary memory diff --git a/util/amisc/aslod.c b/util/amisc/aslod.c index 307f72cb1..afc8f8c3e 100644 --- a/util/amisc/aslod.c +++ b/util/amisc/aslod.c @@ -14,14 +14,12 @@ * CVS...) * * dtrg, 2006-10-17 - * - * $Source$ - * $State$ */ #include #include #include +#include #include #include #include @@ -53,6 +51,8 @@ FILE* output; /* Output stream */ char hdr[HDR_LENGTH] ; +bool verbose = false; + /* Segment numbers understood by aslod. */ enum { @@ -206,6 +206,10 @@ int main(int argc, char* argv[]) program); exit(0); + case 'v': + verbose = true; + break; + default: syntaxerror: fatal("syntax error --- try -h for help"); @@ -301,8 +305,9 @@ int main(int argc, char* argv[]) /* Summarise what we've done. */ + if (verbose) { - long ss = 0; + uint32_t ss = 0; printf(" base : %08"PRIx32"\n", outsect[TEXT].os_base) ; printf(" text = %08"PRIx32"\n", outsect[TEXT].os_size); printf(" rom = %08"PRIx32"\n", outsect[ROM].os_size); @@ -312,7 +317,7 @@ int main(int argc, char* argv[]) ss += outsect[ROM].os_size; ss += outsect[DATA].os_size; ss += outsect[BSS].os_size; - printf("TOTAL = %08lX\n", ss); + printf("TOTAL = %08"PRIx32"\n", ss); } return 0; diff --git a/util/mcgg/LICENSE b/util/mcgg/LICENSE new file mode 100644 index 000000000..b21f90758 --- /dev/null +++ b/util/mcgg/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 1993,1994,1995,1996 David R. Hanson. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + diff --git a/util/mcgg/README b/util/mcgg/README new file mode 100644 index 000000000..21c982457 --- /dev/null +++ b/util/mcgg/README @@ -0,0 +1,12 @@ +mcgg is very heavily based on the iburg code generator by C. W. Fraser, D. R. +Hanson and T. A. Proebsting, described in ACM Letters on Prog. Languages and +Systems 1, 3 (Sep. 1992), 213-226: + +http://storage.webhop.net/documents/iburg.pdf + +For the original source, see the iburg-import branch (this is the version with +all the mcgg extensions). + +iburg is licensed under the MIT open source license; see the LICENSE file. + + diff --git a/util/mcgg/UPSTREAM b/util/mcgg/UPSTREAM new file mode 100644 index 000000000..9cd9ae41e --- /dev/null +++ b/util/mcgg/UPSTREAM @@ -0,0 +1,6 @@ +This is a copy of iburg from head of git here: + +https://github.com/drh/iburg + +Version: 2151dd7d126e2f804b822bb78059c870a3a15f5e + diff --git a/util/mcgg/build.lua b/util/mcgg/build.lua new file mode 100644 index 000000000..f8055050c --- /dev/null +++ b/util/mcgg/build.lua @@ -0,0 +1,91 @@ +include("first/yacc.lua") + +flex { + name = "flex", + srcs = { "./*.l" }, +} + +yacc { + name = "yacc", + srcs = { "./*.y" }, +} + +normalrule { + name = "ircodes", + outleaves = { "ircodes-dyn.h", "ircodes.c" }, + ins = { + "./ircodes.sh", + "./ir.dat" + }, + commands = { + "%{ins[1]} %{ins[2]} %{outs[1]} %{outs[2]}" + } +} + +clibrary { + name = "lib", + srcs = { + matching(filenamesof("+ircodes"), "%.c$"), + }, + deps = { + "+ircodes", + "./ircodes.h" + }, + hdrs = { + matching(filenamesof("+ircodes"), "%.h$"), + "./ircodes.h", + "./mcgg.h" + } +} + +cprogram { + name = "mcgg", + srcs = { + "./*.c", + matching(filenamesof("+flex"), "%.c$"), + matching(filenamesof("+yacc"), "%.c$") + }, + deps = { + "./iburg.h", + "+lib", + "+yacc", + "modules/src/data+lib", + "modules+headers", + } +} + +definerule("mcgg", + { + srcs = { type="targets" } + }, + function(e) + -- Remember this is executed from the caller's directory; local + -- target names will resolve there + if (#e.srcs ~= 1) then + error("you must supply exactly one input file") + end + + local cpptable = cppfile { + name = e.name.."/cpptable", + outleaf = "cpptable", + srcs = e.srcs + } + + return normalrule { + name = e.name, + cwd = e.cwd, + outleaves = { + "tables.c", + "tables.h", + }, + ins = { + "util/mcgg+mcgg", + cpptable + }, + commands = { + "%{ins[1]} -i %{ins[2]} -o %{outs[1]} -h %{outs[2]}", + } + } + end +) + diff --git a/util/mcgg/gram.y b/util/mcgg/gram.y new file mode 100644 index 000000000..891030a10 --- /dev/null +++ b/util/mcgg/gram.y @@ -0,0 +1,234 @@ +%{ +#include +#include +#include +#include +#include +#include +#include "iburg.h" +#include "astring.h" + +#define YYDEBUG 1 + +extern int yylex(void); + +%} +%union { + arith n; + const char* string; + Nonterm nonterm; + Tree tree; + Rule rule; + struct reg* reg; + struct stringlist* stringlist; + struct terminfo terminfo; + struct expr* expr; + struct constraint* constraint; +} + +%term ALIASES +%term COPY +%term CORRUPTED +%term COST +%term DECLARATIONS +%term EMIT +%term EQUALS +%term FRAGMENT +%term NAMED +%term NOTEQUALS +%term PATTERNS +%term PREFERS +%term PRESERVED +%term REGISTERS +%term WHEN +%term WITH + +%token INT +%token ID +%token QFRAGMENT + +%type constraint +%type constraints +%type predicate +%type predicate_arg +%type predicate_args +%type declaration +%type register +%type pattern +%type pattern_constraints +%type pattern_emit +%type nodename +%type aliases +%type names +%type qfragments +%type terminfo +%type rhs +%% + +spec + : REGISTERS registers + DECLARATIONS declarations + PATTERNS patterns + ; + +registers + : /* nothing */ + | registers register ';' + | register ';' + ; + +register + : ID { $$ = makereg($1); } + | register NAMED '(' names ')' { $$ = $1; setregnames($$, $4); } + | register ALIASES '(' aliases ')' { $$ = $1; addregaliases($$, $4); } + | register ID { $$ = $1; addregattr($1, $2); } + ; + +names + : QFRAGMENT { $$ = calloc(1, sizeof(*$$)); stringlist_add($$, $1); } + | names ',' QFRAGMENT { $$ = $1; stringlist_add($$, $3); } + ; + +aliases + : ID { $$ = calloc(1, sizeof(*$$)); stringlist_add($$, $1); } + | aliases ',' ID { $$ = $1; stringlist_add($$, $3); } + ; + +declarations + : /* nothing */ + | declarations declaration ';' + | declaration ';' + ; + +declaration + : ID { $$ = nonterm($1, true); } + | declaration FRAGMENT { $$ = $1; $$->is_fragment = true; } + ; + +patterns + : /* nothing */ + | patterns pattern ';' + | patterns ';' + ; + +pattern + : terminfo '=' rhs { nonterm($1.name, false); $$ = rule(&$1, $3); } + | rhs { $$ = rule(NULL, $1); } + | pattern PREFERS predicate { $$ = $1; array_append(&$$->prefers, $3); } + | pattern WHEN predicate { $$ = $1; array_append(&$$->requires, $3); } + | pattern COST INT { $$ = $1; $$->cost = $3; } + | pattern_constraints { $$ = $1; } + | pattern_emit { $$ = $1; } + ; + +rhs + : terminfo { $$ = tree(&$1, NULL, NULL); } + | terminfo '(' rhs ')' { $$ = tree(&$1, $3, NULL); } + | terminfo '(' rhs ',' rhs ')' { $$ = tree(&$1, $3, $5); } + ; + +terminfo + : nodename { $$.name = $1; } + | '(' ID ')' nodename { $$.attr = $2; $$.name = $4; } + | ID ':' nodename { $$.label = $1; $$.name = $3; } + | ID ':' '(' ID ')' nodename { $$.label = $1; $$.attr = $4; $$.name = $6; } + ; + +nodename + : ID { $$ = $1; } + | ID '.' ID { $$ = aprintf("%s.%s", $1, $3); } + ; + +pattern_emit + : pattern EMIT qfragments { + $$ = $1; + if (!$$->lhs->is_fragment) + stringlist_add($3, "\n"); + stringlist_addall(&$$->code, $3); + } + ; + +pattern_constraints + : pattern WITH constraints { + struct constraint* c = $3; + $$ = $1; + while (c) + { + array_append(&$$->constraints, c); + c = c->next; + } + } + ; + +constraints + : constraint { $$ = $1; } + | constraints ',' constraint { $$ = $3; $$->next = $1; } + ; + +constraint + : '(' constraint ')' { $$ = $2; } + | '%' ID EQUALS '%' ID { $$ = calloc(1, sizeof(*$$)); + $$->type = CONSTRAINT_EQUALS; $$->left = $2; $$->right = $5; } + | CORRUPTED '(' ID ')' { $$ = calloc(1, sizeof(*$$)); + $$->type = CONSTRAINT_CORRUPTED_ATTR; $$->left = $3; } + | PRESERVED '(' '%' ID ')' { $$ = calloc(1, sizeof(*$$)); + $$->type = CONSTRAINT_PRESERVED; $$->left = $4; } + ; + +qfragments + : /* nothing */ { $$ = calloc(1, sizeof *$$); } + | qfragments QFRAGMENT { $$ = $1; stringlist_add($$, $2); } + ; + +predicate + : ID '(' predicate_args ')' { + $$ = calloc(1, sizeof *$$); + $$->type = PREDICATE_FUNCTION; + $$->u.name = $1; + $$->next = $3; + } + ; + +predicate_args + : /* nothing */ { $$ = NULL; } + | predicate_arg { $$ = $1; } + | predicate_arg ',' predicate_args { $$ = $1; $$->next = $3; } + ; + +predicate_arg + : '%' ID { $$ = calloc(1, sizeof *$$); $$->type = PREDICATE_NODE; $$->u.name = $2; } + | INT { $$ = calloc(1, sizeof *$$); $$->type = PREDICATE_NUMBER; $$->u.number = $1; } + ; + +%% +#include +#include + +int errcnt = 0; +static char buf[BUFSIZ], *bp = buf; +static int ppercent = 0; + +void yyerror(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + vfprintf(stderr, fmt, ap); + if (fmt[strlen(fmt)-1] != '\n') + fprintf(stderr, "\n"); + errcnt++; + exit(1); +} + +void yywarn(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/util/mcgg/iburg.c b/util/mcgg/iburg.c new file mode 100644 index 000000000..23705c7d8 --- /dev/null +++ b/util/mcgg/iburg.c @@ -0,0 +1,1478 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iburg.h" +#include "ircodes.h" +#include "astring.h" +#include "smap.h" +#include "mcgg.h" + +static char rcsid[] = "$Id$"; + +int maxcost = SHRT_MAX / 2; + +FILE* infp = NULL; +FILE* outfp = NULL; +FILE* hdrfp = NULL; + +static char* prefix = "burm"; +static int Tflag = 1; /* tracing */ +static int ntnumber = 0; +static Nonterm start = 0; +static Term terms; +static Nonterm nts; +static Rule rules; +static int nrules; + +static SMAPOF(struct reg) registers; +static SMAPOF(struct regattr) registerattrs; + +static void print(const char* fmt, ...); +static void printh(const char* fmt, ...); +static void registerterminals(void); +static struct regattr* makeregattr(const char* id); +static void emitclosure(Nonterm nts); +static void emitcost(Tree t, const char* v); +static void emitcostcalc(Rule r); +static void emitdefs(Nonterm nts, int ntnumber); +static void emitfuncs(void); +static void emitheader(void); +static void emitinsndata(Rule rules); +static void emitkids(Rule rules, int nrules); +static void emitlabel(Nonterm start); +static void emitleaf(Term p, int ntnumber); +static void emitnts(Rule rules, int nrules); +static void emitpredicatedefinitions(Rule rules); +static void emitrecord(char* pre, Rule r, int cost); +static void emitregisterattrs(); +static void emitregisters(); +static void emitrule(Nonterm nts); +static void emitstate(Term terms, Nonterm start, int ntnumber); +static void emitstring(Rule rules); +static void emitstruct(Nonterm nts, int ntnumber); +static void emitterms(Term terms); +static void emittest(Tree t, const char* v, const char* suffix); + +extern int yy_flex_debug; + +int main(int argc, char* argv[]) +{ + int c, i; + Nonterm p; + + #if 0 + extern int yydebug; + yydebug = 1; + #endif + + infp = stdin; + outfp = stdout; + hdrfp = NULL; + yy_flex_debug = 0; + + for (;;) + { + int opt = getopt(argc, argv, "p:i:o:h:yf"); + if (opt == -1) + break; + + switch (opt) + { + case 'p': + prefix = optarg; + break; + + case 'i': + infp = fopen(optarg, "r"); + if (!infp) + { + yyerror("cannot open input file: %s\n", strerror(errno)); + exit(1); + } + break; + + case 'o': + outfp = fopen(optarg, "w"); + if (!outfp) + { + yyerror("cannot open output file: %s\n", strerror(errno)); + exit(1); + } + break; + + case 'h': + hdrfp = fopen(optarg, "w"); + if (!hdrfp) + { + yyerror("cannot open output header file: %s\n", strerror(errno)); + exit(1); + } + break; + + case 'y': + { + extern int yydebug; + yydebug = 1; + break; + } + + case 'f': + { + yy_flex_debug = 1; + break; + } + + default: + yyerror("usage: %s [-p prefix] < input > output\n", argv[0]); + exit(1); + } + } + + emitheader(); + registerterminals(); + + start = nonterm("stmt", true); + + /* Define some standard terms. */ + + { + const static struct terminfo reg = { "reg", NULL, "" }; + const static struct terminfo REG = { "REG", NULL, NULL }; + const static struct terminfo NOPI = { "NOP.I", NULL, NULL }; + const static struct terminfo NOPF = { "NOP.F", NULL, NULL }; + const static struct terminfo NOPL = { "NOP.L", NULL, NULL }; + const static struct terminfo NOPD = { "NOP.D", NULL, NULL }; + const static struct terminfo RET = { "RET", NULL, NULL }; + + nonterm("reg", true); + + rule(NULL, tree(®, NULL, NULL))->cost = 1; + rule(®, tree(®, NULL, NULL))->cost = 1; + rule(®, tree(&NOPI, tree(®, NULL, NULL), NULL))->cost = 1; + rule(®, tree(&NOPF, tree(®, NULL, NULL), NULL))->cost = 1; + rule(®, tree(&NOPL, tree(®, NULL, NULL), NULL))->cost = 1; + rule(®, tree(&NOPD, tree(®, NULL, NULL), NULL))->cost = 1; + rule(NULL, tree(&RET, NULL, NULL))->cost = 1; + } + + yyin = infp; + yyparse(); + + emitregisterattrs(); + emitregisters(); + emitdefs(nts, ntnumber); + emitstruct(nts, ntnumber); + emitnts(rules, nrules); + emitterms(terms); + emitstring(rules); + emitrule(nts); + emitclosure(nts); + emitpredicatedefinitions(rules); + emitinsndata(rules); + if (start) + emitstate(terms, start, ntnumber); + print("#ifdef STATE_LABEL\n"); + if (start) + emitlabel(start); + emitkids(rules, nrules); + emitfuncs(); + print("#endif\n"); + print("#include \"mcgg_generated_footer.h\"\n"); + printh("#endif\n"); + + if (outfp) + fclose(outfp); + if (hdrfp) + fclose(hdrfp); + + return errcnt > 0; +} + +static void registerterminal(const struct ir_data* data, int iropcode, char type) +{ + const char* s = (type == 0) ? data->name : aprintf("%s.%c", data->name, type); + int esn = ir_to_esn(iropcode, type); + + term(s, esn); +} + +static void registerterminals(void) +{ + int i; + + for (i=0; ilink) + if (strcmp(name, p->sym.name) == 0) + return &p->sym; + return 0; +} + +/* install - install symbol name */ +static void* install(const char* name) +{ + struct entry* p = calloc(1, sizeof *p); + int i = hash(name) % HASHSIZE; + + p->sym.name = name; + p->link = table[i]; + table[i] = p; + return &p->sym; +} + +struct reg* makereg(const char* id) +{ + struct reg* p = smap_get(®isters, id); + static int number = 0; + + if (p) + yyerror("redefinition of '%s'", id); + p = calloc(1, sizeof(*p)); + p->name = id; + p->number = number++; + array_append(&p->aliases, p); + smap_put(®isters, id, p); + + return p; +} + +void setregnames(struct reg* reg, struct stringlist* names) +{ + if (reg->names) + yyerror("you can only set one set of register names"); + + reg->names = names; +} + +struct regattr* makeregattr(const char* id) +{ + struct regattr* p = smap_get(®isterattrs, id); + static int number = 0; + + if (p) + yyerror("redefinition of '%s'", id); + p = calloc(1, sizeof(*p)); + p->name = id; + p->number = number++; + smap_put(®isterattrs, id, p); + + return p; +} + +void addregattr(struct reg* reg, const char* id) +{ + struct regattr* p = smap_get(®isterattrs, id); + + if (!p) + p = makeregattr(id); + + reg->attrs |= 1<<(p->number); +} + +void addregalias(struct reg* r1, struct reg* r2) +{ + if (!array_appendu(&r1->aliases, r2)) + { + int i; + + for (i=0; ialiases.count; i++) + addregalias(r1->aliases.item[i], r2); + } +} + +void addregaliases(struct reg* reg, struct stringlist* aliases) +{ + struct stringfragment* f = aliases->first; + + while (f) + { + struct reg* r = smap_get(®isters, f->data); + if (!r) + yyerror("register '%s' is not defined here", f->data); + + array_appendu(®->aliases, r); + array_appendu(&r->aliases, reg); + + f = f->next; + } +} + +struct regattr* getregattr(const char* id) +{ + struct regattr* p = smap_get(®isterattrs, id); + if (!p) + yyerror("'%s' is not the name of a register class", id); + return p; +} + +/* nonterm - create a new terminal id, if necessary */ +Nonterm nonterm(const char* id, bool allocate) +{ + Nonterm p = lookup(id); + Nonterm* q = &nts; + + if (p && p->kind == NONTERM) + return p; + if (p) + yyerror("redefinition of '%s' as something else\n", id); + if (!allocate) + yyerror("'%s' has not been declared\n", id); + + p = install(id); + p->kind = NONTERM; + p->number = ++ntnumber; + if (p->number == 1) + start = p; + while (*q && (*q)->number < p->number) + q = &(*q)->link; + assert(*q == 0 || (*q)->number != p->number); + p->link = *q; + *q = p; + return p; +} + +/* term - create a new terminal id with external symbol number esn */ +Term term(const char* id, int esn) +{ + Term p = lookup(id); + Term* q = &terms; + + if (p) + yyerror("redefinition of '%s'\n", id); + + p = install(id); + p->kind = TERM; + p->esn = esn; + p->arity = -1; + while (*q && (*q)->esn < p->esn) + q = &(*q)->link; + if (*q && (*q)->esn == p->esn) + yyerror("duplicate external symbol number `%s=%d'\n", + p->name, p->esn); + p->link = *q; + *q = p; + return p; +} + +/* tree - create & initialize a tree node with the given fields */ +Tree tree(const struct terminfo* ti, Tree left, Tree right) +{ + Tree t = calloc(1, sizeof *t); + Term p = lookup(ti->name); + int arity = 0; + + if (left && right) + arity = 2; + else if (left) + arity = 1; + if (p == NULL && arity > 0) + { + yyerror("undefined terminal `%s'\n", ti->name); + p = term(ti->name, -1); + } + else if (p == NULL && arity == 0) + p = (Term)nonterm(ti->name, false); + else if (p && p->kind == NONTERM && arity > 0) + { + yyerror("`%s' is a non-terminal\n", ti->name); + p = term(ti->name, -1); + } + if (p->kind == TERM && p->arity == -1) + p->arity = arity; + if (p->kind == TERM && arity != p->arity) + yyerror("inconsistent arity for terminal `%s'\n", ti->name); + t->op = p; + t->nterms = p->kind == TERM; + if (t->left = left) + t->nterms += left->nterms; + if (t->right = right) + t->nterms += right->nterms; + + /* Special rules that have no output register attribute use "" as the + * attribute name; these can't be made by the grammar. */ + + t->label = ti->label; + if ((p->kind == TERM) && (ti->attr)) + yyerror("can't specify an input register attribute for terminal '%s'", ti->name); + if (p->kind == NONTERM) + { + Nonterm nt = (Nonterm)p; + if (nt->is_fragment && ti->attr) + yyerror("can't specify an input register attribute for fragment '%s'", ti->name); + if (!nt->is_fragment && !ti->attr) + yyerror("must specify an input register attribute for non-fragment '%s'", ti->name); + + if (ti->attr && ti->attr[0]) + { + t->attr = smap_get(®isterattrs, ti->attr); + if (!t->attr) + yyerror("'%s' doesn't seem to be a known register attribute", ti->attr); + } + } + return t; +} + +/* rule - create & initialize a rule with the given fields */ +Rule rule(const struct terminfo* ti, Tree pattern) +{ + static int number = 1; + static const struct terminfo stmt = { "stmt", NULL, NULL }; + Rule r = calloc(1, sizeof *r); + Rule *q; + Term p = pattern->op; + + if (!ti) + ti = &stmt; + + nrules++; + r->lineno = yylineno; + r->lhs = nonterm(ti->name, false); + r->packed = ++r->lhs->lhscount; + for (q = &r->lhs->rules; *q; q = &(*q)->decode) + ; + *q = r; + r->pattern = pattern; + r->ern = number++; + if (p->kind == TERM) + { + r->next = p->rules; + p->rules = r; + } + else if (pattern->left == NULL && pattern->right == NULL) + { + Nonterm p = pattern->op; + r->chain = p->chain; + p->chain = r; + } + for (q = &rules; *q && (*q)->ern < r->ern; q = &(*q)->link) + ; + if (*q && (*q)->ern == r->ern) + yyerror("duplicate external rule number `%d'\n", r->ern); + r->link = *q; + *q = r; + + r->label = ti->label; + if (r->lhs->is_fragment && ti->attr) + yyerror("can't specify an output register attribute for a fragment"); + if (!r->lhs->is_fragment && !ti->attr && (r->lhs->number != NONTERM_STMT)) + yyerror("must specify an output register attribute for non-fragments"); + + /* Special rules that have no output register attribute use "" as the + * attribute name; these can't be made by the grammar. */ + + if (ti->attr && ti->attr[0]) + { + r->attr = smap_get(®isterattrs, ti->attr); + if (!r->attr) + yyerror("'%s' doesn't seem to be a known register attribute", ti->attr); + } + + return r; +} + +/* print - formatted output */ + +static void printto(FILE* fp, const char* fmt, va_list ap) +{ + if (!fp) + return; + + for (; *fmt; fmt++) + if (*fmt == '%') + switch (*++fmt) + { + case 'd': + fprintf(fp, "%d", va_arg(ap, int)); + break; + + case 'x': + fprintf(fp, "%x", va_arg(ap, uint32_t)); + break; + + case 's': + fputs(va_arg(ap, char*), fp); + break; + + case 'P': + fprintf(fp, "%s_", prefix); + break; + + case 'T': + { + Tree t = va_arg(ap, Tree); + print("%S", t->op); + if (t->left && t->right) + print("(%T,%T)", t->left, t->right); + else if (t->left) + print("(%T)", t->left); + break; + } + + case 'R': + { + Rule r = va_arg(ap, Rule); + print("%S: %T", r->lhs, r->pattern); + break; + } + + case 'S': + fputs(va_arg(ap, Term)->name, fp); + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + { + int n = *fmt - '0'; + while (n-- > 0) + putc('\t', fp); + break; + } + + default: + putc(*fmt, fp); + break; + } + else + putc(*fmt, fp); +} + +static void print(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + printto(outfp, fmt, ap); + va_end(ap); +} + +static void printh(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + printto(hdrfp, fmt, ap); + va_end(ap); +} + +static void emitregisterattrs(void) +{ + int i; + + print("const char* %Pregister_class_names[] = {\n"); + for (i=0; inumber == i); + + print("%1\"%s\",\n", rc->name); + printh("#define %P%s_ATTR (1U<<%d)\n", rc->name, rc->number); + } + print("};\n\n"); + printh("\n"); +} + +static void emitregisters(void) +{ + int i, j; + + for (i=0; iname); + for (j=0; jaliases.count; j++) + print("&%Pregister_data[%d], ", r->aliases.item[j]->number); + print("NULL\n};\n"); + } + + for (i=0; iname); + if (r->names) + { + struct stringfragment* f = r->names->first; + while (f) + { + print("\"%s\", ", f->data); + f = f->next; + } + } + else + print("\"%s\", ", r->name); + print("NULL\n};\n"); + } + + print("const struct %Pregister_data %Pregister_data[] = {\n"); + for (i=0; inumber == i); + + print("%1{ \"%s\", 0x%x, %Pregister_names_%d_%s, %Pregister_aliases_%d_%s },\n", + r->name, r->attrs, i, r->name, i, r->name); + } + print("%1{ NULL }\n"); + print("};\n\n"); +} + +/* emitcase - emit one case in function state */ +static void emitcase(Term p, int ntnumber) +{ + Rule r; + + if (!p->rules) + return; + + print("%1case %d: /* %S */\n", p->esn, p); + switch (p->arity) + { + case 0: + case -1: + break; + case 1: + print("%2assert(l);\n"); + break; + case 2: + print("%2assert(l && r);\n"); + break; + default: + assert(0); + } + for (r = p->rules; r; r = r->next) + { + switch (p->arity) + { + case 0: + case -1: + print("%2{%1/* %R */\n%3c = ", r); + emitcostcalc(r); + break; + case 1: + if (r->pattern->nterms > 1) + { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "l", " "); + print("%2) {\n%3c = "); + } + else + { + print("%2{%1/* %R */\n%3c = ", r); + } + emitcostcalc(r); + emitcost(r->pattern->left, "l"); + break; + case 2: + if (r->pattern->nterms > 1) + { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "l", + r->pattern->right->nterms ? " && " : " "); + emittest(r->pattern->right, "r", " "); + print("%2) {\n%3c = "); + } + else + { + print("%2{%1/* %R */\n%3c = ", r); + } + emitcostcalc(r); + emitcost(r->pattern->left, "l"); + emitcost(r->pattern->right, "r"); + break; + default: + assert(0); + } + print("%d);\n", r->cost); + emitrecord("\t\t\t", r, 0); + print("%2}\n"); + } + print("%2break;\n"); +} + +/* emitclosure - emit the closure functions */ +static void emitclosure(Nonterm nts) +{ + Nonterm p; + + for (p = nts; p; p = p->link) + if (p->chain) + print("static void %Pclosure_%S(struct %Pstate *, int);\n", p); + print("\n"); + for (p = nts; p; p = p->link) + if (p->chain) + { + Rule r; + print("static void %Pclosure_%S(struct %Pstate *p, int c) {\n", p); + for (r = p->chain; r; r = r->chain) + emitrecord("\t", r, r->cost); + print("}\n\n"); + } +} + +/* emitcost - emit cost computation for tree t */ +static void emitcost(Tree t, const char* v) +{ + Nonterm p = t->op; + + if (p->kind == TERM) + { + if (t->left) + emitcost(t->left, aprintf("%s->left", v)); + if (t->right) + emitcost(t->right, aprintf("%s->right", v)); + } + else + print("%s->cost[%P%S_NT] + ", v, p); +} + +/* emitdefs - emit non-terminal defines and data structures */ +static void emitdefs(Nonterm nts, int ntnumber) +{ + Nonterm p; + + printh("enum {\n"); + for (p = nts; p; p = p->link) + printh("%1%P%S_NT = %d,\n", p, p->number); + printh("%1%Pmax_nt = %d\n", ntnumber); + printh("};\n\n"); + + print("const char *%Pntname[] = {\n%10,\n"); + for (p = nts; p; p = p->link) + print("%1\"%S\",\n", p); + print("%10\n};\n\n"); +} + +/* emitfuncs - emit functions to access node fields */ +static void emitfuncs(void) +{ + print("int %Pop_label(NODEPTR_TYPE p) {\n" + "%1%Passert(p, PANIC(\"NULL tree in %Pop_label\\n\"));\n" + "%1return OP_LABEL(p);\n}\n\n"); + print("STATE_TYPE %Pstate_label(NODEPTR_TYPE p) {\n" + "%1%Passert(p, PANIC(\"NULL tree in %Pstate_label\\n\"));\n" + "%1return STATE_LABEL(p);\n}\n\n"); + print("NODEPTR_TYPE %Pchild(NODEPTR_TYPE p, int index) {\n" + "%1%Passert(p, PANIC(\"NULL tree in %Pchild\\n\"));\n" + "%1switch (index) {\n%1case 0:%1return LEFT_CHILD(p);\n" + "%1case 1:%1return RIGHT_CHILD(p);\n%1}\n" + "%1%Passert(0, PANIC(\"Bad index %%d in %Pchild\\n\", index));\n%1return 0;\n}\n\n"); +} + +/* emitheader - emit initial definitions */ +static void emitheader(void) +{ + print("#include \"mcgg_generated_header.h\"\n"); + if (Tflag) + print("static NODEPTR_TYPE %Pnp;\n\n"); + + printh("#ifndef MCG_DEFS_H\n"); + printh("#define MCG_DEFS_H\n\n"); +} + +/* computekids - compute paths to kids in tree t */ +static char* computekids(Tree node, const char* v, char* bp, int* ip) +{ + Term t = node->op; + + if (!node->left && !node->right) + { + sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v); + bp += strlen(bp); + } + + if (t->kind == TERM) + { + if (t->arity >= 1) + bp = computekids(node->left, aprintf("LEFT_CHILD(%s)", v), bp, ip); + if (t->arity == 2) + bp = computekids(node->right, aprintf("RIGHT_CHILD(%s)", v), bp, ip); + } + return bp; +} + +/* emitkids - emit burm_kids */ +static void emitkids(Rule rules, int nrules) +{ + int i; + Rule r, * rc = calloc(nrules+1, sizeof *rc); + char** str = calloc(nrules+1, sizeof *str); + + for (i = 0, r = rules; r; r = r->link) + { + int j = 0; + char buf[1024], * bp = buf; + *computekids(r->pattern, "p", bp, &j) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) + str[j] = strdup(buf); + r->kids = rc[j]; + rc[j] = r; + } + print("NODEPTR_TYPE *%Pkids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]) {\n" + "%1%Passert(p, PANIC(\"NULL tree in %Pkids\\n\"));\n" + "%1%Passert(kids, PANIC(\"NULL kids in %Pkids\\n\"));\n" + "%1switch (eruleno) {\n"); + for (i = 0; r = rc[i]; i++) + { + for (; r; r = r->kids) + print("%1case %d: /* %R */\n", r->ern, r); + print("%s%2break;\n", str[i]); + } + print("%1default:\n%2%Passert(0, PANIC(\"Bad external rule number %%d in %Pkids\\n\", eruleno));\n%1}\n%1return kids;\n}\n\n"); +} + +/* emitlabel - emit the labelling functions */ +static void emitlabel(Nonterm start) +{ + print("static void %Plabel1(NODEPTR_TYPE p) {\n" + "%1%Passert(p, PANIC(\"NULL tree in %Plabel\\n\"));\n" + "%1switch (%Parity[OP_LABEL(p)]) {\n" + "%1case 0:\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(p, 0, 0);\n%2break;\n" + "%1case 1:\n%2%Plabel1(LEFT_CHILD(p));\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(p,\n" + "%3STATE_LABEL(LEFT_CHILD(p)), 0);\n%2break;\n" + "%1case 2:\n%2%Plabel1(LEFT_CHILD(p));\n%2%Plabel1(RIGHT_CHILD(p));\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(p,\n" + "%3STATE_LABEL(LEFT_CHILD(p)),\n%3STATE_LABEL(RIGHT_CHILD(p)));\n%2break;\n" + "%1}\n}\n\n"); + print( + "STATE_TYPE %Plabel(NODEPTR_TYPE p) {\n%1%Plabel1(p);\n" + "%1return ((struct %Pstate *)STATE_LABEL(p))->rule.%P%S ? STATE_LABEL(p) : 0;\n" + "}\n\n", + start); +} + +/* closure - fill in cost & rule with results of chain rules w/p as rhs */ +static void closure(int cost[], Rule rule[], Nonterm p, int c) +{ + Rule r; + + for (r = p->chain; r; r = r->chain) + if (c + r->cost < cost[r->lhs->number]) + { + cost[r->lhs->number] = c + r->cost; + rule[r->lhs->number] = r; + closure(cost, rule, r->lhs, c + r->cost); + } +} + +/* computents - fill in bp with burm_nts vector for tree t */ +static char* computents(Tree t, char* bp) +{ + if (t) + { + Nonterm p = t->op; + if (!t->left && !t->right) + { + if (p->kind == NONTERM) + sprintf(bp, "%s_%s_NT, ", prefix, p->name); + else + sprintf(bp, "0, "); + bp += strlen(bp); + } + else + bp = computents(t->right, computents(t->left, bp)); + } + return bp; +} + +/* emitnts - emit burm_nts ragged array */ +static void emitnts(Rule rules, int nrules) +{ + Rule r; + int i, j, * nts = calloc(nrules, sizeof *nts); + char** str = calloc(nrules, sizeof *str); + + for (i = 0, r = rules; r; r = r->link) + { + char buf[1024]; + *computents(r->pattern, buf) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) + { + print("static const short %Pnts_%d[] = { %s0 };\n", j, buf); + str[j] = strdup(buf); + } + nts[i++] = j; + } + print("\nconst short *%Pnts[] = {\n"); + for (i = j = 0, r = rules; r; r = r->link) + { + for (; j < r->ern; j++) + print("%10,%1/* %d */\n", j); + print("%1%Pnts_%d,%1/* %d */\n", nts[i++], j++); + } + print("};\n\n"); +} + +/* emitrecord - emit code that tests for a winning match of rule r */ +static void emitrecord(char* pre, Rule r, int cost) +{ + print("%sif (", pre); + if (Tflag) + print("%Ptrace(%Pnp, %d, c + %d, p->cost[%P%S_NT]), ", + r->ern, cost, r->lhs); + print("c + %d < p->cost[%P%S_NT]) {\n" + "%s%1p->cost[%P%S_NT] = c + %d;\n%s%1p->rule.%P%S = %d;\n", + cost, r->lhs, pre, r->lhs, cost, pre, r->lhs, + r->packed); + if (r->lhs->chain) + print("%s%1%Pclosure_%S(p, c + %d);\n", pre, r->lhs, cost); + print("%s}\n", pre); +} + +/* emitrule - emit decoding vectors and burm_rule */ +static void emitrule(Nonterm nts) +{ + Nonterm p; + + for (p = nts; p; p = p->link) + { + Rule r; + print("static const short %Pdecode_%S[] = {\n%10,\n", p); + for (r = p->rules; r; r = r->decode) + print("%1%d,\n", r->ern); + print("};\n\n"); + } + print("int %Prule(STATE_TYPE state, int goalnt) {\n" + "%1%Passert(goalnt >= 1 && goalnt <= %d, PANIC(\"Bad goal nonterminal %%d in %Prule\\n\", goalnt));\n" + "%1if (!state)\n%2return 0;\n%1switch (goalnt) {\n", + ntnumber); + for (p = nts; p; p = p->link) + print("%1case %P%S_NT:" + "%1return %Pdecode_%S[((struct %Pstate *)state)->rule.%P%S];\n", + p, p, p); + print("%1default:\n%2%Passert(0, PANIC(\"Bad goal nonterminal %%d in %Prule\\n\", goalnt));\n%1}\n%1return 0;\n}\n\n"); +} + +static void print_path(uint32_t path) +{ + int i = 0; + + while (path > 0) + { + switch (path % 3) + { + case 1: print("LEFT_CHILD("); break; + case 2: print("RIGHT_CHILD("); break; + } + path /= 3; + i++; + } + + print("node"); + + while (i > 0) + { + print(")"); + i--; + } +} + +static const uint32_t PATH_MISSING = 0xffffffff; + +static uint32_t find_label(Tree root, const char* name, uint32_t path, Tree* found) +{ + uint32_t p; + + if (root->label && (strcmp(root->label, name) == 0)) + { + if (found) + *found = root; + return path; + } + + p = PATH_MISSING; + if (root->left && (p == PATH_MISSING)) + p = find_label(root->left, name, path*3 + 1, found); + if (root->right && (p == PATH_MISSING)) + p = find_label(root->right, name, path*3 + 2, found); + return p; +} + +static void label_not_found(Rule rule, const char* label) +{ + yylineno = rule->lineno; + yyerror("label '%s' not found", label); + exit(1); +} + +static bool find_child_index(Tree node, const char* name, int* index, Tree* found) +{ + /* This must return the same ordering as the burm_kids() function uses. */ + + if (node->label && strcmp(node->label, name) == 0) + { + if (found) + *found = node; + return true; + } + + if (!node->left && !node->right) + (*index)++; + + if (node->left && find_child_index(node->left, name, index, found)) + return true; + if (node->right && find_child_index(node->right, name, index, found)) + return true; + return false; +} + +static void emit_predicate_expr(Rule r, struct expr* p) +{ + bool first = true; + + assert(p->type == PREDICATE_FUNCTION); + print("%1if (%Ppredicate_%s(", p->u.name); + + p = p->next; + while (p) + { + if (!first) + print(", "); + else + first = false; + + switch (p->type) + { + case PREDICATE_NODE: + { + uint32_t path = find_label(r->pattern, p->u.name, 0, NULL); + if (path == PATH_MISSING) + label_not_found(r, p->u.name); + + print_path(path); + break; + } + + case PREDICATE_NUMBER: + { + print("%d", p->u.number); + break; + } + } + + p = p->next; + } + + print("))"); +} + +/* emitpredicates - emit predicates for rules */ +static void emitpredicatedefinitions(Rule r) +{ + int i; + + while (r) + { + print("/* %R */\n", r); + print("static int %Padjust_cost_%d(NODEPTR_TYPE node, int cost) {\n", r->ern); + + for (i=0; iprefers.count; i++) + { + emit_predicate_expr(r, r->prefers.item[i]); + print(" cost -= 1;\n"); + } + + for (i=0; irequires.count; i++) + { + emit_predicate_expr(r, r->requires.item[i]); + print(" {} else return %d;\n", maxcost); + } + + print("%1if (cost > %d) return %d;\n", maxcost, maxcost); + print("%1if (cost < 1) return 1;\n"); + print("%1return cost;\n"); + print("}\n\n"); + r = r->link; + } +} + +static void emit_input_regs(Tree node, int* index) +{ + /* This must return the same ordering as the burm_kids() function uses. */ + + Nonterm nt = node->op; + if ((nt->kind == NONTERM) && !nt->is_fragment && !node->left && !node->right) + { + if (node->attr) + { + uint32_t attr = 1<attr->number; + print("%1data->constrain_input_reg(%d, 0x%x /* %s */);\n", + *index, attr, node->attr->name); + } + } + + if (!node->left && !node->right) + (*index)++; + + if (node->left) + emit_input_regs(node->left, index); + if (node->right) + emit_input_regs(node->right, index); +} + +static void emit_output_constraints(Rule r) +{ + int i; + struct constraint* outputc = NULL; + + for (i=0; iconstraints.count; i++) + { + struct constraint* c = r->constraints.item[i]; + + if (c->type == CONSTRAINT_EQUALS) + { + if (strcmp(c->left, r->label) != 0) + yyerror("equality register constraints must have an output register on the left hand side"); + if (outputc != NULL) + yyerror("you can't specify more than one output register constraint"); + outputc = c; + } + } + + if (outputc) + { + int index = 0; + + if (!find_child_index(r->pattern, outputc->right, &index, NULL)) + label_not_found(r, outputc->right); + + print("%1data->constrain_output_reg_equal_to(%d);\n", index); + } +} + +static void emit_input_constraints(Rule r) +{ + int i; + for (i=0; iconstraints.count; i++) + { + int index; + struct constraint* c = r->constraints.item[i]; + + if (c->type == CONSTRAINT_PRESERVED) + { + if (strcmp(c->left, r->label) == 0) + yyerror("cannot preserve an output register!"); + + index = 0; + if (!find_child_index(r->pattern, c->left, &index, NULL)) + label_not_found(r, c->left); + + print("%1data->constrain_input_reg_preserved(%d);\n", index); + } + } +} + +/* emitinsndata - emit the code generation data */ +static void emitinsndata(Rule rules) +{ + int k; + Rule r; + + r = rules; + while (r) + { + struct stringfragment* f = r->code.first; + yylineno = r->lineno; + + if (!f) + { + /* This instruction has no code; make sure it's not a fragment. */ + if (r->lhs->is_fragment) + { + yylineno = r->lineno; + yyerror("rule is a fragment, but doesn't emit anything"); + } + } + + print("/* %R */\n", r); + print("static void %Pemitter_%d(const struct %Pemitter_data* data) {\n", r->ern); + + if (r->attr) + print("%1data->constrain_output_reg(0x%x /* %s */);\n", + 1<attr->number, r->attr->name); + + { + int index = 0; + emit_input_regs(r->pattern, &index); + } + + emit_output_constraints(r); + emit_input_constraints(r); + + while (f) + { + char* data = strdup(f->data); + int type = *data++; + char* label = strtok(data, "."); + char* nameindex_s = strtok(NULL, "."); + int nameindex = nameindex_s ? atoi(nameindex_s) : 0; + + switch (type) + { + case '%': + { + if (r->label && (strcmp(label, r->label) == 0)) + print("%1data->emit_return_reg(%d);\n", nameindex); + else + { + Tree node; + int index = 0; + if (!find_child_index(r->pattern, label, &index, &node)) + label_not_found(r, label); + Nonterm nt = node->op; + + if (nt->kind == NONTERM) + { + if (nt->is_fragment) + print("%1data->emit_fragment(%d);\n", index); + else + print("%1data->emit_reg(%d, %d);\n", index, nameindex); + } + else + print("%1data->emit_reg(%d, %d);\n", index, nameindex); + } + break; + } + + case '$': + { + int index = 0; + if (!find_child_index(r->pattern, label, &index, NULL)) + label_not_found(r, label); + + if (nameindex != 0) + yyerror("indices other than 0 make no sense for $-values"); + + print("%1data->emit_value(%d);\n", index); + break; + } + + case '\n': + assert(f->data[1] == 0); + print("%1data->emit_eoi();\n"); + break; + + default: + print("%1data->emit_string(\"%s\");\n", f->data); + } + + f = f->next; + } + + print("}\n\n"); + r = r->link; + } + + r = rules; + print("const struct %Pinstruction_data %Pinstruction_data[] = {\n"); + k = 0; + while (r) + { + for (; k < r->ern; k++) + print("%1{ 0 }, /* %d */\n", k); + k++; + + print("%1{ /* %d: %R */\n", r->ern, r); + + print("%2\"%R\",\n", r); + + print("%2&%Pemitter_%d,\n", r->ern); + + print("%2%s,\n", r->lhs->is_fragment ? "true" : "false"); + + { + int i; + uint32_t attrs = 0; + + for (i=0; iconstraints.count; i++) + { + struct constraint* c = r->constraints.item[i]; + + if (c->type == CONSTRAINT_CORRUPTED_ATTR) + { + struct regattr* p = smap_get(®isterattrs, c->left); + if (!p) + yyerror("no such register attribute '%s'", c->left); + + attrs |= 1<<(p->number); + } + } + + print("%2%d, /* corruption attrs */\n", attrs); + } + + print("%1},\n"); + r = r->link; + } + print("};\n\n"); +} + +/* emitcost - emit a cost calculation via a predicate */ +static void emitcostcalc(Rule r) +{ + print("%Padjust_cost_%d(node, ", r->ern); +} + +/* emitstate - emit state function */ +static void emitstate(Term terms, Nonterm start, int ntnumber) +{ + int i; + Term p; + + print("STATE_TYPE %Pstate(NODEPTR_TYPE node, STATE_TYPE left, STATE_TYPE right) {\n%1int c;\n" + "%1int op = OP_LABEL(node);\n" + "%1struct %Pstate* p;\n" + "%1struct %Pstate* l = (struct %Pstate *)left;\n" + "%1struct %Pstate* r = (struct %Pstate *)right;\n" + "\n" + "%1assert(sizeof (STATE_TYPE) >= sizeof (void *));\n%1"); + print("%1p = malloc(sizeof *p);\n" + "%1p->op = op;\n" + "%1p->left = l;\n" + "%1p->right = r;\n" + "%1p->rule.%P%S = 0;\n", + start); + for (i = 1; i <= ntnumber; i++) + print("%1p->cost[%d] =\n", i); + print("%2%d;\n" + "%1switch (op) {\n", maxcost); + for (p = terms; p; p = p->link) + emitcase(p, ntnumber); + print("%1default:\n" + "%2%Ppanic_cannot_match(node);\n" + "%1}\n" + "%1return (STATE_TYPE)p;\n}\n\n"); +} + +/* emitstring - emit array of rules and costs */ +static void emitstring(Rule rules) +{ + Rule r; + int k; + + print("static const short %Pcost[][4] = {\n"); + for (k = 0, r = rules; r; r = r->link) + { + for (; k < r->ern; k++) + print("%1{ 0 },%1/* %d */\n", k); + print("%1{ %d },%1/* %d = %R */\n", r->cost, k++, r); + } + print("};\n\n"); +} + +/* emitstruct - emit the definition of the state structure */ +static void emitstruct(Nonterm nts, int ntnumber) +{ + print("struct %Pstate {\n%1int op;\n%1struct %Pstate *left, *right;\n" + "%1short cost[%d];\n%1struct {\n", + ntnumber + 1); + for (; nts; nts = nts->link) + { + int n = 1, m = nts->lhscount; + while (m >>= 1) + n++; + print("%2unsigned %P%S:%d;\n", nts, n); + } + print("%1} rule;\n};\n\n"); +} + +/* emitterms - emit terminal data structures */ +static void emitterms(Term terms) +{ + Term p; + int k; + + print("static const char %Parity[] = {\n"); + for (k = 0, p = terms; p; p = p->link) + { + for (; k < p->esn; k++) + print("%10,%1/* %d */\n", k); + print("%1%d,%1/* %d=%S */\n", p->arity < 0 ? 0 : p->arity, k++, p); + } + print("};\n\n"); + + print("static const char *%Popname[] = {\n"); + for (k = 0, p = terms; p; p = p->link) + { + for (; k < p->esn; k++) + print("%1/* %d */%10,\n", k); + print("%1/* %d */%1\"%S\",\n", k++, p); + } + print("};\n\n"); +} + +/* emittest - emit clause for testing a match */ +static void emittest(Tree t, const char* v, const char* suffix) +{ + Term p = t->op; + + if (p->kind == TERM) + { + print("%3%s->op == %d%s/* %S */\n", v, p->esn, + t->nterms > 1 ? " && " : suffix, p); + if (t->left) + emittest(t->left, aprintf("%s->left", v), + t->right && t->right->nterms ? " && " : suffix); + if (t->right) + emittest(t->right, aprintf("%s->right", v), suffix); + } +} diff --git a/util/mcgg/iburg.h b/util/mcgg/iburg.h new file mode 100644 index 000000000..739641d4d --- /dev/null +++ b/util/mcgg/iburg.h @@ -0,0 +1,160 @@ +#ifndef BURG_INCLUDED +#define BURG_INCLUDED + +#include "em_arith.h" +#include "stringlist.h" +#include "array.h" + +extern char* stringf(char* fmt, ...); + +typedef enum +{ + TERM = 1, + NONTERM, + REG, + REGCLASS +} Kind; +typedef struct rule* Rule; +typedef struct term* Term; + +enum +{ + CONSTRAINT_EQUALS, + CONSTRAINT_CORRUPTED_ATTR, + CONSTRAINT_PRESERVED, +}; + +struct constraint +{ + int type; + const char* left; + const char* right; + struct constraint* next; +}; + +enum +{ + PREDICATE_FUNCTION, + PREDICATE_NODE, + PREDICATE_NUMBER +}; + +struct expr +{ + int type; + struct expr* next; + union + { + const char* name; + arith number; + } u; +}; + +struct terminfo +{ + const char* name; + const char* label; + const char* attr; +}; + +struct reg +{ + const char* name; /* friendly register name */ + int number; /* identifying number */ + uint32_t attrs; /* bitfield of register attributes */ + struct stringlist* names; /* register names */ + ARRAYOF(struct reg) aliases; /* registers that this one aliases */ +}; + +struct regattr +{ + const char* name; /* class name */ + int number; /* identifying number */ +}; + +extern struct reg* makereg(const char* name); +extern void setregnames(struct reg* reg, struct stringlist* names); +extern void addregattr(struct reg* reg, const char* regattr); +extern void addregaliases(struct reg* reg, struct stringlist* aliases); +extern struct regattr* getregattr(const char* name); + +struct term +{ /* terminals: */ + char* name; /* terminal name */ + Kind kind; /* TERM */ + int esn; /* external symbol number */ + int arity; /* operator arity */ + Term link; /* next terminal in esn order */ + Rule rules; /* rules whose pattern starts with term */ +}; + +typedef struct nonterm* Nonterm; +struct nonterm +{ /* non-terminals: */ + char* name; /* non-terminal name */ + Kind kind; /* NONTERM */ + int number; /* identifying number */ + int lhscount; /* # times nt appears in a rule lhs */ + int reached; /* 1 iff reached from start non-terminal */ + Rule rules; /* rules w/non-terminal on lhs */ + Rule chain; /* chain rules w/non-terminal on rhs */ + Nonterm link; /* next terminal in number order */ + bool is_fragment; /* these instructions are all fragments */ +}; +extern void* lookup(const char* name); +extern Nonterm nonterm(const char* id, bool allocate); +extern Term term(const char* id, int esn); + +typedef struct tree* Tree; +struct tree +{ /* tree patterns: */ + void* op; /* a terminal or non-terminal */ + const char* label; /* user label for this node */ + Tree left, right; /* operands */ + int nterms; /* number of terminal nodes in this tree */ + struct regattr* attr; /* input register attribute */ +}; +extern Tree tree(const struct terminfo* ti, Tree left, Tree right); + +struct rule +{ /* rules: */ + Nonterm lhs; /* lefthand side non-terminal */ + Tree pattern; /* rule pattern */ + int lineno; /* line number where allocated */ + int ern; /* external rule number */ + int packed; /* packed external rule number */ + int cost; /* associated cost */ + const char* label; /* label for LHS */ + struct regattr* attr; /* register attribute of result */ + Rule link; /* next rule in ern order */ + Rule next; /* next rule with same pattern root */ + Rule chain; /* next chain rule with same rhs */ + Rule decode; /* next rule with same lhs */ + Rule kids; /* next rule with same burm_kids pattern */ + ARRAYOF(struct expr) prefers; /* C predicates */ + ARRAYOF(struct expr) requires; /* C predicates */ + ARRAYOF(struct constraint) constraints; /* register constraints */ + struct stringlist code; /* compiler output code strings */ +}; +extern Rule rule(const struct terminfo* ti, Tree pattern); +extern int maxcost; /* maximum cost */ + +/* gram.y: */ +void yyerror(char* fmt, ...); +int yyparse(void); +void yywarn(char* fmt, ...); +extern int errcnt; +extern FILE* infp; +extern FILE* outfp; +extern FILE* hdrfp; + +/* Stupid flex imports --- why mo header file? */ + +extern FILE* yyin; +extern int yylineno; + +extern void printlineno(void); + +#include "mcgg.h" + +#endif diff --git a/util/mcgg/ir.dat b/util/mcgg/ir.dat new file mode 100644 index 000000000..4b7c4e920 --- /dev/null +++ b/util/mcgg/ir.dat @@ -0,0 +1,123 @@ +# Types: +# +# I, F, L, D: int, float, long, double +# i, F: int, float; promoted to long, double based on size +# .: ignore this parameter +# ?: pull/push types from other ? parameters + +# Simple terminals +S ?=.. CONST +V ?=.. REG +S ?=?. NOP +S I=.. LABEL +S I=.. BLOCK +V ?=.. PAIR +S ?=.. ANY +S ?=.. LOCAL +V ?=.. PHI + +# Magic stack operations +S ?=?. PUSH +S ?=.. POP + +# Memory operations +S ?=I. LOAD +S I=I. LOADB +S I=I. LOADH +S ?=I? STORE +S ?=I? STOREB +S ?=I? STOREH + +# Arithemetic operations +S i=ii ADD +S i=ii SUB +S i=ii MUL +S i=ii DIV +S i=ii DIVU +S i=ii MOD +S i=ii MODU +S i=ii NEG + +S f=ff ADDF +S f=ff SUBF +S f=ff MULF +S f=ff DIVF +S f=ff NEGF + +S i=ii AND +S i=ii OR +S i=ii EOR +S i=ii NOT +S i=ii ASL +S i=ii ASR +S i=ii LSL +S i=ii LSR + +# Bitwise conversions +# (Remember, these don't change the value, merely move it) +# (order is important here; the 8-byte version of each must immediate succeed +# the 4-byte version) +S F=I. COPYI +S I=F. COPYF +S D=L. COPYL +S L=D. COPYD + +# Semantic conversions +# (order is important here; the 8-byte version of each must immediate succeed +# the 4-byte version) +S ?=I. FROMUI +S ?=L. FROMUL +S ?=I. FROMSI +S ?=L. FROMSL +S ?=F. FROMUF +S ?=D. FROMUD +S ?=F. FROMSF +S ?=D. FROMSD +S L=II FROMIPAIR +S I=L. FROML0 +S I=L. FROML1 + +# The H versions are only used if wordsize > 2 +S I=I. EXTENDB +S I=I. EXTENDH +S I=I. TRUNCATEB +S I=I. TRUNCATEH + +# Tristate comparisons +# (order is important here; the 8-byte version of each must immediate succeed +# the 4-byte version) +S I=II COMPARESI +S I=LL COMPARESL +S I=II COMPAREUI +S I=LL COMPAREUL +S I=FF COMPAREF +S I=DD COMPARED + +# Tristate to boolean conversion +S I=I. IFEQ +S I=I. IFLT +S I=I. IFLE + +# Procedures +S i=I. CALL +S i=?. GETRET +S ?=i. SETRET + +# Flow control --- these never return +V .=i. JUMP +V .=i. FARJUMP +V .=i. CJUMPEQ +V .=i. CJUMPLT +V .=i. CJUMPLE +V .=.. RET + +# Special +S ?=i. STACKADJUST +S i=.. GETFP +S ?=i. SETFP +S i=.. GETSP +S ?=i. SETSP +S i=i. CHAINFP +S i=i. FPTOAB +S i=i. FPTOLB + diff --git a/util/mcgg/ircodes.h b/util/mcgg/ircodes.h new file mode 100644 index 000000000..d9c05126f --- /dev/null +++ b/util/mcgg/ircodes.h @@ -0,0 +1,28 @@ +#ifndef IRCODES_H +#define IRCODES_H + +enum +{ + IRF_SIZED = 1, +}; + +struct ir_data +{ + const char* name; + int flags; + char returntype; + char lefttype; + char righttype; +}; + +extern const struct ir_data ir_data[]; + +#define TYPE_ATTRS \ + (burm_int_ATTR | burm_long_ATTR | burm_float_ATTR | burm_double_ATTR) + +#include "ircodes-dyn.h" + +#endif + +/* vim: set sw=4 ts=4 expandtab : */ + diff --git a/util/mcgg/ircodes.sh b/util/mcgg/ircodes.sh new file mode 100755 index 000000000..ff2df4845 --- /dev/null +++ b/util/mcgg/ircodes.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +in=$1 +header=$2 +source=$3 + +awk -f - $in >$header << "EOF" + BEGIN { + print "enum ir_opcode {" + } + + /^ *[^# ]+/ { + print "\tIR_" $3 "," + } + + END { + print "\tIR__COUNT" + print "};" + } +EOF + +awk -f - $in >$source << "EOF" + BEGIN { + print "#include \"ircodes.h\"" + print "const struct ir_data ir_data[IR__COUNT] = {" + } + + function char_to_flags(c) { + if (c == "S") return "IRF_SIZED" + return "0" + } + + function char_to_type(c) { + if (c ~ /[A-Za-z]/) return "'"c"'" + if (c == "?") return "'?'" + if (c == ".") return "0" + } + + /^ *[^# ]+/ { + printf("\t{ \"%s\", ", $3) + printf("%s, ", char_to_flags(substr($1, 1, 1))) + printf("%s, ", char_to_type(substr($2, 1, 1))) + printf("%s, ", char_to_type(substr($2, 3, 1))) + printf("%s, ", char_to_type(substr($2, 4, 1))) + printf(" },\n") + } + + END { + print "};" + } +EOF + diff --git a/util/mcgg/mcgg.h b/util/mcgg/mcgg.h new file mode 100644 index 000000000..c0c930a27 --- /dev/null +++ b/util/mcgg/mcgg.h @@ -0,0 +1,83 @@ +#ifndef MCGG_H +#define MCGG_H + +/* Excruciating macro which packs ir opcodes and sizes into an int for iburg's benefit. + * + * Types are mapped to: I=1, F=2, L=3, D=4 + */ +#define ir_to_esn(iropcode, type) \ + ((iropcode)*5 + \ + (((type) == 'I') ? 1 : \ + ((type) == 'F') ? 2 : \ + ((type) == 'L') ? 3 : \ + ((type) == 'D') ? 4 : 0)) + +#define STATE_TYPE void* + +#define STATE_LABEL(p) ((p)->state_label) +#define OP_LABEL(p) ((p)->label) +#define LEFT_CHILD(p) ((p)->left) +#define RIGHT_CHILD(p) ((p)->right) + +struct burm_node +{ + int label; + void* state_label; + struct burm_node* left; + struct burm_node* right; + struct ir* ir; +}; + +typedef struct burm_node* NODEPTR_TYPE; + +extern void* burm_label(NODEPTR_TYPE node); +extern int burm_rule(void* state, int goalnt); +extern const short *burm_nts[]; +extern NODEPTR_TYPE* burm_kids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]); +extern void burm_trace(NODEPTR_TYPE p, int ruleno, int cost, int bestcost); + +struct burm_emitter_data +{ + void (*emit_string)(const char* data); + void (*emit_fragment)(int child); + void (*emit_return_reg)(int index); + void (*emit_reg)(int child, int index); + void (*emit_value)(int child); + void (*emit_eoi)(void); + void (*constrain_input_reg)(int child, uint32_t attr); + void (*constrain_input_reg_preserved)(int child); + void (*constrain_output_reg)(uint32_t attr); + void (*constrain_output_reg_equal_to)(int child); +}; + +typedef void burm_emitter_t(const struct burm_emitter_data* data); + +struct burm_instruction_data +{ + const char* name; + burm_emitter_t* emitter; + bool is_fragment; + uint32_t corrupts; +}; + +extern const struct burm_instruction_data burm_instruction_data[]; + +struct burm_register_data +{ + const char* id; + uint32_t attrs; + const char** names; + const struct burm_register_data** aliases; +}; + +extern const struct burm_register_data burm_register_data[]; +extern const char* burm_register_class_names[]; + +enum +{ + NONTERM_STMT = 1 +}; + +#endif + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/util/mcgg/scan.l b/util/mcgg/scan.l new file mode 100644 index 000000000..447a7d395 --- /dev/null +++ b/util/mcgg/scan.l @@ -0,0 +1,64 @@ +%{ +#include +#include +#include "iburg.h" +#include "y.tab.h" + +static int braces = 0; + +%} +%option warn +%option nodefault +%option noyywrap +%option yylineno +%option debug + +%x QSTRING +%x COMMENT + +%% + +"\"" BEGIN(QSTRING); +"\"" BEGIN(INITIAL); + +[%$][a-zA-Z_][a-zA_Z_0-9]+(\.[0-9]+)? { + yylval.string = strdup(yytext); + return QFRAGMENT; + } + +[^\r\n%$"]+ { + yylval.string = strdup(yytext); + return QFRAGMENT; + } + +"/*" BEGIN(COMMENT); +"*/" BEGIN(INITIAL); +[^*]* ; +"*" ; + +"DECLARATIONS" return DECLARATIONS; +"PATTERNS" return PATTERNS; +"REGISTERS" return REGISTERS; +"aliases" return ALIASES; +"corrupted" return CORRUPTED; +"cost" return COST; +"emit" return EMIT; +"fragment" return FRAGMENT; +"named" return NAMED; +"prefers" return PREFERS; +"preserved" return PRESERVED; +"when" return WHEN; +"with" return WITH; +"==" return EQUALS; +"!=" return NOTEQUALS; + +"//"[^\n]*\n ; + +[A-Za-z_][A-Za-z0-9_]* { yylval.string = strdup(yytext); return ID; } +-?[0-9]+ { yylval.n = atoi(yytext); return INT; } +-?0x[0-9a-fA-F]+ { yylval.n = strtol(yytext, NULL, 0); return INT; } +[ \t\r\n]* ; +. return yytext[0]; + +%% +/* vim: set sw=4 ts=4 expandtab : */