#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 & fIsRead) { 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 & fIsRead) { 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) & fRecordBit) && 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) & fRecordBit) 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) & fRecordBit) 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) & fRecordBit) 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) & fRecordBit) 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 & (fIsRead | fIsWrite | 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) & fRecordBit) 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) & fRecordBit) 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 & (fIsRead | fIsWrite | 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(); }