Replaced the block splicer with a trivial block eliminator (which rewrites

jumps to blocks which contain only a jump). Don't bother storing the bb graph
in the ir nodes; we can find it on demand by walking the tree instead ---
slower, but much easier to understand and more robust. Added a terrible map
library.
This commit is contained in:
David Given 2016-09-23 23:59:15 +02:00
parent f8bbf9e87d
commit ed67d427c9
16 changed files with 276 additions and 171 deletions

View file

@ -6,6 +6,11 @@
int NAME##_count; \
int NAME##_max
#define STATICARRAY(TYPE, NAME) \
static TYPE** NAME; \
static int NAME##_count; \
static int NAME##_max
#define APPEND(ARRAY, VALUE) \
array_append((void***) &ARRAY, &ARRAY##_count, &ARRAY##_max, VALUE)

View file

@ -13,24 +13,8 @@ static void print_blocks(char k, struct procedure* proc)
tracef(k, "%c:\n", k);
tracef(k, "%c: BLOCK: %s\n", k, bb->name);
tracef(k, "%c: from:", k);
for (int j=0; j<bb->inblocks_count; j++)
{
struct basicblock* obb = bb->inblocks[j];
tracef(k, " %s", obb->name);
}
tracef(k, "\n");
tracef(k, "%c: to:", k);
for (int j=0; j<bb->outblocks_count; j++)
{
struct basicblock* obb = bb->outblocks[j];
tracef(k, " %s", obb->name);
}
tracef(k, "\n");
for (int j=0; j<bb->irs_count; j++)
ir_print(k, bb->irs[j]);
}
}
@ -40,9 +24,9 @@ void compile(struct procedure* proc)
print_blocks('1', proc);
pass_eliminate_trivial_blocks(proc);
pass_remove_dead_blocks(proc);
pass_convert_stack_ops(proc);
pass_splice_adjacent_blocks(proc);
print_blocks('2', proc);
}

View file

@ -66,21 +66,21 @@ struct ir* new_localir(int offset)
return ir;
}
struct ir* ir_find(struct ir* ir, int opcode)
struct ir* ir_walk(struct ir* ir, ir_walker_t* cb, void* user)
{
if (ir->opcode == opcode)
if (cb(ir, user))
return ir;
if (ir->left && !ir->left->is_sequence)
{
struct ir* irr = ir_find(ir->left, opcode);
struct ir* irr = ir_walk(ir->left, cb, user);
if (irr)
return irr;
}
if (ir->right && !ir->right->is_sequence)
{
struct ir* irr = ir_find(ir->right, opcode);
struct ir* irr = ir_walk(ir->right, cb, user);
if (irr)
return irr;
}
@ -88,6 +88,19 @@ struct ir* ir_find(struct ir* ir, int opcode)
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_names[ir->opcode]);
@ -110,18 +123,6 @@ static void print_expr(char k, const struct ir* ir)
tracef(k, "%s", ir->u.bvalue->name);
break;
case IR_PHI:
{
int i;
for (i=0; i<ir->u.phivalue.imports_count; i++)
{
if (i > 0)
tracef(k, ", ");
tracef(k, "$%d", ir->u.phivalue.imports[i]->id);
}
}
default:
if (ir->left)
{

View file

@ -1,6 +1,8 @@
#ifndef IR_H
#define IR_H
#include "ircodes.h"
enum
{
IRR_LB = -1,
@ -20,7 +22,7 @@ enum
struct ir
{
int id;
int opcode;
enum ir_opcode opcode;
int size;
struct ir* left;
struct ir* right;
@ -30,10 +32,6 @@ struct ir
int rvalue;
const char* lvalue;
struct basicblock* bvalue;
struct
{
ARRAY(struct ir, imports);
} phivalue;
} u;
bool is_sequence : 1;
};
@ -53,11 +51,11 @@ 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);
#include "ircodes.h"
#endif

View file

@ -6,7 +6,7 @@ source=$3
awk -f - $in >$header << "EOF"
BEGIN {
print "enum {"
print "enum ir_opcode {"
}
/^[^#]+/ {

View file

@ -39,7 +39,7 @@ bool tracing(char k)
{
case 'E': return false;
case '0': return false;
case '1': return true;
case '1': return false;
case '2': return true;
default: return true;
}

58
mach/proto/mcg/map.c Normal file
View file

@ -0,0 +1,58 @@
#include "mcg.h"
static void extend(struct map_node** map, int* count, int* max)
{
if (*count == *max)
{
int newmax = (*max == 0) ? 8 : (*max * 2);
struct map_node* newmap = realloc(*map, newmax * sizeof(struct map_node));
if (!newmap)
fatal("memory allocation failure");
*max = newmax;
*map = newmap;
}
}
void map_set(struct map_node** map, int* count, int* max, void* left, void* right)
{
int i;
struct map_node* node;
for (i=0; i<*count; i++)
{
node = &(*map)[i];
if (node->left == left)
{
node->right = right;
return;
}
}
extend(map, count, max);
node = &(*map)[*count];
node->left = left;
node->right = right;
(*count)++;
}
void map_add(struct map_node** map, int* count, int* max, void* left, void* right)
{
int i;
struct map_node* node;
for (i=0; i<*count; i++)
{
node = &(*map)[i];
if ((node->left == left) && (node->right == right))
return;
}
extend(map, count, max);
node = &(*map)[*count];
node->left = left;
node->right = right;
(*count)++;
}
/* vim: set sw=4 ts=4 expandtab : */

26
mach/proto/mcg/map.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef MAP_H
#define MAP_H
struct map_node
{
void* left;
void* right;
};
#define _MAP(MODIFIER, NAME) \
MODIFIER struct map_node* NAME; \
MODIFIER int NAME##_count; \
MODIFIER int NAME##_max
#define MAP(NAME) _MAP(, NAME)
#define STATICMAP(NAME) _MAP(static, NAME)
#define MAP_SET(MAP, LEFT, RIGHT) \
map_set(&MAP, &MAP##_count, &MAP##_max, LEFT, RIGHT)
extern void map_set(struct map_node** map, int* count, int* max, void* left, void* right);
extern void map_add(struct map_node** map, int* count, int* max, void* left, void* right);
#endif

View file

@ -17,6 +17,7 @@
#include "em_flag.h"
#include "em_ptyp.h"
#include "array.h"
#include "map.h"
#include "ir.h"
extern char em_pseu[][4];
@ -78,10 +79,7 @@ struct basicblock
const char* name;
ARRAY(struct insn, insns);
ARRAY(struct ir, irs);
ARRAY(struct basicblock, inblocks);
ARRAY(struct basicblock, outblocks);
ARRAY(struct ir, inparams);
ARRAY(struct ir, outparams);
bool is_fake : 1;
bool is_root : 1;
bool is_terminated : 1;
};
@ -116,7 +114,7 @@ extern void tb_regvar(arith offset, int size, int type, int priority);
extern void pass_convert_stack_ops(struct procedure* proc);
extern void pass_remove_dead_blocks(struct procedure* proc);
extern void pass_splice_adjacent_blocks(struct procedure* proc);
extern void pass_eliminate_trivial_blocks(struct procedure* proc);
extern void compile(struct procedure* proc);

View file

@ -128,14 +128,6 @@ static void queue_insn_block(int opcode, struct basicblock* left, struct basicbl
insn->u.bvalue.right = right;
APPEND(code_bb->insns, insn);
APPENDU(code_bb->outblocks, left);
APPENDU(left->inblocks, code_bb);
if (right)
{
APPENDU(code_bb->outblocks, right);
APPENDU(right->inblocks, code_bb);
}
terminate_block();
}
@ -256,7 +248,12 @@ static void parse_pseu(void)
*/
if (data_bb)
APPENDU(data_bb->outblocks, bb_get(label));
{
struct insn* insn = new_insn(op_bra);
insn->paramtype = PARAM_BVALUE;
insn->u.bvalue.left = bb_get(label);
APPEND(data_bb->insns, insn);
}
data_offset(label, 0, ro);
break;
@ -361,6 +358,7 @@ void parse_em(void)
const char* label = dlabel_to_str(insn.em_dlb);
data_label(label);
data_bb = bb_get(label);
data_bb->is_fake = true;
break;
}

View file

@ -1,5 +1,9 @@
#include "mcg.h"
STATICMAP(graph);
STATICARRAY(struct ir, pops);
STATICARRAY(struct ir, pushes);
static struct ir* get_last_push(struct basicblock* bb)
{
int i;
@ -37,61 +41,93 @@ static struct ir* get_first_pop(struct basicblock* bb)
return NULL;
}
static void convert_block(struct basicblock* bb)
static bool collect_outputs_cb(struct ir* ir, void* user)
{
struct basicblock* caller = user;
if (ir->opcode == IR_BLOCK)
MAP_SET(graph, caller, ir->u.bvalue);
return false;
}
static void make_bb_graph(struct procedure* proc)
{
int i, j;
graph_count = 0;
for (i=0; i<proc->blocks_count; i++)
{
struct basicblock* bb = proc->blocks[i];
for (j=0; j<bb->irs_count; j++)
ir_walk(bb->irs[j], collect_outputs_cb, bb);
}
}
static void convert_block(struct procedure* proc, 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* success block of this one starts with a pop
/* Abort unless *every* successor block of this one starts with a pop
* of the same size... */
for (i=0; i<bb->outblocks_count; i++)
for (i=0; i<graph_count; i++)
{
struct basicblock* outbb = bb->outblocks[i];
ir = get_first_pop(outbb);
if (!ir || (ir->size != lastpush->size))
return;
/* Also abort unless *every* predecessor block of the one we've
* just found *also* ends in a push of the same size. */
for (j=0; j<outbb->inblocks_count; j++)
if (graph[i].left == bb)
{
struct basicblock* inbb = outbb->inblocks[i];
struct basicblock* outbb = graph[i].right;
ir = get_last_push(inbb);
ir = get_first_pop(outbb);
if (!ir || (ir->size != lastpush->size))
return;
APPENDU(pops, ir);
/* Also abort unless *every* predecessor block of the one we've
* just found *also* ends in a push of the same size. */
for (j=0; j<graph_count; j++)
{
if (graph[j].right == outbb)
{
struct basicblock* inbb = graph[j].left;
ir = get_last_push(inbb);
if (!ir || (ir->size != lastpush->size))
return;
APPENDU(pushes, ir);
}
}
}
}
/* Okay, now we can wire them all up. */
for (i=0; i<bb->outblocks_count; i++)
for (i=0; i<pushes_count; i++)
{
struct basicblock* outbb = bb->outblocks[i];
struct ir* phi = get_first_pop(outbb);
phi->opcode = IR_PHI;
struct ir* ir = pushes[i];
assert(ir->is_sequence);
*ir = *ir->left;
ir->is_sequence = true;
}
/* Also abort unless *every* predecessor block of the one we've
* just found *also* ends in a push of the same size. */
for (i=0; i<pops_count; i++)
{
struct ir* ir = pops[i];
struct ir* pushir = pushes[0];
struct ir* phi = new_ir1(IR_PHI, ir->size, pushir);
for (j=0; j<outbb->inblocks_count; j++)
{
struct basicblock* inbb = outbb->inblocks[j];
for (j=1; j<pushes_count; j++)
phi = new_ir2(IR_PHI, ir->size, phi, pushes[j]);
ir = get_last_push(inbb);
*ir = *ir->left;
ir->is_sequence = true;
APPEND(phi->u.phivalue.imports, ir);
}
phi->is_sequence = ir->is_sequence;
*ir = *phi;
}
}
}
@ -100,8 +136,10 @@ void pass_convert_stack_ops(struct procedure* proc)
{
int i;
make_bb_graph(proc);
for (i=0; i<proc->blocks_count; i++)
convert_block(proc->blocks[i]);
convert_block(proc, proc->blocks[i]);
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -1,27 +0,0 @@
#include "mcg.h"
void pass_remove_dead_blocks(struct procedure* proc)
{
int i, j;
again:
/* Starts at 1 because we don't want to remove the root block! */
for (i=1; i<proc->blocks_count; i++)
{
struct basicblock* bb = proc->blocks[i];
if (bb->inblocks_count == 0)
{
/* Nobody uses this block; disconnect it from its output
* blocks. */
for (j=0; j<bb->outblocks_count; j++)
REMOVE(bb->outblocks[j]->inblocks, bb);
REMOVE(proc->blocks, bb);
goto again;
}
}
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -0,0 +1,41 @@
#include "mcg.h"
static bool rewrite_jumps_cb(struct ir* ir, void* user)
{
if (ir->opcode == IR_BLOCK)
{
struct basicblock* bb = ir->u.bvalue;
if ((bb->irs_count > 0)
&& (bb->irs[0]->opcode == IR_JUMP)
&& (bb->irs[0]->left->opcode == IR_BLOCK))
{
ir->u.bvalue = bb->irs[0]->left->u.bvalue;
}
}
return false;
}
static void rewrite_jumps(struct basicblock* bb)
{
int i;
for (i=0; i<bb->irs_count; i++)
{
struct ir* ir = bb->irs[i];
ir_walk(ir, rewrite_jumps_cb, NULL);
}
}
void pass_eliminate_trivial_blocks(struct procedure* proc)
{
int i;
for (i=0; i<proc->blocks_count; i++)
{
struct basicblock* bb = proc->blocks[i];
rewrite_jumps(bb);
}
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -0,0 +1,40 @@
#include "mcg.h"
STATICARRAY(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 (!CONTAINS(used, bb))
{
APPENDU(used, bb);
for (i=0; i<bb->irs_count; i++)
ir_walk(bb->irs[i], walk_blocks_cb, NULL);
}
}
void pass_remove_dead_blocks(struct procedure* proc)
{
int i, j;
used_count = 0;
walk_blocks(proc->blocks[0]);
proc->blocks_count = 0;
for (i=0; i<used_count; i++)
APPEND(proc->blocks, used[i]);
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -1,47 +0,0 @@
#include "mcg.h"
void pass_splice_adjacent_blocks(struct procedure* proc)
{
int i, j;
again:
for (i=0; i<proc->blocks_count; i++)
{
struct basicblock* bb = proc->blocks[i];
if (bb->outblocks_count == 1)
{
struct basicblock* outbb = bb->outblocks[0];
if (outbb->inblocks_count == 1)
{
struct ir* lastir = bb->irs[bb->irs_count-1];
if ((lastir->opcode == IR_JUMP)
&& (lastir->left->opcode == IR_BLOCK)
&& (lastir->left->u.bvalue == outbb))
{
/* Remove jump instruction. */
bb->irs_count--;
REMOVE(bb->outblocks, outbb);
REMOVE(outbb->inblocks, bb);
for (j=0; j<outbb->irs_count; j++)
APPEND(bb->irs, outbb->irs[j]);
for (j=0; j<outbb->outblocks_count; j++)
{
APPENDU(bb->outblocks, outbb->outblocks[j]);
APPENDU(outbb->outblocks[j]->inblocks, bb);
REMOVE(outbb->outblocks[j]->inblocks, outbb);
}
REMOVE(proc->blocks, outbb);
goto again;
}
}
}
}
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -531,8 +531,6 @@ static void insn_ivalue(int opcode, arith value)
case op_csa:
case op_csb:
{
struct basicblock* data_bb;
int i;
const char* helper = aprintf(".%s%d",
(opcode == op_csa) ? "csa" : "csb",
value);
@ -542,16 +540,10 @@ static void insn_ivalue(int opcode, arith value)
fatal("csa/csb are only supported if they refer "
"directly to a descriptor block");
/* Splice the outgoing bbs in the data block into our own. */
/* Turn the label reference into a block. */
data_bb = bb_get(descriptor->u.lvalue);
for (i=0; i<data_bb->outblocks_count; i++)
{
struct basicblock* bb = data_bb->outblocks[i];
printf("\t; may jump to %s\n", bb->name);
APPENDU(current_bb->outblocks, bb);
APPENDU(bb->inblocks, current_bb);
}
descriptor->opcode = IR_BLOCK;
descriptor->u.bvalue = bb_get(descriptor->u.lvalue);
push(descriptor);
materialise_stack();