The approach I was taking to csa and csb turns out not to work --- critical

edge splitting can cause new basic blocks to be added to the graph, but while
the graph itself gets properly rewritten the descriptor tables can't be updated
to take these into account, so they end up pointing at the wrong blocks. This
causes really hard-to-debug problems.

The new approach is to parse the descriptor blocks and then generate a
comparison chain. Brute force, but much easier for the compiler to reason
about.
This commit is contained in:
David Given 2018-09-20 00:12:03 +02:00
parent ac921080b7
commit ac856f3b09
3 changed files with 207 additions and 56 deletions

View file

@ -59,7 +59,7 @@ 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);
fprintf(outputfile, "\t.data%ld ", size);
writehex(data, size);
fprintf(outputfile, "\n");
}
@ -73,7 +73,7 @@ void data_float(const char* data, size_t size, bool is_ro)
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
assert((size == 4) || (size == 8));
fprintf(outputfile, "\t.dataf%d %s\n", size, data);
fprintf(outputfile, "\t.dataf%ld %s\n", size, data);
}
static bool istext(c)
@ -130,7 +130,7 @@ void data_block(const uint8_t* data, size_t size, bool is_ro)
void data_offset(const char* label, arith offset, bool is_ro)
{
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
fprintf(outputfile, "\t.data%d %s+%lld\n",
fprintf(outputfile, "\t.data%d %s+%ld\n",
EM_pointersize, platform_label(label), offset);
}
@ -140,7 +140,7 @@ void data_bss(arith size, int init)
fatal("non-zero-initialised bss not supported");
emit_header(SECTION_BSS);
fprintf(outputfile, "\t.space %lld\n", size);
fprintf(outputfile, "\t.space %ld\n", size);
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -181,6 +181,36 @@ static void queue_ilabel(arith label)
change_basicblock(bb_get(ilabel_to_str(label)));
}
/* This is really hacky; to handle basic block flow
* descriptor blocks, we need to be able to identify
* them, read them and parse them. So we create
* can exit to. So we create fake bb objects for each
* block, purely to track this, and copy ints and labels
* into them.
*/
static void data_block_int(arith value)
{
if (data_bb)
{
struct em* em = new_insn(op_loc);
em->paramtype = PARAM_IVALUE;
em->u.ivalue = value;
array_append(&data_bb->ems, em);
}
}
static void data_block_label(const char* label)
{
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);
}
}
static void parse_pseu(void)
{
switch (em.em_opcode)
@ -227,6 +257,7 @@ static void parse_pseu(void)
{
arith val = atol(em.em_string);
data_int(val, em.em_size, ro);
data_block_int(val);
break;
}
@ -237,12 +268,16 @@ static void parse_pseu(void)
}
case str_ptyp:
data_block(strdup(em.em_string), em.em_size, ro);
data_block((const uint8_t*) strdup(em.em_string), em.em_size, ro);
break;
case cst_ptyp:
data_int(em.em_cst, EM_wordsize, ro);
{
arith value = em.em_cst;
data_int(value, EM_wordsize, ro);
data_block_int(value);
break;
}
case nof_ptyp:
data_offset(dlabel_to_str(em.em_dlb), em.em_off, ro);
@ -256,22 +291,8 @@ static void parse_pseu(void)
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);
data_block_label(label);
break;
}
@ -384,9 +405,12 @@ static void create_data_label(const char* label)
data_label(label);
if (current_proc)
{
/* Create the fake bb used to track values inside this data block
* (as it's a chance it's a jump table which we'll need to process
* later).
*/
data_bb = bb_get(label);
data_bb->is_fake = true;
array_append(&current_proc->blocks, data_bb);
}
}

View file

@ -5,10 +5,20 @@ static struct basicblock* current_bb;
static int stackptr;
static struct ir* stack[64];
struct jumptable
{
struct basicblock* defaulttarget;
IMAPOF(struct basicblock) targets;
};
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 parse_csa(struct basicblock* data_bb, struct jumptable* table);
static void parse_csb(struct basicblock* data_bb, struct jumptable* table);
static void emit_jumptable(struct ir* targetvalue, struct jumptable* table);
static void reset_stack(void)
{
stackptr = 0;
@ -739,27 +749,6 @@ static void rotate(int opcode, int size, int irop, int irop_reverse)
}
}
static struct ir* extract_block_refs(struct basicblock* bb)
{
struct ir* outir = NULL;
int i;
for (i=0; i<bb->ems.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(
@ -1339,25 +1328,34 @@ static void insn_ivalue(int opcode, arith value)
}
case op_csa:
case op_csb:
{
const char* helper = aprintf(".%s",
(opcode == op_csa) ? "csa" : "csb");
struct ir* descriptor = pop(EM_pointersize);
struct ir* targetvalue = appendir(pop(EM_pointersize));
struct jumptable jumptable = {};
int i;
if (descriptor->opcode != IR_LABEL)
fatal("csa/csb are only supported if they refer "
fatal("csa is only supported if it refers "
"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))
)
);
parse_csa(bb_get(descriptor->u.lvalue), &jumptable);
emit_jumptable(targetvalue, &jumptable);
break;
}
case op_csb:
{
struct ir* descriptor = pop(EM_pointersize);
struct ir* targetvalue = appendir(pop(EM_pointersize));
struct jumptable jumptable = {};
int i;
if (descriptor->opcode != IR_LABEL)
fatal("csb is only supported if it refers "
"directly to a descriptor block");
parse_csb(bb_get(descriptor->u.lvalue), &jumptable);
emit_jumptable(targetvalue, &jumptable);
break;
}
@ -1762,4 +1760,133 @@ void tb_procedure(void)
generate_tree(current_proc->blocks.item[i]);
}
static void parse_csa(struct basicblock* data_bb, struct jumptable* table)
{
struct em* em;
int lowerbound;
int count;
int i;
assert(data_bb->ems.count >= 3);
/* Default target */
em = data_bb->ems.item[0];
assert(em->opcode == op_bra);
assert(em->paramtype == PARAM_BVALUE);
table->defaulttarget = em->u.bvalue.left;
/* Lower bound */
em = data_bb->ems.item[1];
assert(em->opcode == op_loc);
assert(em->paramtype == PARAM_IVALUE);
lowerbound = em->u.ivalue;
/* Count */
em = data_bb->ems.item[2];
assert(em->opcode == op_loc);
assert(em->paramtype == PARAM_IVALUE);
count = em->u.ivalue + 1; /* value in descriptor is inclusive */
assert(data_bb->ems.count >= (count + 3));
/* Now, each target in turn. */
for (i=0; i<count; i++)
{
struct basicblock* target;
em = data_bb->ems.item[3 + i];
assert(em->opcode == op_bra);
assert(em->paramtype == PARAM_BVALUE);
target = em->u.bvalue.left;
imap_put(&table->targets, lowerbound+i, target);
}
}
static void parse_csb(struct basicblock* data_bb, struct jumptable* table)
{
struct em* em;
int count;
int i;
assert(data_bb->ems.count >= 2);
/* Default target */
em = data_bb->ems.item[0];
assert(em->opcode == op_bra);
assert(em->paramtype == PARAM_BVALUE);
table->defaulttarget = em->u.bvalue.left;
/* Number of targets */
em = data_bb->ems.item[1];
assert(em->opcode == op_loc);
assert(em->paramtype == PARAM_IVALUE);
count = em->u.ivalue;
assert(data_bb->ems.count >= (count*2 + 2));
/* Now, each target in turn. */
for (i=0; i<count; i++)
{
int value;
struct basicblock* target;
em = data_bb->ems.item[2 + i*2];
assert(em->opcode == op_loc);
assert(em->paramtype == PARAM_IVALUE);
value = em->u.ivalue;
em = data_bb->ems.item[3 + i*2];
assert(em->opcode == op_bra);
assert(em->paramtype == PARAM_BVALUE);
target = em->u.bvalue.left;
imap_put(&table->targets, value, target);
}
}
static void emit_jumptable(struct ir* targetvalue, struct jumptable* jumptable)
{
int i;
materialise_stack();
for (i=0; i<jumptable->targets.count; i++)
{
int value = jumptable->targets.item[i].left;
struct basicblock* target = jumptable->targets.item[i].right;
struct basicblock* nextblock = bb_get(NULL);
array_append(&current_proc->blocks, nextblock);
appendir(
new_ir2(
IR_CJUMPEQ, 0,
new_ir2(
IR_COMPARESI, EM_wordsize,
targetvalue,
new_wordir(value)
),
new_ir2(
IR_PAIR, 0,
new_bbir(target),
new_bbir(nextblock)
)
)
);
current_bb = nextblock;
}
appendir(
new_ir1(
IR_JUMP, 0,
new_bbir(jumptable->defaulttarget)
)
);
}
/* vim: set sw=4 ts=4 expandtab : */