MWCC/compiler_and_linker/unsorted/ConstantPropagation.c

644 lines
25 KiB
C

#include "compiler/ConstantPropagation.h"
#include "compiler/Alias.h"
#include "compiler/BitVectors.h"
#include "compiler/CompilerTools.h"
#include "compiler/PCode.h"
#include "compiler/PCodeInfo.h"
#include "compiler/RegisterInfo.h"
#include "compiler/StackFrame.h"
#include "compiler/UseDefChains.h"
#include "compiler/objects.h"
int propagatedconstants;
static int changed;
static PCode **defininginstruction;
static PCode **vrdefininginstruction;
static void computedefininginstructions(PCodeBlock *block) {
RegUseOrDef *list;
PCode *instr;
int i;
for (i = 0; i < used_virtual_registers[RegClass_GPR]; i++) {
instr = NULL;
for (list = reg_Defs[RegClass_GPR][i]; list; list = list->next) {
if (bitvectorgetbit(list->id, usedefinfo[block->blockIndex].defvec8)) {
if (instr == NULL) {
instr = Defs[list->id].pcode;
} else {
instr = NULL;
break;
}
}
}
defininginstruction[i] = instr;
}
for (i = 0; i < used_virtual_registers[RegClass_VR]; i++) {
instr = NULL;
for (list = reg_Defs[RegClass_VR][i]; list; list = list->next) {
if (bitvectorgetbit(list->id, usedefinfo[block->blockIndex].defvec8)) {
if (instr == NULL) {
instr = Defs[list->id].pcode;
} else {
instr = NULL;
break;
}
}
}
vrdefininginstruction[i] = instr;
}
}
static PCode *isstackoperand(PCodeArg *op, SInt16 *resultValue, SInt16 addend) {
PCode *instr;
if ((instr = defininginstruction[op->data.reg.reg]) && instr->op == PC_ADDI) {
if (
instr->args[2].kind == PCOp_MEMORY &&
(instr->args[1].data.reg.reg == _FP_ || instr->args[1].data.reg.reg == _CALLER_SP_) &&
instr->args[2].data.mem.obj->datatype == DLOCAL
)
{
if (can_add_displ_to_local(instr->args[2].data.mem.obj, addend)) {
*resultValue = instr->args[2].data.mem.offset;
return instr;
} else {
return NULL;
}
} else {
return NULL;
}
} else {
return NULL;
}
}
static int isconstantoperand(PCodeArg *op, SInt16 *resultValue) {
PCode *instr;
if (
(instr = defininginstruction[op->data.reg.reg]) &&
instr->op == PC_LI &&
instr->args[1].kind == PCOp_IMMEDIATE
)
{
*resultValue = instr->args[1].data.imm.value;
return 1;
} else {
return 0;
}
}
static int isuint16constantoperand(PCodeArg *op, SInt16 *resultValue) {
PCode *instr;
if (
(instr = defininginstruction[op->data.reg.reg]) &&
instr->op == PC_LI &&
instr->args[1].kind == PCOp_IMMEDIATE &&
FITS_IN_USHORT(instr->args[1].data.imm.value)
)
{
*resultValue = instr->args[1].data.imm.value;
return 1;
} else {
return 0;
}
}
static int isvectorconstantoperand(PCodeArg *op, SInt16 *resultValue, Opcode *resultNewOp) {
PCode *instr;
if (
(instr = vrdefininginstruction[op->data.reg.reg]) &&
(instr->op == PC_VSPLTISB || instr->op == PC_VSPLTISH || instr->op == PC_VSPLTISW) &&
instr->args[1].kind == PCOp_IMMEDIATE
)
{
*resultValue = instr->args[1].data.imm.value;
*resultNewOp = instr->op;
return 1;
} else {
return 0;
}
}
static int isunsignedloadoperand(PCodeArg *op) {
PCode *instr;
if ((instr = defininginstruction[op->data.reg.reg])) {
if (instr->flags & fPCodeFlag2) {
if (instr->op >= PC_LHZ && instr->op <= PC_LHZUX)
return 2;
if (instr->op >= PC_LBZ && instr->op <= PC_LBZUX)
return 1;
} else if (instr->op == PC_RLWINM) {
int var3 = instr->args[3].data.imm.value;
int var4 = instr->args[4].data.imm.value;
if (var4 == 31) {
if (var3 == 24)
return 1;
if (var3 == 16)
return 2;
}
}
}
return 0;
}
static int ismaskedoperand(PCodeArg *op, UInt32 *resultMask) {
PCode *instr;
UInt32 mask;
if ((instr = defininginstruction[op->data.reg.reg]) && instr->op == PC_RLWINM) {
if (instr->args[3].data.imm.value <= instr->args[4].data.imm.value) {
mask =
((instr->args[3].data.imm.value > 31) ? 0 : (0xFFFFFFFFU >> instr->args[3].data.imm.value)) &
~(((instr->args[4].data.imm.value + 1) > 31) ? 0 : (0xFFFFFFFFU >> (instr->args[4].data.imm.value + 1)));
} else {
mask =
((instr->args[3].data.imm.value > 31) ? 0 : (0xFFFFFFFFU >> instr->args[3].data.imm.value)) |
~(((instr->args[4].data.imm.value + 1) > 31) ? 0 : (0xFFFFFFFFU >> (instr->args[4].data.imm.value + 1)));
}
*resultMask = mask;
return 1;
}
return 0;
}
static int issignedloadoperand(PCodeArg *op) {
PCode *instr;
if ((instr = defininginstruction[op->data.reg.reg])) {
if (instr->flags & fPCodeFlag2) {
if (instr->op >= PC_LHA && instr->op <= PC_LHAUX)
return 2;
} else if (instr->op == PC_EXTSB) {
return 1;
} else if (instr->op == PC_EXTSH) {
return 2;
}
}
return 0;
}
static void propagateconstantstoblock(PCodeBlock *block) {
PCode *instr;
SInt16 immAddend;
SInt16 value1;
SInt16 valueU16;
Opcode newOpcode;
SInt16 value2;
UInt32 mask;
UInt32 mask2;
int loadSize;
PCodeArg *op;
int i;
for (instr = block->firstPCode; instr; instr = instr->nextPCode) {
switch (instr->op) {
case PC_MR:
if (isconstantoperand(&instr->args[1], &value1)) {
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value1;
instr->args[1].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
}
break;
case PC_VMR:
if (isvectorconstantoperand(&instr->args[1], &value1, &newOpcode)) {
change_opcode(instr, newOpcode);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value1;
instr->args[1].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
}
break;
case PC_RLWINM:
if (
!(PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000) &&
instr->args[2].data.imm.value == 0 &&
instr->args[4].data.imm.value == 31
)
{
if (isconstantoperand(&instr->args[1], &value1)) {
if (
(instr->args[3].data.imm.value == 16 && value1 == (value1 & 0x7FFF)) ||
(instr->args[3].data.imm.value == 24 && value1 == (value1 & 0xFF))
)
{
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
break;
}
}
loadSize = isunsignedloadoperand(&instr->args[1]);
if (
(loadSize == 2 && instr->args[3].data.imm.value <= 16) ||
(loadSize == 1 && instr->args[3].data.imm.value <= 24)
)
{
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
break;
}
if (ismaskedoperand(&instr->args[1], &mask)) {
if (instr->args[3].data.imm.value <= instr->args[4].data.imm.value) {
mask2 =
((instr->args[3].data.imm.value > 31) ? 0 : (0xFFFFFFFFU >> instr->args[3].data.imm.value)) &
~(((instr->args[4].data.imm.value + 1) > 31) ? 0 : (0xFFFFFFFFU >> (instr->args[4].data.imm.value + 1)));
} else {
mask2 =
((instr->args[3].data.imm.value > 31) ? 0 : (0xFFFFFFFFU >> instr->args[3].data.imm.value)) |
~(((instr->args[4].data.imm.value + 1) > 31) ? 0 : (0xFFFFFFFFU >> (instr->args[4].data.imm.value + 1)));
}
if (mask == (mask & mask2)) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
}
}
break;
case PC_EXTSH:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
if (isconstantoperand(&instr->args[1], &value1)) {
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
break;
}
loadSize = issignedloadoperand(&instr->args[1]);
if (loadSize == 1 || loadSize == 2) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
break;
case PC_EXTSB:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
if (
isconstantoperand(&instr->args[1], &value1) &&
value1 >= -128 &&
value1 <= 127
)
{
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
break;
}
loadSize = issignedloadoperand(&instr->args[1]);
if (loadSize == 1) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
break;
case PC_ADDI:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
immAddend = instr->args[2].data.imm.value;
if (
isconstantoperand(&instr->args[1], &value1) &&
FITS_IN_SHORT(immAddend + value1)
)
{
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = immAddend + value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
break;
case PC_ADD:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
if (isconstantoperand(&instr->args[2], &value1)) {
if (value1 == 0) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
} else {
change_opcode(instr, PC_ADDI);
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = value1;
instr->args[2].data.imm.obj = NULL;
}
propagatedconstants = 1;
changed = 1;
immAddend = value1;
}
if (isconstantoperand(&instr->args[1], &value1)) {
if (instr->op == PC_ADDI || instr->op == PC_MR) {
if (FITS_IN_SHORT(immAddend + value1)) {
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = immAddend + value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
} else {
instr->args[1] = instr->args[2];
if (value1 == 0) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
} else {
change_opcode(instr, PC_ADDI);
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = value1;
instr->args[2].data.imm.obj = NULL;
}
propagatedconstants = 1;
changed = 1;
}
}
if (changed) {
if (instr->op == PC_MR) {
PCode *stackInstr;
if ((stackInstr = isstackoperand(&instr->args[1], &value1, 0))) {
change_opcode(instr, PC_ADDI);
instr->flags = stackInstr->flags;
instr->args[1] = stackInstr->args[1];
instr->args[2] = stackInstr->args[2];
change_num_operands(instr, 3);
propagatedconstants = 1;
changed = 1;
}
} else if (instr->op == PC_ADDI && instr->args[2].kind == PCOp_IMMEDIATE) {
PCode *stackInstr;
SInt16 addend = instr->args[2].data.imm.value;
if ((stackInstr = isstackoperand(&instr->args[1], &value1, addend))) {
change_opcode(instr, PC_ADDI);
instr->flags = stackInstr->flags;
instr->args[1] = stackInstr->args[1];
instr->args[2] = stackInstr->args[2];
instr->args[2].data.imm.value = value1 + addend;
if (instr->flags & (fPCodeFlag2 | fPCodeFlag4 | fPCodeFlag20000 | fPCodeFlag40000))
instr->alias = make_alias(instr->args[2].data.imm.obj, instr->args[2].data.imm.value, 1);
propagatedconstants = 1;
changed = 1;
}
}
}
break;
case PC_OR:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
value1 = 0;
immAddend = 0;
if (isconstantoperand(&instr->args[2], &value1)) {
if (isuint16constantoperand(&instr->args[2], &valueU16)) {
if (valueU16 != 0) {
change_opcode(instr, PC_ORI);
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = valueU16;
instr->args[2].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
} else {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
value1 = valueU16;
} else if (value1 == 0) {
change_opcode(instr, PC_MR);
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
immAddend = value1;
}
if (isconstantoperand(&instr->args[1], &value1)) {
if (instr->op == PC_ORI || instr->op == PC_MR) {
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = immAddend | value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
} else if (isuint16constantoperand(&instr->args[1], &valueU16)) {
if (valueU16 != 0) {
change_opcode(instr, PC_ORI);
instr->args[1] = instr->args[2];
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = valueU16;
instr->args[2].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
} else {
change_opcode(instr, PC_MR);
instr->args[1] = instr->args[2];
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
} else if (value1 == 0) {
change_opcode(instr, PC_MR);
instr->args[1] = instr->args[2];
change_num_operands(instr, 2);
propagatedconstants = 1;
changed = 1;
}
}
break;
case PC_SUBF:
if (PCODE_FLAG_SET_F(instr) & fPCodeFlag20000000)
break;
if (isconstantoperand(&instr->args[1], &value1) && FITS_IN_SHORT(-value1)) {
if (isconstantoperand(&instr->args[2], &value2) && FITS_IN_SHORT(value2 - value1)) {
change_opcode(instr, PC_LI);
instr->args[1].kind = PCOp_IMMEDIATE;
instr->args[1].data.imm.value = value2 - value1;
instr->args[1].data.imm.obj = NULL;
change_num_operands(instr, 2);
} else if (value1 == 0) {
change_opcode(instr, PC_MR);
instr->args[1] = instr->args[2];
change_num_operands(instr, 2);
} else {
change_opcode(instr, PC_ADDI);
instr->args[1] = instr->args[2];
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = -value1;
instr->args[2].data.imm.obj = NULL;
}
propagatedconstants = 1;
changed = 1;
value2 = value1;
} else if (isconstantoperand(&instr->args[2], &value1) && FITS_IN_SHORT(-value1)) {
if (value1 == 0) {
change_opcode(instr, PC_NEG);
change_num_operands(instr, 2);
} else {
instr->flags = opcodeinfo[PC_SUBFIC].flags | (instr->flags & ~opcodeinfo[PC_SUBF].flags);
change_opcode(instr, PC_SUBFIC);
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = value1;
instr->args[2].data.imm.obj = NULL;
instr->args[3].kind = PCOp_REGISTER;
instr->args[3].arg = RegClass_SPR;
instr->args[3].data.reg.reg = 0;
instr->args[3].data.reg.effect = EffectWrite;
change_num_operands(instr, 4);
}
propagatedconstants = 1;
changed = 1;
}
break;
case PC_LBZ:
case PC_LHZ:
case PC_LHA:
case PC_LWZ:
case PC_STB:
case PC_STH:
case PC_STW:
case PC_LFS:
case PC_LFD:
case PC_STFS:
case PC_STFD:
if (instr->args[2].kind == PCOp_IMMEDIATE) {
PCode *stackInstr;
SInt16 addend = instr->args[2].data.imm.value;
if ((stackInstr = isstackoperand(&instr->args[1], &value1, addend))) {
instr->args[1] = stackInstr->args[1];
instr->args[2] = stackInstr->args[2];
instr->args[2].data.imm.value = value1 + addend;
if (instr->flags & (fPCodeFlag2 | fPCodeFlag4 | fPCodeFlag20000 | fPCodeFlag40000))
instr->alias = make_alias(instr->args[2].data.imm.obj, instr->args[2].data.imm.value,
nbytes_loaded_or_stored_by(instr));
propagatedconstants = 1;
changed = 1;
}
}
break;
case PC_LBZX:
case PC_LHZX:
case PC_LHAX:
case PC_LWZX:
case PC_STBX:
case PC_STHX:
case PC_STWX:
case PC_LFSX:
case PC_LFDX:
case PC_STFSX:
case PC_STFDX:
if (isconstantoperand(&instr->args[2], &value1)) {
instr->op -= 2;
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = value1;
instr->args[2].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
} else if (isconstantoperand(&instr->args[1], &value1)) {
instr->op -= 2;
instr->args[1] = instr->args[2];
instr->args[2].kind = PCOp_IMMEDIATE;
instr->args[2].data.imm.value = value1;
instr->args[2].data.imm.obj = NULL;
propagatedconstants = 1;
changed = 1;
}
break;
}
for (i = 0, op = instr->args; i < instr->argCount; i++, op++) {
if (
op->kind == PCOp_REGISTER &&
op->arg == RegClass_GPR &&
(op->data.reg.effect & EffectWrite)
)
{
defininginstruction[op->data.reg.reg] = instr;
}
else if (
op->kind == PCOp_REGISTER &&
op->arg == RegClass_VR &&
(op->data.reg.effect & EffectWrite)
)
{
vrdefininginstruction[op->data.reg.reg] = instr;
}
}
}
}
void propagateconstants(void) {
PCodeBlock *block;
int i;
propagatedconstants = 0;
computeusedefchains(0);
defininginstruction = galloc(sizeof(PCode *) * used_virtual_registers[RegClass_GPR]);
vrdefininginstruction = galloc(sizeof(PCode *) * used_virtual_registers[RegClass_VR]);
do {
changed = 0;
for (i = 0; i < pcblockcount; i++) {
if ((block = depthfirstordering[i])) {
computedefininginstructions(block);
propagateconstantstoblock(block);
}
}
} while (changed);
freeoheap();
}