GP-5980: Optimize and clean code for mp-int in p-code emu.

This commit is contained in:
Dan
2025-12-15 13:20:57 +00:00
parent cd1a188eca
commit d4220d6af9
202 changed files with 24377 additions and 10141 deletions
@@ -262,7 +262,8 @@ public class JitCompiler {
oum.dumpResult();
}
JitCodeGenerator gen = new JitCodeGenerator(lookup, context, cfm, dfm, vsm, tm, am, oum);
JitCodeGenerator<?> gen =
new JitCodeGenerator<>(lookup, context, cfm, dfm, vsm, tm, am, oum);
return gen.load();
}
@@ -116,4 +116,39 @@ public enum JitJvmTypeUtils {
default -> throw new UnsupportedOperationException();
};
}
/**
* Compute the erasure of a type variable with the given upper bounds
*
* @param bounds the upper bounds
* @return the erasure
*/
public static Class<?> eraseBounds(Type[] bounds) {
if (bounds.length == 0) {
return Object.class;
}
return erase(bounds[0]);
}
/**
* Compute the erasure of the given type
* <p>
* For a class, this is just the same class. For an array, it is the array of the erasure of the
* element type. For a parameterized type, we take the erasure of the raw type, which should in
* turn be a class. For a wildcard, we take the erasure of its first upper bound.
*
* @param type the type
* @return the erasure
*/
public static Class<?> erase(Type type) {
return switch (type) {
case Class<?> cls -> cls;
case GenericArrayType arr -> Array.newInstance(erase(arr.getGenericComponentType()), 0)
.getClass();
case ParameterizedType pt -> erase(pt.getRawType());
case TypeVariable<?> tv -> eraseBounds(tv.getBounds());
case WildcardType wt -> eraseBounds(wt.getUpperBounds());
default -> throw new UnsupportedOperationException();
};
}
}
@@ -26,13 +26,12 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import org.objectweb.asm.MethodTooLargeException;
import ghidra.pcode.emu.*;
import ghidra.pcode.emu.PcodeMachine.AccessKind;
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
import ghidra.pcode.emu.jit.analysis.JitDataFlowModel;
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
import ghidra.pcode.emu.jit.decode.JitPassageDecoder;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPointPrototype;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
import ghidra.pcode.emu.jit.var.JitVal;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
@@ -0,0 +1,470 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import java.util.ArrayList;
import java.util.List;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.*;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler used for a varnode requiring allocation of multiple integers, where those integers
* correspond exactly to the variable's legs.
* <p>
* In this case, we can usually give the operators direct access to the underlying mp-int operand.
* We do need to be careful that we don't unintentionally permit the operator to use the variable's
* storage for intermediate values. Thus, we have some provision for saying each leg is read-only,
* which will cause attempts to store into them to instead generate a writable temporary local. Such
* intermediate results will get written only by a call to
* {@link #genStoreFromOpnd(Emitter, JitCodeGenerator, Opnd, Ext, Scope)}.
*
* @param legs the list of legs in little-endian order
* @param type the type of the full multi-precision integer variable
* @param vn the complete varnode accessible to this handler
* @param opnd the (writable) list of local operands (in LE order)
* @param roOpnd the read-only version of {@code opnd}.
*/
public record AlignedMpIntHandler(List<JvmLocal<TInt, IntJitType>> legs, MpIntJitType type,
Varnode vn, MpIntLocalOpnd opnd, MpIntLocalOpnd roOpnd) implements VarHandler {
/**
* Static utility for the {@link #AlignedMpIntHandler(List, MpIntJitType, Varnode)} constructor
*
* @param legs the list o legs in little-endian order
* @param type the type of the full mp-int variable
* @param vn the complete varnode
* @return the writable operand
*/
private static MpIntLocalOpnd createOpnd(List<JvmLocal<TInt, IntJitType>> legs,
MpIntJitType type, Varnode vn) {
List<SimpleOpnd<TInt, IntJitType>> opndLegs = new ArrayList<>();
for (JvmLocal<TInt, IntJitType> leg : legs) {
opndLegs.add(leg.opnd());
}
return MpIntLocalOpnd.of(type, VarHandler.nameVn(vn), opndLegs);
}
/**
* Static utility for the {@link #AlignedMpIntHandler(List, MpIntJitType, Varnode)} constructor
*
* @param legs the list o legs in little-endian order
* @param type the type of the full mp-int variable
* @param vn the complete varnode
* @return the read-only operand
*/
private static MpIntLocalOpnd createRoOpnd(List<JvmLocal<TInt, IntJitType>> legs,
MpIntJitType type, Varnode vn) {
List<SimpleOpnd<TInt, IntJitType>> opndLegs = new ArrayList<>();
for (JvmLocal<TInt, IntJitType> leg : legs) {
opndLegs.add(SimpleOpnd.ofIntReadOnly(leg.type(), leg.local()));
}
return MpIntLocalOpnd.of(type, VarHandler.nameVn(vn) + "_ro", opndLegs);
}
/**
* Preferred constructor
*
* @param legs the list o legs in little-endian order
* @param type the type of the full muti-precision integer variable
* @param vn the complete varnode accessible to this handler
*/
public AlignedMpIntHandler(List<JvmLocal<TInt, IntJitType>> legs, MpIntJitType type,
Varnode vn) {
this(legs, type, vn, createOpnd(legs, type, vn), createRoOpnd(legs, type, vn));
}
@Override
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT to, Ext ext) {
return switch (to) {
case IntJitType t -> em
.emit(legs.get(0)::genLoadToStack, gen, to, ext);
case LongJitType t when legs.size() == 1 -> em
.emit(legs.get(0)::genLoadToStack, gen, to, ext);
case LongJitType t -> em
.emit(legs.get(0)::genLoadToStack, gen, LongJitType.I8, Ext.ZERO)
.emit(legs.get(1)::genLoadToStack, gen, LongJitType.I8, Ext.ZERO)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lshl)
.emit(Op::lor)
.emit(Opnd::convert, LongJitType.I8, to, ext);
default -> throw new AssertionError();
};
}
@Override
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope) {
return MpIntToMpInt.INSTANCE.convertOpndToOpnd(em, roOpnd, to, ext, scope);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
IntJitType toType = type.legTypesLE().get(leg);
if (leg >= legs.size()) {
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> em
.emit(legs.getLast()::genLoadToStack, gen, toType, ext)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
};
}
return em
.emit(legs.get(leg)::genLoadToStack, gen, toType, ext);
}
@Override
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope, int slack) {
return MpIntToMpInt.INSTANCE.convertOpndToArray(em, opnd, to, ext, scope, slack);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
var result = em
.emit(legs.get(0)::genLoadToStack, gen, IntJitType.I4, Ext.ZERO);
for (JvmLocal<TInt, IntJitType> leg : legs) {
result = result
.emit(leg::genLoadToStack, gen, IntJitType.I4, Ext.ZERO)
.emit(Op::ior);
}
return result.emit(Opnd::intToBool);
}
/**
* Emit bytecode to store a JVM int from the stack into the given local
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack, having the int on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the int on the stack
* @param local the local to receive the value
* @param ext the kind of extension to apply
* @param scope a scope for generating local storage
* @return the emitter typed with the resulting stack, i.e., having popped the int
*/
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenStoreInt(Emitter<N0> em,
JitCodeGenerator<?> gen, IntJitType type, JvmLocal<TInt, IntJitType> local, Ext ext,
Scope scope) {
return em
.emit(local::genStoreFromStack, gen, type, ext, scope);
}
/**
* Emit bytecode to compute the sign of the int on the stack, and store that int into a given
* local.
* <p>
* The int is copied and stored into the given local. Then, the sign of the int is computed and
* remains on the stack. Signed extension is assumed.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack, having the int on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the int on the stack. Note that this type determines the
* position of the sign bit.
* @param local the local to receive the value
* @param scope a scope for generating local storage
* @return the emitter typed with the resulting stack, i.e., having popped the int and pushed
* the sign
*/
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
doGenStoreIntAndSign(Emitter<N0> em, JitCodeGenerator<?> gen, IntJitType type,
JvmLocal<TInt, IntJitType> local, Scope scope) {
return em
.emit(Op::dup)
.emit(this::doGenStoreInt, gen, type, local, Ext.SIGN, scope)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
/**
* Emit bytecode to store a JVM long from the stack into two given locals
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack, having the long on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the int on the stack
* @param lower the local to receive the lower 32 bits of the value
* @param upper the local to receive the upper 32 bits of the value
* @param ext the kind of extension to apply
* @param scope a scope for generating local storage
* @return the emitter typed with the resulting stack, i.e., having popped the long
*/
protected <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<N1> doGenStoreLong(
Emitter<N0> em, JitCodeGenerator<?> gen, LongJitType type,
JvmLocal<TInt, IntJitType> lower, JvmLocal<TInt, IntJitType> upper, Ext ext,
Scope scope) {
return em
.emit(Op::dup2__2)
.emit(lower::genStoreFromStack, gen, type, ext, scope)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(upper::genStoreFromStack, gen,
LongJitType.forSize(type.size() - Integer.BYTES), ext, scope);
}
/**
* Emit bytecode to compute the sign of the long on the stack, and store that long into two
* given locals.
* <p>
* The long is copied and stored into the given local. Then, the sign of the long is computed
* and remains on the stack as an int. Signed extension is assumed.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack, having the long on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the long on the stack. Note that this type determines the
* position of the sign bit.
* @param lower the local to receive the lower 32 bits of the value
* @param upper the local to receive the upper 32 bits of the value
* @param scope a scope for generating local storage
* @return the emitter typed with the resulting stack, i.e., having popped the long and pushed
* the sign
*/
protected <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TInt>>
doGenStoreLongAndSign(Emitter<N0> em, JitCodeGenerator<?> gen, LongJitType type,
JvmLocal<TInt, IntJitType> lower, JvmLocal<TInt, IntJitType> upper,
Scope scope) {
return em
.emit(Op::dup2__2)
.emit(lower::genStoreFromStack, gen, type, Ext.SIGN, scope)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(Opnd::convert, type, IntJitType.I4, Ext.SIGN)
.emit(Op::dup)
.emit(upper::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
/**
* Emit bytecode to zero fill the given locals
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param locals the locals to zero fill
* @param scope a scope for generating local storage
* @return the emitter typed with the incoming stack
*/
protected <N extends Next> Emitter<N> doGenZeroFill(Emitter<N> em, JitCodeGenerator<?> gen,
List<JvmLocal<TInt, IntJitType>> locals, Scope scope) {
for (JvmLocal<TInt, IntJitType> local : locals) {
em = em
.emit(Op::ldc__i, 0)
.emit(local::genStoreFromStack, gen, IntJitType.I4, Ext.ZERO, scope);
}
return em;
}
/**
* Emit bytecode to sign fill the given locals
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the sign int on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param locals the locals to sign fill
* @param scope a scope for generating local storage
* @return the emitter typed with the resulting stack, i.e., having popped the sign
*/
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenSignFill(Emitter<N0> em,
JitCodeGenerator<?> gen, List<JvmLocal<TInt, IntJitType>> locals, Scope scope) {
for (JvmLocal<TInt, IntJitType> local : locals.subList(0, locals.size() - 1)) {
em = em
.emit(Op::dup)
.emit(local::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
}
JvmLocal<TInt, IntJitType> last = locals.getLast();
return em
.emit(last::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
}
@Override
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT from, Ext ext, Scope scope) {
return switch (from) {
case IntJitType t when legs.size() == 1 -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreInt, gen, t, legs.get(0), ext, scope);
case IntJitType t -> switch (ext) {
case ZERO -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreInt, gen, t, legs.get(0), ext, scope)
.emit(this::doGenZeroFill, gen, legs.subList(1, legs.size()), scope);
case SIGN -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreIntAndSign, gen, t, legs.get(0), scope)
.emit(this::doGenSignFill, gen, legs.subList(1, legs.size()), scope);
};
case LongJitType t when legs.size() == 1 -> em
.emit(legs.get(0)::genStoreFromStack, gen, from, ext, scope);
case LongJitType t when legs.size() == 2 -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreLong, gen, t, legs.get(0), legs.get(1), ext, scope);
case LongJitType t -> switch (ext) {
case ZERO -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreLong, gen, t, legs.get(0), legs.get(1), ext, scope)
.emit(this::doGenZeroFill, gen, legs.subList(2, legs.size()), scope);
case SIGN -> em
.emit(Opnd::castStack1, from, t)
.emit(this::doGenStoreLongAndSign, gen, t, legs.get(0), legs.get(1), scope)
.emit(this::doGenSignFill, gen, legs.subList(2, legs.size()), scope);
};
default -> throw new AssertionError();
};
}
/**
* Emit bytecode to extend the value stored in our legs.
*
* @param <N> the tail of the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param defLegs the number of legs having the input value
* @param legsOut the number of legs to receive the output value. If this is less than or equal
* to {@code defLegs}, there is no extension to apply, so no code is emitted.
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the incoming stack
*/
protected <N extends Next> Emitter<N> genExt(Emitter<N> em, JitCodeGenerator<?> gen,
int defLegs, int legsOut, Ext ext, Scope scope) {
if (legsOut <= defLegs) {
return em;
}
return switch (ext) {
case ZERO -> doGenZeroFill(em, gen, legs.subList(defLegs, legsOut), scope);
case SIGN -> em
.emit(Op::iload, legs.get(defLegs - 1).local())
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr) // Signed
.emit(this::doGenSignFill, gen, legs.subList(defLegs, legsOut), scope);
};
}
@Override
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> from, Ext ext, Scope scope) {
List<SimpleOpnd<TInt, IntJitType>> fromLegs = from.type().castLegsLE(from);
List<SimpleOpnd<TInt, IntJitType>> toLegs = opnd.type().castLegsLE(opnd);
int legsIn = fromLegs.size();
int legsOut = toLegs.size();
int defLegs = Integer.min(legsIn, legsOut);
for (int i = 0; i < defLegs; i++) {
SimpleOpnd<TInt, IntJitType> fromLeg = fromLegs.get(i);
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(i);
em = em
.emit(fromLeg::read)
.emit(Opnd::convertIntToInt, fromLeg.type(), toLeg.type(), ext)
.emit(toLeg::writeDirect);
}
return genExt(em, gen, defLegs, legsOut, ext, scope);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType from, Ext ext, Scope scope) {
List<IntJitType> fromLegTypes = from.legTypesLE();
List<SimpleOpnd<TInt, IntJitType>> toLegs = opnd.type().castLegsLE(opnd);
int legsIn = fromLegTypes.size();
int legsOut = toLegs.size();
int defLegs = Integer.min(legsIn, legsOut);
for (int i = 0; i < defLegs - 1; i++) {
IntJitType fromLegType = fromLegTypes.get(i);
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(i);
em = em
.emit(Op::dup)
.emit(Op::ldc__i, i)
.emit(Op::iaload)
.emit(Opnd::convertIntToInt, fromLegType, toLeg.type(), ext)
.emit(toLeg::writeDirect);
}
IntJitType fromLegType = fromLegTypes.get(defLegs - 1);
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(defLegs - 1);
return em
.emit(Op::ldc__i, defLegs - 1)
.emit(Op::iaload)
.emit(Opnd::convertIntToInt, fromLegType, toLeg.type(), ext)
.emit(toLeg::writeDirect)
.emit(this::genExt, gen, defLegs, legsOut, ext, scope);
}
/**
* A utility for implementing {@link #subpiece(Endian, int, int)}, also used by
* {@link ShiftedMpIntHandler}.
*
* @param endian the endianness of the emulation target. Technically, this is only used in the
* naming of any temporary local variables.
* @param vn the varnode of the original handler
* @param parts the parts (perhaps aligned to the legs) of the original handler
* @param curShift if shifted, the number of bytes. If aligned, 0.
* @param addShift the offset (in bytes) of the subpiece, i.e., additional shift
* @param maxByteSize the size in bytes of the output operand, which indicate the maximum size
* of the resulting handler's varnode.
* @return the resulting handler
*/
static VarHandler subHandler(Endian endian, Varnode vn, List<JvmLocal<TInt, IntJitType>> parts,
int curShift, int addShift, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, addShift, maxByteSize);
int totalShift = curShift + addShift;
int firstPart = totalShift / Integer.BYTES;
int lastPartExcl = (totalShift + subVn.getSize() + Integer.BYTES - 1) / Integer.BYTES;
List<JvmLocal<TInt, IntJitType>> subParts = parts.subList(firstPart, lastPartExcl);
int subShift = totalShift % Integer.BYTES;
if (subParts.size() == 1) {
IntJitType subType = IntJitType.forSize(subVn.getSize());
if (subShift == 0) {
return new IntVarAlloc(subParts.getFirst(), subType);
}
return new IntInIntHandler(subParts.getFirst(), subType, subVn, subShift);
}
MpIntJitType subType = MpIntJitType.forSize(subVn.getSize());
if (subShift == 0) {
return new AlignedMpIntHandler(subParts, subType, subVn);
}
return new ShiftedMpIntHandler(subParts, subType, subVn, subShift);
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
return subHandler(endian, vn, legs, 0, byteOffset, maxByteSize);
}
}
@@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.MpToStackConv;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.program.model.lang.Endian;
/**
* The handler for a p-code variable allocated in one JVM {@code double}.
*
* @param local the JVM local
* @param type the p-code type
*/
public record DoubleVarAlloc(JvmLocal<TDouble, DoubleJitType> local, DoubleJitType type)
implements SimpleVarHandler<TDouble, DoubleJitType> {
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
return genLoadLegToStackC2(em, gen, type, leg, ext);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TDouble, DoubleJitType> getConvToStack() {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
return em
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
.emit(Op::ldc__d, 0.0)
.emit(Op::dcmpl); // So long as lsb is set, it's true
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
throw new AssertionError("Who's subpiecing a double?");
}
}
@@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.MpToStackConv;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.program.model.lang.Endian;
/**
* The handler for a p-code variable allocated in one JVM {@code float}.
*
* @param local the JVM local
* @param type the p-code type
*/
public record FloatVarAlloc(JvmLocal<TFloat, FloatJitType> local, FloatJitType type)
implements SimpleVarHandler<TFloat, FloatJitType> {
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
return genLoadLegToStackC1(em, gen, type, leg, ext);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TFloat, FloatJitType> getConvToStack() {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
return em
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
.emit(Op::ldc__f, 0.0f)
.emit(Op::fcmpl); // So long as lsb is set, it's true
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
throw new AssertionError("Who's subpiecing a float?");
}
}
@@ -0,0 +1,125 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler for an {@link IntJitType int} p-code variable stored in part of a JVM {@code int}.
*
* @param local see {@link #local()}
* @param type see {@link #type()}
* @param vn see {@link #vn()} (wrt. the sub variable)
* @param byteShift see {@link #byteShift()}
*/
public record IntInIntHandler(JvmLocal<TInt, IntJitType> local, IntJitType type, Varnode vn,
int byteShift) implements SubVarHandler<TInt, IntJitType, TInt, IntJitType> {
@SuppressWarnings("javadoc")
public IntInIntHandler {
assertShiftFits(byteShift, type, local);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToSub() {
return MpIntToInt.INSTANCE;
}
@Override
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
return em
.emit(Op::iload, local.local())
.emit(Op::ldc__i, bitShift())
.emit(Op::iushr)
.emit(Opnd::convert, this.type, type, ext);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
if (leg == 0) {
return genLoadToStack(em, gen, type.legTypesLE().get(leg), ext);
}
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> {
int msb = (this.type.size() + byteShift) * Byte.SIZE;
if (msb == Integer.SIZE) {
yield em
.emit(Op::iload, local.local())
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
yield em
.emit(Op::iload, local.local())
.emit(Op::ldc__i, Integer.SIZE - msb)
.emit(Op::ishl)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
};
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
int mask = intMask();
return em
.emit(Op::iload, local.local())
.emit(Op::ldc__i, mask)
.emit(Op::iand)
.emit(Opnd::intToBool);
}
@Override
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
int mask = intMask();
return em
.emit(Opnd::convert, type, local.type(), ext)
.emit(Op::ldc__i, bitShift())
.emit(Op::ishl)
.emit(Op::ldc__i, mask)
.emit(Op::iand)
.emit(Op::iload, local.local())
.emit(Op::ldc__i, ~mask)
.emit(Op::iand)
.emit(Op::ior)
.emit(Op::istore, local.local());
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
return new IntInIntHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
byteShift + byteOffset);
}
}
@@ -0,0 +1,108 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler for an {@link IntJitType int} p-code variable stored in part of a JVM {@code long}.
*
* @param local see {@link #local()}
* @param type see {@link #type()}
* @param vn see {@link #vn()} (wrt. the sub variable)
* @param byteShift see {@link #byteShift()}
*/
public record IntInLongHandler(JvmLocal<TLong, LongJitType> local, IntJitType type, Varnode vn,
int byteShift) implements SubInLongHandler<TInt, IntJitType> {
@SuppressWarnings("javadoc")
public IntInLongHandler {
assertShiftFits(byteShift, type, local);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToSub() {
return MpIntToInt.INSTANCE;
}
@Override
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
return em
.emit(Op::lload, local.local())
.emit(Op::ldc__i, bitShift())
.emit(Op::lushr)
.emit(Op::l2i)
.emit(Opnd::convert, this.type, type, ext);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
if (leg == 0) {
return genLoadToStack(em, gen, type.legTypesLE().get(leg), ext);
}
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> {
int msb = (this.type.size() + byteShift) * Byte.SIZE;
if (msb > Integer.SIZE) {
yield em
.emit(Op::lload, local.local())
.emit(Op::ldc__i, msb - Integer.SIZE)
.emit(Op::lshr)
.emit(Op::l2i)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
if (msb == Integer.SIZE) {
yield em
.emit(Op::lload, local.local())
.emit(Op::l2i)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
yield em
.emit(Op::lload, local.local())
.emit(Op::l2i)
.emit(Op::ldc__i, Integer.SIZE - msb)
.emit(Op::ishl)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
};
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
byteShift + byteOffset);
}
}
@@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Scope;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler for a p-code variable allocated in one JVM {@code int}.
*
* @param local the JVM local
* @param type the p-code type
*/
public record IntVarAlloc(JvmLocal<TInt, IntJitType> local, IntJitType type)
implements SimpleVarHandler<TInt, IntJitType> {
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
return genLoadLegToStackC1(em, gen, type, leg, ext);
}
@Override
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
return IntToMpInt.INSTANCE.doConvert(em, local.opnd(), local.name(), type, ext, scope);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToStack() {
return MpIntToInt.INSTANCE;
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
return em
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
.emit(Opnd::intToBool);
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
Math.min(type.size(), maxByteSize));
if (byteOffset == 0) {
return new IntVarAlloc(local, IntJitType.forSize(subVn.getSize()));
}
return new IntInIntHandler(local, IntJitType.forSize(subVn.getSize()), subVn, byteOffset);
}
}
@@ -0,0 +1,197 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.gen.var.VarGen;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.Varnode;
/**
* An allocated JVM local
*
* @param <T> the JVM type of this local
* @param <JT> the p-code type of this local
* @param local the declared local this wraps
* @param type a type for this local
* @param vn the varnode whose value this local holds
* @param opnd this local as an operand
*/
public record JvmLocal<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>(Local<T> local,
JT type, Varnode vn, SimpleOpnd<T, JT> opnd) {
/**
* Create a {@link JvmLocal} with the given local, type, and varnode
*
* @param <T> the JVM type of the local
* @param <JT> the p-code type of the local
* @param local the local
* @param type the p-code type of the local
* @param vn the varnode to assign to the local
* @return the new local (wrapper)
*/
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> JvmLocal<T, JT>
of(Local<T> local, JT type, Varnode vn) {
SimpleOpnd<T, JT> opnd = SimpleOpnd.of(type, local);
return new JvmLocal<>(local, type, vn, opnd);
}
/**
* Get the name of the wrapped local
*
* @return the name
*/
public String name() {
return local.name();
}
/**
* Cast this local to satisfy checkers when a type variable is known to be of a given type
* <p>
* This will verify at runtime that the types are in fact identical.
*
* @param <TT> the "to" JVM type
* @param <TJT> the "to" p-code type
* @param type the "to" p-code type
* @return this local as the given type
*/
@SuppressWarnings("unchecked")
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>> JvmLocal<TT, TJT>
castOf(TJT type) {
if (this.type != type) {
throw new ClassCastException(
"JvmLocal is not of the given type: this is %s. Requested is %s."
.formatted(this.type, type));
}
return (JvmLocal<TT, TJT>) this;
}
/**
* Emit bytecode into the class constructor needed to access the varnode's actual value from the
* underlying {@link PcodeExecutorState}.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @return the emitter typed with the incoming stack
*/
public <N extends Next> Emitter<N> genInit(Emitter<N> em, JitCodeGenerator<?> gen) {
return VarGen.genVarnodeInit(em, gen, vn);
}
/**
* Emit bytecode to load this local's value onto the JVM stack as the given type
*
* @param <TT> the desired JVM type
* @param <TJT> the desired p-code type
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the desired p-code type
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having pushed the value
*/
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
return em
.emit(opnd::read)
.emit(Opnd::convert, this.type, type, ext);
}
/**
* Emit bytecode to store the value on the JVM stack into the local
*
* @param <FT> the JVM type of the value on the stack
* @param <FJT> the p-code type of the value on the stack
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the value on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the value on the stack
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the resulting stack, i.e., having popped the value
*/
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
return em
.emit(Opnd::convert, type, this.type, ext)
.emit(opnd::writeDirect);
}
/**
* Emit bytecode to bring this varnode into scope.
*
* <p>
* This will copy the value from the {@link JitBytesPcodeExecutorState state} into the local
* variable.
*
* @param <THIS> the type of the compiled passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @return the emitter typed with the incoming stack
*/
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genBirthCode(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(VarGen::genReadValDirectToStack, localThis, gen, type, vn)
.emit(opnd::writeDirect);
}
/**
* Emit bytecode to take this varnode out of scope.
*
* <p>
* This will copy the value from the local variable into the {@link JitBytesPcodeExecutorState
* state}.
*
* @param <THIS> the type of the compiled passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @return the emitter typed with the incoming stack
*/
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genRetireCode(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(opnd::read)
.emit(VarGen::genWriteValDirectFromStack, localThis, gen, type, vn);
}
/**
* {@return the maximum address that would be occupied by the full primitive type}
*/
public Address maxPrimAddr() {
return vn.getAddress().add(type.ext().size() - 1);
}
}
@@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler for an {@link LongJitType long} p-code variable stored in part of a JVM {@code long}.
*
* @param local see {@link #local()}
* @param type see {@link #type()}
* @param vn see {@link #vn()} (wrt. the sub variable)
* @param byteShift see {@link #byteShift()}
*/
public record LongInLongHandler(JvmLocal<TLong, LongJitType> local, LongJitType type, Varnode vn,
int byteShift) implements SubInLongHandler<TLong, LongJitType> {
@SuppressWarnings("javadoc")
public LongInLongHandler {
assertShiftFits(byteShift, type, local);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToSub() {
return MpIntToLong.INSTANCE;
}
@Override
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
return em
.emit(Op::lload, local.local())
.emit(Op::ldc__i, bitShift())
.emit(Op::lushr)
.emit(Opnd::convert, this.type, type, ext);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
if (leg == 0) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
vn, 0, Integer.BYTES);
return new IntInLongHandler(local, IntJitType.I4, subVn, byteShift)
.genLoadLegToStack(em, gen, type, leg, ext);
}
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
vn, Integer.BYTES, vn.getSize() - Integer.BYTES);
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
byteShift + Integer.BYTES).genLoadLegToStack(em, gen, type, leg - 1, ext);
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
if (subVn.getSize() <= Integer.BYTES) {
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
byteShift + byteOffset);
}
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
byteShift + byteOffset);
}
}
@@ -0,0 +1,74 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* The handler for a p-code variable allocated in one JVM {@code long}.
*
* @param local the JVM local
* @param type the p-code type
*/
public record LongVarAlloc(JvmLocal<TLong, LongJitType> local, LongJitType type)
implements SimpleVarHandler<TLong, LongJitType> {
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
return genLoadLegToStackC2(em, gen, type, leg, ext);
}
@Override
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToStack() {
return MpIntToLong.INSTANCE;
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
return em
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
.emit(Op::ldc__l, 0)
.emit(Op::lcmp); // Outputs -1, 0, or 1. So long as lsb is set, it's true.
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
Math.min(type.size(), maxByteSize));
if (byteOffset == 0) {
return new LongVarAlloc(local, LongJitType.forSize(subVn.getSize()));
}
if (subVn.getSize() <= Integer.BYTES) {
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
byteOffset);
}
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
byteOffset);
}
}
@@ -0,0 +1,106 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Scope;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* A dummy handler for values/variables that are not allocated in JVM locals
* <p>
* Every operation on this handler will throw an exception at code generation time.
*/
public enum NoHandler implements VarHandler {
/** Singleton */
INSTANCE;
@Override
public Varnode vn() {
return null;
}
@Override
public JitType type() {
return null;
}
@Override
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
throw new AssertionError();
}
@Override
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
throw new AssertionError();
}
@Override
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
throw new AssertionError();
}
@Override
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
throw new AssertionError();
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
throw new AssertionError();
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
return this;
}
}
@@ -0,0 +1,192 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.pcode.Varnode;
/**
* A handler for p-code variables composed of a single JVM local variable.
*
* @param <T> the JVM type of the variable
* @param <JT> the p-code type of the variable
*/
public interface SimpleVarHandler<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
extends VarHandler {
/**
* Get the local variable into which this p-code variable is allocated
*
* @return the local
*/
JvmLocal<T, JT> local();
@Override
default Varnode vn() {
return local().vn();
}
@Override
JT type();
@Override
default <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
return local().genLoadToStack(em, gen, type, ext);
}
@Override
default <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
return em
.emit(this::genLoadToStack, gen, type(), ext)
.emit(Opnd::convertToOpnd, type(), local().name(), type, ext, scope);
}
/**
* This provides the implementation of
* {@link #genLoadLegToStack(Emitter, JitCodeGenerator, MpIntJitType, int, Ext)} for category-1
* primitives, i.e., {@code int} and {@code float}.
* <p>
* Only leg 0 is meaningful for a category-1 primitive. Any other leg is just the extension of
* the one leg.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the complete multi-precision value
* @param leg the index of the leg to load, 0 being least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
*/
default <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStackC1(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
if (leg == 0) {
return em
.emit(this::genLoadToStack, gen, type.legTypesLE().get(leg), ext);
}
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> {
IntJitType intType = IntJitType.forSize(type().size());
yield em
.emit(this::genLoadToStack, gen, intType, ext)
.emit(Op::ldc__i, Integer.SIZE - intType.size() * Byte.SIZE)
.emit(Op::ishl)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
}
};
}
/**
* This provides the implementation of
* {@link #genLoadLegToStack(Emitter, JitCodeGenerator, MpIntJitType, int, Ext)} for category-2
* primitives, i.e., {@code long} and {@code double}.
* <p>
* Only legs 0 and 1 are meaningful for a category-2 primitive. Any other leg is just the
* extension of the upper leg.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the complete multi-precision value
* @param leg the index of the leg to load, 0 being least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
*/
default <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStackC2(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
if (leg == 0) {
return em
.emit(this::genLoadToStack, gen, type.legTypesLE().get(leg), ext);
}
if (leg == 1) {
LongJitType longType = LongJitType.forSize(type().size());
return em
.emit(this::genLoadToStack, gen, longType, ext)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lshr)
.emit(Op::l2i)
.emit(Opnd::convertIntToInt, IntJitType.forSize(type().size() - Integer.BYTES),
type.legTypesLE().get(leg), ext);
}
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> {
LongJitType longType = LongJitType.forSize(type().size());
yield em
.emit(this::genLoadToStack, gen, longType, ext)
.emit(Op::ldc__i, longType.size() * Byte.SIZE - Integer.SIZE)
.emit(Op::lshr)
.emit(Op::l2i)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr); // FIXME: Is size conversion required here?
}
};
}
@Override
default <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
return em
.emit(this::genLoadToStack, gen, type(), ext)
.emit(Opnd::convertToArray, type(), local().name(), type, ext, scope, slack);
}
@Override
default <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
return local().genStoreFromStack(em, gen, type, ext, scope);
}
/**
* Get the converter of multi-precision integers to the stack type of this handler's local.
* <p>
* Note that the converter need only extract the least 1 or 2 legs of the source multi-precision
* int, depending on the category of the destination's type. The converter knows how to handle
* both the operand (series of locals) and array forms.
*
* @return the converter
*/
MpToStackConv<TInt, IntJitType, MpIntJitType, T, JT> getConvToStack();
@Override
default <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
return em
.emit(getConvToStack()::convertOpndToStack, opnd, this.type(), ext)
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
}
@Override
default <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
return em
.emit(getConvToStack()::convertArrayToStack, type, this.type(), ext)
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
}
}
@@ -0,0 +1,66 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.*;
/**
* A handler for a p-code variable stored in part of a JVM {@code long}.
*
* @param <ST> the JVM type of the sub variable
* @param <SJT> the p-code type of the sub variable
*/
public interface SubInLongHandler<ST extends BPrim<?>, SJT extends SimpleJitType<ST, SJT>>
extends SubVarHandler<ST, SJT, TLong, LongJitType> {
@Override
default <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
long mask = longMask();
return em
.emit(Op::lload, local().local())
.emit(Op::ldc__l, mask)
.emit(Op::land)
.emit(Op::ldc__l, 0)
.emit(Op::lcmp);
}
@Override
default <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
long mask = longMask();
return em
.emit(Opnd::convert, type, LongJitType.I8, ext)
.emit(Op::ldc__i, bitShift())
.emit(Op::lshl)
.emit(Op::ldc__l, mask)
.emit(Op::land)
.emit(Op::lload, local().local())
.emit(Op::ldc__l, ~mask)
.emit(Op::land)
.emit(Op::lor)
.emit(Op::lstore, local().local());
}
}
@@ -0,0 +1,139 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Scope;
import ghidra.pcode.emu.jit.gen.util.Types.*;
/**
* A handler to p-code variables stored in just a portion of a single JVM local variable.
*
* @param <ST> the JVM type of the sub variable
* @param <SJT> the p-code type of the sub variable
* @param <WT> the JVM type of the containing variable
* @param <WJT> the p-code type of the containing variable
*/
public interface SubVarHandler<ST extends BPrim<?>, SJT extends SimpleJitType<ST, SJT>,
WT extends BPrim<?>, WJT extends SimpleJitType<WT, WJT>> extends VarHandler {
/**
* Verify that the sub variable as shifted actually fits in the containing variable
*
* @param byteShift the number of unused bytes in the container variable to the right of the sub
* variable
* @param type the type of the sub variable
* @param local the containing local variable
*/
default void assertShiftFits(int byteShift, SJT type, JvmLocal<WT, WJT> local) {
assert byteShift >= 0 && byteShift + type.size() <= local.type().size();
}
/**
* {@return the number of unused bytes in the container variable to the right of the sub
* variable}
*/
int byteShift();
/**
* {@return the number of bits in the sub variable}
*/
default int bitSize() {
return type().size() * Byte.SIZE;
}
/**
* {@return the number of unused bits in the container variable to the right of the sub
* variable}
*/
default int bitShift() {
return byteShift() * Byte.SIZE;
}
/**
* {@return the mask indicating which parts of the {@code int} containing variable are within
* the sub variable}
*/
default int intMask() {
return (-1 >>> (Integer.SIZE - bitSize())) << bitShift();
}
/**
* {@return the mask indicating which parts of the {@code long} containing variable are within
* the sub variable}
*/
default long longMask() {
return (-1L >>> (Long.SIZE - bitSize())) << bitShift();
}
/**
* {@return The containing local variable}
*/
JvmLocal<WT, WJT> local();
@Override
SJT type();
/**
* Get the converter of multi-precision integers to the type of the sub variable.
* <p>
* The converter need not worry about positioning or masking wrt. the sub variable. It should
* extract from the multi-precision integer the minimum number of legs needed to fill the sub
* variable, i.e., it need only consider the sub variable's size. This handler will then mask
* and position it within the containing variable for storage.
*
* @return the converter
*/
MpToStackConv<TInt, IntJitType, MpIntJitType, ST, SJT> getConvToSub();
@Override
default <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
return em
.emit(this::genLoadToStack, gen, this.type(), ext)
.emit(Opnd::convertToOpnd, this.type(), name(), type, ext, scope);
}
@Override
default <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
return em
.emit(this::genLoadToStack, gen, this.type(), ext)
.emit(Opnd::convertToArray, this.type(), name(), type, ext, scope, slack);
}
@Override
default <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
return em
.emit(getConvToSub()::convertOpndToStack, opnd, this.type(), ext)
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
}
@Override
default <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
return em
.emit(getConvToSub()::convertArrayToStack, type, this.type(), ext)
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
}
}
@@ -0,0 +1,226 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.alloc;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.op.SubPieceOpGen;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Scope;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
/**
* A handler that knows how to load and store variables' values from local storage.
* <p>
* Some variables are hosted in a single JVM local of compatible type. Others, notably
* multi-precision integers, are allocated among two or more JVM local integers. Each such integer
* is called a "leg" of the multi-precision integer. Other literature may call these "digits" (whose
* value is in [0, 0xffffffff]). Depending on the operator implementation, value may need to be
* loaded with alternative types or in different forms. e.g., any float operator will need to
* convert its inputs into the appropriate float type, even if the operands were allocated as an int
* type. Similarly, some operators are implement their multi-precision computations by invoking
* static methods whose parameters are {@code int[]}, and so they will load and store the array
* forms instead of accessing the legs' locals. This interface provides generators for the various
* circumstances. Each subclass provides the implementations for various allocations.
*/
public interface VarHandler {
/**
* Generate a name for the variable representing the given varnode
* <p>
* These are for debugging purposes. When dumping generating bytecode, the declared local
* variables and their scopes are often also dumped. This provides a human with the local
* variable index for various varnodes.
*
* @param vn the varnode
* @return the name
*/
static String nameVn(Varnode vn) {
return "var_%s_%x_%d".formatted(
vn.getAddress().getAddressSpace().getName(),
vn.getOffset(),
vn.getSize());
}
/**
* Get the complete varnode accessible to this handler
*
* @return the varnode
*/
Varnode vn();
/**
* Get the name for this handler's local variable, named after the varnode is represents.
*
* @return the name of the local variable
*/
default String name() {
return nameVn(vn());
}
/**
* Get the p-code type of the local variable this handler uses.
*
* @return the type
*/
JitType type();
/**
* Emit bytecode to load the varnode's value onto the JVM stack.
*
* @param <TT> the JVM type of the value to load onto the stack
* @param <TJT> the p-code type of the value to load onto the stack
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
* @return the emitter typed with the resulting stack
*/
<TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next> Emitter<Ent<N, TT>>
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext);
/**
* Emit bytecode to load the varnode's value into several locals.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the operand containing the locals, and the emitter typed with the incoming stack
*/
<N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
MpIntJitType type, Ext ext, Scope scope);
/**
* Emit bytecode to load one leg of a multi-precision value from the varnode onto the JVM stack.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the complete multi-precision value
* @param leg the index of the leg to load, 0 being least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
*/
<N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em, JitCodeGenerator<?> gen,
MpIntJitType type, int leg, Ext ext);
/**
* Emit bytecode to load the varnode's value into an integer array in little-endian order,
* pushing its ref onto the JVM stack.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the complete multi-precision value
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @param slack the number of additional, more significant, elements to allocate in the array
* @return the emitter typed with the resulting stack, i.e., having the ref pushed onto it
*/
<N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack);
/**
* Emit bytecode to load the varnode's value, interpreted as a boolean, as an integer onto the
* JVM stack.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @return the emitter typed with the resulting stack, i.e., having the int boolean pushed onto
* it
*/
<N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em, JitCodeGenerator<?> gen);
/**
* Emit bytecode to store a value into a variable from the JVM stack.
*
* @param <FT> the JVM type of the value on the stack
* @param <FJT> the p-code type of the value on the stack
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the value to store on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the value on the stack
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the resulting stack, i.e., having popped the value
*/
<FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope);
/**
* Emit bytecode to store a varnode's value from several locals.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param opnd the operand whose locals contain the value to be stored
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the incoming stack
*/
<N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> opnd, Ext ext, Scope scope);
/**
* Emit bytecode to store a varnode's value from an array of integer legs, in little endian
* order
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the array ref on top
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param type the p-code type of the value on the stack
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the resulting stack, i.e., having popped the array
*/
<N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(Emitter<N0> em,
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope);
/**
* Create a handler for a {@link PcodeOp#SUBPIECE} of a value.
* <p>
* To implement {@link SubPieceOpGen subpiece}, we could load the entire varnode and then
* extract the designated portion. Or, we could load only the designated portion, averting any
* code and execution cost of loading the un-designated portions. We accomplish this by
* re-writing the subpiece op and a load of the sub-varnode.
*
* @param endian the endianness of the emulation target
* @param byteOffset the number of least-significant bytes to remove
* @param maxByteSize the maximum size of the resulting variable. In general, a subpiece should
* never exceed the size of the parent varnode, but if it does, this will truncate
* that excess.
* @return the resulting subpiece handler
*/
VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize);
}
@@ -79,6 +79,13 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
return endian;
}
/**
* Remove the given number of bytes from the higher-offset end of the varnode
*
* @param vn the varnode
* @param amt the number of bytes to remove
* @return the resulting varnode
*/
public Varnode truncVnFromRight(Varnode vn, int amt) {
return new Varnode(vn.getAddress(), vn.getSize() - amt);
}
@@ -102,6 +109,13 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
return subpiece(outVn, endian.isBigEndian() ? amt : 0, in1);
}
/**
* Remove the given number of bytes from the lower-offset end of the varnode
*
* @param vn the varnode
* @param amt the number of bytes to remove
* @return the resulting varnode
*/
public Varnode truncVnFromLeft(Varnode vn, int amt) {
return new Varnode(vn.getAddress().add(amt), vn.getSize() - amt);
}
@@ -228,11 +242,25 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
return dfm.notifyOp(new JitSynthSubPieceOp(out, offset, v)).out();
}
private Varnode subPieceVn(int size, int offset, Varnode whole) {
if (endian.isBigEndian()) {
return new Varnode(whole.getAddress().add(whole.getSize() - offset - size), size);
/**
* Compute the varnode representing a {@link JitSubPieceOp subpiece} of the given varnode
*
* @param endian the endianness of the emulation target
* @param whole the whole varnode
* @param offset the number of least-significant bytes to remove
* @param size the size of the subpiece (maximum, since truncation may occur)
* @return the resulting subpiece.
*/
public static Varnode subPieceVn(Endian endian, Varnode whole, int offset, int size) {
int minSize = Math.min(whole.getSize() - offset, size);
if (minSize < 1) {
throw new AssertionError("subpiece would have non-positive size");
}
return new Varnode(whole.getAddress().add(offset), size);
int addrOffset = switch (endian) {
case BIG -> whole.getSize() - offset - minSize;
case LITTLE -> offset;
};
return new Varnode(whole.getAddress().add(addrOffset), minSize);
}
/**
@@ -246,7 +274,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
* @return the output
*/
public JitVal shaveFromRight(int amt, JitVal in1) {
return subpiece(in1.size() - amt, amt, in1);
return subpiece(in1, amt, in1.size() - amt);
}
/**
@@ -260,7 +288,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
* @return the output
*/
public JitVal shaveFromLeft(int amt, JitVal in1) {
return subpiece(in1.size() - amt, 0, in1);
return subpiece(in1, 0, in1.size() - amt);
}
/**
@@ -286,14 +314,14 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
* @param v the input value
* @return the output value
*/
public JitVal subpiece(int size, int offset, JitVal v) {
public JitVal subpiece(JitVal v, int offset, int size) {
if (v instanceof JitConstVal c) {
return new JitConstVal(size,
OB_SUBPIECE.evaluateBinary(size, v.size(), c.value(), BigInteger.valueOf(offset)));
}
if (v instanceof JitVarnodeVar vv) {
Varnode inVn = vv.varnode();
Varnode outVn = subPieceVn(size, offset, inVn);
Varnode outVn = subPieceVn(endian, inVn, offset, size);
return subpiece(outVn, offset, v);
}
throw new UnsupportedOperationException("unsupported subpiece of " + v);
@@ -15,13 +15,15 @@
*/
package ghidra.pcode.emu.jit.analysis;
import static org.objectweb.asm.Opcodes.*;
import java.util.*;
import org.objectweb.asm.Opcodes;
import ghidra.lifecycle.Unfinished;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
import ghidra.pcode.emu.jit.gen.util.Types;
import ghidra.pcode.emu.jit.gen.util.Types.*;
/**
* The p-code type of an operand.
@@ -35,6 +37,42 @@ import ghidra.lifecycle.Unfinished;
*/
public interface JitType {
/**
* Get the smallest type to which both of the given types can be converted without loss.
* <p>
* When the given types are a mix of integral and floating-point, this chooses an integral type
* whose size is the greater of the two.
*
* @param a the first type
* @param b the second type
* @return the uniform type
*/
static JitType unify(JitType a, JitType b) {
if (a == b) {
return a;
}
int size = Math.max(a.size(), b.size());
return JitTypeBehavior.INTEGER.type(size);
}
/**
* Similar to {@link #unify(JitType, JitType)}, except that it takes the lesser size.
* <p>
* This is used when culling of unnecessary loads is desired and loss of precision is
* acceptable.
*
* @param a the first type
* @param b the second type
* @return the uniform type
*/
static JitType unifyLeast(JitType a, JitType b) {
if (a == b) {
return a;
}
int size = Math.min(a.size(), b.size());
return JitTypeBehavior.INTEGER.type(size);
}
/**
* Compare two types by preference. The type with the more preferred behavior then smaller size
* is preferred.
@@ -67,54 +105,88 @@ public interface JitType {
* @see JitDataFlowUseropLibrary
*/
public static JitType forJavaType(Class<?> cls) {
if (cls == boolean.class) {
return IntJitType.I4;
return SimpleJitType.forJavaType(cls);
}
/**
* A type comprising of legs, each of simple type
*
* @param <T> the JVM type of each leg
* @param <LT> the p-code type of each leg
*/
public interface LeggedJitType<T extends BPrim<?>, LT extends SimpleJitType<T, LT>>
extends JitType {
@Override
List<? extends LT> legTypesBE();
/**
* Cast the given operand's legs as having this type's leg type.
* <p>
* This is (sadly) necessary because of the loss of type information in {@link Opnd} when it
* has a legged type.
*
* @param opnd the operand whose legs to cast
* @return the legs in little-endian order.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
default List<SimpleOpnd<T, LT>> castLegsLE(Opnd<? extends LeggedJitType<?, ?>> opnd) {
return (List) opnd.legsLE();
}
if (cls == byte.class) {
return IntJitType.I1;
}
if (cls == short.class) {
return IntJitType.I2;
}
if (cls == int.class) {
return IntJitType.I4;
}
if (cls == long.class) {
return LongJitType.I8;
}
if (cls == float.class) {
return FloatJitType.F4;
}
if (cls == double.class) {
return DoubleJitType.F8;
}
throw new IllegalArgumentException();
}
/**
* A p-code type that can be represented in a single JVM variable.
*
* @param <T> the JVM type for this JIT type
* @param <JT> this JIT type (recursive)
*/
public interface SimpleJitType extends JitType {
public interface SimpleJitType<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
extends LeggedJitType<T, JT> {
/**
* Identify the p-code type that is exactly represented by the given JVM type.
*
* <p>
* This is used during Direct userop invocation to convert the arguments and return value.
*
* @param cls the primitive class (not boxed)
* @return the p-code type
* @see JitDataFlowUseropLibrary
*/
@SuppressWarnings("unchecked")
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> JT forJavaType(
Class<?> cls) {
if (cls == boolean.class) {
return (JT) IntJitType.I1;
}
if (cls == byte.class) {
return (JT) IntJitType.I1;
}
if (cls == short.class) {
return (JT) IntJitType.I2;
}
if (cls == int.class) {
return (JT) IntJitType.I4;
}
if (cls == long.class) {
return (JT) LongJitType.I8;
}
if (cls == float.class) {
return (JT) FloatJitType.F4;
}
if (cls == double.class) {
return (JT) DoubleJitType.F8;
}
throw new IllegalArgumentException();
}
/**
* The JVM type of the variable that can represent a p-code variable of this type
*
* @return the primitive class (not boxed)
* @return the primitive type (not boxed)
*/
Class<?> javaType();
/**
* The JVM opcode to load a local variable of this type onto the stack
*
* @return the opcode
*/
int opcodeLoad();
/**
* The JVM opcode to store a local variable of this type from the stack
*
* @return the opcode
*/
int opcodeStore();
T bType();
/**
* Re-apply the {@link JitTypeBehavior#INTEGER integer} behavior to this type
@@ -125,10 +197,10 @@ public interface JitType {
*
* @return this type as an int
*/
SimpleJitType asInt();
SimpleJitType<?, ?> asInt();
@Override
SimpleJitType ext();
SimpleJitType<T, JT> ext();
}
/**
@@ -136,7 +208,7 @@ public interface JitType {
*
* @param size the size in bytes
*/
public record IntJitType(int size) implements SimpleJitType {
public record IntJitType(int size) implements SimpleJitType<TInt, IntJitType> {
/** {@code int1}: a 1-byte integer */
public static final IntJitType I1 = new IntJitType(1);
/** {@code int2}: a 2-byte integer */
@@ -183,18 +255,8 @@ public interface JitType {
}
@Override
public Class<?> javaType() {
return int.class;
}
@Override
public int opcodeLoad() {
return ILOAD;
}
@Override
public int opcodeStore() {
return ISTORE;
public TInt bType() {
return Types.T_INT;
}
@Override
@@ -208,7 +270,12 @@ public interface JitType {
}
@Override
public List<IntJitType> legTypes() {
public List<IntJitType> legTypesBE() {
return List.of(this);
}
@Override
public List<IntJitType> legTypesLE() {
return List.of(this);
}
}
@@ -218,7 +285,7 @@ public interface JitType {
*
* @param size the size in bytes
*/
public record LongJitType(int size) implements SimpleJitType {
public record LongJitType(int size) implements SimpleJitType<TLong, LongJitType> {
/** {@code int5}: a 5-byte integer */
public static final LongJitType I5 = new LongJitType(5);
/** {@code int6}: a 6-byte integer */
@@ -228,6 +295,12 @@ public interface JitType {
/** {@code int8}: a 8-byte integer */
public static final LongJitType I8 = new LongJitType(8);
// These are needed only as intermediates during conversion
public static final LongJitType I1 = new LongJitType(1);
public static final LongJitType I2 = new LongJitType(2);
public static final LongJitType I3 = new LongJitType(3);
public static final LongJitType I4 = new LongJitType(4);
/**
* Get the type for an integer of the given size 5 through 8
*
@@ -241,6 +314,11 @@ public interface JitType {
case 6 -> I6;
case 7 -> I7;
case 8 -> I8;
// For intermediate conversion only
case 1 -> I1;
case 2 -> I2;
case 3 -> I3;
case 4 -> I4;
default -> throw new IllegalArgumentException("size:" + size);
};
}
@@ -265,18 +343,8 @@ public interface JitType {
}
@Override
public Class<?> javaType() {
return long.class;
}
@Override
public int opcodeLoad() {
return LLOAD;
}
@Override
public int opcodeStore() {
return LSTORE;
public TLong bType() {
return Types.T_LONG;
}
@Override
@@ -290,7 +358,12 @@ public interface JitType {
}
@Override
public List<LongJitType> legTypes() {
public List<LongJitType> legTypesBE() {
return List.of(this);
}
@Override
public List<LongJitType> legTypesLE() {
return List.of(this);
}
}
@@ -298,7 +371,7 @@ public interface JitType {
/**
* The p-code type for floating-point of size 4, i.e., that fits in a JVM float.
*/
public enum FloatJitType implements SimpleJitType {
public enum FloatJitType implements SimpleJitType<TFloat, FloatJitType> {
/** {@code float4}: a 4-byte float */
F4;
@@ -318,18 +391,8 @@ public interface JitType {
}
@Override
public Class<?> javaType() {
return float.class;
}
@Override
public int opcodeLoad() {
return FLOAD;
}
@Override
public int opcodeStore() {
return FSTORE;
public TFloat bType() {
return Types.T_FLOAT;
}
@Override
@@ -343,7 +406,12 @@ public interface JitType {
}
@Override
public List<FloatJitType> legTypes() {
public List<FloatJitType> legTypesBE() {
return List.of(this);
}
@Override
public List<FloatJitType> legTypesLE() {
return List.of(this);
}
}
@@ -351,7 +419,7 @@ public interface JitType {
/**
* The p-code type for floating-point of size 8, i.e., that fits in a JVM double.
*/
public enum DoubleJitType implements SimpleJitType {
public enum DoubleJitType implements SimpleJitType<TDouble, DoubleJitType> {
/** {@code float8}: a 8-byte float */
F8;
@@ -371,18 +439,8 @@ public interface JitType {
}
@Override
public Class<?> javaType() {
return double.class;
}
@Override
public int opcodeLoad() {
return DLOAD;
}
@Override
public int opcodeStore() {
return DSTORE;
public TDouble bType() {
return Types.T_DOUBLE;
}
@Override
@@ -396,7 +454,12 @@ public interface JitType {
}
@Override
public List<DoubleJitType> legTypes() {
public List<DoubleJitType> legTypesBE() {
return List.of(this);
}
@Override
public List<DoubleJitType> legTypesLE() {
return List.of(this);
}
}
@@ -410,10 +473,33 @@ public interface JitType {
* no matter the language endianness.
*
* @param size the size in bytes
* @param legTypesBE the type of each leg, in big-endian order
* @param legTypesLE the type of each leg, in little-endian order
*/
public record MpIntJitType(int size) implements JitType {
public record MpIntJitType(int size, List<IntJitType> legTypesBE, List<IntJitType> legTypesLE)
implements LeggedJitType<TInt, IntJitType> {
private static final Map<Integer, MpIntJitType> FOR_SIZES = new HashMap<>();
private static int legsAlloc(int size) {
return (size + Integer.BYTES - 1) / Integer.BYTES;
}
private static int partialSize(int size) {
return size % Integer.BYTES;
}
private static List<IntJitType> computeLegTypesBE(int size) {
IntJitType[] types = new IntJitType[legsAlloc(size)];
int i = 0;
if (partialSize(size) != 0) {
types[i++] = IntJitType.forSize(partialSize(size));
}
for (; i < types.length; i++) {
types[i] = IntJitType.I4;
}
return Arrays.asList(types);
}
/**
* Get the type for an integer of the given size 9 or greater
*
@@ -425,6 +511,14 @@ public interface JitType {
return FOR_SIZES.computeIfAbsent(size, MpIntJitType::new);
}
private MpIntJitType(int size, List<IntJitType> legTypesBE) {
this(size, legTypesBE, legTypesBE.reversed());
}
private MpIntJitType(int size) {
this(size, computeLegTypesBE(size));
}
@Override
public int pref() {
return 4;
@@ -441,7 +535,7 @@ public interface JitType {
* @return the total number of legs
*/
public int legsAlloc() {
return (size + Integer.BYTES - 1) / Integer.BYTES;
return legsAlloc(size);
}
/**
@@ -459,20 +553,7 @@ public interface JitType {
* @return the number of bytes in the partial leg, or 0 if all legs are whole
*/
public int partialSize() {
return size % Integer.BYTES;
}
@Override
public List<IntJitType> legTypes() {
IntJitType[] types = new IntJitType[legsAlloc()];
int i = 0;
if (partialSize() != 0) {
types[i++] = IntJitType.forSize(partialSize());
}
for (; i < types.length; i++) {
types[i] = IntJitType.I4;
}
return Arrays.asList(types);
return partialSize(size);
}
@Override
@@ -486,7 +567,7 @@ public interface JitType {
*
* @param size the size in bytes
*/
public record MpFloatJitType(int size) implements JitType {
public record MpFloatJitType(int size) implements LeggedJitType<TInt, IntJitType> {
private static final Map<Integer, MpFloatJitType> FOR_SIZES = new HashMap<>();
/**
@@ -516,7 +597,12 @@ public interface JitType {
}
@Override
public List<SimpleJitType> legTypes() {
public List<IntJitType> legTypesBE() {
return Unfinished.TODO("MpFloat");
}
@Override
public List<IntJitType> legTypesLE() {
return Unfinished.TODO("MpFloat");
}
}
@@ -563,5 +649,16 @@ public interface JitType {
*
* @return the list of types, each fitting in a JVM int, in big-endian order.
*/
List<? extends SimpleJitType> legTypes();
List<? extends SimpleJitType<?, ?>> legTypesBE();
/**
* Get the p-code type that describes the part of the variable in each leg
*
* <p>
* Each whole leg will have the type {@link IntJitType#I4}, and the partial leg, if applicable,
* will have its appropriate smaller integer type.
*
* @return the list of types, each fitting in a JVM int, in little-endian order.
*/
List<? extends SimpleJitType<?, ?>> legTypesLE();
}
@@ -69,9 +69,8 @@ import ghidra.program.model.pcode.PcodeOp;
* 16-byte quadruple-precision, and 32-byte octuple-precision. Some p-code types map precisely to
* JVM counterparts: The 4- and 8-byte integer types map precisely to the JVM's {@code int} and
* {@code long} types. Similarly, the 4- and 8-byte float types map precisely to {@code float} and
* {@code double}. <b>TODO</b>: The JIT translator does not currently support integral types greater
* than 8 bytes (64 bits) in size nor floating-point types other than 4 and 8 bytes (single and
* double precision) in size.
* {@code double}. <b>TODO</b>: The JIT translator does not currently support floating-point types
* other than 4 and 8 bytes (single and double precision) in size.
*
* <h3>Signedness</h3>
* <p>
@@ -15,14 +15,13 @@
*/
package ghidra.pcode.emu.jit.gen;
import static org.objectweb.asm.Opcodes.ATHROW;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.pcode.PcodeOp;
/**
@@ -42,9 +41,9 @@ import ghidra.program.model.pcode.PcodeOp;
*
* @param op the op which may cause an exception
* @param block the block containing the op
* @param label the label at the start of the handler
* @param lbl the label at the start of the handler
*/
public record ExceptionHandler(PcodeOp op, JitBlock block, Label label) {
public record ExceptionHandler(PcodeOp op, JitBlock block, Lbl<Ent<Bot, TRef<Throwable>>> lbl) {
/**
* Construct a handler, generating a new label
*
@@ -52,23 +51,24 @@ public record ExceptionHandler(PcodeOp op, JitBlock block, Label label) {
* @param block the block containing the op
*/
public ExceptionHandler(PcodeOp op, JitBlock block) {
this(op, block, new Label());
this(op, block, Lbl.create());
}
/**
* Emit the handler's code into the {@link JitCompiledPassage#run(int) run} method.
*
* @param <THIS> the type of the compiled passage
* @param em the dead emitter
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
* @return the dead emitter
*/
public void generateRunCode(JitCodeGenerator gen, MethodVisitor rv) {
rv.visitLabel(label);
// [exc]
gen.generatePassageExit(block, () -> {
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
}, gen.getExitContext(op), rv);
// [exc]
rv.visitInsn(ATHROW);
// []
public <THIS extends JitCompiledPassage> Emitter<Dead> genRun(Emitter<Dead> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(Lbl::placeDead, lbl)
.emit(gen::genExit, localThis, block, PcGen.loadOffset(gen.getAddressForOp(op)),
gen.getExitContext(op))
.emit(Op::athrow);
}
}
@@ -15,15 +15,18 @@
*/
package ghidra.pcode.emu.jit.gen;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.Address;
/**
@@ -35,7 +38,7 @@ import ghidra.program.model.address.Address;
*
* @param address the address contained by the page to pre-fetch
*/
public record FieldForArrDirect(Address address) implements InstanceFieldReq {
public record FieldForArrDirect(Address address) implements InstanceFieldReq<TRef<byte[]>> {
@Override
public String name() {
return "arrDir_%s_%x".formatted(address.getAddressSpace().getName(),
@@ -61,29 +64,27 @@ public record FieldForArrDirect(Address address) implements InstanceFieldReq {
* </pre>
*/
@Override
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_BYTE_ARR, null, null);
// [...]
InitFixedLocal.THIS.generateLoadCode(iv);
// [...,this]
gen.generateLoadJitStateSpace(address.getAddressSpace(), iv);
// [...,jitspace]
iv.visitLdcInsn(address.getOffset());
// [...,arr]
iv.visitMethodInsn(INVOKEVIRTUAL, NAME_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE,
"getDirect", MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT, false);
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_BYTE_ARR);
// [...]
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, Types.T_BYTE_ARR, name());
return em
.emit(Op::aload, localThis)
.emit(gen::genLoadJitStateSpace, localThis, address.getAddressSpace())
.emit(Op::ldc__l, address.getOffset())
.emit(Op::invokevirtual, GenConsts.T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE,
"getDirect", GenConsts.MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT,
false)
.step(Inv::takeArg)
.step(Inv::takeObjRef)
.step(Inv::ret)
.emit(Op::putfield, gen.typeThis, name(), Types.T_BYTE_ARR);
}
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
// [...]
RunFixedLocal.THIS.generateLoadCode(rv);
// [...,this]
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
TDESC_BYTE_ARR);
// [...,arr]
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<byte[]>>>
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(Op::aload, localThis)
.emit(Op::getfield, gen.typeThis, name(), Types.T_BYTE_ARR);
}
}
@@ -19,16 +19,20 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.RegisterValue;
/**
* A field request for pre-constructed contextreg value
*/
record FieldForContext(RegisterValue ctx) implements StaticFieldReq {
record FieldForContext(RegisterValue ctx) implements StaticFieldReq<TRef<RegisterValue>> {
@Override
public String name() {
return "CTX_%s".formatted(ctx.getUnsignedValue().toString(16));
@@ -45,33 +49,28 @@ record FieldForContext(RegisterValue ctx) implements StaticFieldReq {
* </pre>
*/
@Override
public void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv) {
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
ClassVisitor cv) {
if (ctx == null) {
return;
return em;
}
cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, name(), TDESC_REGISTER_VALUE, null,
null);
// []
sv.visitFieldInsn(GETSTATIC, gen.nameThis, "LANGUAGE", TDESC_LANGUAGE);
// [language]
sv.visitLdcInsn(ctx.getUnsignedValue().toString(16));
// [language,ctx:STR]
sv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "createContext",
MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT, true);
// [ctx:RV]
sv.visitFieldInsn(PUTSTATIC, gen.nameThis, name(), TDESC_REGISTER_VALUE);
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, T_REGISTER_VALUE, name());
return em
.emit(Op::getstatic, gen.typeThis, "LANGUAGE", T_LANGUAGE)
.emit(Op::ldc__a, ctx.getUnsignedValue().toString(16))
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createContext",
MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::putstatic, gen.typeThis, name(), T_REGISTER_VALUE);
}
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
// [...]
if (ctx == null) {
rv.visitInsn(ACONST_NULL);
}
else {
rv.visitFieldInsn(GETSTATIC, gen.nameThis, name(), TDESC_REGISTER_VALUE);
}
// [...,ctx]
public <N extends Next> Emitter<Ent<N, TRef<RegisterValue>>> genLoad(Emitter<N> em,
JitCodeGenerator<?> gen) {
return ctx == null
? em.emit(Op::aconst_null, T_REGISTER_VALUE)
: em.emit(Op::getstatic, gen.typeThis, name(), T_REGISTER_VALUE);
}
}
@@ -16,19 +16,22 @@
package ghidra.pcode.emu.jit.gen;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
import ghidra.pcode.emu.jit.JitPassage.ExtBranch;
import ghidra.pcode.emu.jit.JitPcodeThread;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.lang.RegisterValue;
/**
@@ -44,7 +47,7 @@ import ghidra.program.model.lang.RegisterValue;
*
* @param target the target address-contextreg pair of the branch exiting via this slot
*/
public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq<TRef<ExitSlot>> {
@Override
public String name() {
return "exit_%x_%s".formatted(target.address.getOffset(), target.biCtx.toString(16));
@@ -72,32 +75,29 @@ public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
* as needed.
*/
@Override
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
FieldForContext ctxField = gen.requestStaticFieldForContext(target.rvCtx);
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_EXIT_SLOT, null, null);
// []
InitFixedLocal.THIS.generateLoadCode(iv);
// [this]
iv.visitInsn(DUP);
// [this,this]
iv.visitLdcInsn(target.address.getOffset());
// [this,this,target:LONG]
ctxField.generateLoadCode(gen, iv);
// [this,this,target:LONG,ctx:RV]
iv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "createExitSlot",
MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT, true);
// [this,slot]
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
// []
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_EXIT_SLOT, name());
return em
.emit(Op::aload, localThis)
.emit(Op::dup)
.emit(Op::ldc__l, target.address.getOffset())
.emit(ctxField::genLoad, gen)
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "createExitSlot",
MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeObjRef)
.step(Inv::ret)
.emit(Op::putfield, gen.typeThis, name(), T_EXIT_SLOT);
}
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
// []
RunFixedLocal.THIS.generateLoadCode(rv);
// [this]
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
// [slot]
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<ExitSlot>>>
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(Op::aload, localThis)
.emit(Op::getfield, gen.typeThis, name(), T_EXIT_SLOT);
}
}
@@ -15,15 +15,18 @@
*/
package ghidra.pcode.emu.jit.gen;
import static ghidra.pcode.emu.jit.gen.GenConsts.TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE;
import static org.objectweb.asm.Opcodes.*;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.AddressSpace;
/**
@@ -36,7 +39,8 @@ import ghidra.program.model.address.AddressSpace;
*
* @param space the address space of the state space to pre-fetch
*/
public record FieldForSpaceIndirect(AddressSpace space) implements InstanceFieldReq {
public record FieldForSpaceIndirect(AddressSpace space)
implements InstanceFieldReq<TRef<JitBytesPcodeExecutorStateSpace>> {
@Override
public String name() {
return "spaceInd_" + space.getName();
@@ -60,27 +64,21 @@ public record FieldForSpaceIndirect(AddressSpace space) implements InstanceField
* </pre>
*/
@Override
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(),
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, null, null);
// [...]
InitFixedLocal.THIS.generateLoadCode(iv);
// [...,this]
gen.generateLoadJitStateSpace(space, iv);
// [...,this,jitspace]
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(),
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
// [...]
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, name());
return em
.emit(Op::aload, localThis)
.emit(gen::genLoadJitStateSpace, localThis, space)
.emit(Op::putfield, gen.typeThis, name(), T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
}
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
// [...]
RunFixedLocal.THIS.generateLoadCode(rv);
// [...,this]
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
// [...,jitspace]
public <THIS extends JitCompiledPassage, N extends Next>
Emitter<Ent<N, TRef<JitBytesPcodeExecutorStateSpace>>>
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(Op::aload, localThis)
.emit(Op::getfield, gen.typeThis, name(), T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
}
}
@@ -16,15 +16,18 @@
package ghidra.pcode.emu.jit.gen;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
/**
@@ -36,7 +39,8 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
* @param userop the definition to pre-fetch
* @see JitDataFlowUseropLibrary
*/
public record FieldForUserop(PcodeUseropDefinition<?> userop) implements InstanceFieldReq {
public record FieldForUserop(PcodeUseropDefinition<?> userop)
implements InstanceFieldReq<TRef<PcodeUseropDefinition<?>>> {
@Override
public String name() {
return "userop_" + userop.getName();
@@ -60,30 +64,27 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop) implements Instanc
* </pre>
*/
@Override
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_PCODE_USEROP_DEFINITION, null,
null);
// []
InitFixedLocal.THIS.generateLoadCode(iv);
// [this]
iv.visitInsn(DUP);
// [this,this]
iv.visitLdcInsn(userop.getName());
// [this,this,name]
iv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "getUseropDefinition",
MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION, true);
// [this,userop]
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
// []
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_PCODE_USEROP_DEFINITION, name());
return em
.emit(Op::aload, localThis)
.emit(Op::dup)
.emit(Op::ldc__a, userop.getName())
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "getUseropDefinition",
MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION)
.step(Inv::takeArg)
.step(Inv::takeObjRef)
.step(Inv::ret)
.emit(Op::putfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION);
}
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
// []
RunFixedLocal.THIS.generateLoadCode(rv);
// [this]
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
// [userop]
public <THIS extends JitCompiledPassage, N extends Next>
Emitter<Ent<N, TRef<PcodeUseropDefinition<?>>>>
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em
.emit(Op::aload, localThis)
.emit(Op::getfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION);
}
}
@@ -19,10 +19,14 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.gen.var.VarGen;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
@@ -37,7 +41,7 @@ import ghidra.program.model.pcode.Varnode;
* @param vn the varnode to pre-construct
* @see JitDataFlowUseropLibrary
*/
public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
public record FieldForVarnode(Varnode vn) implements StaticFieldReq<TRef<Varnode>> {
@Override
public String name() {
Address addr = vn.getAddress();
@@ -56,16 +60,22 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
* </pre>
*/
@Override
public void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv) {
cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, name(), TDESC_VARNODE, null, null);
sv.visitFieldInsn(GETSTATIC, gen.nameThis, "ADDRESS_FACTORY", TDESC_ADDRESS_FACTORY);
sv.visitLdcInsn(vn.getAddress().getAddressSpace().getName());
sv.visitLdcInsn(vn.getAddress().getOffset());
sv.visitLdcInsn(vn.getSize());
sv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "createVarnode",
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true);
sv.visitFieldInsn(PUTSTATIC, gen.nameThis, name(), TDESC_VARNODE);
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
ClassVisitor cv) {
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, T_VARNODE, name());
return em
.emit(Op::getstatic, gen.typeThis, "ADDRESS_FACTORY", T_ADDRESS_FACTORY)
.emit(Op::ldc__a, vn.getAddress().getAddressSpace().getName())
.emit(Op::ldc__l, vn.getAddress().getOffset())
.emit(Op::ldc__i, vn.getSize())
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createVarnode",
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::putstatic, gen.typeThis, name(), T_VARNODE);
}
/**
@@ -79,7 +89,9 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
*
*/
@Override
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
rv.visitFieldInsn(GETSTATIC, gen.nameThis, name(), TDESC_VARNODE);
public <N extends Next> Emitter<Ent<N, TRef<Varnode>>> genLoad(Emitter<N> em,
JitCodeGenerator<?> gen) {
return em
.emit(Op::getstatic, gen.typeThis, name(), T_VARNODE);
}
}
@@ -15,14 +15,14 @@
*/
package ghidra.pcode.emu.jit.gen;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
/**
* A field request for a pre-fetched or pre-constructed element
*
* @param <T> the type of the field
*/
public interface FieldReq {
public interface FieldReq<T extends BNonVoid> {
/**
* Derive a suitable name for the field
*
@@ -30,12 +30,4 @@ public interface FieldReq {
*/
String name();
/**
* Emit code to load the field onto the JVM stack
*
* @param gen the code generator
* @param rv the visitor often for the {@link JitCompiledPassage#run(int) run} method, but could
* be the static initializer or constructor
*/
void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv);
}
File diff suppressed because it is too large Load Diff
@@ -16,12 +16,21 @@
package ghidra.pcode.emu.jit.gen;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Local;
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
/**
* An instance field request initialized in the class constructor
*
* @param <T> the JVM type of the field
*/
public interface InstanceFieldReq extends FieldReq {
public interface InstanceFieldReq<T extends BNonVoid> extends FieldReq<T> {
/**
* Emit the field declaration and its initialization bytecode
*
@@ -29,9 +38,27 @@ public interface InstanceFieldReq extends FieldReq {
* The declaration is emitted into the class definition, and the initialization code is emitted
* into the class constructor.
*
* @param <THIS> the type of the compiled passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param cv the visitor for the class definition
* @param iv the visitor for the class constructor
* @return the emitter typed with the incoming stack
*/
void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv);
<THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv);
/**
* Emit code to load the field onto the JVM stack
*
* @param <THIS> the type of the compiled passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @return the emitter typed with the resulting stack, i.e., having pushed the value
*/
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, T>> genLoad(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen);
}
@@ -16,12 +16,18 @@
package ghidra.pcode.emu.jit.gen;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
/**
* A static field request initialized in the class initializer
*
* @param <T> the JVM type of the field
*/
public interface StaticFieldReq extends FieldReq {
public interface StaticFieldReq<T extends BNonVoid> extends FieldReq<T> {
/**
* Emit the field declaration and its initialization bytecode
*
@@ -29,9 +35,22 @@ public interface StaticFieldReq extends FieldReq {
* The declaration is emitted into the class definition, and the initialization code is emitted
* into the class initializer.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param cv the visitor for the class definition
* @param sv the visitor for the class (static) initializer
* @return the emitter typed with the incoming stack
*/
void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv);
<N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
ClassVisitor cv);
/**
* Emit code to load the field onto the JVM stack
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @return the emitter typed with the resulting stack, i.e., having pushed the value
*/
<N extends Next> Emitter<Ent<N, T>> genLoad(Emitter<N> em, JitCodeGenerator<?> gen);
}
@@ -0,0 +1,177 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitLoadOp;
import ghidra.pcode.emu.jit.op.JitStoreOp;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* A generator to emit code that accesses variables of various size in a
* {@link JitBytesPcodeExecutorState state}, for a specific type and byte order.
* <p>
* This is used by variable birthing and retirement as well as direct memory accesses. Dynamic
* memory accesses, i.e., {@link JitStoreOp store} and {@link JitLoadOp load} do not use this,
* though they may borrow some portions.
*
* @param <JT> the JIT type of the operand
*/
public interface AccessGen<JT extends JitType> {
/**
* Lookup the generator for accessing variables for the given type and byte order
*
* @param endian the byte order
* @param type the p-code type of the variable
* @return the access generator
*/
@SuppressWarnings("unchecked")
public static <T extends JitType> AccessGen<T> lookup(Endian endian, T type) {
return (AccessGen<T>) switch (endian) {
case BIG -> switch (type) {
case IntJitType t -> IntAccessGen.BE;
case LongJitType t -> LongAccessGen.BE;
case FloatJitType t -> FloatAccessGen.BE;
case DoubleJitType t -> DoubleAccessGen.BE;
case MpIntJitType t -> MpIntAccessGen.BE;
default -> throw new AssertionError();
};
case LITTLE -> switch (type) {
case IntJitType t -> IntAccessGen.LE;
case LongJitType t -> LongAccessGen.LE;
case FloatJitType t -> FloatAccessGen.LE;
case DoubleJitType t -> DoubleAccessGen.LE;
case MpIntJitType t -> MpIntAccessGen.LE;
default -> throw new AssertionError();
};
};
}
/**
* Lookup the generator for accessing variables of simple types and the given byte order
*
* @param <T> the JVM type of the variable
* @param <JT> the p-code type of the variable
* @param endian the byte order
* @param type the p-code type of the variable
* @return the access generator
*/
@SuppressWarnings("unchecked")
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> SimpleAccessGen<T, JT>
lookupSimple(Endian endian, JT type) {
return (SimpleAccessGen<T, JT>) switch (endian) {
case BIG -> switch (type) {
case IntJitType t -> IntAccessGen.BE;
case LongJitType t -> LongAccessGen.BE;
case FloatJitType t -> FloatAccessGen.BE;
case DoubleJitType t -> DoubleAccessGen.BE;
default -> throw new AssertionError();
};
case LITTLE -> switch (type) {
case IntJitType t -> IntAccessGen.LE;
case LongJitType t -> LongAccessGen.LE;
case FloatJitType t -> FloatAccessGen.LE;
case DoubleJitType t -> DoubleAccessGen.LE;
default -> throw new AssertionError();
};
};
}
/**
* Lookup the generator for accessing variables of multi-precision integer type and the given
* byte order
*
* @param endian the byte order
* @return the access generator
*/
public static MpIntAccessGen lookupMp(Endian endian) {
return switch (endian) {
case BIG -> MpIntAccessGen.BE;
case LITTLE -> MpIntAccessGen.LE;
};
}
/**
* Emit bytecode to read the given varnode onto the stack as a p-code bool (JVM int)
*
* @param <THIS> the type of the generated passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @return the emitter typed with the resulting stack, i.e., having pushed the value
*/
public static <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
genReadToBool(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
Varnode vn) {
AddressSpace space = vn.getAddress().getAddressSpace();
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size < BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::ldc__i, size)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::ldc__i, BLOCK_SIZE - off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ior);
}
}
@@ -0,0 +1,68 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import ghidra.pcode.emu.jit.analysis.JitType.DoubleJitType;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Local;
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.pcode.Varnode;
/**
* The generator for accessing doubles
*
* <p>
* This is accomplished by delegating to the long access generator with type conversion.
*/
public enum DoubleAccessGen implements SimpleAccessGen<TDouble, DoubleJitType> {
/** The big-endian instance */
BE(LongAccessGen.BE),
/** The little-endian instance */
LE(LongAccessGen.LE);
final LongAccessGen longGen;
private DoubleAccessGen(LongAccessGen longGen) {
this.longGen = longGen;
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TDouble>>
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
Varnode vn) {
return em
.emit(longGen::genReadToStack, localThis, gen, vn)
.emit(LongToDouble.INSTANCE::convertStackToStack, LongJitType.I8, DoubleJitType.F8,
Ext.ZERO);
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TDouble>>
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
return em
.emit(DoubleToLong.INSTANCE::convertStackToStack, DoubleJitType.F8, LongJitType.I8,
Ext.ZERO)
.emit(longGen::genWriteFromStack, localThis, gen, vn);
}
}
@@ -0,0 +1,110 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import static ghidra.pcode.emu.jit.gen.GenConsts.BLOCK_SIZE;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Local;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.pcode.Varnode;
/**
* A generator that exports part of its implementation for use in a {@link MpIntAccessGen}.
*
* <p>
* This really just avoids the re-creation of {@link Varnode} objects for each leg of a large
* varnode. The method instead takes the (space,offset,size) triple as well as the offset of the
* block containing its start.
*/
public interface ExportsLegAccessGen extends SimpleAccessGen<TInt, IntJitType> {
/**
* Emit code to read one JVM int, either a whole variable or one leg of a multi-precision int
* variable.
*
* <p>
* Legs that span blocks are handled as in
* {@link #genReadToStack(Emitter, Local, JitCodeGenerator, Varnode)}
*
* @param <THIS> the type of the generated passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param space the address space of the varnode
* @param block the block offset containing the varnode (or leg)
* @param off the offset of the varnode (or leg)
* @param size the size of the varnode in bytes (or leg)
* @return the emitter typed with the resulting stack, i.e., having pushed the value
*/
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>> genReadLegToStack(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
AddressSpace space, long block, int off, int size);
/**
* Emit code to write one JVM int, either a whole variable or one leg of a multi-precision int
* variable.
*
* <p>
* Legs that span blocks are handled as in
* {@link #genWriteFromStack(Emitter, Local, JitCodeGenerator, Varnode)}
*
* @param <THIS> the type of the generated passage
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the value on top
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param space the address space of the varnode
* @param block the block offset containing the varnode (or leg)
* @param off the offset of the varnode (or leg)
* @param size the size of the varnode in bytes (or leg)
* @return the emitter typed with the resulting stack, i.e., having popped the value
*/
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1>
genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off, int size);
@Override
default <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>> genReadToStack(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn) {
AddressSpace space = vn.getAddress().getAddressSpace();
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
return genReadLegToStack(em, localThis, gen, space, block, off, size);
}
@Override
default <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1>
genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
AddressSpace space = vn.getAddress().getAddressSpace();
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
return genWriteLegFromStack(em, localThis, gen, space, block, off, size);
}
}
@@ -0,0 +1,67 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import ghidra.pcode.emu.jit.analysis.JitType.FloatJitType;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Local;
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.pcode.Varnode;
/**
* The generator for writing floats
*
* <p>
* This is accomplished by delegating to the int access generator with type conversion.
*/
public enum FloatAccessGen implements SimpleAccessGen<TFloat, FloatJitType> {
/** The big-endian instance */
BE(IntAccessGen.BE),
/** The little-endian instance */
LE(IntAccessGen.LE);
final IntAccessGen intGen;
private FloatAccessGen(IntAccessGen intGen) {
this.intGen = intGen;
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TFloat>> genReadToStack(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn) {
return em
.emit(intGen::genReadToStack, localThis, gen, vn)
.emit(IntToFloat.INSTANCE::convertStackToStack, IntJitType.I4, FloatJitType.F4,
Ext.ZERO);
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TFloat>>
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
return em
.emit(FloatToInt.INSTANCE::convertStackToStack, FloatJitType.F4, IntJitType.I4,
Ext.ZERO)
.emit(intGen::genWriteFromStack, localThis, gen, vn);
}
}
@@ -0,0 +1,265 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Endian;
/**
* The generator for writing integers.
*/
public enum IntAccessGen implements MethodAccessGen, ExportsLegAccessGen {
/** The big-endian instance */
BE {
@Override
public String chooseReadName(int size) {
return switch (size) {
case 1 -> "readInt1";
case 2 -> "readIntBE2";
case 3 -> "readIntBE3";
case 4 -> "readIntBE4";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
genReadLegToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
int size) {
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(size),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
.emit(Op::ishl)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ior);
}
@Override
public String chooseWriteName(int size) {
return switch (size) {
case 1 -> "writeInt1";
case 2 -> "writeIntBE2";
case 3 -> "writeIntBE3";
case 4 -> "writeIntBE4";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>>
Emitter<N1> genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
int size) {
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(size),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(Op::dup)
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
.emit(Op::iushr)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
},
/** The little-endian instance */
LE {
@Override
public String chooseReadName(int size) {
return switch (size) {
case 1 -> "readInt1";
case 2 -> "readIntLE2";
case 3 -> "readIntLE3";
case 4 -> "readIntLE4";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
genReadLegToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
int size) {
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(size),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ior);
}
@Override
public String chooseWriteName(int size) {
return switch (size) {
case 1 -> "writeInt1";
case 2 -> "writeIntLE2";
case 3 -> "writeIntLE3";
case 4 -> "writeIntLE4";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>>
Emitter<N1> genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
int size) {
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(size),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(Op::dup)
.emit(Op::ldc__i, BLOCK_SIZE - off)
.emit(Op::iushr)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
};
/**
* Get the {@code int} access generator for the given byte order
*
* @param endian the byte order
* @return the access generator
*/
public static IntAccessGen forEndian(Endian endian) {
return switch (endian) {
case BIG -> BE;
case LITTLE -> LE;
};
}
}
@@ -0,0 +1,299 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.pcode.Varnode;
/**
* Bytes writer for longs in big endian order.
*/
public enum LongAccessGen implements MethodAccessGen, SimpleAccessGen<TLong, LongJitType> {
/** The big-endian instance */
BE {
@Override
public String chooseReadName(int size) {
return switch (size) {
case 1 -> "readLong1";
case 2 -> "readLongBE2";
case 3 -> "readLongBE3";
case 4 -> "readLongBE4";
case 5 -> "readLongBE5";
case 6 -> "readLongBE6";
case 7 -> "readLongBE7";
case 8 -> "readLongBE8";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TLong>>
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
AddressSpace space = vn.getAddress().getAddressSpace();
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(size),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
.emit(Op::lshl)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::lor);
}
@Override
public String chooseWriteName(int size) {
return switch (size) {
case 1 -> "writeLong1";
case 2 -> "writeLongBE2";
case 3 -> "writeLongBE3";
case 4 -> "writeLongBE4";
case 5 -> "writeLongBE5";
case 6 -> "writeLongBE6";
case 7 -> "writeLongBE7";
case 8 -> "writeLongBE8";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TLong>>
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
AddressSpace space = vn.getAddress().getAddressSpace();
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(size),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(Op::dup2__2)
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
.emit(Op::lushr)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
},
/** The little-endian instance */
LE {
@Override
public String chooseReadName(int size) {
return switch (size) {
case 1 -> "readLong1";
case 2 -> "readLongLE2";
case 3 -> "readLongLE3";
case 4 -> "readLongLE4";
case 5 -> "readLongLE5";
case 6 -> "readLongLE6";
case 7 -> "readLongLE7";
case 8 -> "readLongLE8";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TLong>>
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
AddressSpace space = vn.getAddress().getAddressSpace();
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(size),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseReadName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::lor);
}
@Override
public String chooseWriteName(int size) {
return switch (size) {
case 1 -> "writeLong1";
case 2 -> "writeLongLE2";
case 3 -> "writeLongLE3";
case 4 -> "writeLongLE4";
case 5 -> "writeLongLE5";
case 6 -> "writeLongLE6";
case 7 -> "writeLongLE7";
case 8 -> "writeLongLE8";
default -> throw new AssertionError();
};
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TLong>>
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
long offset = vn.getOffset();
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int size = vn.getSize();
AddressSpace space = vn.getAddress().getAddressSpace();
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
if (off + size <= BLOCK_SIZE) {
return em
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(size),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
FieldForArrDirect nxtField =
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
return em
.emit(Op::dup2__2)
.emit(Op::ldc__i, BLOCK_SIZE - off)
.emit(Op::lushr)
.emit(nxtField::genLoad, localThis, gen)
.emit(Op::ldc__i, 0)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(off + size - BLOCK_SIZE),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid)
.emit(blkField::genLoad, localThis, gen)
.emit(Op::ldc__i, off)
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
chooseWriteName(BLOCK_SIZE - off),
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid);
}
};
/**
* Get the {@code long} access generator for the given byte order
*
* @param endian the byte order
* @return the access generator
*/
public static LongAccessGen forEndian(Endian endian) {
return switch (endian) {
case BIG -> BE;
case LITTLE -> LE;
};
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.type;
package ghidra.pcode.emu.jit.gen.access;
import ghidra.pcode.emu.jit.gen.op.LoadOpGen;
import ghidra.pcode.emu.jit.gen.op.StoreOpGen;
@@ -26,13 +26,22 @@ import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
* <p>
* This is needed by {@link LoadOpGen} and {@link StoreOpGen}.
*/
public interface MethodAccessGen extends TypedAccessGen {
public interface MethodAccessGen {
/**
* Choose the name of a method, e.g. {@link JitCompiledPassage#readInt1(byte[], int)} to use for
* the given variable size.
* Choose the name of the read method, e.g. {@link JitCompiledPassage#readInt1(byte[], int)} to
* use for the given variable size.
*
* @param size the size in bytes
* @return the name of the method
*/
String chooseName(int size);
String chooseReadName(int size);
/**
* Choose the name of the write method, e.g.
* {@link JitCompiledPassage#writeInt1(int,byte[], int)} to use for the given variable size.
*
* @param size the size in bytes
* @return the name of the method
*/
String chooseWriteName(int size);
}
@@ -0,0 +1,106 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.pcode.Varnode;
/**
* An access generator for a multi-precision integer variable
*/
public interface MpAccessGen extends AccessGen<MpIntJitType> {
/**
* Emit bytecode to load the varnode's value into several locals.
*
* @param <THIS> the type of the generated passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @param type desired the p-code type of the value
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @return the operand containing the locals, and the emitter typed with the incoming stack
*/
<THIS extends JitCompiledPassage, N extends Next> OpndEm<MpIntJitType, N> genReadToOpnd(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
MpIntJitType type, Ext ext, Scope scope);
/**
* Emit bytecode to load the varnode's value into an integer array in little-endian order,
* pushing its ref onto the JVM stack.
*
* @param <THIS> the type of the generated passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @param type desired the p-code type of the value
* @param ext the kind of extension to apply
* @param scope a scope for generating temporary local storage
* @param slack the number of additional, more significant, elements to allocate in the array
* @return the emitter typed with the resulting stack, i.e., having the ref pushed onto it
*/
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<int[]>>> genReadToArray(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
MpIntJitType type, Ext ext, Scope scope, int slack);
/**
* Emit bytecode to store a value into a variable from the JVM stack.
*
* @param <THIS> the type of the generated passage
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param opnd the operand whose locals contain the value to be stored
* @param vn the varnode
* @return the emitter typed with the incoming stack
*/
<THIS extends JitCompiledPassage, N extends Next> Emitter<N> genWriteFromOpnd(Emitter<N> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Opnd<MpIntJitType> opnd,
Varnode vn);
/**
* Emit bytecode to store a varnode's value from an array of integer legs, in little endian
* order
*
* @param <THIS> the type of the generated passage
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the array ref on top
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the resulting stack, i.e., having popped the array
*/
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1>
genWriteFromArray(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn, Scope scope);
}
@@ -0,0 +1,194 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import static ghidra.pcode.emu.jit.gen.GenConsts.BLOCK_SIZE;
import java.util.ArrayList;
import java.util.List;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.*;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.pcode.Varnode;
/**
* The generator for writing multi-precision ints.
*/
public enum MpIntAccessGen implements MpAccessGen {
/** The big-endian instance */
BE(IntAccessGen.BE) {
@Override
protected List<IntJitType> orderedLegTypes(MpIntJitType type) {
return type.legTypesBE();
}
@Override
protected List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd) {
return opnd.type().castLegsLE(opnd).reversed();
}
},
/** The little-endian instance */
LE(IntAccessGen.LE) {
@Override
protected List<IntJitType> orderedLegTypes(MpIntJitType type) {
return type.legTypesLE();
}
@Override
protected List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd) {
return opnd.type().castLegsLE(opnd);
}
};
final IntAccessGen legGen;
private MpIntAccessGen(IntAccessGen legGen) {
this.legGen = legGen;
}
/**
* Arrange the leg types so that the least-significant one is first
*
* @param type the mp-int type
* @return the leg types in little-endian order
*/
protected abstract List<IntJitType> orderedLegTypes(MpIntJitType type);
/**
* Arrange the operand legs so that the least-significant one is first
*
* @param opnd the mp-int operand
* @return the legs in little-endian order
*/
protected abstract List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd);
@Override
public <THIS extends JitCompiledPassage, N extends Next> OpndEm<MpIntJitType, N> genReadToOpnd(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
MpIntJitType type, Ext ext, Scope scope) {
AddressSpace space = vn.getAddress().getAddressSpace();
List<SimpleOpnd<TInt, IntJitType>> legs = new ArrayList<>();
MpIntJitType fromType = MpIntJitType.forSize(vn.getSize());
long offset = vn.getOffset();
for (IntJitType t : orderedLegTypes(fromType)) {
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
String name = "mpl_%s_%x_%d_leg%x".formatted(space.getName(), vn.getOffset(),
vn.getSize(), offset);
int legSize = t.size();
var result = em
.emit(legGen::genReadLegToStack, localThis, gen, space, block, off, legSize)
.emit(Opnd::createInt, t, name, scope);
legs.add(result.opnd());
em = result.em();
offset += legSize;
}
MpIntLocalOpnd temp = MpIntLocalOpnd.of(fromType,
"mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()), legs);
return MpIntToMpInt.INSTANCE.convertOpndToOpnd(em, temp, type, ext, scope);
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<int[]>>>
genReadToArray(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
Varnode vn, MpIntJitType type, Ext ext, Scope scope, int slack) {
AddressSpace space = vn.getAddress().getAddressSpace();
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR,
"mpa_mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()));
List<IntJitType> fromLegTypes = orderedLegTypes(MpIntJitType.forSize(vn.getSize()));
List<IntJitType> toLegTypes = orderedLegTypes(type);
int legsOut = toLegTypes.size();
int legsIn = fromLegTypes.size();
int defLegs = Integer.min(legsIn, legsOut);
em = em
.emit(Op::ldc__i, defLegs + slack)
.emit(Op::newarray, Types.T_INT)
.emit(Op::astore, arr);
long offset = vn.getOffset();
for (int i = 0; i < defLegs; i++) {
IntJitType fromLegType = fromLegTypes.get(i);
IntJitType toLegType = toLegTypes.get(i);
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int legSize = fromLegType.size();
em = em
.emit(Op::aload, arr)
.emit(Op::ldc__i, i)
.emit(legGen::genReadLegToStack, localThis, gen, space, block, off, legSize)
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
.emit(Op::iastore);
offset += legSize;
}
return em
.emit(MpIntToMpInt::doGenArrExt, arr, legsOut, defLegs, ext, scope)
.emit(Op::aload, arr);
}
@Override
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genWriteFromOpnd(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
Opnd<MpIntJitType> opnd, Varnode vn) {
AddressSpace space = vn.getAddress().getAddressSpace();
long offset = vn.getOffset();
for (SimpleOpnd<TInt, IntJitType> leg : orderedLegs(opnd)) {
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int legSize = leg.type().size();
em = em
.emit(leg::read)
.emit(legGen::genWriteLegFromStack, localThis, gen, space, block, off, legSize);
offset += legSize;
}
return em;
}
@Override
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TRef<int[]>>>
Emitter<N1> genWriteFromArray(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn, Scope scope) {
AddressSpace space = vn.getAddress().getAddressSpace();
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR,
"mpa_mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()));
var em1 = em
.emit(Op::astore, arr);
List<IntJitType> legTypes = orderedLegTypes(MpIntJitType.forSize(vn.getSize()));
final int legCount = legTypes.size();
long offset = vn.getOffset();
for (int i = 0; i < legCount; i++) {
IntJitType t = legTypes.get(i);
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
int off = (int) (offset - block);
int legSize = t.size();
em1 = em1
.emit(Op::aload, arr)
.emit(Op::ldc__i, i)
.emit(Op::iaload)
.emit(legGen::genWriteLegFromStack, localThis, gen, space, block, off, legSize);
offset += legSize;
}
return em1;
}
}
@@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.access;
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Local;
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.program.model.pcode.Varnode;
/**
* An access generator for simple-typed variables
*
* @param <T> the JVM type of the variable
* @param <JT> the p-code type of the variable
*/
public interface SimpleAccessGen<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
extends AccessGen<JT> {
/**
* Emit code to read a varnode
* <p>
* If the varnode fits completely in the block (the common case), then this accesses the bytes
* from the one block, using the method chosen by size. If the varnode extends into the next
* block, then this will split the varnode into two portions according to machine byte order.
* Each portion is accessed using the method for the size of that portion. The results are
* reassembled into a single operand.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @return the code generator with the resulting stack, i.e., having pushed the value
*/
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, T>> genReadToStack(
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn);
/**
* Emit code to write a varnode
* <p>
* If the varnode fits completely in the block (the common case), then this accesses the bytes
* from the one block, using the method chosen by size. If the varnode extends into the next
* block, then this will split the varnode into two portions according to machine byte order.
* Each portion is accessed using the method for the size of that portion.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the value on top
* @param em the emitter typed with the incoming stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param vn the varnode
* @return the code generator with the resulting stack, i.e., having popped the value
*/
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, T>> Emitter<N1>
genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn);
}
@@ -15,19 +15,15 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_JIT_COMPILED_PASSAGE;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.gen.GenConsts;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitBinOp;
/**
@@ -66,74 +62,54 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
* arrays, invoke the method, and then place the result legs on the stack, least-significant leg
* on top.
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param gen the code generator
* @param localThis a handle to the local holding the {@code this} reference
* @param type the type of the operands
* @param methodName the name of the method in {@link JitCompiledPassage} to invoke
* @param mv the method visitor
* @param overProvisionLeft the number of extra ints to allocate for the left operand's array.
* This is to facilitate Knuth's division algorithm, which may require an extra
* leading leg in the dividend after normalization.
* @param op the p-code op
* @param slackLeft the number of extra ints to allocate for the left operand's array. This is
* to facilitate Knuth's division algorithm, which may require an extra leading leg
* in the dividend after normalization.
* @param takeOut indicates which operand of the static method to actually take for the output.
* This is to facilitate the remainder operator, because Knuth's algorithm leaves the
* remainder where there dividend was.
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the empty stack
*/
static void generateMpDelegationToStaticMethod(JitCodeGenerator gen, MpIntJitType type,
String methodName, MethodVisitor mv, int overProvisionLeft, TakeOut takeOut) {
default <THIS extends JitCompiledPassage> Emitter<Bot> genMpDelegationToStaticMethod(
Emitter<Bot> em, JitCodeGenerator<THIS> gen, Local<TRef<THIS>> localThis,
MpIntJitType type, String methodName, JitBinOp op, int slackLeft, TakeOut takeOut,
Scope scope) {
/**
* The strategy here will be to allocate an array for each of the operands (output and 2
* inputs) and then invoke a static method to do the actual operation. It might be nice to
* generate inline code for small multiplications, but we're going to leave that for later.
*/
// [lleg1,...,llegN,rleg1,...,rlegN]
JitAllocationModel am = gen.getAllocationModel();
int legCount = type.legsAlloc();
try (
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", legCount);
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", legCount)) {
// [rleg1,...,rlegN,lleg1,...,llegN]
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
// [lleg1,...,llegN]
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
// []
switch (takeOut) {
case OUT -> {
// []
mv.visitLdcInsn(legCount);
// [count:INT]
mv.visitIntInsn(NEWARRAY, T_INT);
// [out:INT[count]]
mv.visitInsn(DUP);
// [out,out]
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
// [inL,out,out]
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
// [inR,inL,out,out]
}
case LEFT -> {
// []
mv.visitLdcInsn(legCount);
// [count:INT]
mv.visitIntInsn(NEWARRAY, T_INT);
// [out]
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
// [inL,out]
mv.visitInsn(DUP_X1);
// [inL,out,inL]
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
// [inR,inL,out,inL]
}
default -> throw new AssertionError();
}
}
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName,
MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP, true);
// [out||inL:INT[count]]
// Push the result back, in reverse order
OpGen.generateMpLegsFromArray(legCount, mv);
var emParams = switch (takeOut) {
case OUT -> em
.emit(Op::ldc__i, legCount)
.emit(Op::newarray, Types.T_INT)
.emit(Op::dup)
.emit(gen::genReadToArray, localThis, op.l(), type, ext(), scope, slackLeft)
.emit(gen::genReadToArray, localThis, op.r(), type, rExt(), scope, 0);
case LEFT -> em
.emit(Op::aconst_null, Types.T_INT_ARR)
.emit(gen::genReadToArray, localThis, op.l(), type, ext(), scope, slackLeft)
.emit(Op::dup_x1)
.emit(gen::genReadToArray, localThis, op.r(), type, rExt(), scope, 0);
};
return emParams
.emit(Op::invokestatic, GenConsts.T_JIT_COMPILED_PASSAGE, methodName,
GenConsts.MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP, true)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::retVoid)
.emit(gen::genWriteFromArray, localThis, op.out(), type, ext(), scope);
}
/**
@@ -164,63 +140,4 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
default Ext rExt() {
return ext();
}
/**
* Emit code between reading the left and right operands
*
* <p>
* This is invoked immediately after emitting code to push the left operand onto the stack,
* giving the implementation an opportunity to perform any manipulations of that operand
* necessary to set up the operation, before code to push the right operand is emitted.
*
* @param gen the code generator
* @param op the operator
* @param lType the actual type of the left operand
* @param rType the actual type of the right operand
* @param rv the method visitor
* @return the new actual type of the left operand
*/
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
MethodVisitor rv) {
return lType;
}
/**
* Emit code for the binary operator
*
* <p>
* At this point both operands are on the stack. After this returns, code to write the result
* from the stack into the destination operand will be emitted.
*
* @param gen the code generator
* @param op the operator
* @param block the block containing the operator
* @param lType the actual type of the left operand
* @param rType the actual type of the right operand
* @param rv the method visitor
* @return the actual type of the result
*/
JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
JitType rType, MethodVisitor rv);
/**
* {@inheritDoc}
*
* <p>
* This default implementation emits code to load the left operand, invokes the
* {@link #afterLeft(JitCodeGenerator, JitBinOp, JitType, JitType, MethodVisitor) after-left}
* hook point, emits code to load the right operand, invokes
* {@link #generateBinOpRunCode(JitCodeGenerator, JitBinOp, JitBlock, JitType, JitType, MethodVisitor)
* generate-binop}, and finally emits code to write the destination operand.
*/
@Override
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
JitType lType = gen.generateValReadCode(op.l(), op.lType(), ext());
JitType rType = op.rType().resolve(gen.getTypeModel().typeOf(op.r()));
lType = afterLeft(gen, op, lType, rType, rv);
JitType checkRType = gen.generateValReadCode(op.r(), op.rType(), rExt());
assert checkRType == rType;
JitType outType = generateBinOpRunCode(gen, op, block, lType, rType, rv);
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
}
}
@@ -1,117 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.op.JitBinOp;
/**
* An extension for bitwise binary operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface BitwiseBinOpGen<T extends JitBinOp> extends IntBinOpGen<T> {
@Override
default boolean isSigned() {
return false;
}
/**
* The JVM opcode to implement this operator with int operands on the stack.
*
* @return the opcode
*/
int intOpcode();
/**
* The JVM opcode to implement this operator with long operands on the stack.
*
* @return the opcode
*/
int longOpcode();
/**
* The implementation for multi-precision ints.
*
* @param gen the code generator
* @param type the type of each operand, including the reuslt
* @param mv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
default void generateMpIntBinOp(JitCodeGenerator gen, MpIntJitType type,
MethodVisitor mv) {
/**
* We need temp locals to get things in order. Read in right operand, do the op as we pop
* each left op. Then push it all back.
*
* No masking of the result is required, since both operands should already be masked, and
* the bitwise op cannot generate bits of more significance.
*/
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
int legCount = type.legsAlloc();
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
for (int i = 0; i < legCount; i++) {
// [lleg1,...,llegN:INT]
mv.visitVarInsn(ILOAD, result.idx(i));
// [lleg1,...,llegN:INT,rlegN:INT]
mv.visitInsn(intOpcode());
// [lleg1,...,olegN:INT]
mv.visitVarInsn(ISTORE, result.idx(i));
// [lleg1,...]
}
OpGen.generateMpLegsFromTemp(result, legCount, mv);
}
}
@Override
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
MethodVisitor rv) {
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
}
/**
* {@inheritDoc}
*
* <p>
* This implementation reduces the need to just the JVM opcode. We simply ensure both operands
* have the same size and JVM type, select and emit the correct opcode, and return the type of
* the result.
*/
@Override
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
JitType rType, MethodVisitor rv) {
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
switch (rType) {
case IntJitType t -> rv.visitInsn(intOpcode());
case LongJitType t -> rv.visitInsn(longOpcode());
case MpIntJitType t when t.size() == lType.size() -> generateMpIntBinOp(gen, t, rv);
case MpIntJitType t -> TODO("MpInt of differing sizes");
default -> throw new AssertionError();
}
return rType;
}
}
@@ -15,6 +15,14 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.pcode.emu.jit.op.JitBoolAndOp;
import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
@@ -28,17 +36,19 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
* behavior," we could technically optimize this by only ANDing the least significant leg
* when we're dealing with mp-ints.
*/
public enum BoolAndOpGen implements BitwiseBinOpGen<JitBoolAndOp> {
public enum BoolAndOpGen implements IntBitwiseBinOpGen<JitBoolAndOp> {
/** The generator singleton */
GEN;
@Override
public int intOpcode() {
return IAND;
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
return Op.iand(em);
}
@Override
public int longOpcode() {
return LAND;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
return Op.land(em);
}
}
@@ -15,23 +15,27 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitBoolNegateOp;
import ghidra.pcode.opbehavior.OpBehaviorBoolNegate;
/**
* The generator for a {@link JitBoolNegateOp bool_negate}.
* <p>
* This emits ^1, as observed in code emitted by {@code javac}. For multi-precision, we perform that
* operation only on the least-significant leg.
*
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
* This allows us to use bitwise logic instead of having to check for any non-zero value,
* just like {@link OpBehaviorBoolNegate}.
* just like {@link OpBehaviorBoolNegate}. Additionally, boolean operands ought to be a
* byte, but certainly no larger than an int (4 bytes).
*/
public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
public enum BoolNegateOpGen implements IntOpUnOpGen<JitBoolNegateOp> {
/** The generator singleton */
GEN;
@@ -41,24 +45,34 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitBoolNegateOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case IntJitType t -> {
rv.visitLdcInsn(1);
rv.visitInsn(IXOR);
}
case LongJitType t -> {
rv.visitLdcInsn(1L);
rv.visitInsn(LXOR);
}
case MpIntJitType t -> {
// Least-sig leg is on top, and it's an int.
rv.visitLdcInsn(1);
rv.visitInsn(IXOR);
}
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
opForInt(Emitter<N0> em) {
return em
.emit(Op::ldc__i, 1)
.emit(Op::ixor);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
opForLong(Emitter<N0> em) {
return em
.emit(Op::ldc__l, 1)
.emit(Op::lxor);
}
@Override
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitBoolNegateOp op,
MpIntJitType type, Scope scope) {
/**
* NOTE: This will needlessly overwrite the upper legs of the mp-int output. That said,
* Sleigh-spec authors should keep "boolean" operands no larger than an int, preferably a
* byte.
*/
return em
.emit(gen::genReadLegToStack, localThis, op.u(), type, 0, ext())
.emit(this::opForInt)
.emit(gen::genWriteFromStack, localThis, op.out(), type.legTypesLE().getFirst(),
ext(), scope);
}
}
@@ -15,6 +15,14 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.pcode.emu.jit.op.JitBoolOrOp;
import ghidra.pcode.opbehavior.OpBehaviorBoolOr;
@@ -24,18 +32,23 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolOr;
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
* This allows us to use bitwise logic instead of having to check for any non-zero value,
* just like {@link OpBehaviorBoolOr}. Thus, this is identical to {@link IntOrOpGen}.
* @implNote Because having bits other than the least significant set in the inputs is "undefined
* behavior," we could technically optimize this by only ANDing the least significant leg
* when we're dealing with mp-ints.
*/
public enum BoolOrOpGen implements BitwiseBinOpGen<JitBoolOrOp> {
public enum BoolOrOpGen implements IntBitwiseBinOpGen<JitBoolOrOp> {
/** The generator singleton */
GEN;
@Override
public int intOpcode() {
return IOR;
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
return Op.ior(em);
}
@Override
public int longOpcode() {
return LOR;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
return Op.lor(em);
}
}
@@ -15,6 +15,14 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.util.Emitter;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.pcode.emu.jit.op.JitBoolXorOp;
import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
@@ -24,18 +32,23 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
* This allows us to use bitwise logic instead of having to check for any non-zero value,
* just like {@link OpBehaviorBoolXor}. Thus, this is identical to {@link IntXorOpGen}.
* @implNote Because having bits other than the least significant set in the inputs is "undefined
* behavior," we could technically optimize this by only ANDing the least significant leg
* when we're dealing with mp-ints.
*/
public enum BoolXorOpGen implements BitwiseBinOpGen<JitBoolXorOp> {
public enum BoolXorOpGen implements IntBitwiseBinOpGen<JitBoolXorOp> {
/** The generator singleton */
GEN;
@Override
public int intOpcode() {
return IXOR;
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
return Op.ixor(em);
}
@Override
public int longOpcode() {
return LXOR;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
return Op.lxor(em);
}
}

Some files were not shown because too many files have changed in this diff Show More