/*
* RTLBitrange.java - This file is part of the Jakstab project.
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab.rtl.expressions;
import java.util.Set;
import org.jakstab.rtl.Context;
import org.jakstab.rtl.TypeInferenceException;
import org.jakstab.ssl.Architecture;
import org.jakstab.util.Logger;
/**
* A bitrange over another expression. Both start and endbits can be expressions, but the
* bitwidth can only be determined if start and end position are numbers.
*
* @author Johannes Kinder
*/
public class RTLBitRange extends AbstractRTLExpression implements RTLExpression, Writable {
/**
* Creates a Bitmask in which all bits from startbit to and including
* endbit are set.
*
* @param startBit The positive index of the first bit to set
* @param endBit The positive index of the last bit to set
* @return The bitmask
*/
public final static long bitMask(int startBit, int endBit) {
return ((1L << (endBit - startBit + 1) ) - 1) << startBit;
}
public static final RTLNumber calculate(RTLNumber firstIndex, RTLNumber lastIndex, RTLNumber operand) {
int firstBit = firstIndex.intValue();
int lastBit = lastIndex.intValue();
return calculate(firstBit, lastBit, operand);
}
public static final RTLNumber calculate(int firstBit, int lastBit, RTLNumber operand) {
long value = operand.longValue();
return ExpressionFactory.createNumber((bitMask(firstBit, lastBit) & value) >> firstBit,
lastBit - firstBit + 1);
}
private final static Logger logger = Logger.getLogger(RTLBitRange.class);
private final RTLExpression firstBit;
private final RTLExpression lastBit;
private final RTLExpression operand;
private final int size;
private SetOfVariables usedVariablesOnWrite = null;
protected RTLBitRange(RTLExpression variable, RTLExpression firstBit, RTLExpression lastBit) {
super();
this.operand = variable;
this.firstBit = firstBit;
this.lastBit = lastBit;
this.size = 1 + operand.size() + firstBit.size() + lastBit.size();
}
@Override
public RTLExpression evaluate(Context context) {
RTLExpression evaldOperand = operand.evaluate(context);
RTLExpression evaldFirstBit = firstBit.evaluate(context);
RTLExpression evaldLastBit = lastBit.evaluate(context);
if (evaldFirstBit instanceof RTLNumber && evaldLastBit instanceof RTLNumber) {
int firstBit = ((RTLNumber)evaldFirstBit).intValue();
int lastBit = ((RTLNumber)evaldLastBit).intValue();
// Check for unnecessary index
if (firstBit == 0 && evaldOperand instanceof RTLVariable &&
((RTLVariable)evaldOperand).getBitWidth() == lastBit + 1) {
logger.debug("Removing unnecessary Bitrange from " + this.toString());
return evaldOperand;
}
if (evaldOperand instanceof RTLNumber) {
//long value = ((RTLNumber)evaldOperand).longValue();
//return ExpressionFactory.createNumber((bitMask(firstBit, lastBit) & value) >> firstBit,
// lastBit - firstBit + 1);
return calculate((RTLNumber)evaldFirstBit, (RTLNumber)evaldLastBit, (RTLNumber)evaldOperand);
}
}
return ExpressionFactory.createBitRange(evaldOperand, evaldFirstBit, evaldLastBit);
}
@Override
public SetOfVariables getDefinedVariablesOnWrite() {
if (operand instanceof Writable)
return ((Writable)operand).getDefinedVariablesOnWrite();
else
throw new RuntimeException("Non-writable operand in bitrange!");
//return operand.getUsedVariables();
}
/**
* @return the firstBit
*/
public RTLExpression getFirstBitIndex() {
return firstBit;
}
/**
* @return the lastBit
*/
public RTLExpression getLastBitIndex() {
return lastBit;
}
/**
* @return the operand
*/
public RTLExpression getOperand() {
return operand;
}
/**
* Creates a bitmasking expression over the supplied parameter that is the
* inverse of this bitrange. Can be used for simplifying assignments such as
* x[0:7] = y; to x := (x & bitmask) | (size of x)y;
* x[8:15] = y; to x := (x & bitmask) | ((size of x)y << 8)
*
* @param rhs the expression that the inverse should be applied to
* @return The inverse of this bitrange applied to the parameter
*/
public RTLExpression applyInverse(RTLExpression rhs) {
int firstBit = ((RTLNumber)getFirstBitIndex()).intValue();
int lastBit = ((RTLNumber)getLastBitIndex()).intValue();
long bitMask = RTLBitRange.bitMask(0, firstBit - 1) |
RTLBitRange.bitMask(lastBit + 1, operand.getBitWidth());
// Bring rhs up to the size of the operand
RTLExpression castToOpSize = ExpressionFactory.createZeroFill(
ExpressionFactory.createNumber(rhs.getBitWidth(), 8),
ExpressionFactory.createNumber(getOperand().getBitWidth() - 1, 8),
rhs
);
// Shift rhs if necessary
RTLExpression maskedRhs;
if (firstBit == 0) {
maskedRhs = castToOpSize;
} else {
maskedRhs = ExpressionFactory.createShiftLeft(
castToOpSize,
getFirstBitIndex()
);
}
// Create the mask
RTLExpression ret = ExpressionFactory.createOr(
ExpressionFactory.createAnd(
getOperand(),
ExpressionFactory.createNumber(bitMask, getOperand().getBitWidth())
), maskedRhs
);
//logger.debug("Created " + ret);
return ret;
}
@Override
public SetOfVariables getUsedVariables() {
if (usedVariables == null) {
usedVariables = new SetOfVariables();
if (firstBit != null) usedVariables.addAll(firstBit.getUsedVariables());
if (lastBit != null) usedVariables.addAll(lastBit.getUsedVariables());
if (operand != null) usedVariables.addAll(operand.getUsedVariables());
}
return usedVariables;
}
@Override
public SetOfVariables getUsedVariablesOnWrite() {
if (usedVariablesOnWrite == null) {
usedVariablesOnWrite = new SetOfVariables();
if (firstBit != null) usedVariablesOnWrite.addAll(firstBit.getUsedVariables());
if (lastBit != null) usedVariablesOnWrite.addAll(lastBit.getUsedVariables());
// This was a temp fix for AH, AL etc.
// usedVariablesOnWrite.addAll(operand.getUsedVariables());
}
return usedVariablesOnWrite;
}
@Override
public Set<RTLMemoryLocation> getUsedMemoryLocations() {
return operand.getUsedMemoryLocations();
}
@Override
public Set<RTLMemoryLocation> getUsedMemoryLocationsOnWrite() {
if (operand instanceof Writable)
return ((Writable)operand).getUsedMemoryLocationsOnWrite();
else
throw new RuntimeException("Non-writable operand in bitrange!");
//return operand.getUsedVariables();
}
@Override
public Set<RTLMemoryLocation> getDefinedMemoryLocationsOnWrite() {
if (operand instanceof Writable)
return ((Writable)operand).getDefinedMemoryLocationsOnWrite();
else
throw new RuntimeException("Non-writable operand in bitrange!");
//return operand.getUsedVariables();
}
@Override
public String toString() {
String bitRange;
if (firstBit instanceof RTLNumber && lastBit instanceof RTLNumber) {
int first = ((RTLNumber)firstBit).intValue();
int last = ((RTLNumber)lastBit).intValue();
if (first == last)
bitRange = Integer.toString(first);
else
bitRange = first + ":" + last;
} else {
bitRange = firstBit.toString() + ":" + lastBit.toString();
}
return operand.toString() + "@[" + bitRange + "]";
}
@Override
public int getBitWidth() {
if (firstBit instanceof RTLNumber && lastBit instanceof RTLNumber) {
int result = ((RTLNumber)lastBit).intValue() - ((RTLNumber)firstBit).intValue() + 1;
return result;
} else {
if (firstBit.equals(lastBit)) return 1;
assert false : "Cannot determine bitwidth for bitrange from " + firstBit + " to " + lastBit;
return -1;
}
}
@Override
public int size() {
return size;
}
/*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) return false;
RTLBitRange other = (RTLBitRange)obj;
return other.operand.equals(this.operand) &&
other.firstBit.equals(this.firstBit) &&
other.lastBit.equals(this.lastBit);
}
/*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return 3 + firstBit.hashCode() + lastBit.hashCode() + operand.hashCode();
}
@Override
public <T> T accept(ExpressionVisitor<T> visitor) {
return visitor.visit(this);
}
/*
* @see org.jakstab.rtl.expressions.AbstractRTLExpression#inferBitWidth(org.jakstab.ssl.Architecture, int)
*/
@Override
public RTLExpression inferBitWidth(Architecture arch, int expectedBitWidth)
throws TypeInferenceException {
super.inferBitWidth(arch, expectedBitWidth);
// Try to make bit indices 8 bit, if it fails, leave them the way they are
RTLExpression typedFirstBit;
try {
typedFirstBit = firstBit.inferBitWidth(arch, 8);
} catch (TypeInferenceException e) {
logger.warn("Exception on inferring type of first bit index " + firstBit);
typedFirstBit = firstBit;
}
RTLExpression typedLastBit;
try {
typedLastBit = lastBit.inferBitWidth(arch, 8);
} catch (TypeInferenceException e) {
logger.warn("Exception on inferring type of last bit index " + lastBit);
typedLastBit = lastBit;
}
return ExpressionFactory.createBitRange(operand, typedFirstBit, typedLastBit);
}
}