/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.compiler.ir.quad;
import org.jnode.vm.compiler.ir.AddressingMode;
import org.jnode.vm.compiler.ir.CodeGenerator;
import org.jnode.vm.compiler.ir.Constant;
import org.jnode.vm.compiler.ir.IRBasicBlock;
import org.jnode.vm.compiler.ir.Operand;
import org.jnode.vm.compiler.ir.RegisterLocation;
import org.jnode.vm.compiler.ir.StackLocation;
import org.jnode.vm.compiler.ir.Variable;
import static org.jnode.vm.compiler.ir.AddressingMode.CONSTANT;
import static org.jnode.vm.compiler.ir.AddressingMode.REGISTER;
import static org.jnode.vm.compiler.ir.AddressingMode.STACK;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.DADD;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.DMUL;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.FADD;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.FMUL;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.IADD;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.IAND;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.IMUL;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.IOR;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.IXOR;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.LADD;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.LAND;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.LMUL;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.LOR;
import static org.jnode.vm.compiler.ir.quad.BinaryOperation.LXOR;
/**
* This class represents binary operations of the form:
* <p/>
* lhs = operand1 operation operand2, where operation is +, -, <<, |, && etc.
* <p/>
* The left hand side (lhs) is a Variable inherited from AssignQuad.
*
* @author Madhu Siddalingaiah
* @author Levente S\u00e1ntha
*/
public class BinaryQuad<T> extends AssignQuad<T> {
private enum Mode {
/*
* These are used to simplify addressing mode testing
*/
MODE_RCC(REGISTER, CONSTANT, CONSTANT),
MODE_RCR(REGISTER, CONSTANT, REGISTER),
MODE_RCS(REGISTER, CONSTANT, STACK),
MODE_RRC(REGISTER, REGISTER, CONSTANT),
MODE_RRR(REGISTER, REGISTER, REGISTER),
MODE_RRS(REGISTER, REGISTER, STACK),
MODE_RSC(REGISTER, STACK, CONSTANT),
MODE_RSR(REGISTER, STACK, REGISTER),
MODE_RSS(REGISTER, STACK, STACK),
MODE_SCC(STACK, CONSTANT, CONSTANT),
MODE_SCR(STACK, CONSTANT, REGISTER),
MODE_SCS(STACK, CONSTANT, STACK),
MODE_SRC(STACK, REGISTER, CONSTANT),
MODE_SRR(STACK, REGISTER, REGISTER),
MODE_SRS(STACK, REGISTER, STACK),
MODE_SSC(STACK, STACK, CONSTANT),
MODE_SSR(STACK, STACK, REGISTER),
MODE_SSS(STACK, STACK, STACK);
private final AddressingMode m1;
private final AddressingMode m2;
private final AddressingMode m3;
private Mode(AddressingMode m1, AddressingMode m2, AddressingMode m3) {
this.m1 = m1;
this.m2 = m2;
this.m3 = m3;
}
public static Mode valueOf(AddressingMode m1, AddressingMode m2, AddressingMode m3) {
for (Mode m : values()) {
if ((m.m1 == m1) && (m.m2 == m2) && (m.m3 == m3)) {
return m;
}
}
throw new IllegalArgumentException();
}
}
private BinaryOperation operation;
private Operand<T> refs[];
private boolean commutative;
/**
* @param address
* @param block
* @param lhsIndex
*/
public BinaryQuad(int address, IRBasicBlock<T> block, int lhsIndex,
int varIndex1, BinaryOperation operation, int varIndex2) {
super(address, block, lhsIndex);
this.operation = operation;
refs = new Operand[]{getOperand(varIndex1), getOperand(varIndex2)};
this.commutative =
operation == IADD || operation == IMUL ||
operation == LADD || operation == LMUL ||
operation == FADD || operation == FMUL ||
operation == DADD || operation == DMUL ||
operation == IAND || operation == LAND ||
operation == IOR || operation == LOR ||
operation == IXOR || operation == LXOR;
}
public BinaryQuad(int address, IRBasicBlock<T> block, int lhsIndex,
int varIndex1, BinaryOperation operation, Operand<T> op2) {
super(address, block, lhsIndex);
this.operation = operation;
refs = new Operand[]{getOperand(varIndex1), op2};
this.commutative =
operation == IADD || operation == IMUL ||
operation == LADD || operation == LMUL ||
operation == FADD || operation == FMUL ||
operation == DADD || operation == DMUL ||
operation == IAND || operation == LAND ||
operation == IOR || operation == LOR ||
operation == IXOR || operation == LXOR;
}
/**
* @see org.jnode.vm.compiler.ir.quad.Quad#getReferencedOps()
*/
public Operand<T>[] getReferencedOps() {
return refs;
}
/**
* @return the first operand
*/
public Operand getOperand1() {
return refs[0];
}
/**
* @return the second operand
*/
public Operand getOperand2() {
return refs[1];
}
/**
* @return the operation
*/
public BinaryOperation getOperation() {
return operation;
}
public String toString() {
return getAddress() + ": " + getLHS().toString() + " = " +
refs[0].toString() + ' ' + operation.getOperation() +
' ' + refs[1].toString();
}
/**
* If refs[0] and refs[1] are both Constants, then fold them.
*
* @return resulting Quad after folding
*/
public Quad<T> foldConstants() {
if (refs[0] instanceof Constant && refs[1] instanceof Constant) {
Constant<T> c1 = (Constant<T>) refs[0];
Constant<T> c2 = (Constant<T>) refs[1];
Constant<T> c3 = compute(c1, c2);
int address = this.getAddress();
IRBasicBlock<T> basicBlock = this.getBasicBlock();
int index = this.getLHS().getIndex();
return new ConstantRefAssignQuad<T>(address, basicBlock, index, c3);
}
return this;
}
/**
* If refs[0] and refs[1] are both Constants, then fold them.
*
* @return resulting Quad after folding
*/
public ConstantRefAssignQuad<T> foldConstants2() {
if (refs[0] instanceof Constant && refs[1] instanceof Constant) {
Constant<T> c1 = (Constant<T>) refs[0];
Constant<T> c2 = (Constant<T>) refs[1];
Constant<T> c3 = compute(c1, c2);
int address = this.getAddress();
int byteCodeAddress = this.getByteCodeAddress();
IRBasicBlock<T> basicBlock = this.getBasicBlock();
Variable<T> lhs = this.getLHS();
return new ConstantRefAssignQuad<T>(address, byteCodeAddress, basicBlock, lhs, c3);
} else {
throw new IllegalArgumentException("Binary quad has a non-constant operand: " + this);
}
}
private Constant<T> compute(Constant<T> c1, Constant<T> c2) {
Constant<T> c3;
switch (operation) {
case IADD:
c3 = c1.iAdd(c2);
break;
case ISUB:
c3 = c1.iSub(c2);
break;
case IMUL:
c3 = c1.iMul(c2);
break;
case IDIV:
c3 = c1.iDiv(c2);
break;
case IREM:
c3 = c1.iRem(c2);
break;
case IAND:
c3 = c1.iAnd(c2);
break;
case IOR:
c3 = c1.iOr(c2);
break;
case IXOR:
c3 = c1.iXor(c2);
break;
case ISHL:
c3 = c1.iShl(c2);
break;
case ISHR:
c3 = c1.iShr(c2);
break;
case IUSHR:
c3 = c1.iUshr(c2);
break;
case LADD:
c3 = c1.lAdd(c2);
break;
case LSUB:
c3 = c1.lSub(c2);
break;
case LMUL:
c3 = c1.lMul(c2);
break;
case LDIV:
c3 = c1.lDiv(c2);
break;
case LREM:
c3 = c1.lRem(c2);
break;
case LAND:
c3 = c1.lAnd(c2);
break;
case LOR:
c3 = c1.lOr(c2);
break;
case LXOR:
c3 = c1.lXor(c2);
break;
case LSHL:
c3 = c1.lShl(c2);
break;
case LSHR:
c3 = c1.lShr(c2);
break;
case LUSHR:
c3 = c1.lUshr(c2);
break;
case FADD:
c3 = c1.fAdd(c2);
break;
case FSUB:
c3 = c1.fSub(c2);
break;
case FMUL:
c3 = c1.fMul(c2);
break;
case FDIV:
c3 = c1.fDiv(c2);
break;
case FREM:
c3 = c1.fRem(c2);
break;
case LCMP:
c3 = c1.lCmp(c2);
break;
case DADD:
c3 = c1.dAdd(c2);
break;
case DSUB:
c3 = c1.dSub(c2);
break;
case DMUL:
c3 = c1.dMul(c2);
break;
case DDIV:
c3 = c1.dDiv(c2);
break;
case DREM:
c3 = c1.dRem(c2);
break;
default:
throw new IllegalArgumentException("Don't know how to fold those yet...");
}
return c3;
}
/**
* @see org.jnode.vm.compiler.ir.quad.AssignQuad#propagate(org.jnode.vm.compiler.ir.Variable)
*/
public Operand<T> propagate(Variable<T> operand) {
Quad<T> quad = foldConstants();
if (quad instanceof ConstantRefAssignQuad) {
setDeadCode(true);
ConstantRefAssignQuad<T> cop = (ConstantRefAssignQuad<T>) quad;
return cop.getRHS();
}
return operand;
}
/**
* Simplifies operands by calling operand.simplify().
* simplify will combine phi references and propagate copies
* This method will also update liveness of operands by setting last use addr
*
* @see org.jnode.vm.compiler.ir.quad.Quad#doPass2()
*/
public void doPass2() {
refs[0] = refs[0].simplify();
refs[1] = refs[1].simplify();
getLHS().setAssignQuad(this);
}
/**
* Code generation is complicated by the permutations of addressing modes.
* This is not as nice as it could be, but it could be worse!
*
* @see org.jnode.vm.compiler.ir.quad.Quad#generateCode(org.jnode.vm.compiler.ir.CodeGenerator)
*/
public void generateCode(CodeGenerator<T> cg) {
cg.checkLabel(getAddress());
Variable<T> lhs = getLHS();
final AddressingMode lhsMode = lhs.getAddressingMode();
final AddressingMode op1Mode = refs[0].getAddressingMode();
final AddressingMode op2Mode = refs[1].getAddressingMode();
T reg1 = null;
if (lhsMode == AddressingMode.REGISTER) {
RegisterLocation<T> regLoc = (RegisterLocation<T>) lhs.getLocation();
reg1 = regLoc.getRegister();
}
T reg2 = null;
if (op1Mode == AddressingMode.REGISTER) {
Variable<T> var = (Variable<T>) refs[0];
RegisterLocation<T> regLoc = (RegisterLocation<T>) var.getLocation();
reg2 = regLoc.getRegister();
}
T reg3 = null;
if (op2Mode == AddressingMode.REGISTER) {
Variable<T> var = (Variable<T>) refs[1];
RegisterLocation<T> regLoc = (RegisterLocation<T>) var.getLocation();
reg3 = regLoc.getRegister();
}
int disp1 = 0;
if (lhsMode == AddressingMode.STACK) {
StackLocation<T> stackLoc = (StackLocation<T>) lhs.getLocation();
disp1 = stackLoc.getDisplacement();
}
int disp2 = 0;
if (op1Mode == AddressingMode.STACK) {
Variable<T> var = (Variable<T>) refs[0];
StackLocation<T> stackLoc = (StackLocation<T>) var.getLocation();
disp2 = stackLoc.getDisplacement();
}
int disp3 = 0;
if (op2Mode == AddressingMode.STACK) {
Variable<T> var = (Variable<T>) refs[1];
StackLocation<T> stackLoc = (StackLocation<T>) var.getLocation();
disp3 = stackLoc.getDisplacement();
}
Constant<T> c2 = null;
if (op1Mode == AddressingMode.CONSTANT) {
c2 = (Constant<T>) refs[0];
}
Constant<T> c3 = null;
if (op2Mode == AddressingMode.CONSTANT) {
c3 = (Constant<T>) refs[1];
}
final Mode aMode = Mode.valueOf(lhsMode, op1Mode, op2Mode);
switch (aMode) {
case MODE_RCC:
cg.generateBinaryOP(reg1, c2, operation, c3);
break;
case MODE_RCR:
if (reg1 == reg3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(reg1, reg3, operation, c2);
} else {
cg.generateBinaryOP(reg1, c2, operation, reg3);
}
break;
case MODE_RCS:
cg.generateBinaryOP(reg1, c2, operation, disp3);
break;
case MODE_RRC:
cg.generateBinaryOP(reg1, reg2, operation, c3);
break;
case MODE_RRR:
if (reg1 == reg3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(reg1, reg3, operation, reg2);
} else {
cg.generateBinaryOP(reg1, reg2, operation, reg3);
}
break;
case MODE_RRS:
cg.generateBinaryOP(reg1, reg2, operation, disp3);
break;
case MODE_RSC:
cg.generateBinaryOP(this, reg1, disp2, operation, c3);
break;
case MODE_RSR:
if (reg1 == reg3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(reg1, reg3, operation, disp2);
} else {
cg.generateBinaryOP(reg1, disp2, operation, reg3);
}
break;
case MODE_RSS:
cg.generateBinaryOP(reg1, disp2, operation, disp3);
break;
case MODE_SCC:
cg.generateBinaryOP(disp1, c2, operation, c3);
break;
case MODE_SCR:
cg.generateBinaryOP(disp1, c2, operation, reg3);
break;
case MODE_SCS:
if (disp1 == disp3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(this, disp1, disp3, operation, c2);
} else {
cg.generateBinaryOP(disp1, c2, operation, disp3);
}
break;
case MODE_SRC:
cg.generateBinaryOP(disp1, reg2, operation, c3);
break;
case MODE_SRR:
cg.generateBinaryOP(disp1, reg2, operation, reg3);
break;
case MODE_SRS:
if (disp1 == disp3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(disp1, disp3, operation, reg2);
} else {
cg.generateBinaryOP(disp1, reg2, operation, disp3);
}
break;
case MODE_SSC:
cg.generateBinaryOP(this, disp1, disp2, operation, c3);
break;
case MODE_SSR:
cg.generateBinaryOP(disp1, disp2, operation, reg3);
break;
case MODE_SSS:
if (disp1 == disp3 && commutative && !cg.supports3AddrOps()) {
cg.generateBinaryOP(this, disp1, disp3, operation, disp2);
} else {
cg.generateBinaryOP(this, disp1, disp2, operation, disp3);
}
break;
default:
throw new IllegalArgumentException("Undefined addressing mode: " + aMode);
}
}
/**
* @see org.jnode.vm.compiler.ir.quad.AssignQuad#getLHSLiveAddress()
*/
public int getLHSLiveAddress() {
CodeGenerator<T> cg = CodeGenerator.getInstance();
int addr = this.getAddress();
if (cg.supports3AddrOps() || commutative) {
return addr + 1;
}
return addr;
}
}