mirror of https://git.wuffs.org/MWCC
662 lines
18 KiB
C
662 lines
18 KiB
C
#include "compiler/ValueNumbering.h"
|
|
#include "compiler/Alias.h"
|
|
#include "compiler/PCode.h"
|
|
#include "compiler/Registers.h"
|
|
#include "compiler/RegisterInfo.h"
|
|
#include "compiler/CompilerTools.h"
|
|
#include "compiler/CError.h"
|
|
|
|
typedef struct ValueLabel {
|
|
struct ValueLabel *next;
|
|
PCodeArg op;
|
|
} ValueLabel;
|
|
|
|
typedef struct AvailableValue {
|
|
struct AvailableValue *next;
|
|
ValueLabel *labelled;
|
|
PCode *pcode;
|
|
int killedregister;
|
|
int aliasnumber;
|
|
int opnumbers[0];
|
|
} AvailableValue;
|
|
|
|
typedef struct RegValue {
|
|
int number;
|
|
int x4;
|
|
AvailableValue *available;
|
|
} RegValue;
|
|
|
|
typedef struct State {
|
|
void *stackedvalues;
|
|
int valueceiling;
|
|
} State;
|
|
|
|
typedef struct StackedValue {
|
|
struct StackedValue *next;
|
|
PCodeArg op;
|
|
RegValue value;
|
|
Alias *alias;
|
|
PCode *valuepcode;
|
|
} StackedValue;
|
|
|
|
int removedcommonsubexpressions;
|
|
int nextvaluenumber;
|
|
static AvailableValue *opvalue[428];
|
|
static RegValue *regvalue[RegClassMax];
|
|
static StackedValue *stackedvalues;
|
|
static int valueceiling;
|
|
static int moreaggressiveoptimization;
|
|
|
|
static void allocatecsedatastructures(void) {
|
|
char rclass;
|
|
|
|
for (rclass = 0; rclass < RegClassMax; rclass++)
|
|
regvalue[(char) rclass] = oalloc(sizeof(RegValue) * used_virtual_registers[(char) rclass]);
|
|
}
|
|
|
|
static void initializecsedatastructures(void) {
|
|
RegValue *rv;
|
|
char rclass;
|
|
int i;
|
|
|
|
nextvaluenumber = 0;
|
|
|
|
for (i = 0; i < 428; i++)
|
|
opvalue[i] = NULL;
|
|
|
|
for (rclass = 0; rclass < RegClassMax; rclass++) {
|
|
rv = regvalue[(char) rclass];
|
|
for (i = 0; i < used_virtual_registers[(char) rclass]; i++, rv++) {
|
|
rv->number = nextvaluenumber++;
|
|
rv->x4 = 0;
|
|
rv->available = NULL;
|
|
}
|
|
}
|
|
|
|
initialize_alias_values();
|
|
stackedvalues = NULL;
|
|
valueceiling = 0x7FFFFFFF;
|
|
}
|
|
|
|
static void labelvalue(AvailableValue *av, PCodeArg *op) {
|
|
ValueLabel *label = oalloc(sizeof(ValueLabel));
|
|
label->op = *op;
|
|
label->next = av->labelled;
|
|
av->labelled = label;
|
|
}
|
|
|
|
static void unlabelvalue(AvailableValue *av, PCodeArg *op) {
|
|
ValueLabel *labelled;
|
|
ValueLabel **ptr;
|
|
|
|
ptr = &av->labelled;
|
|
while ((labelled = *ptr)) {
|
|
if (labelled->op.data.reg.reg == op->data.reg.reg)
|
|
*ptr = labelled->next;
|
|
else
|
|
ptr = &labelled->next;
|
|
}
|
|
}
|
|
|
|
static void stackregistervalue(PCodeArg *op, RegValue *value) {
|
|
StackedValue *stacked = oalloc(sizeof(StackedValue));
|
|
stacked->next = stackedvalues;
|
|
stackedvalues = stacked;
|
|
|
|
stacked->op = *op;
|
|
stacked->value = *value;
|
|
}
|
|
|
|
static void stackmemoryvalue(Alias *alias) {
|
|
StackedValue *stacked = oalloc(sizeof(StackedValue));
|
|
stacked->next = stackedvalues;
|
|
stackedvalues = stacked;
|
|
|
|
stacked->op.kind = PCOp_MEMORY;
|
|
stacked->alias = alias;
|
|
stacked->value.number = alias->valuenumber;
|
|
stacked->valuepcode = alias->valuepcode;
|
|
}
|
|
|
|
static void unstackvalue(StackedValue *stacked) {
|
|
PCodeArg *op = &stacked->op;
|
|
RegValue *value;
|
|
|
|
if (stacked->op.kind == PCOp_MEMORY) {
|
|
stacked->alias->valuenumber = stacked->value.number;
|
|
stacked->alias->valuepcode = stacked->valuepcode;
|
|
} else {
|
|
value = ®value[op->arg][op->data.reg.reg];
|
|
if (value->available)
|
|
unlabelvalue(value->available, op);
|
|
value->number = stacked->value.number;
|
|
value->x4 = stacked->value.x4;
|
|
value->available = stacked->value.available;
|
|
if (value->available)
|
|
labelvalue(value->available, op);
|
|
}
|
|
}
|
|
|
|
static int samevalue(PCodeArg *op1, PCodeArg *op2) {
|
|
return regvalue[op1->arg][op1->data.reg.reg].number == regvalue[op2->arg][op2->data.reg.reg].number;
|
|
}
|
|
|
|
static int killregister(PCodeArg *op) {
|
|
RegValue *value;
|
|
|
|
value = ®value[op->arg][op->data.reg.reg];
|
|
if (value->number < valueceiling && nextvaluenumber >= valueceiling)
|
|
stackregistervalue(op, value);
|
|
|
|
if (value->available)
|
|
unlabelvalue(value->available, op);
|
|
value->available = NULL;
|
|
value->x4 = 0;
|
|
return value->number = nextvaluenumber++;
|
|
}
|
|
|
|
void killmemory(Alias *alias, PCode *newValue) {
|
|
if (alias->valuenumber < valueceiling && nextvaluenumber >= valueceiling)
|
|
stackmemoryvalue(alias);
|
|
|
|
if (newValue) {
|
|
alias->valuenumber = regvalue[newValue->args[0].arg][newValue->args[0].data.reg.reg].number;
|
|
alias->valuepcode = newValue;
|
|
} else {
|
|
alias->valuenumber = nextvaluenumber++;
|
|
alias->valuepcode = NULL;
|
|
}
|
|
}
|
|
|
|
static void killspecificCSEs(short op) {
|
|
AvailableValue *av;
|
|
ValueLabel *labelled;
|
|
|
|
for (av = opvalue[op]; av; av = av->next) {
|
|
for (labelled = av->labelled; labelled; labelled = labelled->next)
|
|
killregister(&labelled->op);
|
|
}
|
|
}
|
|
|
|
static void killallCSEs(void) {
|
|
AvailableValue *av;
|
|
ValueLabel *labelled;
|
|
int i;
|
|
|
|
for (i = 0; i < 428; i++) {
|
|
for (av = opvalue[i]; av; av = av->next) {
|
|
for (labelled = av->labelled; labelled; labelled = labelled->next)
|
|
killregister(&labelled->op);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void killregisters(PCode *pcode) {
|
|
PCodeArg *op;
|
|
int i;
|
|
|
|
for (i = 0, op = pcode->args; i < pcode->argCount; i++, op++) {
|
|
if (op->kind == PCOp_REGISTER && (op->data.reg.effect & EffectWrite))
|
|
killregister(op);
|
|
}
|
|
}
|
|
|
|
static void copyregister(PCodeArg *src, PCodeArg *dest) {
|
|
RegValue *srcvalue;
|
|
RegValue *destvalue;
|
|
|
|
srcvalue = ®value[src->arg][src->data.reg.reg];
|
|
destvalue = ®value[dest->arg][dest->data.reg.reg];
|
|
|
|
if (destvalue->number < valueceiling && nextvaluenumber >= valueceiling)
|
|
stackregistervalue(dest, destvalue);
|
|
|
|
if (destvalue->available)
|
|
unlabelvalue(destvalue->available, dest);
|
|
destvalue->available = srcvalue->available;
|
|
if (destvalue->available)
|
|
labelvalue(destvalue->available, dest);
|
|
|
|
destvalue->number = srcvalue->number;
|
|
|
|
if (srcvalue->x4 && srcvalue->number == regvalue[src->arg][srcvalue->x4].number)
|
|
destvalue->x4 = srcvalue->x4;
|
|
else
|
|
destvalue->x4 = src->data.reg.reg;
|
|
}
|
|
|
|
static int matchvalues(AvailableValue *av, PCode *match) {
|
|
PCodeArg *avOp;
|
|
PCodeArg *matchOp;
|
|
int i;
|
|
|
|
for (avOp = &av->pcode->args[0], matchOp = &match->args[0], i = 0; i < match->argCount; i++, avOp++, matchOp++) {
|
|
if (i != 0) {
|
|
switch (avOp->kind) {
|
|
case PCOp_REGISTER:
|
|
if (av->opnumbers[i] != regvalue[matchOp->arg][matchOp->data.reg.reg].number)
|
|
return 0;
|
|
break;
|
|
case PCOp_MEMORY:
|
|
if (matchOp->kind != PCOp_MEMORY)
|
|
return 0;
|
|
if (matchOp->data.mem.obj != avOp->data.mem.obj)
|
|
return 0;
|
|
if (matchOp->data.mem.offset != avOp->data.mem.offset)
|
|
return 0;
|
|
if ((unsigned char) matchOp->arg != (unsigned char) avOp->arg)
|
|
return 0;
|
|
break;
|
|
case PCOp_IMMEDIATE:
|
|
if (matchOp->kind != PCOp_IMMEDIATE)
|
|
return 0;
|
|
if (matchOp->data.imm.value != avOp->data.imm.value)
|
|
return 0;
|
|
break;
|
|
case PCOp_LABEL:
|
|
if (matchOp->kind != PCOp_LABEL)
|
|
return 0;
|
|
if (matchOp->data.label.label != avOp->data.label.label)
|
|
return 0;
|
|
break;
|
|
case PCOp_SYSREG:
|
|
CError_FATAL(572);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((match->flags & (fIsRead | fPCodeFlag20000)) && match->alias->valuenumber != av->aliasnumber)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void chooselocation(AvailableValue *av, PCodeArg *op) {
|
|
ValueLabel *labelled;
|
|
PCodeArg *baseop;
|
|
|
|
baseop = &av->pcode->args[0];
|
|
labelled = av->labelled;
|
|
while (labelled) {
|
|
if (labelled->op.data.reg.reg == baseop->data.reg.reg) {
|
|
*op = labelled->op;
|
|
return;
|
|
}
|
|
labelled = labelled->next;
|
|
}
|
|
|
|
*op = av->labelled[0].op;
|
|
}
|
|
|
|
static int findavailablevalue(PCode *pcode, PCodeArg *op) {
|
|
AvailableValue *av;
|
|
PCodeArg tmp1;
|
|
PCodeArg tmp2;
|
|
|
|
for (av = opvalue[pcode->op]; av; av = av->next) {
|
|
if (av->labelled && av->pcode->flags == pcode->flags && av->pcode->argCount == pcode->argCount) {
|
|
if (!matchvalues(av, pcode)) {
|
|
if (!(pcode->flags & fCommutative))
|
|
continue;
|
|
|
|
tmp1 = pcode->args[1];
|
|
pcode->args[1] = pcode->args[2];
|
|
pcode->args[2] = tmp1;
|
|
|
|
if (!matchvalues(av, pcode)) {
|
|
tmp2 = pcode->args[1];
|
|
pcode->args[1] = pcode->args[2];
|
|
pcode->args[2] = tmp2;
|
|
continue;
|
|
}
|
|
}
|
|
chooselocation(av, op);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void addavailablevalue(PCode *pcode) {
|
|
AvailableValue *av;
|
|
PCodeArg *op;
|
|
int i;
|
|
|
|
av = oalloc(sizeof(AvailableValue) + sizeof(int) * pcode->argCount);
|
|
av->labelled = NULL;
|
|
av->pcode = pcode;
|
|
for (i = 0, op = &pcode->args[0]; i < pcode->argCount; i++, op++) {
|
|
if (op->kind == PCOp_REGISTER)
|
|
av->opnumbers[i] = regvalue[op->arg][op->data.reg.reg].number;
|
|
}
|
|
|
|
if (pcode->flags & (fIsRead | fPCodeFlag20000))
|
|
av->aliasnumber = pcode->alias->valuenumber;
|
|
|
|
op = &pcode->args[0];
|
|
av->killedregister = killregister(op);
|
|
labelvalue(av, op);
|
|
regvalue[op->arg][op->data.reg.reg].available = av;
|
|
av->next = opvalue[pcode->op];
|
|
opvalue[pcode->op] = av;
|
|
}
|
|
|
|
static int isCSEop(PCode *pcode) {
|
|
PCodeArg *baseOp;
|
|
PCodeArg *op;
|
|
int i;
|
|
|
|
baseOp = &pcode->args[0];
|
|
|
|
switch (pcode->op) {
|
|
case PC_CMPI:
|
|
case PC_CMP:
|
|
case PC_CMPLI:
|
|
case PC_CMPL:
|
|
case PC_FCMPU:
|
|
case PC_FCMPO:
|
|
if (!moreaggressiveoptimization)
|
|
return 0;
|
|
break;
|
|
case PC_LI:
|
|
case PC_LIS:
|
|
if (!moreaggressiveoptimization)
|
|
return 0;
|
|
if (pcode->args[0].data.reg.reg < first_fe_temporary_register[RegClass_GPR] || pcode->args[0].data.reg.reg > last_temporary_register[RegClass_GPR])
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
if (PCODE_FLAG_SET_F(pcode) & (fIsVolatile | fSideEffects | fOverflow | fSetsCarry | fRecordBit))
|
|
return 0;
|
|
|
|
for (i = 0, op = &pcode->args[0]; i < pcode->argCount; i++, op++) {
|
|
if (op != baseOp &&
|
|
op->kind == baseOp->kind &&
|
|
op->arg == baseOp->arg &&
|
|
op->data.reg.reg == baseOp->data.reg.reg)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int isCSEload(PCode *pcode) {
|
|
PCodeArg *op;
|
|
int i;
|
|
int count;
|
|
|
|
count = 0;
|
|
for (i = 0, op = &pcode->args[0]; i < pcode->argCount; i++, op++) {
|
|
if (op->kind == PCOp_REGISTER && (op->data.reg.effect & EffectWrite))
|
|
count++;
|
|
}
|
|
|
|
return count == 1;
|
|
}
|
|
|
|
static void registercopy(PCode *pcode) {
|
|
PCodeArg *op1;
|
|
PCodeArg *op2;
|
|
|
|
op1 = &pcode->args[0];
|
|
op2 = &pcode->args[1];
|
|
if (samevalue(op2, op1))
|
|
deletepcode(pcode);
|
|
else
|
|
copyregister(op2, op1);
|
|
}
|
|
|
|
static PCode *recentlystored(Alias *alias, PCodeArg *op) {
|
|
PCode *pc;
|
|
if ((pc = alias->valuepcode) && alias->valuenumber == regvalue[pc->args[0].arg][pc->args[0].data.reg.reg].number) {
|
|
*op = pc->args[0];
|
|
return pc;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void simpleload(PCode *pcode) {
|
|
PCodeArg *origOp;
|
|
PCodeArg op;
|
|
PCode *rs;
|
|
|
|
origOp = &pcode->args[0];
|
|
if ((pcode->flags & fIsVolatile) || !isCSEload(pcode)) {
|
|
killregisters(pcode);
|
|
return;
|
|
}
|
|
|
|
if (findavailablevalue(pcode, &op)) {
|
|
if (!samevalue(origOp, &op)) {
|
|
insertpcodebefore(pcode, makecopyinstruction(&op, origOp));
|
|
copyregister(&op, origOp);
|
|
}
|
|
deletepcode(pcode);
|
|
removedcommonsubexpressions = 1;
|
|
} else if ((rs = recentlystored(pcode->alias, &op)) && can_reuse_stored_value(rs, pcode)) {
|
|
if (!samevalue(origOp, &op)) {
|
|
insertpcodebefore(pcode, makecopyinstruction(&op, origOp));
|
|
copyregister(&op, origOp);
|
|
}
|
|
deletepcode(pcode);
|
|
removedcommonsubexpressions = 1;
|
|
} else {
|
|
addavailablevalue(pcode);
|
|
}
|
|
}
|
|
|
|
static void simplestore(PCode *pcode) {
|
|
update_alias_value(pcode->alias, pcode);
|
|
killregisters(pcode);
|
|
}
|
|
|
|
static void pointerload(PCode *pcode) {
|
|
PCodeArg *op;
|
|
PCodeArg buf;
|
|
|
|
op = &pcode->args[0];
|
|
|
|
if ((pcode->flags & fIsVolatile) || !isCSEload(pcode)) {
|
|
killregisters(pcode);
|
|
return;
|
|
}
|
|
|
|
if (findavailablevalue(pcode, &buf)) {
|
|
if (!samevalue(op, &buf)) {
|
|
insertpcodebefore(pcode, makecopyinstruction(&buf, op));
|
|
copyregister(&buf, op);
|
|
}
|
|
deletepcode(pcode);
|
|
removedcommonsubexpressions = 1;
|
|
} else {
|
|
addavailablevalue(pcode);
|
|
}
|
|
}
|
|
|
|
static void pointerstore(PCode *pcode) {
|
|
update_alias_value(pcode->alias, NULL);
|
|
killregisters(pcode);
|
|
}
|
|
|
|
static void arithmeticop(PCode *pcode) {
|
|
PCodeArg *op;
|
|
PCodeArg buf;
|
|
|
|
op = &pcode->args[0];
|
|
|
|
if (findavailablevalue(pcode, &buf)) {
|
|
if (!samevalue(op, &buf)) {
|
|
insertpcodebefore(pcode, makecopyinstruction(&buf, op));
|
|
copyregister(&buf, op);
|
|
}
|
|
deletepcode(pcode);
|
|
removedcommonsubexpressions = 1;
|
|
} else {
|
|
addavailablevalue(pcode);
|
|
}
|
|
}
|
|
|
|
static void functioncall(PCode *pcode) {
|
|
killregisters(pcode);
|
|
if (coloring) {
|
|
update_all_alias_values();
|
|
killallCSEs();
|
|
} else {
|
|
update_alias_value(pcode->alias, NULL);
|
|
}
|
|
}
|
|
|
|
static void operatefrommemory(PCode *pcode) {
|
|
CError_FATAL(980);
|
|
}
|
|
|
|
static void operatetomemory(PCode *pcode) {
|
|
CError_FATAL(1011);
|
|
}
|
|
|
|
static void propagatecopiesto(PCode *pcode) {
|
|
PCodeArg *op;
|
|
int i;
|
|
|
|
for (i = 0, op = &pcode->args[0]; i < pcode->argCount; i++, op++) {
|
|
if (
|
|
op->kind == PCOp_REGISTER &&
|
|
(op->data.reg.effect & (EffectRead | EffectWrite | Effect8)) == EffectRead &&
|
|
op->data.reg.reg >= n_real_registers[op->arg] &&
|
|
regvalue[op->arg][op->data.reg.reg].x4 &&
|
|
regvalue[op->arg][op->data.reg.reg].x4 >= n_real_registers[op->arg] &&
|
|
regvalue[op->arg][op->data.reg.reg].number == regvalue[op->arg][regvalue[op->arg][op->data.reg.reg].x4].number
|
|
) {
|
|
op->data.reg.reg = regvalue[op->arg][op->data.reg.reg].x4;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void removecsesfrombasicblock(PCodeBlock *block) {
|
|
PCode *pcode;
|
|
PCode *next;
|
|
|
|
for (pcode = block->firstPCode; pcode; pcode = next) {
|
|
next = pcode->nextPCode;
|
|
propagatecopiesto(pcode);
|
|
if (pcode->flags & fIsMove) {
|
|
registercopy(pcode);
|
|
} else if ((pcode->flags & fIsCall) && (pcode->flags & (fLink | fSideEffects))) {
|
|
functioncall(pcode);
|
|
} else if (pcode->flags & fIsRead) {
|
|
if (pcode->flags & fIsPtrOp)
|
|
pointerload(pcode);
|
|
else
|
|
simpleload(pcode);
|
|
} else if (pcode->flags & fIsWrite) {
|
|
if (pcode->flags & fIsPtrOp)
|
|
pointerstore(pcode);
|
|
else
|
|
simplestore(pcode);
|
|
} else if (pcode->flags & fPCodeFlag20000) {
|
|
operatefrommemory(pcode);
|
|
} else if (pcode->flags & fPCodeFlag40000) {
|
|
operatetomemory(pcode);
|
|
} else if ((pcode->flags & fIsCSE) && isCSEop(pcode)) {
|
|
arithmeticop(pcode);
|
|
} else {
|
|
killregisters(pcode);
|
|
}
|
|
}
|
|
|
|
block->flags |= fVisited;
|
|
}
|
|
|
|
static void getvaluestate(State *state) {
|
|
state->stackedvalues = stackedvalues;
|
|
state->valueceiling = valueceiling;
|
|
}
|
|
|
|
static void setvaluestate(State *state) {
|
|
stackedvalues = state->stackedvalues;
|
|
valueceiling = state->valueceiling;
|
|
}
|
|
|
|
static void forkvaluestate(int number) {
|
|
stackedvalues = NULL;
|
|
valueceiling = number;
|
|
}
|
|
|
|
static void regressvaluestate(void) {
|
|
AvailableValue *av;
|
|
AvailableValue **ptr;
|
|
int i;
|
|
StackedValue *stacked;
|
|
|
|
for (i = 0; i < 428; i++) {
|
|
ptr = &opvalue[i];
|
|
while ((av = *ptr)) {
|
|
if (av->killedregister >= valueceiling)
|
|
*ptr = av->next;
|
|
else
|
|
ptr = &av->next;
|
|
}
|
|
}
|
|
|
|
for (stacked = stackedvalues; stacked; stacked = stacked->next)
|
|
unstackvalue(stacked);
|
|
}
|
|
|
|
static void removecsesfromextendedbasicblock(PCodeBlock *block) {
|
|
PCLink *succ;
|
|
int counter;
|
|
State state;
|
|
|
|
removecsesfrombasicblock(block);
|
|
while (block->successors &&
|
|
!block->successors->nextLink &&
|
|
block->successors->block->predecessors &&
|
|
!block->successors->block->predecessors->nextLink) {
|
|
block = block->successors->block;
|
|
removecsesfrombasicblock(block);
|
|
}
|
|
|
|
counter = 0;
|
|
for (succ = block->successors; succ; succ = succ->nextLink) {
|
|
if (!(succ->block->flags & fVisited) && succ->block->predecessors && !succ->block->predecessors->nextLink)
|
|
counter++;
|
|
}
|
|
|
|
if (counter) {
|
|
getvaluestate(&state);
|
|
forkvaluestate(nextvaluenumber);
|
|
for (succ = block->successors; succ; succ = succ->nextLink) {
|
|
if (!(succ->block->flags & fVisited) && succ->block->predecessors && !succ->block->predecessors->nextLink) {
|
|
removecsesfromextendedbasicblock(succ->block);
|
|
regressvaluestate();
|
|
}
|
|
}
|
|
setvaluestate(&state);
|
|
}
|
|
}
|
|
|
|
void removecommonsubexpressions(Object *proc, int flag) {
|
|
PCodeBlock *block;
|
|
|
|
moreaggressiveoptimization = flag;
|
|
removedcommonsubexpressions = 0;
|
|
gather_alias_info();
|
|
allocatecsedatastructures();
|
|
|
|
for (block = pcbasicblocks; block; block = block->nextBlock)
|
|
block->flags &= ~fVisited;
|
|
|
|
for (block = pcbasicblocks; block; block = block->nextBlock) {
|
|
if (!(block->flags & fVisited)) {
|
|
initializecsedatastructures();
|
|
removecsesfromextendedbasicblock(block);
|
|
}
|
|
}
|
|
|
|
freeoheap();
|
|
}
|
|
|