Merge remote-tracking branch 'origin/GP-5980_Dan_jitMpIntMore--SQUASHED'

This commit is contained in:
Ryan Kurtz
2025-12-15 08:23:26 -05:00
202 changed files with 24377 additions and 10141 deletions

View File

@@ -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();
}

View File

@@ -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();
};
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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?");
}
}

View File

@@ -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?");
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,607 @@
/* ###
* 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.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.opnd.SimpleOpnd.SimpleOpndEm;
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
* <em>do not</em> align to the variable's legs.
* <p>
* The below diagram is an example shifted allocation, whose {@code byteShift} value is 3, and whose
* varnode size is 11 (admittedly pathological, but made to illustrate a complicated example).
*
* <pre>
* +--*--*--*--+--*--*--*--+--*--*--*--+--*--*--*--+
* | parts[3] | parts[2] | parts[1] | parts[0] |
* +-----------+-----------+-----------+-----------+
* +--*--*--+--*--*--*--+--*--*--*--+
* | leg2 | leg1 | leg0 |
* +--------+-----------+-----------+
* </pre>
* <p>
* In the unaligned case, all loads and stores require copying the shifted value into a series of
* temporary locals, representing the legs of the value. Because these are already temporary, the
* operator may freely use the legs as temporary storage.
*
* @param parts the list of locals spanned by the variable, in little-endian order.
* @param type the type of the multi-precision integer variable (only considering the varnode, not
* the whole comprised of the spanned parts). In the diagram, this would be
* {@link MpIntJitType}{@code (size=11)}.
* @param vn the complete varnode accessible to this handler. NOTE: The handler must take care not
* to modify or permit access to portions of the parts at either end not actually part of
* its varnode. In the example, the lower 24 bits of {@code parts[0]} and the upper 16
* bits of {@code parts[3]} cannot be accessed. Should a caller to
* {@link #genLoadToOpnd(Emitter, JitCodeGenerator, MpIntJitType, Ext, Scope)} specify a
* type larger than 11 bytes, only the 11-byte value is loaded, then extended to the
* requested size. We do not load the more sigificant portion of {@code parts[3]}.
* @param byteShift the number of least-significant bytes of the handler's least-significant part
* that are <em>excluded</em> from the variable's least-significant leg. I.e., the number
* of bytes to shift right when loading the value. In the example, this is 3.
*/
public record ShiftedMpIntHandler(List<JvmLocal<TInt, IntJitType>> parts, MpIntJitType type,
Varnode vn, int byteShift) implements VarHandler {
@SuppressWarnings("javadoc")
public ShiftedMpIntHandler {
assert byteShift > 0 && byteShift < 4;
assert parts.size() > 1;
}
private int bitShift() {
return byteShift * Byte.SIZE;
}
/**
* Emit bytecode to load the right portion of a given leg
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param i the index of the part, 0 being the least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having the portion pushed onto it
* positioned in an int
*/
private <N extends Next> Emitter<Ent<N, TInt>> doGenLoadIntRight(Emitter<N> em,
JitCodeGenerator<?> gen, int i, Ext ext) {
return em
.emit(parts.get(i)::genLoadToStack, gen, IntJitType.I4, ext)
.emit(Op::ldc__i, bitShift())
.emit(Op::iushr);
}
/**
* Emit bytecode to load and {@code or} in the left portion of a given leg
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the right portion of the same leg already on it
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param i the index of the part, 0 being the least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the incoming stack, though the top value has been modified.
*/
private <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
doGenLoadOrIntLeft(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
if (i >= parts.size()) {
// TODO: This may require attention to ext
return Misc.cast1(em);
}
return em
.emit(parts.get(i)::genLoadToStack, gen, IntJitType.I4, ext)
.emit(Op::ldc__i, Integer.SIZE - bitShift())
.emit(Op::ishl)
.emit(Op::ior);
}
/**
* Emit bytecode to load the right portion of a long
* <p>
* This operates as if the long is positioned at the same byte offset as the least-sigificant
* leg. Adapting the example from the class documentation:
*
* <pre>
* +--*--*--*--+--*--*--*--+--*--*--*--+--*--*--*--+
* | parts[3] | parts[2] | parts[1] | parts[0] |
* +-----------+-----------+-----------+-----------+
* +--*--*--+--*--*--*--+--*--*--*--+
* | leg2 | leg1 | leg0 |
* +--------+-----------+-----------+
* +-----------------------+
* | as long |
* +-----------------------+
* </pre>
* <p>
* Thus, to load the full long, we need to retrieve and shift into place the values from
* {@code parts[0]}, {@code [1]}, and {@code [2]}. This method loads the right-most portion.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param i the index of the part, 0 being the least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having the portion pushed onto it
* positioned in a long
*/
private <N extends Next> Emitter<Ent<N, TLong>> doGenLoadLongRight(Emitter<N> em,
JitCodeGenerator<?> gen, int i, Ext ext) {
return em
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
.emit(Op::ldc__i, bitShift())
.emit(Op::lushr);
}
/**
* Emit bytecode to load and {@code or} in the middle portion of a long
*
* @see #doGenLoadLongRight(Emitter, JitCodeGenerator, int, Ext)
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the right portion of the long already on it
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param i the index of the part, 0 being the least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the incoming stack, though the top value has been modified.
*/
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
doGenLoadOrLongMiddle(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
return em
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
.emit(Op::ldc__i, Integer.SIZE - bitShift())
.emit(Op::lshl)
.emit(Op::lor);
}
/**
* Emit bytecode to load and {@code or} in the left portion of a long
*
* @see #doGenLoadLongRight(Emitter, JitCodeGenerator, int, Ext)
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the right and middle portions of the long already
* {@code or}ed together on it
* @param em the emitter typed with the incoming stack
* @param gen the code generator
* @param i the index of the part, 0 being least significant
* @param ext the kind of extension to apply
* @return the emitter typed with the incoming stack, though the top value has been modified.
*/
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
doGenLoadOrLongLeft(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
return em
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
.emit(Op::ldc__i, Integer.SIZE * 2 - bitShift())
.emit(Op::lshl)
.emit(Op::lor);
}
@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 switch (type) {
case IntJitType t when t.size() + byteShift <= Integer.BYTES -> em
// We know at most 4 bytes (1 part) are involved
.emit(this::doGenLoadIntRight, gen, 0, ext)
.emit(Opnd::convert, IntJitType.forSize(Integer.BYTES - byteShift), type, ext);
case IntJitType t -> em
.emit(this::doGenLoadIntRight, gen, 0, ext)
.emit(this::doGenLoadOrIntLeft, gen, 1, ext)
.emit(Opnd::convert, IntJitType.I4, type, ext);
case LongJitType t when t.size() + byteShift <= Long.BYTES -> em
// We know at most 8 bytes (2 parts) are involved
.emit(this::doGenLoadLongRight, gen, 0, ext)
.emit(this::doGenLoadOrLongMiddle, gen, 1, ext)
.emit(Opnd::convert, LongJitType.forSize(Long.BYTES - byteShift), type, ext);
case LongJitType t -> em
.emit(this::doGenLoadLongRight, gen, 0, ext)
.emit(this::doGenLoadOrLongMiddle, gen, 1, ext)
.emit(this::doGenLoadOrLongLeft, gen, 2, ext)
.emit(Opnd::convert, LongJitType.I8, type, 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) {
/**
* NOTE: Even though we can access more significant parts, we should not incorporate
* anything beyond what is allowed by the type (which corresponds to the varnode size).
*/
List<IntJitType> fromLegTypes = type.legTypesLE();
List<IntJitType> toLegTypes = to.legTypesLE();
List<SimpleOpnd<TInt, IntJitType>> toLegs = new ArrayList<>();
int legsOut = toLegTypes.size();
int legsIn = fromLegTypes.size();
int defLegs = Integer.min(legsIn, legsOut);
for (int i = 0; i < defLegs; i++) {
IntJitType fromLegType = fromLegTypes.get(i);
IntJitType toLegType = toLegTypes.get(i);
var result = em
.emit(this::doGenLoadIntRight, gen, i, ext)
.emit(this::doGenLoadOrIntLeft, gen, i + 1, ext)
// This chained convert should blot out anything outside the varnode
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
.emit(Opnd::createInt, toLegType, "%s_leg%d".formatted(name(), i), scope);
em = result.em();
toLegs.add(result.opnd());
}
if (legsOut > defLegs) {
var sign = switch (ext) {
case ZERO -> new SimpleOpndEm<>(IntConstOpnd.ZERO_I4, em);
case SIGN -> em
.emit(toLegs.getLast()::read)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr) // Signed
.emit(Opnd::createIntReadOnly, IntJitType.I4,
"%s_sign".formatted(name()), scope);
};
em = sign.em();
for (int i = defLegs; i < legsOut; i++) {
toLegs.add(sign.opnd());
}
}
return new OpndEm<>(MpIntLocalOpnd.of(to, name(), toLegs), em);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
List<IntJitType> fromLegTypes = type.legTypesLE();
int fromLegCount = fromLegTypes.size();
if (leg >= fromLegCount) {
return switch (ext) {
case ZERO -> em
.emit(Op::ldc__i, 0);
case SIGN -> em
.emit(this::doGenLoadIntRight, gen, fromLegCount - 1, ext) // Remove ???
.emit(this::doGenLoadOrIntLeft, gen, fromLegCount, ext)
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegTypes.getLast(), ext)
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::ishr);
};
}
IntJitType fromLegType = fromLegTypes.get(leg);
IntJitType toLegType = type.legTypesLE().get(leg);
return em
.emit(this::doGenLoadIntRight, gen, leg, ext)
.emit(this::doGenLoadOrIntLeft, gen, leg + 1, ext)
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
.emit(Opnd::convertIntToInt, fromLegType, toLegType, 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) {
List<IntJitType> fromLegTypes = type.legTypesLE();
List<IntJitType> toLegTypes = to.legTypesLE();
int legsOut = toLegTypes.size();
int legsIn = fromLegTypes.size();
int defLegs = Integer.min(legsIn, legsOut);
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR, name());
em = em
.emit(Op::ldc__i, legsOut + slack)
.emit(Op::newarray, Types.T_INT)
.emit(Op::astore, arr);
for (int i = 0; i < defLegs; i++) {
IntJitType fromLegType = fromLegTypes.get(i);
IntJitType toLegType = toLegTypes.get(i);
em = em
.emit(Op::aload, arr)
.emit(Op::ldc__i, i)
.emit(this::doGenLoadIntRight, gen, i, ext)
.emit(this::doGenLoadOrIntLeft, gen, i + 1, ext)
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
.emit(Op::iastore);
}
return em
.emit(MpIntToMpInt::doGenArrExt, arr, legsOut, defLegs, ext, scope)
.emit(Op::aload, arr);
}
@Override
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
JitCodeGenerator<?> gen) {
int maskRight = -1 << bitShift();
var result = em
.emit(Op::iload, parts.get(0).local())
.emit(Op::ldc__i, maskRight)
.emit(Op::iand);
for (int i = 0; i < parts.size(); i++) {
int bytesLeft = type.size() - byteShift - i * Integer.SIZE;
var twoInts = result
.emit(Op::iload, parts.get(i).local());
if (bytesLeft < Integer.SIZE) {
int maskLeft = -1 >>> (Integer.SIZE - bytesLeft);
twoInts = twoInts
.emit(Op::ldc__i, maskLeft)
.emit(Op::iand);
}
result = twoInts
.emit(Op::ior);
}
return result.emit(Opnd::intToBool);
}
/**
* Emit bytecode to store from the stack into a given part
* <p>
* This will combined the existing value and the positioned value using a mask of the accessible
* portion of the given part. This code will compute that mask and emit the bytecode to apply
* it.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack having the value on top, positioned in an int
* @param em the emitter typed with the incoming stack
* @param i the index of the part, 0 being the least significant
* @return the emitter typed with the resulting stack, i.e., having popped the value
*/
private <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenStoreInt(Emitter<N0> em,
int i) {
int bitShift = byteShift * Byte.SIZE;
int mask = -1;
if (i == 0) {
mask &= -1 << bitShift;
}
// bytes we'd exceed to the left
int bytesToRight = Integer.BYTES * (i + 1) - type.size() - byteShift;
if (bytesToRight > 0) {
mask &= -1 >>> (bytesToRight * Byte.SIZE);
}
JvmLocal<TInt, IntJitType> part = parts.get(i);
assert mask != 0;
return mask == -1
? em
.emit(Op::istore, part.local())
: em
.emit(Op::ldc__i, mask)
.emit(Op::iand)
.emit(Op::iload, part.local())
.emit(Op::ldc__i, ~mask)
.emit(Op::iand)
.emit(Op::ior)
.emit(Op::istore, part.local());
}
@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) {
int bitShift = byteShift * Byte.SIZE;
return switch (from) {
case IntJitType t -> {
var emConvPos = em
.emit(Opnd::convert, from, LongJitType.I8, ext)
.emit(Op::ldc__i, bitShift)
.emit(Op::lshl);
for (int i = 0; i < parts.size() - 1; i++) {
emConvPos = emConvPos
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(this::doGenStoreInt, i)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Opnd::lextshr, ext);
}
yield emConvPos
.emit(Op::l2i)
.emit(this::doGenStoreInt, parts.size() - 1);
}
case LongJitType t -> {
var emConvPos = em
.emit(Opnd::convert, from, LongJitType.I8, ext)
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(Op::ldc__i, bitShift)
.emit(Op::ishl)
.emit(this::doGenStoreInt, 0)
.emit(Op::ldc__i, Integer.SIZE - bitShift)
.emit(Opnd::lextshr, ext);
for (int i = 1; i < parts.size() - 1; i++) {
emConvPos = emConvPos
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(this::doGenStoreInt, i)
.emit(Op::ldc__i, Integer.SIZE)
.emit(Opnd::lextshr, ext);
}
yield emConvPos
.emit(Op::l2i)
.emit(this::doGenStoreInt, parts.size() - 1);
}
default -> throw new AssertionError();
};
}
/**
* Emit bytecode to load a leg from the source operand and position it within a long on the
* stack.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param leg the leg to load
* @param bitShift the number of bits to shift left
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having pushed the long
*/
private <N extends Next> Emitter<Ent<N, TLong>> positionOpndLeg(Emitter<N> em,
SimpleOpnd<TInt, IntJitType> leg, int bitShift, Ext ext) {
return em
.emit(leg::read)
.emit(Opnd::convert, leg.type(), LongJitType.I8, ext)
.emit(Op::ldc__i, bitShift)
.emit(Op::lshl);
}
/**
* Emit bytecode to store a leg into its two overlapped parts, then load the next leg from the
* source positioning it and the remainder into the long on the top of the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack
* @param em the emitter typed with the incoming stack
* @param fromLegs the list of source legs in little-endian order
* @param i the index of the part to store into, 0 being the least significant
* @param bitShift the number of bits to shift the next leg left
* @param ext the kind of extension to apply
* @return the emitter typed with the incoming stack, though the top value has been modified
*/
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
storePartAndPositionNextOpndLeg(Emitter<N0> em,
List<SimpleOpnd<TInt, IntJitType>> fromLegs, int i, int bitShift, Ext ext) {
var emStored = em
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(this::doGenStoreInt, i);
return i + 1 < fromLegs.size()
? emStored
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(this::positionOpndLeg, fromLegs.get(i + 1), bitShift, ext)
.emit(Op::lor)
: emStored
.emit(Op::ldc__i, Integer.SIZE)
.emit(Opnd::lextshr, ext);
}
/**
* {@inheritDoc}
* <p>
* The general strategy is to load the source operand one leg at a time. In order to put each
* leg with the remaining portion of the previous leg in position, we use a long on the stack as
* a temporary. This eases "gluing" the legs together and then writing the shifted portion into
* each part.
*/
@Override
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
List<SimpleOpnd<TInt, IntJitType>> fromLegs = opnd.type().castLegsLE(opnd);
int bitShift = byteShift * Byte.SIZE;
var emConvPos = em.emit(this::positionOpndLeg, fromLegs.get(0), bitShift, ext);
for (int i = 0; i < parts.size() - 1; i++) {
emConvPos =
emConvPos.emit(this::storePartAndPositionNextOpndLeg, fromLegs, i, bitShift, ext);
}
return emConvPos
.emit(Op::l2i)
.emit(this::doGenStoreInt, parts.size() - 1);
}
/**
* The analog to {@link #positionOpndLeg(Emitter, SimpleOpnd, int, Ext)}, but for a source value
* in an array.
*
* @param <N> the incoming stack
* @param em the emitter typed with the incoming stack
* @param arr a handle to the local holding the source array reference
* @param legType the type of the leg being loaded
* @param bitShift the number of bits to shift left
* @param ext the kind of extension to apply
* @return the emitter typed with the resulting stack, i.e., having pushed the long
*/
private <N extends Next> Emitter<Ent<N, TLong>> positionArrLeg(Emitter<N> em,
Local<TRef<int[]>> arr, IntJitType legType, int bitShift, Ext ext) {
return em
.emit(Op::aload, arr)
.emit(Op::ldc__i, 0)
.emit(Op::iaload)
.emit(Opnd::convert, legType, LongJitType.I8, ext)
.emit(Op::ldc__i, bitShift)
.emit(Op::lshl);
}
/**
* The analog to {@link #storePartAndPositionNextOpndLeg(Emitter, List, int, int, Ext)}, but for
* a source value in an array.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack
* @param em the emitter typed with the incoming stack
* @param arr a handle to the local holding the source array reference
* @param fromLegTypes the list of leg types in the source value, in little-endian order
* @param i the index of the part to store into, 0 being the least significant
* @param bitShift the number of bits to shift the next leg left
* @param ext the kind of extension to apply
* @return the emitter typed with the incoming stack, though the top value has been modified
*/
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
storePartAndPositionNextArrLeg(Emitter<N0> em, Local<TRef<int[]>> arr,
List<IntJitType> fromLegTypes, int i, int bitShift, Ext ext) {
var emStored = em
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(this::doGenStoreInt, i);
return i + 1 < fromLegTypes.size()
? emStored
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(this::positionArrLeg, arr, fromLegTypes.get(i + 1), bitShift, ext)
.emit(Op::lor)
: emStored
.emit(Op::ldc__i, Integer.SIZE)
.emit(Opnd::lextshr, ext);
}
/**
* {@inheritDoc}
* <p>
* The strategy here is the same as for
* {@link #genStoreFromOpnd(Emitter, JitCodeGenerator, Opnd, 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) {
try (SubScope ss = scope.sub()) {
Local<TRef<int[]>> arr = ss.decl(Types.T_INT_ARR, "temp_arr");
List<IntJitType> fromLegTypes = from.legTypesLE();
int bitShift = byteShift + Byte.SIZE;
var emConvPos = em
.emit(Op::astore, arr)
.emit(this::positionArrLeg, arr, fromLegTypes.get(0), bitShift, ext);
for (int i = 0; i < parts.size() - 1; i++) {
emConvPos = emConvPos.emit(this::storePartAndPositionNextArrLeg, arr, fromLegTypes,
i, bitShift, ext);
}
return emConvPos
.emit(Op::l2i)
.emit(this::doGenStoreInt, parts.size() - 1);
}
}
@Override
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
return AlignedMpIntHandler.subHandler(endian, vn, parts, byteShift, byteOffset,
maxByteSize);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -15,10 +15,10 @@
*/
package ghidra.pcode.emu.jit.gen;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.reflect.TypeLiteral;
import org.objectweb.asm.Type;
import ghidra.generic.util.datastruct.SemisparseByteArray;
@@ -28,6 +28,11 @@ import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
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.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Methods.MthDesc;
import ghidra.pcode.emu.jit.gen.util.Types;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
@@ -44,201 +49,374 @@ import ghidra.program.model.pcode.Varnode;
public interface GenConsts {
public static final int BLOCK_SIZE = SemisparseByteArray.BLOCK_SIZE;
public static final String TDESC_ADDRESS = Type.getDescriptor(Address.class);
public static final String TDESC_ADDRESS_FACTORY = Type.getDescriptor(AddressFactory.class);
public static final String TDESC_ADDRESS_SPACE = Type.getDescriptor(AddressSpace.class);
public static final String TDESC_BYTE_ARR = Type.getDescriptor(byte[].class);
public static final String TDESC_EXIT_SLOT = Type.getDescriptor(ExitSlot.class);
public static final String TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE =
Type.getDescriptor(JitBytesPcodeExecutorState.class);
public static final String TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE =
Type.getDescriptor(JitBytesPcodeExecutorStateSpace.class);
public static final String TDESC_JIT_PCODE_THREAD = Type.getDescriptor(JitPcodeThread.class);
public static final String TDESC_LANGUAGE = Type.getDescriptor(Language.class);
public static final String TDESC_LIST = Type.getDescriptor(List.class);
public static final String TDESC_PCODE_USEROP_DEFINITION =
Type.getDescriptor(PcodeUseropDefinition.class);
public static final String TDESC_REGISTER_VALUE = Type.getDescriptor(RegisterValue.class);
public static final String TDESC_STRING = Type.getDescriptor(String.class);
public static final String TDESC_VARNODE = Type.getDescriptor(Varnode.class);
public static final TRef<Object[]> TARR_OBJECT = Types.refOf(Object[].class);
public static final TRef<Varnode[]> TARR_VARNODE = Types.refOf(Varnode[].class);
public static final TRef<Double> TR_DOUBLE = Types.refOf(Double.class);
public static final TRef<Float> TR_FLOAT = Types.refOf(Float.class);
public static final TRef<Integer> TR_INTEGER = Types.refOf(Integer.class);
public static final TRef<Long> TR_LONG = Types.refOf(Long.class);
public static final TRef<Address> T_ADDRESS = Types.refOf(Address.class);
public static final TRef<AddressFactory> T_ADDRESS_FACTORY =
Types.refOf(AddressFactory.class);
public static final TRef<AddressSpace> T_ADDRESS_SPACE = Types.refOf(AddressSpace.class);
public static final TRef<AddrCtx> T_ADDR_CTX = Types.refOf(AddrCtx.class);
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final TRef<ArrayList<?>> T_ARRAY_LIST = (TRef) Types.refOf(ArrayList.class);
public static final TRef<AssertionError> T_ASSERTION_ERROR =
Types.refOf(AssertionError.class);
public static final TRef<DecodePcodeExecutionException> T_DECODE_PCODE_EXECUTION_EXCEPTION =
Types.refOf(DecodePcodeExecutionException.class);
public static final TRef<EntryPoint> T_ENTRY_POINT = Types.refOf(EntryPoint.class);
public static final TRef<ExitSlot> T_EXIT_SLOT = Types.refOf(ExitSlot.class);
public static final TRef<IllegalArgumentException> T_ILLEGAL_ARGUMENT_EXCEPTION =
Types.refOf(IllegalArgumentException.class);
public static final TRef<JitBytesPcodeExecutorState> T_JIT_BYTES_PCODE_EXECUTOR_STATE =
Types.refOf(JitBytesPcodeExecutorState.class);
public static final TRef<JitBytesPcodeExecutorStateSpace> //
T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE = Types.refOf(JitBytesPcodeExecutorStateSpace.class);
public static final TRef<JitCompiledPassage> T_JIT_COMPILED_PASSAGE =
Types.refOf(JitCompiledPassage.class);
public static final TRef<JitPcodeThread> T_JIT_PCODE_THREAD =
Types.refOf(JitPcodeThread.class);
public static final TRef<JitThreadBytesPcodeExecutorState> //
T_JIT_THREAD_BYTES_PCODE_EXECUTOR_STATE = Types.refOf(JitThreadBytesPcodeExecutorState.class);
public static final TRef<Language> T_LANGUAGE = Types.refOf(Language.class);
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final TRef<List<?>> T_LIST = (TRef) Types.refOf(List.class);
public static final TRef<LowlevelError> T_LOWLEVEL_ERROR = Types.refOf(LowlevelError.class);
public static final TRef<Math> T_MATH = Types.refOf(Math.class);
public static final TRef<Object> T_OBJECT = Types.refOf(Object.class);
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final TRef<PcodeUseropDefinition<?>> T_PCODE_USEROP_DEFINITION =
(TRef) Types.refOf(PcodeUseropDefinition.class);
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final TRef<PcodeUseropLibrary<?>> T_PCODE_USEROP_LIBRARY =
(TRef) Types.refOf(PcodeUseropLibrary.class);
public static final TRef<PrintStream> T_PRINT_STREAM = Types.refOf(PrintStream.class);
public static final TRef<RegisterValue> T_REGISTER_VALUE = Types.refOf(RegisterValue.class);
public static final TRef<SleighLinkException> T_SLEIGH_LINK_EXCEPTION =
Types.refOf(SleighLinkException.class);
public static final TRef<String> T_STRING = Types.refOf(String.class);
public static final TRef<System> T_SYSTEM = Types.refOf(System.class);
public static final TRef<Throwable> T_THROWABLE = Types.refOf(Throwable.class);
public static final TRef<Varnode> T_VARNODE = Types.refOf(Varnode.class);
public static final String TSIG_LIST_ADDRCTX =
JitJvmTypeUtils.typeToSignature(new TypeLiteral<List<AddrCtx>>() {}.value);
public static final String MDESC_ADDR_CTX__$INIT = Type.getMethodDescriptor(Type.VOID_TYPE,
Type.getType(RegisterValue.class), Type.getType(Address.class));
public static final String MDESC_ADDRESS_FACTORY__GET_ADDRESS_SPACE =
Type.getMethodDescriptor(Type.getType(AddressSpace.class), Type.INT_TYPE);
public static final String MDESC_ADDRESS_SPACE__GET_ADDRESS =
Type.getMethodDescriptor(Type.getType(Address.class), Type.LONG_TYPE);
public static final String MDESC_ARRAY_LIST__$INIT = Type.getMethodDescriptor(Type.VOID_TYPE);
public static final MthDesc<TVoid,
Ent<Ent<Bot, TRef<RegisterValue>>, TRef<Address>>> MDESC_ADDR_CTX__$INIT =
MthDesc.returns(Types.T_VOID).param(T_REGISTER_VALUE).param(T_ADDRESS).build();
public static final MthDesc<TRef<AddressSpace>,
Ent<Bot, TInt>> MDESC_ADDRESS_FACTORY__GET_ADDRESS_SPACE =
MthDesc.returns(T_ADDRESS_SPACE).param(Types.T_INT).build();
public static final MthDesc<TRef<Address>, Ent<Bot, TLong>> MDESC_ADDRESS_SPACE__GET_ADDRESS =
MthDesc.returns(T_ADDRESS).param(Types.T_LONG).build();
public static final MthDesc<TVoid, Bot> MDESC_ARRAY_LIST__$INIT =
MthDesc.returns(Types.T_VOID).build();
// NOTE: The void (String) form is private....
public static final String MDESC_ASSERTION_ERROR__$INIT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class));
public static final String MDESC_DOUBLE__DOUBLE_TO_RAW_LONG_BITS =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.DOUBLE_TYPE);
public static final String MDESC_DOUBLE__IS_NAN =
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE);
public static final String MDESC_DOUBLE__LONG_BITS_TO_DOUBLE =
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.LONG_TYPE);
public static final String MDESC_FLOAT__FLOAT_TO_RAW_INT_BITS =
Type.getMethodDescriptor(Type.INT_TYPE, Type.FLOAT_TYPE);
public static final String MDESC_FLOAT__INT_BITS_TO_FLOAT =
Type.getMethodDescriptor(Type.FLOAT_TYPE, Type.INT_TYPE);
public static final String MDESC_FLOAT__IS_NAN =
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.FLOAT_TYPE);
public static final String MDESC_ILLEGAL_ARGUMENT_EXCEPTION__$INIT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
public static final String MDESC_INTEGER__BIT_COUNT =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_INTEGER__COMPARE =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_INTEGER__TO_UNSIGNED_LONG =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.INT_TYPE);
public static final MthDesc<TVoid, Ent<Bot, TRef<Object>>> MDESC_ASSERTION_ERROR__$INIT =
MthDesc.returns(Types.T_VOID).param(T_OBJECT).build();
public static final MthDesc<TLong, Ent<Bot, TDouble>> MDESC_DOUBLE__DOUBLE_TO_RAW_LONG_BITS =
MthDesc.returns(Types.T_LONG).param(Types.T_DOUBLE).build();
public static final MthDesc<TInt, Ent<Bot, TDouble>> MDESC_DOUBLE__IS_NAN =
MthDesc.<Boolean, Double> derive(d -> Double.isNaN(d))
.check(MthDesc::returns, Types.T_BOOL)
.check(MthDesc::param, Types.T_DOUBLE)
.check(MthDesc::build);
public static final MthDesc<TDouble, Ent<Bot, TLong>> MDESC_DOUBLE__LONG_BITS_TO_DOUBLE =
MthDesc.returns(Types.T_DOUBLE).param(Types.T_LONG).build();
public static final MthDesc<TInt, Ent<Bot, TFloat>> MDESC_FLOAT__FLOAT_TO_RAW_INT_BITS =
MthDesc.returns(Types.T_INT).param(Types.T_FLOAT).build();
public static final MthDesc<TFloat, Ent<Bot, TInt>> MDESC_FLOAT__INT_BITS_TO_FLOAT =
MthDesc.returns(Types.T_FLOAT).param(Types.T_INT).build();
public static final MthDesc<TInt, Ent<Bot, TFloat>> MDESC_FLOAT__IS_NAN =
MthDesc.<Boolean, Float> derive(d -> Float.isNaN(d))
.check(MthDesc::returns, Types.T_BOOL)
.check(MthDesc::param, Types.T_FLOAT)
.check(MthDesc::build);
public static final MthDesc<TVoid,
Ent<Bot, TRef<String>>> MDESC_ILLEGAL_ARGUMENT_EXCEPTION__$INIT =
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
public static final MthDesc<TInt, Ent<Bot, TInt>> MDESC_INTEGER__BIT_COUNT =
MthDesc.derive(Integer::bitCount)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_INTEGER__COMPARE =
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
public static final MthDesc<TInt, Ent<Bot, TInt>> MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
MthDesc.derive(Integer::numberOfLeadingZeros)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TLong, Ent<Bot, TInt>> MDESC_INTEGER__TO_UNSIGNED_LONG =
MthDesc.returns(Types.T_LONG).param(Types.T_INT).build();
public static final MthDesc<TRef<Integer>, Ent<Bot, TInt>> MDESC_INTEGER__VALUE_OF =
MthDesc.returns(TR_INTEGER).param(Types.T_INT).build();
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_LANGUAGE =
Type.getMethodDescriptor(Type.getType(Language.class));
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_SPACE_FOR =
Type.getMethodDescriptor(Type.getType(JitBytesPcodeExecutorStateSpace.class),
Type.getType(AddressSpace.class));
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT =
Type.getMethodDescriptor(Type.getType(byte[].class), Type.LONG_TYPE);
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__READ =
Type.getMethodDescriptor(Type.getType(byte[].class), Type.LONG_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__WRITE =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(byte[].class),
Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__COUNT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT =
Type.getMethodDescriptor(Type.getType(RegisterValue.class), Type.getType(Language.class),
Type.getType(String.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_DECODE_ERROR =
Type.getMethodDescriptor(Type.getType(DecodePcodeExecutionException.class),
Type.getType(String.class), Type.LONG_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT =
Type.getMethodDescriptor(Type.getType(ExitSlot.class), Type.LONG_TYPE,
Type.getType(RegisterValue.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
Type.getMethodDescriptor(Type.getType(Varnode.class), Type.getType(AddressFactory.class),
Type.getType(String.class), Type.LONG_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED =
Type.getMethodDescriptor(Type.getType(EntryPoint.class), Type.getType(ExitSlot.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
Type.getMethodDescriptor(Type.getType(Language.class), Type.getType(String.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION =
Type.getMethodDescriptor(Type.getType(PcodeUseropDefinition.class),
Type.getType(String.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PcodeUseropDefinition.class),
Type.getType(Varnode.class), Type.getType(Varnode[].class));
public static final String MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class),
Type.getType(int[].class), Type.getType(int[].class));
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_COUNTER_AND_CONTEXT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__SET_COUNTER_AND_CONTEXT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_INT_RAW =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_LONG_RAW =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_MP_INT =
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(int[].class),
Type.getType(int[].class), Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(byte[].class),
Type.INT_TYPE);
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(byte[].class),
Type.INT_TYPE);
public static final String MDESC_JIT_PCODE_THREAD__GET_STATE =
Type.getMethodDescriptor(Type.getType(JitThreadBytesPcodeExecutorState.class));
public static final String MDESC_LANGUAGE__GET_ADDRESS_FACTORY =
Type.getMethodDescriptor(Type.getType(AddressFactory.class));
public static final MthDesc<TRef<JitBytesPcodeExecutorStateSpace>,
Ent<Bot, TRef<AddressSpace>>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_SPACE_FOR =
MthDesc.returns(T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE).param(T_ADDRESS_SPACE).build();
public static final MthDesc<TRef<byte[]>,
Ent<Bot, TLong>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT =
MthDesc.returns(Types.T_BYTE_ARR).param(Types.T_LONG).build();
public static final MthDesc<TRef<byte[]>,
Ent<Ent<Bot, TLong>, TInt>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__READ =
MthDesc.returns(Types.T_BYTE_ARR).param(Types.T_LONG).param(Types.T_INT).build();
public static final MthDesc<TVoid,
Ent<Ent<Ent<Ent<Bot, TLong>, TRef<byte[]>>, TInt>,
TInt>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__WRITE =
MthDesc.returns(Types.T_VOID)
.param(Types.T_LONG)
.param(Types.T_BYTE_ARR)
.param(Types.T_INT)
.param(Types.T_INT)
.build();
public static final MthDesc<TLong,
Ent<Ent<Bot, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG =
MthDesc.returns(Types.T_LONG).param(Types.T_INT).param(Types.T_INT).build();
public static final MthDesc<TVoid,
Ent<Ent<Bot, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__COUNT =
MthDesc.returns(Types.T_VOID).param(Types.T_INT).param(Types.T_INT).build();
public static final MthDesc<TRef<RegisterValue>,
Ent<Ent<Bot, TRef<Language>>, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT =
MthDesc.returns(T_REGISTER_VALUE).param(T_LANGUAGE).param(T_STRING).build();
public static final MthDesc<TRef<DecodePcodeExecutionException>,
Ent<Ent<Bot, TRef<String>>, TLong>> MDESC_JIT_COMPILED_PASSAGE__CREATE_DECODE_ERROR =
MthDesc.returns(T_DECODE_PCODE_EXECUTION_EXCEPTION)
.param(T_STRING)
.param(Types.T_LONG)
.build();
public static final MthDesc<TRef<ExitSlot>,
Ent<Ent<Bot, TLong>, TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT =
MthDesc.returns(T_EXIT_SLOT).param(Types.T_LONG).param(T_REGISTER_VALUE).build();
public static final MthDesc<TRef<Varnode>,
Ent<Ent<Ent<Ent<Bot, TRef<AddressFactory>>, TRef<String>>, TLong>,
TInt>> MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
MthDesc.returns(T_VARNODE)
.param(T_ADDRESS_FACTORY)
.param(T_STRING)
.param(Types.T_LONG)
.param(Types.T_INT)
.build();
public static final MthDesc<TRef<EntryPoint>,
Ent<Bot, TRef<ExitSlot>>> MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED =
MthDesc.returns(T_ENTRY_POINT).param(T_EXIT_SLOT).build();
public static final MthDesc<TRef<Language>,
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
MthDesc.returns(T_LANGUAGE).param(T_STRING).build();
public static final MthDesc<TRef<PcodeUseropDefinition<?>>,
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION =
MthDesc.returns(T_PCODE_USEROP_DEFINITION).param(T_STRING).build();
public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TRef<PcodeUseropDefinition<?>>>, TRef<Varnode>>,
TRef<Varnode[]>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
MthDesc.returns(Types.T_VOID)
.param(T_PCODE_USEROP_DEFINITION)
.param(T_VARNODE)
.param(TARR_VARNODE)
.build();
public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
TRef<int[]>>> MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
MthDesc.returns(Types.T_VOID)
.param(Types.T_INT_ARR)
.param(Types.T_INT_ARR)
.param(Types.T_INT_ARR)
.build();
public static final MthDesc<TInt,
Ent<Ent<Ent<Bot, TRef<byte[]>>, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N =
MthDesc.returns(Types.T_BOOL)
.param(Types.T_BYTE_ARR)
.param(Types.T_INT)
.param(Types.T_INT)
.build();
public static final MthDesc<TInt,
Ent<Ent<Bot, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
MthDesc.returns(Types.T_INT).param(Types.T_BYTE_ARR).param(Types.T_INT).build();
public static final MthDesc<TLong,
Ent<Ent<Bot, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
MthDesc.returns(Types.T_LONG).param(Types.T_BYTE_ARR).param(Types.T_INT).build();
public static final MthDesc<TVoid, Ent<Ent<Bot, TLong>,
TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__SET_$OR_WRITE_COUNTER_AND_CONTEXT =
MthDesc.returns(Types.T_VOID).param(Types.T_LONG).param(T_REGISTER_VALUE).build();
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>,
TInt>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_INT_RAW =
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>,
TLong>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_LONG_RAW =
MthDesc.returns(Types.T_LONG).param(Types.T_LONG).param(Types.T_LONG).build();
public static final MthDesc<TInt,
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
TInt>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_MP_INT =
MthDesc.returns(Types.T_INT)
.param(Types.T_INT_ARR)
.param(Types.T_INT_ARR)
.param(Types.T_INT)
.build();
/**
* This is just to assure all the methods referred to below have the same signature. The fields
* here should not be used in any code, written, generated, or otherwise.
*/
interface WriteIntX {
WriteIntX AE1 = JitCompiledPassage::writeInt1;
WriteIntX BE2 = JitCompiledPassage::writeIntBE2;
WriteIntX BE3 = JitCompiledPassage::writeIntBE3;
WriteIntX BE4 = JitCompiledPassage::writeIntBE4;
WriteIntX LE2 = JitCompiledPassage::writeIntLE2;
WriteIntX LE3 = JitCompiledPassage::writeIntLE3;
WriteIntX LE4 = JitCompiledPassage::writeIntLE4;
void writeIntX(int value, byte[] arr, int offset);
}
/**
* This is just to assure all the methods referred to below have the same signature. The fields
* here should not be used in any code, written, generated, or otherwise.
*/
interface WriteLongX {
WriteLongX AE1 = JitCompiledPassage::writeLong1;
WriteLongX BE2 = JitCompiledPassage::writeLongBE2;
WriteLongX BE3 = JitCompiledPassage::writeLongBE3;
WriteLongX BE4 = JitCompiledPassage::writeLongBE4;
WriteLongX BE5 = JitCompiledPassage::writeLongBE5;
WriteLongX BE6 = JitCompiledPassage::writeLongBE6;
WriteLongX BE7 = JitCompiledPassage::writeLongBE7;
WriteLongX BE8 = JitCompiledPassage::writeLongBE8;
WriteLongX LE2 = JitCompiledPassage::writeLongLE2;
WriteLongX LE3 = JitCompiledPassage::writeLongLE3;
WriteLongX LE4 = JitCompiledPassage::writeLongLE4;
WriteLongX LE5 = JitCompiledPassage::writeLongLE5;
WriteLongX LE6 = JitCompiledPassage::writeLongLE6;
WriteLongX LE7 = JitCompiledPassage::writeLongLE7;
WriteLongX LE8 = JitCompiledPassage::writeLongLE8;
void writeLongX(long value, byte[] arr, int offset);
}
public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TInt>, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
MthDesc.derive(JitCompiledPassage::writeInt1)
.check(MthDesc::returns, Types.T_VOID)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_BYTE_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TLong>, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX =
MthDesc.derive(JitCompiledPassage::writeLong1)
.check(MthDesc::returns, Types.T_VOID)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::param, Types.T_BYTE_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TRef<JitThreadBytesPcodeExecutorState>,
Bot> MDESC_JIT_PCODE_THREAD__GET_STATE =
MthDesc.returns(T_JIT_THREAD_BYTES_PCODE_EXECUTOR_STATE).build();
public static final MthDesc<TRef<AddressFactory>, Bot> MDESC_LANGUAGE__GET_ADDRESS_FACTORY =
MthDesc.returns(T_ADDRESS_FACTORY).build();
public static final String MDESC_LANGUAGE__GET_DEFAULT_SPACE =
Type.getMethodDescriptor(Type.getType(AddressSpace.class));
public static final String MDESC_LIST__ADD =
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class));
public static final String MDESC_LONG__BIT_COUNT =
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE);
public static final String MDESC_LONG__COMPARE_UNSIGNED =
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
public static final String MDESC_LONG__NUMBER_OF_LEADING_ZEROS =
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE);
public static final String MDESC_LOW_LEVEL_ERROR__$INIT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
public static final String MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY =
Type.getMethodDescriptor(Type.getType(PcodeUseropLibrary.class));
public static final String MDESC_SLEIGH_LINK_EXCEPTION__$INIT =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
public static final MthDesc<TInt, Ent<Bot, TRef<Object>>> MDESC_LIST__ADD =
MthDesc.returns(Types.T_BOOL).param(T_OBJECT).build();
public static final MthDesc<TInt, Ent<Bot, TLong>> MDESC_LONG__BIT_COUNT =
MthDesc.derive(Long::bitCount)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::build);
public static final MthDesc<TInt, Ent<Ent<Bot, TLong>, TLong>> MDESC_LONG__COMPARE =
MthDesc.returns(Types.T_INT).param(Types.T_LONG).param(Types.T_LONG).build();
public static final MthDesc<TInt, Ent<Bot, TLong>> MDESC_LONG__NUMBER_OF_LEADING_ZEROS =
MthDesc.derive(Long::numberOfLeadingZeros)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::build);
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_LOWLEVEL_ERROR__$INIT =
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
public static final MthDesc<TVoid, Bot> MDESC_OBJECT__$INIT =
MthDesc.returns(Types.T_VOID).build();
public static final MthDesc<TRef<PcodeUseropLibrary<?>>,
Bot> MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY =
MthDesc.returns(T_PCODE_USEROP_LIBRARY).build();
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_PRINT_STREAM__PRINTLN =
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_SLEIGH_LINK_EXCEPTION__$INIT =
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
public static final MthDesc<TRef<String>, Ent<Bot, TRef<Object[]>>> MDESC_STRING__FORMATTED =
MthDesc.returns(T_STRING).param(TARR_OBJECT).build();
public static final String MDESC_$DOUBLE_UNOP =
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
public static final String MDESC_$FLOAT_UNOP =
Type.getMethodDescriptor(Type.FLOAT_TYPE, Type.FLOAT_TYPE);
public static final String MDESC_$INT_BINOP =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final String MDESC_$LONG_BINOP =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
public static final String MDESC_$SHIFT_AA =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
Type.getType(int[].class), Type.getType(int[].class));
public static final String MDESC_$SHIFT_AJ =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
Type.getType(int[].class), Type.LONG_TYPE);
public static final String MDESC_$SHIFT_AI =
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
Type.getType(int[].class), Type.INT_TYPE);
public static final String MDESC_$SHIFT_JA =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.getType(int[].class));
public static final String MDESC_$SHIFT_JJ =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
public static final String MDESC_$SHIFT_JI =
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.INT_TYPE);
public static final String MDESC_$SHIFT_IA =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.getType(int[].class));
public static final String MDESC_$SHIFT_IJ =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
public static final String MDESC_$SHIFT_II =
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
public static final MthDesc<TDouble, Ent<Bot, TDouble>> MDESC_$DOUBLE_UNOP =
MthDesc.returns(Types.T_DOUBLE).param(Types.T_DOUBLE).build();
public static final MthDesc<TFloat, Ent<Bot, TFloat>> MDESC_$FLOAT_UNOP =
MthDesc.returns(Types.T_FLOAT).param(Types.T_FLOAT).build();
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_$INT_BINOP =
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TLong>> MDESC_$LONG_BINOP =
MthDesc.returns(Types.T_LONG).param(Types.T_LONG).param(Types.T_LONG).build();
public static final String NAME_ADDR_CTX = Type.getInternalName(AddrCtx.class);
public static final String NAME_ADDRESS = Type.getInternalName(Address.class);
public static final String NAME_ADDRESS_FACTORY = Type.getInternalName(AddressFactory.class);
public static final String NAME_ADDRESS_SPACE = Type.getInternalName(AddressSpace.class);
public static final String NAME_ARRAY_LIST = Type.getInternalName(ArrayList.class);
public static final String NAME_ASSERTION_ERROR = Type.getInternalName(AssertionError.class);
public static final String NAME_DOUBLE = Type.getInternalName(Double.class);
public static final String NAME_EXIT_SLOT = Type.getInternalName(ExitSlot.class);
public static final String NAME_FLOAT = Type.getInternalName(Float.class);
public static final String NAME_ILLEGAL_ARGUMENT_EXCEPTION =
Type.getInternalName(IllegalArgumentException.class);
public static final String NAME_INTEGER = Type.getInternalName(Integer.class);
public static final String NAME_JIT_BYTES_PCODE_EXECUTOR_STATE =
Type.getInternalName(JitBytesPcodeExecutorState.class);
public static final String NAME_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE =
Type.getInternalName(JitBytesPcodeExecutorStateSpace.class);
public static final String NAME_JIT_COMPILED_PASSAGE =
Type.getInternalName(JitCompiledPassage.class);
public static final String NAME_JIT_PCODE_THREAD = Type.getInternalName(JitPcodeThread.class);
public static final String NAME_LANGUAGE = Type.getInternalName(Language.class);
public static final String NAME_LIST = Type.getInternalName(List.class);
public static final String NAME_LONG = Type.getInternalName(Long.class);
public static final String NAME_LOW_LEVEL_ERROR = Type.getInternalName(LowlevelError.class);
public static final String NAME_MATH = Type.getInternalName(Math.class);
public static final String NAME_OBJECT = Type.getInternalName(Object.class);
public static final String NAME_PCODE_USEROP_DEFINITION =
Type.getInternalName(PcodeUseropDefinition.class);
public static final String NAME_SLEIGH_LINK_EXCEPTION =
Type.getInternalName(SleighLinkException.class);
public static final String NAME_THROWABLE = Type.getInternalName(Throwable.class);
public static final String NAME_VARNODE = Type.getInternalName(Varnode.class);
public static final MthDesc<TVoid,
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TRef<int[]>>> MDESC_$SHIFT_AA =
MthDesc.<int[], Integer, int[], int[]> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_VOID)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::build);
public static final MthDesc<TVoid,
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TLong>> MDESC_$SHIFT_AJ =
MthDesc.<int[], Integer, int[], Long> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_VOID)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::build);
public static final MthDesc<TVoid,
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TInt>> MDESC_$SHIFT_AI =
MthDesc.<int[], Integer, int[], Integer> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_VOID)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TRef<int[]>>> MDESC_$SHIFT_JA =
MthDesc.<Long, Long, int[]> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_LONG)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::build);
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TLong>> MDESC_$SHIFT_JJ =
MthDesc.<Long, Long, Long> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_LONG)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::build);
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TInt>> MDESC_$SHIFT_JI =
MthDesc.<Long, Long, Integer> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_LONG)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TRef<int[]>>> MDESC_$SHIFT_IA =
MthDesc.<Integer, Integer, int[]> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT_ARR)
.check(MthDesc::build);
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TLong>> MDESC_$SHIFT_IJ =
MthDesc.<Integer, Integer, Long> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_LONG)
.check(MthDesc::build);
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_$SHIFT_II =
MthDesc.<Integer, Integer, Integer> derive(JitCompiledPassage::intLeft)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
};
}
}

View File

@@ -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;
};
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -15,17 +15,21 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitPassage.RIndBranch;
import ghidra.pcode.emu.jit.JitPcodeThread;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
import ghidra.pcode.emu.jit.gen.GenConsts;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.BranchGen;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.UBranchGen;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Emitter.Dead;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.RegisterValue;
@@ -52,48 +56,49 @@ public enum BranchIndOpGen implements OpGen<JitBranchIndOp> {
* @param block the block containing the op
* @param rv the run method visitor
*/
static void generateExitCode(JitCodeGenerator gen, JitBranchIndOp op, RegisterValue ctx,
JitBlock block, MethodVisitor rv) {
gen.generatePassageExit(block, () -> {
// [...]
JitType targetType = gen.generateValReadCode(op.target(), op.targetType(), Ext.ZERO);
// [...,target:?]
TypeConversions.generateToLong(targetType, LongJitType.I8, Ext.ZERO, rv);
// [...,target:LONG]
}, ctx, rv);
rv.visitInsn(ACONST_NULL);
rv.visitInsn(ARETURN);
static <THIS extends JitCompiledPassage> Emitter<Dead> genExit(Emitter<Bot> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitBranchIndOp op, RegisterValue ctx, JitBlock block) {
PcGen tgtGen = PcGen.loadTarget(localThis, gen, op.target());
return em
.emit(gen::genExit, localThis, block, tgtGen, ctx)
.emit(Op::aconst_null, GenConsts.T_ENTRY_POINT)
.emit(Op::areturn, retReq);
}
/**
* A branch code generator for indirect branches
*/
static class IndBranchGen extends BranchGen<RIndBranch, JitBranchIndOp> {
static class IndBranchGen extends UBranchGen<RIndBranch, JitBranchIndOp> {
/** Singleton */
static final IndBranchGen IND = new IndBranchGen();
@Override
Address exit(JitCodeGenerator gen, RIndBranch branch) {
Address exit(JitCodeGenerator<?> gen, RIndBranch branch) {
return null;
}
@Override
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitBranchIndOp op, RIndBranch branch,
JitBlock block, MethodVisitor rv) {
generateExitCode(gen, op, branch.flowCtx(), block, rv);
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitBranchIndOp op, RIndBranch branch, JitBlock block) {
return genExit(em, localThis, retReq, gen, op, branch.flowCtx(), block);
}
@Override
void generateCodeWithCtxmod(JitCodeGenerator gen, JitBranchIndOp op, Address exit,
JitBlock block, MethodVisitor rv) {
generateExitCode(gen, op, null, block, rv);
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitBranchIndOp op,
Address exit, JitBlock block) {
return genExit(em, localThis, retReq, gen, op, null, block);
}
}
@Override
public void generateRunCode(JitCodeGenerator gen, JitBranchIndOp op, JitBlock block,
MethodVisitor rv) {
IndBranchGen.IND.generateCode(gen, op, op.branch(), block, rv);
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitBranchIndOp op, JitBlock block, Scope scope) {
return new DeadOpResult(IndBranchGen.IND.genRun(
em, localThis, localCtxmod, retReq, gen, op, op.branch(), block));
}
}

View File

@@ -15,15 +15,21 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitPassage.*;
import ghidra.pcode.emu.jit.JitPcodeThread;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.gen.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
import ghidra.pcode.emu.jit.gen.op.CBranchOpGen.ExtCBranchGen;
import ghidra.pcode.emu.jit.gen.op.CBranchOpGen.IntCBranchGen;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.gen.var.VarGen;
import ghidra.pcode.emu.jit.op.JitBranchOp;
import ghidra.pcode.emu.jit.op.JitOp;
@@ -35,7 +41,7 @@ import ghidra.program.model.lang.RegisterValue;
*
* <p>
* With an {@link IntBranch} record, this simply looks up the label for the target block and emits a
* block transition followed by a {@link #GOTO goto}.
* block transition followed by a {@link Op#goto_(Emitter) goto}.
*
* <p>
* With an {@link ExtBranch} record, this emits code to retire the target to the program counter,
@@ -56,13 +62,10 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param block the block containing the op
* @param rv the run method visitor
*/
static void generateRetireCode(JitCodeGenerator gen, Address exit, RegisterValue ctx,
JitBlock block, MethodVisitor rv) {
gen.generatePassageExit(block, () -> {
// [...]
rv.visitLdcInsn(exit.getOffset());
// [...,target:LONG]
}, ctx, rv);
static <THIS extends JitCompiledPassage> Emitter<Bot> genRetire(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Address exit,
RegisterValue ctx, JitBlock block) {
return gen.genExit(em, localThis, block, PcGen.loadOffset(exit), ctx);
}
/**
@@ -76,20 +79,24 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param block the block containing the op
* @param rv the run method visitor
*/
static void generateExitCode(JitCodeGenerator gen, Address exit, JitBlock block,
MethodVisitor rv) {
generateRetireCode(gen, exit, null, block, rv);
rv.visitInsn(ACONST_NULL);
rv.visitInsn(ARETURN);
static <THIS extends JitCompiledPassage> Emitter<Dead> genExit(Emitter<Bot> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, Address exit, JitBlock block) {
return em
.emit(BranchOpGen::genRetire, localThis, gen, exit, (RegisterValue) null, block)
.emit(Op::aconst_null, GenConsts.T_ENTRY_POINT)
.emit(Op::areturn, retReq);
}
/**
* A branch code generator
*
* @param <NR> the stack after the JVM branch bytecode (may be {@link Dead})
* @param <NI> the stack before the JVM branch bytecode (cannot be {@link Dead})
* @param <TB> the type of branch
* @param <TO> the type of op
*/
static abstract class BranchGen<TB extends RBranch, TO extends JitOp> {
static abstract class BranchGen<NR, NI extends Next, TB extends RBranch, TO extends JitOp> {
/**
* Get the target address of the branch
*
@@ -97,7 +104,7 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param branch the branch
* @return the target address
*/
abstract Address exit(JitCodeGenerator gen, TB branch);
abstract Address exit(JitCodeGenerator<?> gen, TB branch);
/**
* Generate code for the branch in the case a context modification has not occurred.
@@ -111,8 +118,9 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param block the block containing the op
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
abstract void generateCodeWithoutCtxmod(JitCodeGenerator gen, TO op, TB branch,
JitBlock block, MethodVisitor rv);
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRunWithoutCtxmod(Emitter<NI> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, TO op, TB branch, JitBlock block);
/**
* Generate code for the branch in the case a context modification may have occurred.
@@ -127,8 +135,10 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param block the block containing the op
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
abstract void generateCodeWithCtxmod(JitCodeGenerator gen, TO op, Address exit,
JitBlock block, MethodVisitor rv);
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRunWithCtxmod(Emitter<NI> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, Address exit,
JitBlock block);
/**
* Emit code that jumps or exits via a direct branch
@@ -139,95 +149,140 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
* @param block the block containing the op
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
void generateCode(JitCodeGenerator gen, TO op, TB branch, JitBlock block,
MethodVisitor rv) {
switch (branch.reach()) {
case WITH_CTXMOD -> generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
case WITHOUT_CTXMOD -> generateCodeWithoutCtxmod(gen, op, branch, block, rv);
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRun(Emitter<NI> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
JitBlock block);
}
/**
* An abstract branch code generator for unconditional branches.
*
* @param <TB> the type of branch
* @param <TO> the type of op
*/
abstract static class UBranchGen<TB extends RBranch, TO extends JitOp>
extends BranchGen<Dead, Bot, TB, TO> {
/**
* Emit code that jumps or exits via a direct branch
*
* @param gen the code generator
* @param op the branch op
* @param branch the branch from the op
* @param block the block containing the op
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
@Override
<THIS extends JitCompiledPassage> Emitter<Dead> genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
JitBlock block) {
return switch (branch.reach()) {
case WITH_CTXMOD -> genRunWithCtxmod(em, localThis, localCtxmod, retReq, gen, op,
exit(gen, branch), block);
case WITHOUT_CTXMOD -> genRunWithoutCtxmod(em, localThis, retReq, gen, op, branch,
block);
case MAYBE_CTXMOD -> {
Label withModctx = new Label();
RunFixedLocal.CTXMOD.generateLoadCode(rv);
rv.visitJumpInsn(IFNE, withModctx);
generateCodeWithoutCtxmod(gen, op, branch, block, rv);
rv.visitLabel(withModctx);
generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
var emIf = em
.emit(Op::iload, localCtxmod)
.emit(Op::ifne);
yield emIf.em()
.emit(this::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
block)
.emit(Lbl::placeDead, emIf.lbl())
// NB. genRun is already branching. No need for if-else construct.
.emit(this::genRunWithCtxmod, localThis, localCtxmod, retReq, gen, op,
exit(gen, branch), block);
}
default -> throw new AssertionError();
}
};
}
}
/**
* A branch code generator for internal branches
*
* @implNote We leave {@code TO:=}{@link JitOp} here, because we want {@link IntCBranchGen} to
* be able to delegate to this instance.
*/
static class IntBranchGen extends BranchGen<RIntBranch, JitOp> {
static class IntBranchGen extends UBranchGen<RIntBranch, JitOp> {
/** Singleton */
static final IntBranchGen INT = new IntBranchGen();
@Override
Address exit(JitCodeGenerator gen, RIntBranch branch) {
Address exit(JitCodeGenerator<?> gen, RIntBranch branch) {
return gen.getAddressForOp(branch.to());
}
@Override
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
JitBlock block, MethodVisitor rv) {
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitOp op, RIntBranch branch, JitBlock block) {
JitBlock target = block.getTargetBlock(branch);
Label label = gen.labelForBlock(target);
VarGen.computeBlockTransition(gen, block, target).generate(rv);
rv.visitJumpInsn(GOTO, label);
Lbl<Bot> label = gen.labelForBlock(target);
return em
.emit(VarGen.computeBlockTransition(localThis, gen, block, target)::genFwd)
.emit(Op::goto_, label);
}
@Override
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
MethodVisitor rv) {
generateExitCode(gen, exit, block, rv);
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitOp op, Address exit,
JitBlock block) {
return genExit(em, localThis, retReq, gen, exit, block);
}
}
/**
* A branch code generator for external branches
*
* @implNote We leave {@code TO:=}{@link JitOp} here, because we want {@link ExtCBranchGen} to
* be able to delegate to this instance.
*/
static class ExtBranchGen extends BranchGen<RExtBranch, JitOp> {
static class ExtBranchGen extends UBranchGen<RExtBranch, JitOp> {
/** Singleton */
static final ExtBranchGen EXT = new ExtBranchGen();
@Override
Address exit(JitCodeGenerator gen, RExtBranch branch) {
Address exit(JitCodeGenerator<?> gen, RExtBranch branch) {
return branch.to().address;
}
@Override
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
JitBlock block, MethodVisitor rv) {
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitOp op, RExtBranch branch, JitBlock block) {
AddrCtx exit = branch.to();
FieldForExitSlot slotField = gen.requestFieldForExitSlot(exit);
generateRetireCode(gen, exit.address, exit.rvCtx, block, rv);
// []
slotField.generateLoadCode(gen, rv);
// [slot]
rv.visitMethodInsn(INVOKESTATIC, GenConsts.NAME_JIT_COMPILED_PASSAGE, "getChained",
GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true);
// [chained:ENTRY]
rv.visitInsn(ARETURN);
return em
.emit(BranchOpGen::genRetire, localThis, gen, exit.address, exit.rvCtx, block)
.emit(slotField::genLoad, localThis, gen)
.emit(Op::invokestatic, GenConsts.T_JIT_COMPILED_PASSAGE, "getChained",
GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::areturn, retReq);
}
@Override
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
MethodVisitor rv) {
generateExitCode(gen, exit, block, rv);
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitOp op, Address exit,
JitBlock block) {
return genExit(em, localThis, retReq, gen, exit, block);
}
}
@Override
public void generateRunCode(JitCodeGenerator gen, JitBranchOp op, JitBlock block,
MethodVisitor rv) {
switch (op.branch()) {
case RIntBranch ib -> IntBranchGen.INT.generateCode(gen, op, ib, block, rv);
case RExtBranch eb -> ExtBranchGen.EXT.generateCode(gen, op, eb, block, rv);
public <THIS extends JitCompiledPassage> DeadOpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitBranchOp op, JitBlock block, Scope scope) {
return new DeadOpResult(switch (op.branch()) {
case RIntBranch ib -> IntBranchGen.INT.genRun(em, localThis, localCtxmod, retReq, gen,
op, ib, block);
case RExtBranch eb -> ExtBranchGen.EXT.genRun(em, localThis, localCtxmod, retReq, gen,
op, eb, block);
default -> throw new AssertionError("Branch type confusion");
}
});
}
}

View File

@@ -15,23 +15,22 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import ghidra.pcode.emu.jit.JitPassage.*;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitDataFlowModel;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.ExtBranchGen;
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.IntBranchGen;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.*;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.gen.var.VarGen;
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
import ghidra.pcode.emu.jit.op.JitCBranchOp;
import ghidra.pcode.emu.jit.op.JitOp;
import ghidra.pcode.emu.jit.var.JitFailVal;
import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.PcodeOp;
@@ -44,76 +43,138 @@ import ghidra.program.model.pcode.PcodeOp;
*
* <p>
* With an {@link IntBranch} record, this looks up the label for the target block and checks if a
* transition is necessary. If one is necessary, it emits an {@link #IFEQ ifeq} with the transition
* and {@link #GOTO goto} it guards. The {@code ifeq} skips to the fall-through case. If a
* transition is not necessary, it simply emits an {@link #IFNE ifne} to the target label.
* transition is necessary. If one is necessary, it emits an {@link Op#ifeq(Emitter) ifeq} with the
* transition and {@link Op#goto_(Emitter) goto} it guards. The {@code ifeq} skips to the
* fall-through case. If a transition is not necessary, it simply emits an {@link Op#ifne(Emitter)
* ifne} to the target label.
*
* <p>
* With an {@link ExtBranch} record, this does the same as {@link BranchOpGen} but guarded by an
* {@link #IFEQ ifeq} that skips to the fall-through case.
* {@link Op#ifeq(Emitter) ifeq} that skips to the fall-through case.
*/
public enum CBranchOpGen implements OpGen<JitCBranchOp> {
/** The generator singleton */
GEN;
/**
* An abstract branch code generator for conditional branches.
*
* @param <TB> the type of branch
* @param <TO> the type of op
*/
abstract static class CBranchGen<TB extends RBranch, TO extends JitCBranchOp>
extends BranchGen<Bot, Ent<Bot, TInt>, TB, TO> {
@Override
<THIS extends JitCompiledPassage> Emitter<Bot> genRun(Emitter<Ent<Bot, TInt>> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
JitBlock block) {
return switch (branch.reach()) {
case WITH_CTXMOD -> genRunWithCtxmod(em, localThis, localCtxmod, retReq, gen, op,
exit(gen, branch), block);
case WITHOUT_CTXMOD -> genRunWithoutCtxmod(em, localThis, retReq, gen, op, branch,
block);
case MAYBE_CTXMOD -> {
var lblIf = em.emit(Op::iload, localCtxmod)
.emit(Op::ifne);
var lblGoto = lblIf.em()
.emit(this::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
block)
.emit(Op::goto_);
yield lblGoto.em()
.emit(Lbl::placeDead, lblIf.lbl())
.emit(this::genRunWithCtxmod, localThis, localCtxmod, retReq, gen, op,
exit(gen, branch), block)
.emit(Lbl::place, lblGoto.lbl());
}
};
}
}
/**
* A branch code generator for internal conditional branches
*/
static class IntCBranchGen extends IntBranchGen {
static class IntCBranchGen extends CBranchGen<RIntBranch, JitCBranchOp> {
/** Singleton */
static final IntCBranchGen C_INT = new IntCBranchGen();
@Override
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
JitBlock block, MethodVisitor rv) {
JitBlock target = block.getTargetBlock(branch);
Label label = gen.labelForBlock(target);
BlockTransition transition = VarGen.computeBlockTransition(gen, block, target);
if (transition.needed()) {
Label fall = new Label();
rv.visitJumpInsn(IFEQ, fall);
transition.generate(rv);
rv.visitJumpInsn(GOTO, label);
rv.visitLabel(fall);
}
else {
rv.visitJumpInsn(IFNE, label);
}
Address exit(JitCodeGenerator<?> gen, RIntBranch branch) {
return IntBranchGen.INT.exit(gen, branch);
}
@Override
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
MethodVisitor rv) {
Label fall = new Label();
rv.visitJumpInsn(IFEQ, fall);
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
rv.visitLabel(fall);
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithoutCtxmod(
Emitter<Ent<Bot, TInt>> em, Local<TRef<THIS>> localThis,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
RIntBranch branch, JitBlock block) {
JitBlock target = block.getTargetBlock(branch);
Lbl<Bot> label = gen.labelForBlock(target);
BlockTransition<THIS> transition =
VarGen.computeBlockTransition(localThis, gen, block, target);
if (!transition.needed()) {
return em
.emit(Op::ifne, label);
}
var lblFall = em
.emit(Op::ifeq);
return lblFall.em()
.emit(transition::genFwd)
.emit(Op::goto_, label)
.emit(Lbl::placeDead, lblFall.lbl());
}
@Override
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithCtxmod(Emitter<Ent<Bot, TInt>> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
Address exit, JitBlock block) {
var lblFall = em
.emit(Op::ifeq);
return lblFall.em()
.emit(IntBranchGen.INT::genRunWithCtxmod, localThis, localCtxmod, retReq, gen,
op, exit, block)
.emit(Lbl::placeDead, lblFall.lbl());
}
}
/**
* A branch code generator for external conditional branches
*/
static class ExtCBranchGen extends ExtBranchGen {
static class ExtCBranchGen extends CBranchGen<RExtBranch, JitCBranchOp> {
/** Singleton */
static final ExtCBranchGen C_EXT = new ExtCBranchGen();
@Override
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
JitBlock block, MethodVisitor rv) {
Label fall = new Label();
rv.visitJumpInsn(IFEQ, fall);
super.generateCodeWithoutCtxmod(gen, op, branch, block, rv);
rv.visitLabel(fall);
Address exit(JitCodeGenerator<?> gen, RExtBranch branch) {
return ExtBranchGen.EXT.exit(gen, branch);
}
@Override
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
MethodVisitor rv) {
Label fall = new Label();
rv.visitJumpInsn(IFEQ, fall);
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
rv.visitLabel(fall);
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithoutCtxmod(
Emitter<Ent<Bot, TInt>> em, Local<TRef<THIS>> localThis,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
RExtBranch branch, JitBlock block) {
var lblFall = em
.emit(Op::ifeq);
return lblFall.em()
.emit(ExtBranchGen.EXT::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
block)
.emit(Lbl::placeDead, lblFall.lbl());
}
@Override
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithCtxmod(Emitter<Ent<Bot, TInt>> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
Address exit, JitBlock block) {
var lblFall = em
.emit(Op::ifeq);
return lblFall.em()
.emit(ExtBranchGen.EXT::genRunWithCtxmod, localThis, localCtxmod, retReq, gen,
op, exit, block)
.emit(Lbl::placeDead, lblFall.lbl());
}
}
@@ -135,24 +196,27 @@ public enum CBranchOpGen implements OpGen<JitCBranchOp> {
* which will ensure we apply special handling here.
*/
@Override
public void generateRunCode(JitCodeGenerator gen, JitCBranchOp op, JitBlock block,
MethodVisitor rv) {
public <THIS extends JitCompiledPassage> LiveOpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitCBranchOp op, JitBlock block, Scope scope) {
if (op.op() instanceof ExitPcodeOp && op.branch() instanceof RExtBranch eb) {
assert eb.reach() == Reachability.MAYBE_CTXMOD;
Label fall = new Label();
RunFixedLocal.CTXMOD.generateLoadCode(rv);
rv.visitJumpInsn(IFEQ, fall);
BranchOpGen.generateExitCode(gen, eb.to().address, block, rv);
rv.visitLabel(fall);
return;
var lblFall = em
.emit(Op::iload, localCtxmod)
.emit(Op::ifeq);
return new LiveOpResult(lblFall.em()
.emit(BranchOpGen::genExit, localThis, retReq, gen, eb.to().address, block)
.emit(Lbl::placeDead, lblFall.lbl()));
}
JitType cType = gen.generateValReadCode(op.cond(), op.condType(), Ext.ZERO);
TypeConversions.generateIntToBool(cType, rv);
switch (op.branch()) {
case RIntBranch ib -> IntCBranchGen.C_INT.generateCode(gen, op, ib, block, rv);
case RExtBranch eb -> ExtCBranchGen.C_EXT.generateCode(gen, op, eb, block, rv);
default -> throw new AssertionError("Branch type confusion");
}
var emBool = gen.genReadToBool(em, localThis, op.cond());
return new LiveOpResult(switch (op.branch()) {
case RIntBranch ib -> IntCBranchGen.C_INT.genRun(emBool, localThis, localCtxmod, retReq,
gen, op, ib, block);
case RExtBranch eb -> ExtCBranchGen.C_EXT.genRun(emBool, localThis, localCtxmod, retReq,
gen, op, eb, block);
default -> throw new AssertionError();
});
}
}

View File

@@ -16,12 +16,19 @@
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_SLEIGH_LINK_EXCEPTION__$INIT;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_SLEIGH_LINK_EXCEPTION;
import org.objectweb.asm.MethodVisitor;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_SLEIGH_LINK_EXCEPTION;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
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.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitCallOtherMissingOp;
import ghidra.pcode.exec.SleighLinkException;
@@ -37,23 +44,21 @@ public enum CallOtherMissingOpGen implements OpGen<JitCallOtherMissingOp> {
GEN;
@Override
public void generateRunCode(JitCodeGenerator gen, JitCallOtherMissingOp op, JitBlock block,
MethodVisitor rv) {
gen.generatePassageExit(block, () -> {
rv.visitLdcInsn(gen.getAddressForOp(op.op()).getOffset());
}, gen.getExitContext(op.op()), rv);
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitCallOtherMissingOp op, JitBlock block, Scope scope) {
String message = gen.getErrorMessage(op.op());
// [...]
rv.visitTypeInsn(NEW, NAME_SLEIGH_LINK_EXCEPTION);
// [...,error:NEW]
rv.visitInsn(DUP);
// [...,error:NEW,error:NEW]
rv.visitLdcInsn(message);
// [...,error:NEW,error:NEW,message]
rv.visitMethodInsn(INVOKESPECIAL, NAME_SLEIGH_LINK_EXCEPTION, "<init>",
MDESC_SLEIGH_LINK_EXCEPTION__$INIT, false);
// [...,error]
rv.visitInsn(ATHROW);
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op.op()));
return new DeadOpResult(em
.emit(gen::genExit, localThis, block, pcGen, gen.getExitContext(op.op()))
.emit(Op::new_, T_SLEIGH_LINK_EXCEPTION)
.emit(Op::dup)
.emit(Op::ldc__a, message)
.emit(Op::invokespecial, T_SLEIGH_LINK_EXCEPTION, "<init>",
MDESC_SLEIGH_LINK_EXCEPTION__$INIT, false)
.step(Inv::takeArg)
.step(Inv::takeObjRef)
.step(Inv::retVoid)
.emit(Op::athrow));
}
}

View File

@@ -19,31 +19,33 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.objectweb.asm.*;
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
import ghidra.pcode.emu.jit.analysis.*;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
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;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.*;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.gen.var.VarGen;
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
import ghidra.pcode.emu.jit.op.JitCallOtherDefOp;
import ghidra.pcode.emu.jit.op.JitCallOtherOpIf;
import ghidra.pcode.emu.jit.var.JitVal;
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.OpOutput;
import ghidra.pcode.exec.PcodeUseropLibrary;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
@@ -53,9 +55,9 @@ import ghidra.program.model.pcode.Varnode;
*
* <p>
* The checks if Direct invocation is possible. If so, it emits code using
* {@link #generateRunCodeUsingDirectStrategy(JitCodeGenerator, JitCallOtherOpIf, JitBlock, MethodVisitor)}.
* {@link #genRunDirectStrategy(Emitter, Local, JitCodeGenerator, JitCallOtherOpIf, JitBlock, Scope)}.
* If not, it emits code using
* {@link #generateRunCodeUsingRetirementStrategy(JitCodeGenerator, PcodeOp, JitBlock, PcodeUseropDefinition, MethodVisitor)}.
* {@link #genRunRetirementStrategy(Emitter, Local, JitCodeGenerator, PcodeOp, JitBlock, PcodeUseropDefinition)}.
* Direct invocation is possible when the userop is {@link PcodeUseropDefinition#isFunctional()
* functional} and all of its parameters and return type have a supported primitive type.
* ({@code char} is not supported.) Regarding the invocation strategies, see
@@ -83,82 +85,80 @@ import ghidra.program.model.pcode.Varnode;
* stack. We then emit the invocation of the Java method, guarded by the exception handler. We then
* have to consider whether the userop has an output operand and whether its definition returns a
* value. If both are true, we emit code to write the result. If neither is true, we're done. If a
* result is returned, but no output operand is provided, we <em>must</em> still emit a {@link #POP
* pop}.
* result is returned, but no output operand is provided, we <em>must</em> still emit a
* {@link Op#pop(Emitter) pop}.
*/
public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
/** The generator singleton */
GEN;
private static <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<Varnode>>>
genLoadVarnodeOrNull(Emitter<N> em, Local<TRef<THIS>> localThis,
JitCodeGenerator<THIS> gen, Varnode vn) {
if (vn == null) {
return em.emit(Op::aconst_null, T_VARNODE);
}
FieldForVarnode field = gen.requestStaticFieldForVarnode(vn);
return em.emit(field::genLoad, gen);
}
/**
* Emit code to implement the Standard strategy (see the class documentation)
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param op the p-code op
* @param block the block containing the op
* @param userop the userop definition, wrapped by the {@link JitDataFlowUseropLibrary}
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
* @return the result of emitting the userop's bytecode
*/
public static void generateRunCodeUsingRetirementStrategy(JitCodeGenerator gen, PcodeOp op,
JitBlock block, PcodeUseropDefinition<?> userop, MethodVisitor rv) {
public static <THIS extends JitCompiledPassage> OpResult genRunRetirementStrategy(
Emitter<Bot> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
PcodeOp op, JitBlock block, PcodeUseropDefinition<?> userop) {
/**
* This is about the simplest (laziest) approach we could take for the moment, but it should
* suffice, depending on the frequency of CALLOTHER executions. We immediately retire all
* variables, then invoke the userop as it would be by the p-code interpreter. It can access
* its variables in the usual fashion. Although not ideal, it can also feed the executor
* (interpreter) ops to execute --- they won't be jitted here. Then, we liven the variables
* back.
*
* This is about the simplest (laziest) approach we could take, but it should suffice when
* we cannot invoke directly. We immediately retire all variables, then invoke the userop as
* it would be by the p-code interpreter. It can access its variables in the usual fashion.
* Although not ideal, it can also feed the executor (interpreter) ops to execute --- they
* won't be jitted here. Then, we liven the variables back.
* <p>
* NOTE: The output variable should be "alive", so we need not store it into a local. It'll
* be made alive in the return block transition.
*
* TODO: Implement direct invocation for functional userops. NOTE: Cannot avoid block
* retirement and re-birth unless I also do direct invocation. Otherwise, the parameters are
* read from the state instead of from the local variables.
*/
BlockTransition transition = VarGen.computeBlockTransition(gen, block, null);
transition.generate(rv);
BlockTransition<THIS> transition =
VarGen.computeBlockTransition(localThis, gen, block, null);
gen.generateRetirePcCtx(() -> {
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
}, gen.getExitContext(op), RetireMode.SET, rv);
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op));
// []
RunFixedLocal.THIS.generateLoadCode(rv);
// [this]
gen.requestFieldForUserop(userop).generateLoadCode(gen, rv);
// [this,userop]
var emArr = em
.emit(transition::genFwd)
.emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op),
RetireMode.SET)
.emit(Op::aload, localThis)
.emit(gen.requestFieldForUserop(userop)::genLoad, localThis, gen)
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getOutput())
.emit(Op::ldc__i, op.getNumInputs() - 1)
.emit(Op::anewarray, T_VARNODE);
if (op.getOutput() == null) {
rv.visitInsn(ACONST_NULL);
}
else {
gen.requestStaticFieldForVarnode(op.getOutput()).generateLoadCode(gen, rv);
}
// [this,userop,outVn]
rv.visitLdcInsn(op.getNumInputs() - 1);
rv.visitTypeInsn(ANEWARRAY, NAME_VARNODE);
// [this,userop,outVn,inVns:ARR]
for (int i = 1; i < op.getNumInputs(); i++) {
// [this,userop,outVn,inVns:ARR]
rv.visitInsn(DUP);
// [this,userop,outVn,inVns:ARR,inVns:ARR]
rv.visitLdcInsn(i - 1);
// [this,userop,outVn,inVns:ARR,inVns:ARR,index]
// Yes, including constants :/
Varnode input = op.getInput(i);
gen.requestStaticFieldForVarnode(input).generateLoadCode(gen, rv);
// [this,userop,outVn,inVns:ARR,inVns:ARR,index,inVn]
rv.visitInsn(AASTORE);
// [this,userop,outVn,inVns:ARR]
emArr = emArr
.emit(Op::dup)
.emit(Op::ldc__i, i - 1)
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getInput(i))
.emit(Op::aastore);
}
// [this,userop,outVn,inVns:ARR]
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "invokeUserop",
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP, true);
transition.generateInv(rv);
return new LiveOpResult(emArr
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "invokeUserop",
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeObjRef)
.step(Inv::retVoid)
.emit(transition::genInv));
}
static Parameter findOutputParameter(Parameter[] parameters, Method method) {
@@ -189,13 +189,19 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
/**
* Emit code to implement the Direct strategy (see the class documentation)
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param op the p-code op use-def node
* @param block the block containing the op
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
* @param scope a scope for generating temporary local storage
* @return the result of emitting the userop's bytecode
*/
public static void generateRunCodeUsingDirectStrategy(JitCodeGenerator gen,
JitCallOtherOpIf op, JitBlock block, MethodVisitor rv) {
public static <THIS extends JitCompiledPassage, LIB extends PcodeUseropLibrary<?>> OpResult
genRunDirectStrategy(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitCallOtherOpIf op,
JitBlock block, Scope scope) {
FieldForUserop useropField = gen.requestFieldForUserop(op.userop());
// Set<Varnode> live = gen.vsm.getLiveVars(block);
@@ -203,23 +209,9 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
* NOTE: It doesn't matter if there are live variables. We still have to "retire" the
* program counter and contextreg if the userop throws an exception.
*/
final Label tryStart = new Label();
final Label tryEnd = new Label();
rv.visitTryCatchBlock(tryStart, tryEnd,
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).label(), NAME_THROWABLE);
JitAllocationModel am = gen.getAllocationModel();
// []
useropField.generateLoadCode(gen, rv);
// [userop]
rv.visitMethodInsn(INVOKEINTERFACE, NAME_PCODE_USEROP_DEFINITION, "getDefiningLibrary",
MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY, true);
// [library:PcodeUseropLibrary]
Method method = op.userop().getJavaMethod();
String owningLibName = Type.getInternalName(method.getDeclaringClass());
rv.visitTypeInsn(CHECKCAST, owningLibName);
// [library:OWNING_TYPE]
Parameter[] parameters = method.getParameters();
Parameter outputParameter = findOutputParameter(parameters, method);
if (outputParameter != null && method.getReturnType() != void.class) {
@@ -228,100 +220,138 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
It's applied to %s of %s""".formatted(
OpOutput.class.getSimpleName(), outputParameter, method.getName()));
}
try (JvmTempAlloc out =
am.allocateTemp(rv, "out", int[].class, outputParameter == null ? 0 : 1)) {
MpIntJitType outMpType;
if (outputParameter != null) {
if (!(op instanceof JitCallOtherDefOp defOp)) {
outMpType = null;
rv.visitInsn(ACONST_NULL);
}
else {
outMpType = MpIntJitType.forSize(defOp.out().size());
rv.visitLdcInsn(outMpType.legsAlloc());
rv.visitIntInsn(NEWARRAY, T_INT);
}
rv.visitVarInsn(ASTORE, out.idx(0));
Local<TRef<int[]>> localOut;
MpIntJitType outMpType;
if (outputParameter != null) {
localOut = scope.decl(Types.T_INT_ARR, "out");
if (op instanceof JitCallOtherDefOp defOp) {
outMpType = MpIntJitType.forSize(defOp.out().size());
em = em
.emit(Op::ldc__i, outMpType.legsAlloc())
.emit(Op::newarray, Types.T_INT)
.emit(Op::astore, localOut);
}
else {
outMpType = null;
}
int argIdx = 0;
for (int i = 0; i < parameters.length; i++) {
Parameter p = parameters[i];
if (p == outputParameter) {
rv.visitVarInsn(ALOAD, out.idx(0));
continue;
}
JitVal arg = op.args().get(argIdx++);
// TODO: Should this always be zero extension?
JitType type = gen.generateValReadCode(arg, JitTypeBehavior.ANY, Ext.ZERO);
if (p.getType() == boolean.class) {
TypeConversions.generateIntToBool(type, rv);
continue;
}
if (p.getType() == int[].class) {
MpIntJitType mpType = MpIntJitType.forSize(type.size());
// NOTE: Would be nice to have annotation specify signedness
TypeConversions.generate(gen, type, mpType, Ext.ZERO, rv);
int legCount = mpType.legsAlloc();
try (JvmTempAlloc temp = am.allocateTemp(rv, "temp", legCount)) {
OpGen.generateMpLegsIntoTemp(temp, legCount, rv);
OpGen.generateMpLegsIntoArray(temp, legCount, legCount, rv);
}
continue;
}
// Some primitive/simple type
// TODO: Should this always be zero extension? Can annotation specify?
TypeConversions.generate(gen, type, JitType.forJavaType(p.getType()), Ext.ZERO,
rv);
}
// [library,params...]
rv.visitLabel(tryStart);
rv.visitMethodInsn(INVOKEVIRTUAL, owningLibName, method.getName(),
Type.getMethodDescriptor(method), false);
// [return?]
rv.visitLabel(tryEnd);
if (outputParameter != null) {
if (outMpType != null && op instanceof JitCallOtherDefOp defOp) {
rv.visitVarInsn(ALOAD, out.idx(0));
OpGen.generateMpLegsFromArray(outMpType.legsAlloc(), rv);
// NOTE: Want annotation to specify signedness
gen.generateVarWriteCode(defOp.out(), outMpType, Ext.ZERO);
}
// Else there's either no @OpOutput or the output operand is absent
}
else if (op instanceof JitCallOtherDefOp defOp) {
// TODO: Can annotation specify signedness of return value?
gen.generateVarWriteCode(defOp.out(), JitType.forJavaType(method.getReturnType()),
Ext.ZERO);
}
else if (method.getReturnType() != void.class) {
TypeConversions.generatePop(JitType.forJavaType(method.getReturnType()), rv);
em = em
.emit(Op::aconst_null, Types.T_INT_ARR)
.emit(Op::astore, localOut);
}
}
}
else {
outMpType = null;
localOut = null;
}
static class ResourceGroup implements AutoCloseable {
private final List<AutoCloseable> resources = new ArrayList<>();
TRef<LIB> libType = Types.refExtends(T_PCODE_USEROP_LIBRARY, method.getDeclaringClass());
@Override
public void close() throws Exception {
for (AutoCloseable r : resources) {
r.close();
var rec = new Object() {
<N extends Next> Emitter<? extends Ent<N, ?>> doReadArg(Emitter<N> em, JitVal arg,
Parameter param) {
if (param.getType() == boolean.class) {
return gen.genReadToBool(em, localThis, arg);
}
if (param.getType() == int[].class) {
MpIntJitType t = MpIntJitType.forSize(arg.size());
// TODO: Annotation/attribute to specify slack?
return gen.genReadToArray(em, localThis, arg, t, Ext.ZERO, scope, 0);
}
return switch (JitType.forJavaType(param.getType())) {
case IntJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
case LongJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
case FloatJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
case DoubleJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
default -> throw new AssertionError();
};
}
}
public <T extends AutoCloseable> T add(T resource) {
resources.add(resource);
return resource;
<N extends Next> ObjInv<?, LIB, N, ?> doInv(Emitter<N> em, List<JitVal> args,
List<Parameter> params) {
if (params.isEmpty()) {
/**
* NOTE: Can't put try-catch block here because the handler's all expect
* Ent<Bot,Throwable>
*/
return Op.invokevirtual(em, libType, method.getName(), MthDesc.reflect(method),
false);
}
Parameter param = params.getFirst();
if (param == outputParameter) {
var emOut = em.emit(Op::aload, localOut);
var inv = doInv(emOut, args, params.subList(1, params.size()));
return Inv.takeQArg(inv);
}
JitVal arg = args.getFirst();
// TODO: Annotation/attribute to describe signedness?
// TODO: Or a way to receive the byte size
var emRead = doReadArg(em, arg, param);
var inv =
doInv(emRead, args.subList(1, args.size()), params.subList(1, params.size()));
return Inv.takeQArg(inv);
}
};
var tryCatchBlock = Misc.tryCatch(em, Lbl.create(),
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).lbl(),
GenConsts.T_THROWABLE);
em = tryCatchBlock.em();
var emLib = em
.emit(useropField::genLoad, localThis, gen)
.emit(Op::invokeinterface, T_PCODE_USEROP_DEFINITION, "getDefiningLibrary",
MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY)
.step(Inv::takeObjRef)
.step(Inv::ret)
.emit(Op::checkcast, libType);
var inv = rec
.doInv(emLib, op.args(), Arrays.asList(parameters))
.step(Inv::takeQObjRef);
if (outputParameter != null) {
if (outMpType != null && op instanceof JitCallOtherDefOp defOp) {
em = inv
.step(Inv::retQVoid)
.emit(Op::aload, localOut)
.emit(gen::genWriteFromArray, localThis, defOp.out(), outMpType, Ext.ZERO,
scope);
}
// Else there's either no @OpOutput or the output operand is absent
}
else if (op instanceof JitCallOtherDefOp defOp) {
// TODO: Can annotation specify signedness of return value?
var write = new Object() {
public <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> Emitter<Bot> doWrite(
Inv<?, Bot, Bot> inv, Class<?> returnType) {
JT type = SimpleJitType.forJavaType(returnType);
return inv
.step(Inv::retQ, type.bType())
.emit(gen::genWriteFromStack, localThis, defOp.out(), type, Ext.ZERO,
scope);
}
};
em = inv.step(write::doWrite, method.getReturnType());
}
else if (method.getReturnType() != void.class) {
em = switch (JitType.forJavaType(method.getReturnType())) {
case IntJitType t -> inv
.step(Inv::retQ, t.bType())
.emit(Op::pop);
case LongJitType t -> inv
.step(Inv::retQ, t.bType())
.emit(Op::pop2__2);
case FloatJitType t -> inv
.step(Inv::retQ, t.bType())
.emit(Op::pop);
case DoubleJitType t -> inv
.step(Inv::retQ, t.bType())
.emit(Op::pop2__2);
default -> throw new AssertionError();
};
}
return new LiveOpResult(em
.emit(Lbl::place, tryCatchBlock.end()));
}
/**
@@ -350,17 +380,17 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
}
@Override
public void generateRunCode(JitCodeGenerator gen, JitCallOtherOpIf op, JitBlock block,
MethodVisitor rv) {
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitCallOtherOpIf op, JitBlock block, Scope scope) {
if (op.userop().modifiesContext()) {
rv.visitLdcInsn(1);
RunFixedLocal.CTXMOD.generateStoreCode(rv);
em = em
.emit(Op::ldc__i, 1)
.emit(Op::istore, localCtxmod);
}
if (canDoDirectInvocation(op)) {
generateRunCodeUsingDirectStrategy(gen, op, block, rv);
}
else {
generateRunCodeUsingRetirementStrategy(gen, op.op(), block, op.userop(), rv);
return genRunDirectStrategy(em, localThis, gen, op, block, scope);
}
return genRunRetirementStrategy(em, localThis, gen, op.op(), block, op.userop());
}
}

View File

@@ -15,11 +15,16 @@
*/
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.JitVarScopeModel;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitCatenateOp;
/**
@@ -36,8 +41,9 @@ public enum CatenateOpGen implements OpGen<JitCatenateOp> {
GEN;
@Override
public void generateRunCode(JitCodeGenerator gen, JitCatenateOp op, JitBlock block,
MethodVisitor rv) {
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitCatenateOp op, JitBlock block, Scope scope) {
throw new AssertionError("Cannnot generate synthetic op");
}
}

View File

@@ -1,102 +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 org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import ghidra.lifecycle.Unfinished;
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.type.TypeConversions;
import ghidra.pcode.emu.jit.op.JitFloatTestOp;
/**
* An extension for float comparison operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends FloatBinOpGen<T> {
/**
* The JVM opcode to perform the comparison with float operands on the stack.
*
* @return the opcode
*/
int fcmpOpcode();
/**
* The JVM opcode to perform the comparison with double operands on the stack.
*
* @return the opcode
*/
int dcmpOpcode();
/**
* The JVM opcode to perform the conditional jump.
*
* <p>
* The condition should correspond to the true case of the p-code operator.
*
* @return the opcode
*/
int condOpcode();
/**
* {@inheritDoc}
*
* <p>
* This implementation reduces the need to just a few opcodes: 1) the opcode for comparing in
* case of JVM {@code float}, 2) the opcode for comparing in the case of JVM {@code double}, and
* 3) the conditional jump on the result of that comparison. First, the comparison opcode is
* emitted. It should result in and int &lt;0, ==0, or &gt;0 on the stack, depending on whether
* L&lt;R, L==R, or L&gt;R, respectively. Then the conditional jump is emitted. We place labels
* in an if-else pattern to place either a 1 (true) or 0 (false) value of the appropriate p-code
* type on the stack.
*
* @implNote This template is consistently generated by the Java compiler (Adoptium OpenJDK 21),
* despite there being possible branchless implementations. That could indicate one of
* a few things: 1) the HotSpot JIT knows how to optimize this pattern, perhaps using
* branchless native instructions, 2) branchless optimizations don't yield the speedup
* here we might expect, or 3) they didn't care to optimize. <b>TODO</b>: Investigate
* in case it's thing 3. We might like to see if branchless JVM bytecodes can improve
* performance.
*/
@Override
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
JitType rType, MethodVisitor rv) {
assert rType == lType;
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
Label lblTrue = new Label();
Label lblDone = new Label();
switch (rType) {
case FloatJitType t -> rv.visitInsn(fcmpOpcode());
case DoubleJitType t -> rv.visitInsn(dcmpOpcode());
case MpFloatJitType t -> Unfinished.TODO("MpFloat");
default -> throw new AssertionError();
}
rv.visitJumpInsn(condOpcode(), lblTrue);
TypeConversions.generateLdcFalse(outType, rv);
rv.visitJumpInsn(GOTO, lblDone);
rv.visitLabel(lblTrue);
TypeConversions.generateLdcTrue(outType, rv);
rv.visitLabel(lblDone);
return outType;
}
}

View File

@@ -1,173 +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.pcode.emu.jit.gen.GenConsts.*;
import org.objectweb.asm.Label;
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.op.JitIntTestOp;
/**
* An extension for integer comparison operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends IntBinOpGen<T> {
/**
* {@inheritDoc}
* <p>
* If the comparison is unsigned, we will emit invocations of
* {@link Integer#compareUnsigned(int, int)} or {@link Long#compareUnsigned(long, long)},
* followed by a conditional jump corresponding to this p-code comparison op. If the comparison
* is signed, and the type fits in a JVM int, we emit the conditional jump of ints directly
* implementing this p-code comparison op. If the type requires a JVM long, we first emit an
* {@link #LCMP lcmp}, followed by the same opcode that would be used in the unsigned case.
*
* @return true if signed, false if not
*/
@Override
boolean isSigned();
/**
* The JVM opcode to perform the conditional jump for signed integers.
*
* @return the opcode
*/
int icmpOpcode();
default void generateIntCmp(String methodName, MethodVisitor rv) {
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, methodName, MDESC_INTEGER__COMPARE,
false);
}
/**
* Emits bytecode for the JVM int case
*
* @param lblTrue the target bytecode label for the true case
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
default void generateIntJump(Label lblTrue, MethodVisitor rv) {
if (isSigned()) {
rv.visitJumpInsn(icmpOpcode(), lblTrue);
}
else {
generateIntCmp("compareUnsigned", rv);
rv.visitJumpInsn(ifOpcode(), lblTrue);
}
}
/**
* Emits bytecode for the JVM long case
*
* @param lblTrue the target bytecode label for the true case
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
*/
default void generateLongJump(Label lblTrue, MethodVisitor rv) {
if (isSigned()) {
rv.visitInsn(LCMP);
}
else {
rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "compareUnsigned",
MDESC_LONG__COMPARE_UNSIGNED, false);
}
rv.visitJumpInsn(ifOpcode(), lblTrue);
}
/**
* The JVM opcode to perform the conditional jump for unsigned or long integers.
* <p>
* This is emitted <em>after</em> the application of {@link #LCMP} or the comparator method.
*
* @return the opcode
*/
int ifOpcode();
@Override
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
MethodVisitor rv) {
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
}
default JitType generateMpIntCmp(JitCodeGenerator gen, MpIntJitType type, Label lblTrue,
MethodVisitor mv) {
int legCount = type.legsAlloc();
Label lblDone = new Label();
// Need two temps, because comparison is from *most* to least-significant
try (
JvmTempAlloc tmpL = gen.getAllocationModel().allocateTemp(mv, "tmpL", legCount);
JvmTempAlloc tmpR = gen.getAllocationModel().allocateTemp(mv, "tmpR", legCount)) {
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
for (int i = 0; i < legCount; i++) {
mv.visitVarInsn(ILOAD, tmpL.idx(legCount - i - 1));
mv.visitVarInsn(ILOAD, tmpR.idx(legCount - i - 1));
//OpGen.generateSyserrInts(gen, 2, mv);
generateIntCmp(i == 0 ? "compare" : "compareUnsigned", mv);
if (i != legCount - 1) {
mv.visitInsn(DUP);
mv.visitJumpInsn(IFNE, lblDone);
mv.visitInsn(POP);
}
}
}
mv.visitLabel(lblDone);
mv.visitJumpInsn(ifOpcode(), lblTrue);
return IntJitType.I4;
}
/**
* {@inheritDoc}
*
* <p>
* This reduces the implementation to a flag for signedness, the opcode for the conditional jump
* on integer operands, and the opcode for a conditional jump after the comparison of longs. The
* JVM, does not provide conditional jumps on long operands, so we must first compare the longs,
* pushing an int onto the stack, and then conditionally jumping on that. This pattern is
* similar for unsigned comparison of integers.
*/
@Override
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
JitType rType, MethodVisitor rv) {
Label lblTrue = new Label();
Label lblDone = new Label();
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
switch (rType) {
case IntJitType t -> generateIntJump(lblTrue, rv);
case LongJitType t -> generateLongJump(lblTrue, rv);
case MpIntJitType t -> generateMpIntCmp(gen, t, lblTrue, rv);
default -> throw new AssertionError();
}
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
TypeConversions.generateLdcFalse(outType, rv);
rv.visitJumpInsn(GOTO, lblDone);
rv.visitLabel(lblTrue);
TypeConversions.generateLdcTrue(outType, rv);
rv.visitLabel(lblDone);
return outType;
}
}

View File

@@ -15,22 +15,16 @@
*/
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.op.JitCopyOp;
/**
* The generator for a {@link JitCopyOp copy}.
*
* <p>
* This uses the unary operator generator and emits nothing extra. The unary generator template will
* emit code to load the input operand, this emits nothing, and then the template emits code to
* write the output operand, effecting a simple copy.
* This is identical to {@link IntZExtOpGen}, except that we expect (require?) the output and input
* operand to agree in size, and so we don't actually expect any extension. In the event that is not
* the case, it seems agreeable that zero extension is applied.
*/
public enum CopyOpGen implements UnOpGen<JitCopyOp> {
public enum CopyOpGen implements IntExtUnOpGen<JitCopyOp> {
/** The generator singleton */
GEN;
@@ -38,10 +32,4 @@ public enum CopyOpGen implements UnOpGen<JitCopyOp> {
public boolean isSigned() {
return false;
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitCopyOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
return uType;
}
}

View File

@@ -15,15 +15,15 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
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.gen.JitCodeGenerator;
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.Methods.Inv;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatAbsOp;
/**
@@ -33,21 +33,25 @@ import ghidra.pcode.emu.jit.op.JitFloatAbsOp;
* This uses the unary operator generator and emits an invocation of {@link Math#abs(float)} or
* {@link Math#abs(double)}, depending on the type.
*/
public enum FloatAbsOpGen implements FloatUnOpGen<JitFloatAbsOp> {
public enum FloatAbsOpGen implements FloatOpUnOpGen<JitFloatAbsOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatAbsOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "abs",
MDESC_$FLOAT_UNOP, false);
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "abs",
MDESC_$DOUBLE_UNOP, false);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return em
.emit(Op::invokestatic, T_MATH, "abs", MDESC_$FLOAT_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return em
.emit(Op::invokestatic, T_MATH, "abs", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
}

View File

@@ -15,37 +15,34 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatAddOp;
/**
* The generator for a {@link JitFloatAddOp float_add}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #FADD} or {@link #DADD} depending
* on the type.
* This uses the binary operator generator and simply emits {@link Op#fadd(Emitter) fadd} or
* {@link Op#dadd(Emitter) dadd} depending on the type.
*/
public enum FloatAddOpGen implements FloatBinOpGen<JitFloatAddOp> {
public enum FloatAddOpGen implements FloatOpBinOpGen<JitFloatAddOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatAddOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
assert rType == lType;
switch (lType) {
case FloatJitType t -> rv.visitInsn(FADD);
case DoubleJitType t -> rv.visitInsn(DADD);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return lType;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
return Op.fadd(em);
}
@Override
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
return Op.dadd(em);
}
}

View File

@@ -15,16 +15,16 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
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.gen.JitCodeGenerator;
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.Methods.Inv;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatCeilOp;
/**
@@ -34,25 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatCeilOp;
* This uses the unary operator generator and emits an invocation of {@link Math#ceil(double)},
* possibly surrounding it with conversions from and to float.
*/
public enum FloatCeilOpGen implements FloatUnOpGen<JitFloatCeilOp> {
public enum FloatCeilOpGen implements FloatOpUnOpGen<JitFloatCeilOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatCeilOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> {
// There apparently is no Math.ceil(float)???
rv.visitInsn(F2D);
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "ceil", MDESC_$DOUBLE_UNOP, false);
rv.visitInsn(D2F);
}
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "ceil",
MDESC_$DOUBLE_UNOP, false);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return em
.emit(Op::f2d)
.emit(this::opForDouble)
.emit(Op::d2f);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return em
.emit(Op::invokestatic, T_MATH, "ceil", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
}

View File

@@ -0,0 +1,140 @@
/* ###
* 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 java.util.Comparator;
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.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatTestOp;
/**
* An extension for float comparison operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface FloatCompareBinOpGen<T extends JitFloatTestOp> extends BinOpGen<T> {
@Override
default boolean isSigned() {
return false;
}
/**
* Emit the JVM bytecode to perform the comparison with float operands on the stack.
* <p>
* The result should be as defined by {@link Comparator#compare(Object, Object)}.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>>
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em);
/**
* Emit the JVM bytecode to perform the comparison with double operands on the stack.
* <p>
* The result should be as defined by {@link Comparator#compare(Object, Object)}.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>>
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em);
/**
* Emit the JVM opcode to perform the conditional jump.
* <p>
* The condition should correspond to the true case of the p-code operator.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack including the comparison result
* @param em the emitter typed with the incoming stack
* @return the target label and emitter typed with the resulting stack, i.e., with the
* comparison result popped
*/
<N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em);
/**
* {@inheritDoc}
*
* <p>
* This implementation reduces the need to just a few opcodes: 1) the opcode for comparing in
* case of JVM {@code float}, 2) the opcode for comparing in the case of JVM {@code double}, and
* 3) the conditional jump on the result of that comparison. First, the comparison opcode is
* emitted. It should result in and int &lt;0, ==0, or &gt;0 on the stack, depending on whether
* L&lt;R, L==R, or L&gt;R, respectively. Then the conditional jump is emitted. We place labels
* in an if-else pattern to place either a 1 (true) or 0 (false) value of the appropriate p-code
* type on the stack.
*
* @implNote This template is consistently generated by the Java compiler (Adoptium OpenJDK 21),
* despite there being possible branchless implementations. That could indicate one of
* a few things: 1) the HotSpot JIT knows how to optimize this pattern, perhaps using
* branchless native instructions, 2) branchless optimizations don't yield the speedup
* here we might expect, or 3) they didn't care to optimize. <b>TODO</b>: Investigate
* in case it's thing 3. We might like to see if branchless JVM bytecodes can improve
* performance.
*/
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType lType = gen.resolveType(op.l(), op.lType());
JitType rType = gen.resolveType(op.r(), op.rType());
assert rType == lType;
Emitter<Ent<Bot, TInt>> emCmp = switch (lType) {
case FloatJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
.emit(this::opForFloatCmp);
case DoubleJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, Ext.ZERO)
.emit(gen::genReadToStack, localThis, op.r(), t, Ext.ZERO)
.emit(this::opForDoubleCmp);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
};
// LATER: Given the type conversion during write, we may not need a jump at all.
var lblTrue = emCmp
.emit(this::opForCondJump);
var lblDone = lblTrue.em()
.emit(Op::ldc__i, 0)
.emit(Op::goto_);
return new LiveOpResult(lblDone.em()
.emit(Lbl::placeDead, lblTrue.lbl())
.emit(Op::ldc__i, 1)
.emit(Lbl::place, lblDone.lbl())
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, Ext.ZERO, scope));
}
}

View File

@@ -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.gen.op;
import java.util.function.Function;
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.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitUnOp;
/**
* An extension for float conversion operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface FloatConvertUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
/**
* An implementation based on a given bytecode op
*
* @param <THIS> the type of the generated passage
* @param <UT> the JVM type of the input operand
* @param <UJT> the p-code type of the input operand
* @param <OT> the JVM type of the output operand
* @param <OJT> the p-code type of the output operand
* @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 op the p-code op
* @param ut the p-code type of the input operand
* @param ot the p-code type of the output operand
* @param opcode a method reference, e.g., to {@link Op#f2d(Emitter)} for the conversion
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the incoming stack
*/
default <THIS extends JitCompiledPassage, UT extends BPrim<?>,
UJT extends SimpleJitType<UT, UJT>, OT extends BPrim<?>,
OJT extends SimpleJitType<OT, OJT>> Emitter<Bot> gen(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, UJT ut, OJT ot,
Function<? super Emitter<Ent<Bot, UT>>, Emitter<Ent<Bot, OT>>> opcode,
Scope scope) {
return em
.emit(gen::genReadToStack, localThis, op.u(), ut, ext())
.emit(opcode)
.emit(gen::genWriteFromStack, localThis, op.out(), ot, ext(), scope);
}
}

View File

@@ -15,37 +15,34 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatDivOp;
/**
* The generator for a {@link JitFloatDivOp float_div}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #FDIV} or {@link #DDIV} depending
* on the type.
* This uses the binary operator generator and simply emits {@link Op#fdiv(Emitter) fdiv} or
* {@link Op#ddiv(Emitter) ddiv} depending on the type.
*/
public enum FloatDivOpGen implements FloatBinOpGen<JitFloatDivOp> {
public enum FloatDivOpGen implements FloatOpBinOpGen<JitFloatDivOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatDivOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
assert rType == lType;
switch (lType) {
case FloatJitType t -> rv.visitInsn(FDIV);
case DoubleJitType t -> rv.visitInsn(DDIV);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return lType;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
return Op.fdiv(em);
}
@Override
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
return Op.ddiv(em);
}
}

View File

@@ -15,31 +15,40 @@
*/
package ghidra.pcode.emu.jit.gen.op;
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.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatEqualOp;
/**
* The generator for a {@link JitFloatEqualOp float_equal}.
*
* <p>
* This uses the float comparison operator generator and simply emits {@link #FCMPL} or
* {@link #DCMPL} depending on the type and then {@link #IFEQ}.
* This uses the float comparison operator generator and simply emits {@link Op#fcmpl(Emitter)
* fcmpl} or {@link Op#dcmpl(Emitter) dcmpl} depending on the type and then {@link Op#ifeq(Emitter)
* ifeq}.
*/
public enum FloatEqualOpGen implements CompareFloatOpGen<JitFloatEqualOp> {
public enum FloatEqualOpGen implements FloatCompareBinOpGen<JitFloatEqualOp> {
/** The generator singleton */
GEN;
@Override
public int fcmpOpcode() {
return FCMPL;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
return Op.fcmpl(em);
}
@Override
public int dcmpOpcode() {
return DCMPL;
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
return Op.dcmpl(em);
}
@Override
public int condOpcode() {
return IFEQ;
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
return Op.ifeq(em);
}
}

View File

@@ -17,48 +17,56 @@ package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitFloatFloat2FloatOp;
/**
* The generator for a {@link JitFloatFloat2FloatOp float_float2float}.
*
* <p>
* This uses the unary operator generator and emits {@link #F2D} or {@link #D2F}.
* This uses the unary operator generator and emits {@link Op#f2d(Emitter) f2d} or
* {@link Op#d2f(Emitter) d2f}.
*/
public enum FloatFloat2FloatOpGen implements FloatUnOpGen<JitFloatFloat2FloatOp> {
public enum FloatFloat2FloatOpGen implements FloatConvertUnOpGen<JitFloatFloat2FloatOp> {
/** The generator singleton */
GEN;
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
rv.visitInsn(opcode);
return type;
@Override
public boolean isSigned() {
return false;
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatFloat2FloatOp op,
JitBlock block, JitType uType, MethodVisitor rv) {
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
return switch (uType) {
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitFloatFloat2FloatOp op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
JitType outType = gen.resolveType(op.out(), op.type());
return new LiveOpResult(switch (uType) {
case FloatJitType ut -> switch (outType) {
case FloatJitType ot -> ot;
case DoubleJitType ot -> gen(rv, F2D, ot);
case FloatJitType ot -> em;
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2d, scope);
case MpFloatJitType ot -> TODO("MpFloat");
default -> throw new AssertionError();
};
case DoubleJitType ut -> switch (outType) {
case FloatJitType ot -> gen(rv, D2F, ot);
case DoubleJitType ot -> ot;
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2f, scope);
case DoubleJitType ot -> em;
case MpFloatJitType ot -> TODO("MpFloat");
default -> throw new AssertionError();
};
case MpFloatJitType ot -> TODO("MpFloat");
default -> throw new AssertionError();
};
});
}
}

View File

@@ -15,16 +15,16 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
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.gen.JitCodeGenerator;
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.Methods.Inv;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatFloorOp;
/**
@@ -34,25 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatFloorOp;
* This uses the unary operator generator and emits an invocation of {@link Math#floor(double)},
* possibly surrounding it with conversions from and to float.
*/
public enum FloatFloorOpGen implements FloatUnOpGen<JitFloatFloorOp> {
public enum FloatFloorOpGen implements FloatOpUnOpGen<JitFloatFloorOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatFloorOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> {
// There apparently is no Math.floor(float)???
rv.visitInsn(F2D);
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
rv.visitInsn(D2F);
}
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor",
MDESC_$DOUBLE_UNOP, false);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return em
.emit(Op::f2d)
.emit(this::opForDouble)
.emit(Op::d2f);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return em
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
}

View File

@@ -17,22 +17,27 @@ package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitFloatInt2FloatOp;
/**
* The generator for a {@link JitFloatInt2FloatOp float_int2float}.
*
* <p>
* This uses the unary operator generator and emits {@link #I2F}, {@link #I2D}, {@link #L2F}, or
* {@link #L2D}.
* This uses the unary operator generator and emits {@link Op#i2f(Emitter) i2f},
* {@link Op#i2d(Emitter) i2d}, {@link Op#l2f(Emitter) l2f}, or {@link Op#l2d(Emitter) l2d}.
*/
public enum FloatInt2FloatOpGen implements UnOpGen<JitFloatInt2FloatOp> {
public enum FloatInt2FloatOpGen implements FloatConvertUnOpGen<JitFloatInt2FloatOp> {
/** The generator singleton */
GEN;
@@ -41,30 +46,27 @@ public enum FloatInt2FloatOpGen implements UnOpGen<JitFloatInt2FloatOp> {
return false; // TODO: Is it signed? Test to figure it out.
}
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
rv.visitInsn(opcode);
return type;
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatInt2FloatOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
return switch (uType) {
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitFloatInt2FloatOp op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
JitType outType = gen.resolveType(op.out(), op.type());
return new LiveOpResult(switch (uType) {
case IntJitType ut -> switch (outType) {
case FloatJitType ot -> gen(rv, I2F, ot);
case DoubleJitType ot -> gen(rv, I2D, ot);
case MpFloatJitType ot -> TODO("MpInt/Float");
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::i2f, scope);
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::i2d, scope);
case MpFloatJitType ot -> TODO("MpFloat");
default -> throw new AssertionError();
};
case LongJitType ut -> switch (outType) {
case FloatJitType ot -> gen(rv, L2F, ot);
case DoubleJitType ot -> gen(rv, L2D, ot);
case MpFloatJitType ot -> TODO("MpInt/Float");
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::l2f, scope);
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::l2d, scope);
case MpFloatJitType ot -> TODO("MpFloat");
default -> throw new AssertionError();
};
case MpIntJitType ut -> TODO("MpInt/Float");
case MpIntJitType ut -> TODO("MpInt->Float");
default -> throw new AssertionError();
};
});
}
}

View File

@@ -15,31 +15,40 @@
*/
package ghidra.pcode.emu.jit.gen.op;
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.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatLessEqualOp;
/**
* The generator for a {@link JitFloatLessEqualOp float_lessequal}.
*
* <p>
* This uses the float comparison operator generator and simply emits {@link #FCMPG} or
* {@link #DCMPG} depending on the type and then {@link #IFLE}.
* This uses the float comparison operator generator and simply emits {@link Op#fcmpg(Emitter)
* fcmpg} or {@link Op#dcmpg(Emitter) dcmpg} depending on the type and then {@link Op#ifle(Emitter)
* ifle}.
*/
public enum FloatLessEqualOpGen implements CompareFloatOpGen<JitFloatLessEqualOp> {
public enum FloatLessEqualOpGen implements FloatCompareBinOpGen<JitFloatLessEqualOp> {
/** The generator singleton */
GEN;
@Override
public int fcmpOpcode() {
return FCMPG;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
return Op.fcmpg(em);
}
@Override
public int dcmpOpcode() {
return DCMPG;
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
return Op.dcmpg(em);
}
@Override
public int condOpcode() {
return IFLE;
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
return Op.ifle(em);
}
}

View File

@@ -15,31 +15,40 @@
*/
package ghidra.pcode.emu.jit.gen.op;
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.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatLessOp;
/**
* The generator for a {@link JitFloatLessOp float_less}.
*
* <p>
* This uses the float comparison operator generator and simply emits {@link #FCMPG} or
* {@link #DCMPG} depending on the type and then {@link #IFLT}.
* This uses the float comparison operator generator and simply emits {@link Op#fcmpg(Emitter)
* fcmpg} or {@link Op#dcmpg(Emitter) dcmpg} depending on the type and then {@link Op#iflt(Emitter)
* iflt}.
*/
public enum FloatLessOpGen implements CompareFloatOpGen<JitFloatLessOp> {
public enum FloatLessOpGen implements FloatCompareBinOpGen<JitFloatLessOp> {
/** The generator singleton */
GEN;
@Override
public int fcmpOpcode() {
return FCMPG;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
return Op.fcmpg(em);
}
@Override
public int dcmpOpcode() {
return DCMPG;
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
return Op.dcmpg(em);
}
@Override
public int condOpcode() {
return IFLT;
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
return Op.iflt(em);
}
}

View File

@@ -15,37 +15,34 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatMultOp;
/**
* The generator for a {@link JitFloatMultOp float_mult}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #FMUL} or {@link #DMUL} depending
* on the type.
* This uses the binary operator generator and simply emits {@link Op#fmul(Emitter) fmul} or
* {@link Op#dmul(Emitter) dmul} depending on the type.
*/
public enum FloatMultOpGen implements FloatBinOpGen<JitFloatMultOp> {
public enum FloatMultOpGen implements FloatOpBinOpGen<JitFloatMultOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatMultOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
assert rType == lType;
switch (lType) {
case FloatJitType t -> rv.visitInsn(FMUL);
case DoubleJitType t -> rv.visitInsn(DMUL);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return lType;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
return Op.fmul(em);
}
@Override
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
return Op.dmul(em);
}
}

View File

@@ -18,12 +18,18 @@ package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
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.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitFloatNaNOp;
/**
@@ -33,21 +39,35 @@ import ghidra.pcode.emu.jit.op.JitFloatNaNOp;
* This uses the unary operator generator and emits an invocation of {@link Float#isNaN(float)} or
* {@link Double#isNaN(double)}, depending on the type.
*/
public enum FloatNaNOpGen implements FloatUnOpGen<JitFloatNaNOp> {
public enum FloatNaNOpGen implements UnOpGen<JitFloatNaNOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatNaNOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_FLOAT, "isNaN",
MDESC_FLOAT__IS_NAN, false);
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_DOUBLE, "isNaN",
MDESC_DOUBLE__IS_NAN, false);
public boolean isSigned() {
return false;
}
@Override
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitFloatNaNOp op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
return new LiveOpResult(switch (uType) {
case FloatJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(Op::invokestatic, TR_FLOAT, "isNaN", MDESC_FLOAT__IS_NAN, false)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, ext(), scope);
case DoubleJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(Op::invokestatic, TR_DOUBLE, "isNaN", MDESC_DOUBLE__IS_NAN, false)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, ext(), scope);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return IntJitType.I4;
});
}
}

View File

@@ -15,35 +15,34 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatNegOp;
/**
* The generator for a {@link JitFloatNegOp float_neg}.
*
* <p>
* This uses the unary operator generator and emits {@link #FNEG} or {@link #DNEG}.
* This uses the unary operator generator and emits {@link Op#fneg(Emitter) fneg} or
* {@link Op#dneg(Emitter) dneg}.
*/
public enum FloatNegOpGen implements FloatUnOpGen<JitFloatNegOp> {
public enum FloatNegOpGen implements FloatOpUnOpGen<JitFloatNegOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatNegOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> rv.visitInsn(FNEG);
case DoubleJitType t -> rv.visitInsn(DNEG);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return Op.fneg(em);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return Op.dneg(em);
}
}

View File

@@ -15,31 +15,40 @@
*/
package ghidra.pcode.emu.jit.gen.op;
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.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Op;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatNotEqualOp;
/**
* The generator for a {@link JitFloatNotEqualOp float_notequal}.
*
* <p>
* This uses the float comparison operator generator and simply emits {@link #FCMPL} or
* {@link #DCMPL} depending on the type and then {@link #IFNE}.
* This uses the float comparison operator generator and simply emits {@link Op#fcmpl(Emitter)
* fcmpl} or {@link Op#dcmpl(Emitter) dcmpl} depending on the type and then {@link Op#ifne(Emitter)
* ifne}.
*/
public enum FloatNotEqualOpGen implements CompareFloatOpGen<JitFloatNotEqualOp> {
public enum FloatNotEqualOpGen implements FloatCompareBinOpGen<JitFloatNotEqualOp> {
/** The generator singleton */
GEN;
@Override
public int fcmpOpcode() {
return FCMPL;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
return Op.fcmpl(em);
}
@Override
public int dcmpOpcode() {
return DCMPL;
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
return Op.dcmpl(em);
}
@Override
public int condOpcode() {
return IFNE;
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
return Op.ifne(em);
}
}

View File

@@ -0,0 +1,91 @@
/* ###
* 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 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.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatBinOp;
/**
* An extension for floating-point binary operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface FloatOpBinOpGen<T extends JitFloatBinOp> extends BinOpGen<T> {
@Override
default boolean isSigned() {
return false;
}
/**
* Emit the JVM bytecode to perform the operator with float operands on the stack.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>>
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em);
/**
* Emit the JVM bytecode to perform the operator with double operands on the stack.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>>
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em);
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType lType = gen.resolveType(op.l(), op.lType());
JitType rType = gen.resolveType(op.r(), op.rType());
assert rType == lType;
return new LiveOpResult(switch (lType) {
case FloatJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
.emit(this::opForFloat)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case DoubleJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, Ext.ZERO)
.emit(gen::genReadToStack, localThis, op.r(), t, Ext.ZERO)
.emit(this::opForDouble)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
});
}
}

View File

@@ -0,0 +1,83 @@
/* ###
* 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 ghidra.lifecycle.Unfinished;
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.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitFloatUnOp;
/**
* An extension for floating-point unary operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface FloatOpUnOpGen<T extends JitFloatUnOp> extends UnOpGen<T> {
@Override
default boolean isSigned() {
return false;
}
/**
* Emit the JVM bytecode to perform the operator with float operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em);
/**
* Emit the JVM bytecode to perform the operator with double operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em);
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
return new LiveOpResult(switch (uType) {
case FloatJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForFloat)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case DoubleJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForDouble)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case MpFloatJitType t -> Unfinished.TODO("MpFloat");
default -> throw new AssertionError();
});
}
}

View File

@@ -15,16 +15,16 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
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.gen.JitCodeGenerator;
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.Methods.Inv;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatRoundOp;
/**
@@ -37,30 +37,31 @@ import ghidra.pcode.emu.jit.op.JitFloatRoundOp;
* {@code round(x) = floor(x + 0.5)}. This uses the unary operator generator and emits the bytecode
* to implement that definition, applying type conversions as needed.
*/
public enum FloatRoundOpGen implements FloatUnOpGen<JitFloatRoundOp> {
public enum FloatRoundOpGen implements FloatOpUnOpGen<JitFloatRoundOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatRoundOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
// Math.round also converts to int/long
case FloatJitType t -> {
rv.visitLdcInsn(0.5f);
rv.visitInsn(FADD);
rv.visitInsn(F2D);
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
rv.visitInsn(D2F);
}
case DoubleJitType t -> {
rv.visitLdcInsn(0.5d);
rv.visitInsn(DADD);
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
}
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return em
.emit(Op::ldc__f, 0.5f)
.emit(Op::fadd)
.emit(Op::f2d)
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::d2f);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return em
.emit(Op::ldc__d, 0.5)
.emit(Op::dadd)
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
}

View File

@@ -15,16 +15,16 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
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.gen.JitCodeGenerator;
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.Methods.Inv;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatSqrtOp;
/**
@@ -34,24 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatSqrtOp;
* This uses the unary operator generator and emits an invocation of {@link Math#sqrt(double)},
* possibly surrounding it with conversions from and to float.
*/
public enum FloatSqrtOpGen implements FloatUnOpGen<JitFloatSqrtOp> {
public enum FloatSqrtOpGen implements FloatOpUnOpGen<JitFloatSqrtOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatSqrtOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case FloatJitType t -> {
rv.visitInsn(F2D);
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "sqrt", MDESC_$DOUBLE_UNOP, false);
rv.visitInsn(D2F);
}
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "sqrt",
MDESC_$DOUBLE_UNOP, false);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return uType;
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
opForFloat(Emitter<N0> em) {
return em
.emit(Op::f2d)
.emit(this::opForDouble)
.emit(Op::d2f);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
opForDouble(Emitter<N0> em) {
return em
.emit(Op::invokestatic, T_MATH, "sqrt", MDESC_$DOUBLE_UNOP, false)
.step(Inv::takeArg)
.step(Inv::ret);
}
}

View File

@@ -15,37 +15,34 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
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.TFloat;
import ghidra.pcode.emu.jit.op.JitFloatSubOp;
/**
* The generator for a {@link JitFloatSubOp float_sub}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #FSUB} or {@link #DSUB} depending
* on the type.
* This uses the binary operator generator and simply emits {@link Op#fsub(Emitter) fsub} or
* {@link Op#dsub(Emitter) dsub} depending on the type.
*/
public enum FloatSubOpGen implements FloatBinOpGen<JitFloatSubOp> {
public enum FloatSubOpGen implements FloatOpBinOpGen<JitFloatSubOp> {
/** The generator singleton */
GEN;
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatSubOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
assert rType == lType;
switch (lType) {
case FloatJitType t -> rv.visitInsn(FSUB);
case DoubleJitType t -> rv.visitInsn(DSUB);
case MpFloatJitType t -> TODO("MpFloat");
default -> throw new AssertionError();
}
return lType;
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
return Op.fsub(em);
}
@Override
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
return Op.dsub(em);
}
}

View File

@@ -17,49 +17,56 @@ package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitFloatTruncOp;
/**
* The generator for a {@link JitFloatTruncOp float_trunc}.
*
* <p>
* This uses the unary operator generator and emits {@link #F2I}, {@link #F2L}, {@link #D2I}, or
* {@link #D2L}.
* This uses the unary operator generator and emits {@link Op#f2i(Emitter) f2i},
* {@link Op#f2l(Emitter) f2l}, {@link Op#d2i(Emitter) d2i}, or {@link Op#d2l(Emitter) d2l}.
*/
public enum FloatTruncOpGen implements FloatUnOpGen<JitFloatTruncOp> {
public enum FloatTruncOpGen implements FloatConvertUnOpGen<JitFloatTruncOp> {
/** The generator singleton */
GEN;
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
rv.visitInsn(opcode);
return type;
@Override
public boolean isSigned() {
return false; // TODO: Is it signed? Test to figure it out.
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatTruncOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
return switch (uType) {
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, JitFloatTruncOp op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
JitType outType = gen.resolveType(op.out(), op.type());
return new LiveOpResult(switch (uType) {
case FloatJitType ut -> switch (outType) {
case IntJitType ot -> gen(rv, F2I, ot);
case LongJitType ot -> gen(rv, F2L, ot);
case MpIntJitType ot -> TODO("MpFloat/Int");
case IntJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2i, scope);
case LongJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2l, scope);
case MpIntJitType ot -> TODO("Float->MpInt");
default -> throw new AssertionError();
};
case DoubleJitType ut -> switch (outType) {
case IntJitType ot -> gen(rv, D2I, ot);
case LongJitType ot -> gen(rv, D2L, ot);
case MpIntJitType ot -> TODO("MpFloat/Int");
case IntJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2i, scope);
case LongJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2l, scope);
case MpIntJitType ot -> TODO("Float->MpInt");
default -> throw new AssertionError();
};
case MpIntJitType ot -> TODO("MpFloat/Int");
case MpFloatJitType ut -> TODO("MpInt->Float");
default -> throw new AssertionError();
};
});
}
}

View File

@@ -15,27 +15,35 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import java.util.ArrayList;
import java.util.List;
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.analysis.JitType.IntJitType;
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.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd.SimpleOpndEm;
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.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitInt2CompOp;
/**
* The generator for a {@link JitInt2CompOp int_2comp}.
*
* <p>
* This uses the unary operator generator and emits {@link #INEG} or {@link #LNEG}, depending on
* type.
* This uses the unary operator generator and emits {@link Op#ineg(Emitter) ineg} or
* {@link Op#lneg(Emitter) lneg}, depending on type.
* <p>
* The multi-precision logic is similar to {@link IntAddOpGen}. We follow the process "flip the bits
* and add 1", so for each leg, we consider that it may have a carry in. We then invert all the bits
* using ^-1 and then add that carry in. The least significant leg is assumed to have a carry in,
* effecting the +1.
*/
public enum Int2CompOpGen implements IntUnOpGen<JitInt2CompOp> {
public enum Int2CompOpGen implements IntOpUnOpGen<JitInt2CompOp> {
/** The generator singleton */
GEN;
@@ -44,62 +52,139 @@ public enum Int2CompOpGen implements IntUnOpGen<JitInt2CompOp> {
return false; // TODO: Is it? Test with 3-byte operands to figure it out.
}
private void generateMpIntLeg2Cmp(int idx, IntJitType type, boolean givesCarry,
MethodVisitor mv) {
// [carryN-1:LONG]
mv.visitVarInsn(ILOAD, idx);
// [legN:INT,carry:LONG]
mv.visitLdcInsn(-1 >>> (Integer.SIZE - type.size() * Byte.SIZE));
// [ff:INT,legN:INT,carry:LONG]
mv.visitInsn(IXOR);
// [invN:INT,carry:LONG]
TypeConversions.generateIntToLong(type, LongJitType.I8, Ext.ZERO, mv);
// [invN:LONG,carry:LONG]
mv.visitInsn(LADD);
// [carry|2cmpN:LONG]
if (givesCarry) {
mv.visitInsn(DUP2);
// [carry|2cmpN:LONG,carry|2cmpN:LONG]
TypeConversions.generateLongToInt(LongJitType.I8, type, Ext.ZERO, mv);
// [2cmpN:INT,carry|2cmpN:LONG]
mv.visitVarInsn(ISTORE, idx);
// [carry|2cmpN:LONG]
mv.visitLdcInsn(Integer.SIZE);
// [32:INT, carry:LONG]
mv.visitInsn(LUSHR);
// [carryN:LONG]
}
else {
TypeConversions.generateLongToInt(LongJitType.I8, type, Ext.ZERO, mv);
// [2cmpN:INT]
mv.visitVarInsn(ISTORE, idx);
// []
}
}
private void generateMpInt2Comp(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
int legCount = type.legsAlloc();
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
List<IntJitType> types = type.legTypes().reversed();
mv.visitLdcInsn(1L); // Seed the "carry in" with the 1 to add
for (int i = 0; i < legCount; i++) {
boolean isLast = i == legCount - 1;
generateMpIntLeg2Cmp(result.idx(i), types.get(i), !isLast, mv);
}
OpGen.generateMpLegsFromTemp(result, legCount, mv);
}
@Override
public <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
opForInt(Emitter<N0> em) {
return Op.ineg(em);
}
@Override
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitInt2CompOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case IntJitType t -> rv.visitInsn(INEG);
case LongJitType t -> rv.visitInsn(LNEG);
case MpIntJitType t -> generateMpInt2Comp(gen, t, rv);
default -> throw new AssertionError();
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
opForLong(Emitter<N0> em) {
return Op.lneg(em);
}
/**
* Emit bytecode to process a leg of the input operand with the carry in
*
* @param em the emitter typed with a stack of one long, the carry in
* @param opnd the operand containing the leg from the input multi-precision operand
* @param shiftCarry indicates whether or not the long needs to be shifted to position the carry
* bit
* @return the emitter typed with a stack of one long, the inverted leg and carry out
*/
static Emitter<Ent<Bot, TLong>> prepOpndAndCarry(Emitter<Ent<Bot, TLong>> em,
SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry) {
if (shiftCarry) {
em = em
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr);
}
return uType;
return em
.emit(opnd::read)
.emit(Op::ldc__i, -1 >>> (Integer.SIZE - opnd.type().size() * Byte.SIZE))
.emit(Op::ixor)
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "toUnsignedLong",
GenConsts.MDESC_INTEGER__TO_UNSIGNED_LONG, false)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ladd);
}
/**
* Emit bytecode to invert the given leg of the input operand, leaving the carry out on the
* stack.
* <p>
* This assumes the stack has a carry from the previous leg's complement. It is either in bit
* position 32, if {@code shiftCarry} is set, or bit position 0, otherwise. It shifts, if
* needed, the carry bit into position 0 and then adds the inverted leg. It writes the lower 32
* bits of that, i.e., the resulting complement, into an output operand, and then leaves the
* carry out in bit position 32 of the long on the stack.
* <p>
* If {@code opnd} is writable, the result is written there and that operand returned.
* Otherwise, a new operand is generated. The caller must em>always</em> use the returned
* operand to construct the legs of the final multi-precision output operand. It must
* <em>never</em> use {@code opnd}, nor the multi-precision operand containing it, as the final
* output.
*
* @param em the emitter typed with a stack of one long, the carry out of the previous leg's
* complement, i.e., the carry in for this leg's complement.
* @param opnd the leg for the input multi-precision operand
* @param shiftCarry true to indicate the carry bit must be shifted from position 32 to 0.
* @param scope a scope for generating temporary local storage
* @return the output operand and the emitter typed with a stack of one long whose value is the
* carry out for this leg's complement.
*/
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLeg2CmpTakesAndGivesCarry(
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry,
Scope scope) {
return em
.emit(Int2CompOpGen::prepOpndAndCarry, opnd, shiftCarry)
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(opnd::write, scope);
}
/**
* Emit bytecode as in
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} except that
* we do not leave a carry out on the stack.
*
* @param em the emitter typed with a stack of one long, the carry out of the previous leg's
* complement, i.e., the carry in for this leg's complement.
* @param opnd the leg for the input multi-precision operand
* @param shiftCarry true to indicate the carry bit must be shifted from position 32 to 0.
* @param scope a scope for generating temporary local storage
* @return the output operand and the emitter typed with the empty stack.
*/
static SimpleOpndEm<TInt, IntJitType, Bot> genMpIntLeg2CmpTakesCarry(
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry,
Scope scope) {
return em
.emit(Int2CompOpGen::prepOpndAndCarry, opnd, shiftCarry)
.emit(Op::l2i)
.emit(opnd::write, scope);
}
/**
* {@inheritDoc}
* <p>
* The strategy here follows after
* {@link IntAddOpGen#genRunMpInt(Emitter, Local, JitCodeGenerator, ghidra.pcode.emu.jit.op.JitIntAddOp, MpIntJitType, Scope)}.
* We no longer need to assert a minimum length, since we provide a "carry in" of 1 for the
* initial leg. We initialize the complement by loading that 1 onto the stack and invoking
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} with
* {@code shiftCarry} cleared. We terminate the operation with
* {@link #genMpIntLeg2CmpTakesCarry(Emitter, SimpleOpnd, boolean, Scope)}. We use
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} with
* {@code shiftCarry} set for each middle leg. The resulting legs are all appended to form the
* final multi-precision output.
*/
@Override
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitInt2CompOp op,
MpIntJitType type, Scope scope) {
var opnd = gen.genReadToOpnd(em, localThis, op.u(), type, ext(), scope);
em = opnd.em();
var legs = opnd.opnd().type().castLegsLE(opnd.opnd());
int legCount = type.legsAlloc();
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
var emCarry = em
.emit(Op::ldc__l, 1);
for (int i = 0; i < legCount - 1; i++) {
var result = emCarry
.emit(Int2CompOpGen::genMpIntLeg2CmpTakesAndGivesCarry, legs.get(i), i != 0,
scope);
emCarry = result.em();
outLegs.add(result.opnd());
}
var result = emCarry
.emit(Int2CompOpGen::genMpIntLeg2CmpTakesCarry, legs.getLast(), true, scope);
em = result.em();
outLegs.add(result.opnd());
var out = MpIntLocalOpnd.of(type, "out", outLegs);
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
}
}

View File

@@ -15,120 +15,261 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.MethodVisitor;
import java.util.ArrayList;
import java.util.List;
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.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd.SimpleOpndEm;
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.JitIntAddOp;
/**
* The generator for a {@link JitIntAddOp int_add}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #IADD} or {@link #LADD} depending
* on the type.
*
* This uses the binary operator generator and simply emits {@link Op#iadd(Emitter) iadd} or
* {@link Op#ladd(Emitter) ladd} depending on the type.
* <p>
* NOTE: The multi-precision integer parts of this are a work in progress.
* The multi-precision integer logic is not such a simple matter.
*/
public enum IntAddOpGen implements IntBinOpGen<JitIntAddOp> {
public enum IntAddOpGen implements IntOpBinOpGen<JitIntAddOp> {
/** The generator singleton */
GEN;
static void generateMpIntLegAdd(JitCodeGenerator gen, int idx, boolean takesCarry,
boolean givesCarry, boolean storesResult, MethodVisitor mv) {
if (takesCarry) {
// [...,llegN:INT,olegN+1:LONG]
mv.visitLdcInsn(32);
mv.visitInsn(LUSHR);
// [...,lleg1...,carryinN:LONG]
mv.visitInsn(DUP2_X1);
mv.visitInsn(POP2);
// [...,carryinN:LONG,llegN:INT]
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
// [...,carryinN:LONG,llegN:LONG]
mv.visitInsn(LADD);
// [...,sumpartN:LONG]
}
else {
// [...,llegN:INT]
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
// [...,sumpartN:LONG] (legN + 0)
}
mv.visitVarInsn(ILOAD, idx);
// [...,sumpartN:LONG,rlegN:INT]
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
// [...,sumpartN:LONG,rlegN:LONG]
mv.visitInsn(LADD);
// [...,olegN:LONG]
if (storesResult) {
if (givesCarry) {
mv.visitInsn(DUP2);
}
// [...,(olegN:LONG),olegN:LONG]
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, Ext.ZERO, mv);
// [...,(olegN:LONG),olegN:INT]
/** NB. The store will perform the masking */
mv.visitVarInsn(ISTORE, idx);
// [...,(olegN:LONG)]
}
else {
if (!givesCarry) {
mv.visitInsn(POP2);
}
}
}
private void generateMpIntAdd(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
/**
* The strategy is to allocate a temp local for each leg of the result. First, we'll pop the
* right operand into the temp. Then, as we work with each leg of the left operand, we'll
* execute the algorithm. Convert both right and left legs to a long and add them (along
* with a possible carry in). Store the result back into the temp locals. Shift the leg
* right 32 to get the carry out, then continue to the next leg up. The final carry out can
* be dropped (overflow). The result legs are then pushed back to the stack.
*/
// [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);
// [lleg1,...,llegN:INT]
for (int i = 0; i < legCount; i++) {
boolean isLast = i == legCount - 1;
boolean takesCarry = i != 0; // not first
generateMpIntLegAdd(gen, result.idx(i), takesCarry, !isLast, true, mv);
}
OpGen.generateMpLegsFromTemp(result, legCount, mv);
}
}
@Override
public boolean isSigned() {
return false;
}
@Override
public JitType afterLeft(JitCodeGenerator gen, JitIntAddOp op, JitType lType, JitType rType,
MethodVisitor rv) {
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
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.iadd(em);
}
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntAddOp 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(IADD);
case LongJitType t -> rv.visitInsn(LADD);
case MpIntJitType t when t.size() == lType.size() -> generateMpIntAdd(gen, t, rv);
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
default -> throw new AssertionError();
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.ladd(em);
}
/**
* Emit bytecode to load a leg of the left operand and combine it with the carry in
* <p>
* The carry in is a long where bit 32 has the value 0 or 1. The lower 32 bits, which are
* actually the result of the previous legs' addition, are ignored. We use a long because it can
* hold the full result of addition, where the carry out winds up at bit 32 of the long. This
* routine emits bytecode to shift the previous sum out, so that the carry bit is now at bit 0,
* i.e., the long how has the value 0 or 1. It then loads and adds the left leg into that long.
*
* @param em the emitter typed with a stack of one long, the carry in
* @param left the operand containing the leg from the left multi-precision operand
* @return the emitter typed with a stack of one long, the summed left and carry in
*/
static Emitter<Ent<Bot, TLong>> prepLeftAndCarry(Emitter<Ent<Bot, TLong>> em,
SimpleOpnd<TInt, IntJitType> left) {
return em
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(left::read)
.emit(Op::i2l)
.emit(Op::ladd);
}
/**
* Emit bytecode to load a leg of the right operand and add it to the summed left and carry in
* <p>
* This completes the addition of the left and right legs along with the carry in. The long on
* the stack is now the result, with the carry out in bit position 32.
*
* @param em the emitter typed with a stack of one long, the summed left and carry in
* @param right the operand containing the leg from the right multi-precision operand
* @return the emitter typed with a stack of one long, the result and carry out
*/
static Emitter<Ent<Bot, TLong>> addRight(Emitter<Ent<Bot, TLong>> em,
SimpleOpnd<TInt, IntJitType> right) {
return em
.emit(right::read)
.emit(Op::i2l)
.emit(Op::ladd);
}
/**
* Conditionally emit bytecode to store the result into a leg of the output operand
* <p>
* Other operators may borrow some components of this mp-int addition routine, e.g., to compute
* only the output carry bit. In those cases, storing the actual result of the addition is not
* necessary, but we still need to keep the intermediate carry bit. Thus, we provide this
* routine to conditionally tee the sum (lower 32-bits of the long on the stack) for a leg into
* a result leg. Whether or not we emit such bytecode, we ensure the value on the stack remains
* unchanged.
* <p>
* See
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
* regarding the returned record.
*
* @param em the emitter typed with a stack of one long, the result and carry out for a given
* leg
* @param into the output leg operand for the multi-precision output operand
* @param store true to emit the bytecode, false to emit nothing
* @param scope a scope for generating temporary local storage
* @return the output operand or null, and the emitter typed with a stack of one long whose
* value is unchanged.
*/
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> maybeStore(Emitter<Ent<Bot, TLong>> em,
SimpleOpnd<TInt, IntJitType> into, boolean store, Scope scope) {
if (!store) {
return new SimpleOpndEm<>(null, em);
}
return rType;
return em
.emit(Op::dup2__2)
.emit(Op::l2i)
.emit(into::write, scope);
}
/**
* Emit bytecode to add two corresponding legs of the operands, leaving the carry out on the
* stack.
* <p>
* This assumes the stack has a carry from the previous legs' sum in bit position 32. It shifts
* it into position and then adds in the two given legs. It conditionally writes the lower 32
* bits of that, i.e., the resulting sum, into an output operand, and then leaves the carry out
* in bit position 32 of the long on the stack.
* <p>
* The returned value is always a non-null record, but the value of the operand may vary. If
* {@code store} is false, the operand is always null. This will be the case, e.g., for
* computing the carry out of multi-precision addition, because the actual result is not needed.
* If {@code store} is true, the the returned operand may or may not be identical to the given
* {@code left} parameter, depending on whether or not that operand can be written. The caller
* must <em>always</em> use the returned operand to construct the legs of the final
* multi-precision output operand. It must <em>never</em> use {@code left}, nor the
* multi-precision operand containing it, as the final output.
*
* @param em the emitter typed with a stack of one long, the carry out of the previous legs'
* sum, i.e., the carry in for these legs' sum.
* @param left the leg for the left multi-precision operand
* @param right the leg for the right multi-precision operand
* @param storesResult true to receive the leg for the output multi-precision operand
* @param scope a scope for generating temporary local storage
* @return the output operand or null, and the emitter typed with a stack of one long whose
* value is the carry out for these legs' sum
*/
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLegAddTakesAndGivesCarry(
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> left,
SimpleOpnd<TInt, IntJitType> right, boolean storesResult, Scope scope) {
return em
.emit(IntAddOpGen::prepLeftAndCarry, left)
.emit(IntAddOpGen::addRight, right)
.emit(IntAddOpGen::maybeStore, left, storesResult, scope);
}
/**
* Emit bytecode as in
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
* except that we do not expect a carry in on the stack.
* <p>
* This should be used to initiate the addition, taking the least-significant legs of the input
* multi-precision operands.
*
* @param em the emitter typed with the empty stack
* @param left the leg for the left multi-precision operand
* @param right the leg for the right multi-precision operand
* @param storesResult true to receive the leg for the output multi-precision operand
* @param scope a scope for generating temporary local storage
* @return the output operand or null, and the emitter typed with a stack of one long whose
* value is the carry out for these legs' sum
*/
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLegAddGivesCarry(Emitter<Bot> em,
SimpleOpnd<TInt, IntJitType> left, SimpleOpnd<TInt, IntJitType> right,
boolean storesResult, Scope scope) {
return em
.emit(left::read)
.emit(Op::i2l)
.emit(IntAddOpGen::addRight, right)
.emit(IntAddOpGen::maybeStore, left, storesResult, scope);
}
/**
* Emit bytecode as in
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
* except that we do not leave a carry out on the stack.
* <p>
* This should be used to finalize the addition, taking the most-significant legs of the input
* multi-precision operands. Note that this always stores the result and returns an output
* operand. Otherwise, this would give no output at all, since it does not leave a carry out on
* the stack.
*
* @param em the emitter typed with a stack of one long, the carry out of the previous legs'
* sum, i.e., the carry in for these legs' sum.
* @param left the leg for the left multi-precision operand
* @param right the leg for the right multi-precision operand
* @param scope a scope for generating temporary local storage
* @return the output operand and the emitter typed with the empty stack
*/
static SimpleOpndEm<TInt, IntJitType, Bot> genMpIntLegAddTakesCarry(Emitter<Ent<Bot, TLong>> em,
SimpleOpnd<TInt, IntJitType> left, SimpleOpnd<TInt, IntJitType> right, Scope scope) {
return em
.emit(IntAddOpGen::prepLeftAndCarry, left)
.emit(IntAddOpGen::addRight, right)
.emit(Op::l2i)
.emit(left::write, scope);
}
/**
* {@inheritDoc}
* <p>
* The strategy here follows from grade-school long addition. We assert that there are at least
* two legs, otherwise we would have just emitted a single add bytecode. This allows us to
* unconditionally initialize the addition with
* {@link #genMpIntLegAddGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)} and
* terminate it with {@link #genMpIntLegAddTakesCarry(Emitter, SimpleOpnd, SimpleOpnd, Scope)}.
* When there are more than 2 legs, we use
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)} as
* many times as necessary in the middle. For all legs, we store the result and append it as a
* leg to the final output.
*/
@Override
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntAddOp op,
MpIntJitType type, Scope scope) {
/**
* The strategy here is fairly simple. Ensure both operands are stored in locals. One of the
* operands <em>must</em> be in temporaries to ensure we don't modify an input in place.
* <p>
* For each leg position, convert both right and left legs to a long and add them (along
* with a possible carry in). Store the result back into the temp locals. Shift the long sum
* right 32 to get the carry out, then continue to the next leg up. The final carry out can
* be dropped (overflow).
*/
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
em = right.em();
var lLegs = left.opnd().type().castLegsLE(left.opnd());
assert lLegs.size() >= 2;
var rLegs = right.opnd().type().castLegsLE(right.opnd());
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
int legCount = type.legsAlloc();
var first = genMpIntLegAddGivesCarry(em, lLegs.getFirst(), rLegs.getFirst(), true, scope);
var emCarry = first.em();
outLegs.add(first.opnd());
for (int i = 1; i < legCount - 1; i++) {
var result =
genMpIntLegAddTakesAndGivesCarry(emCarry, lLegs.get(i), rLegs.get(i), true, scope);
emCarry = result.em();
outLegs.add(result.opnd());
}
var last = genMpIntLegAddTakesCarry(emCarry, lLegs.getLast(), rLegs.getLast(), scope);
em = last.em();
outLegs.add(last.opnd());
var out = MpIntLocalOpnd.of(type, "out", outLegs);
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
}
}

View File

@@ -15,26 +15,36 @@
*/
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.JitIntAndOp;
/**
* The generator for a {@link JitIntAndOp int_and}.
*
* <p>
* This uses the bitwise binary operator and emits {@link #IAND} or {@link #LAND} depending on the
* type.
* This uses the bitwise binary operator and emits {@link Op#iand(Emitter) iand} or
* {@link Op#land(Emitter) land} depending on the type.
*/
public enum IntAndOpGen implements BitwiseBinOpGen<JitIntAndOp> {
public enum IntAndOpGen implements IntBitwiseBinOpGen<JitIntAndOp> {
/** 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);
}
}

View File

@@ -0,0 +1,73 @@
/* ###
* 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 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.MpIntLocalOpnd;
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.Bot;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitBinOp;
/**
* An extension for bitwise binary operators
* <p>
* This provides a simple strategy for multi-precision integer implementation. Since all bit
* positions are considered independently, we just apply the same
* {@link #opForInt(Emitter, IntJitType)} operator to each pair of corresponding legs independently
* to compute each corresponding output leg.
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntBitwiseBinOpGen<T extends JitBinOp> extends IntOpBinOpGen<T> {
@Override
default boolean isSigned() {
return false;
}
@Override
default <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
Scope scope) {
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
em = right.em();
var lLegs = left.opnd().type().castLegsLE(left.opnd());
var rLegs = right.opnd().type().castLegsLE(right.opnd());
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
int legCount = type.legsAlloc();
for (int i = 0; i < legCount; i++) {
var result = em
.emit(lLegs.get(i)::read)
.emit(rLegs.get(i)::read)
.emit(this::opForInt, IntJitType.I4)
.emit(lLegs.get(i)::write, scope);
em = result.em();
outLegs.add(result.opnd());
}
var out = MpIntLocalOpnd.of(type, "out", outLegs);
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
}
}

View File

@@ -15,171 +15,142 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.lifecycle.Unfinished.TODO;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
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.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
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.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitIntCarryOp;
/**
* The generator for a {@link JitIntCarryOp int_carry}.
*
* <p>
* This uses the binary operator generator. First we have to consider which strategy we are going to
* use. If the p-code type is strictly smaller than its host JVM type, we can simply add the two
* operands and examine the next bit up. This is accomplished by emitting {@link #IADD} or
* {@link #LADD}, depending on the type, followed by a shift right and a mask.
*
* This uses the integer predicate operator generator. First we have to consider which strategy we
* are going to use. If the p-code type is strictly smaller than its host JVM type, we can simply
* add the two operands and examine the next bit up. This is accomplished by emitting
* {@link Op#iadd(Emitter) iadd} or {@link Op#ladd(Emitter) ladd}, depending on the type, followed
* by a shift right and a mask.
* <p>
* If the p-code type exactly fits its host JVM type, we still add, but we will need to compare the
* result to one of the operands. Thus, we override
* {@link #afterLeft(JitCodeGenerator, JitIntCarryOp, JitType, JitType, MethodVisitor) afterLeft}
* and emit code to duplicate the left operand. We can then add and invoke
* {@link Integer#compareUnsigned(int, int)} to determine whether there was overflow. If there was,
* then we know the carry bit would have been set. We can spare the conditional flow by just
* shifting the sign bit into the 1's place.
*
* result to one of the operands. Thus, we emit code to duplicate the left operand. We can then add
* and invoke {@link Integer#compareUnsigned(int, int)} (or similar for longs) to determine whether
* there was overflow. If there was, then we know the carry bit would have been set. We can spare
* the conditional flow by just shifting the sign bit into the 1's place.
* <p>
* NOTE: The multi-precision integer parts of this are a work in progress.
* For multi-precision integers, we invoke the subroutines in {@link IntAddOpGen}, but do not store
* the results, because we only need the carry. When we reach the end, we take advantage of the fact
* that the final stack result is actually the full 33-bit result for the last leg. We can just
* shift it the required number of bytes (depending on the type of the input operands) and mask for
* the desired carry bit.
*/
public enum IntCarryOpGen implements IntBinOpGen<JitIntCarryOp> {
public enum IntCarryOpGen implements IntPredBinOpGen<JitIntCarryOp> {
/** The generator singleton */
GEN;
private void generateMpIntCarry(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
/**
* Similar strategy as for INT_ADD. In fact, we call its per-leg logic.
*/
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
int legCount = type.legsAlloc();
int remSize = type.partialSize();
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", legCount)) {
for (int i = 0; i < legCount; i++) {
mv.visitVarInsn(ISTORE, temp.idx(i));
// NOTE: More significant legs have higher indices (reverse of stack)
}
// [lleg1,...,llegN:INT]
for (int i = 0; i < legCount; i++) {
boolean takesCarry = i != 0; // not first
IntAddOpGen.generateMpIntLegAdd(gen, temp.idx(i), takesCarry, true, false, mv);
}
// [olegN:LONG]
if (remSize == 0) {
// The last leg was full, so extract bit 32
mv.visitLdcInsn(32);
}
else {
// The last leg was partial, so get the next more significant bit
mv.visitLdcInsn(remSize * Byte.SIZE);
}
mv.visitInsn(LUSHR);
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, Ext.ZERO, mv);
mv.visitLdcInsn(1);
mv.visitInsn(IAND);
}
}
@Override
public boolean isSigned() {
return false;
}
@Override
public JitType afterLeft(JitCodeGenerator gen, JitIntCarryOp op, JitType lType, JitType rType,
MethodVisitor rv) {
/**
* There are two strategies to use here depending on whether or not there's room to capture
* the carry bit. If there's not room, we have to compare the sum to one of the input
* operands. If the sum is less, then we can conclude there was a carry. For that strategy,
* we will need to keep a copy of the left operand, so duplicate it.
*
* On the other hand, if there is room to capture the carry, we can just add the two
* operands and extract the carry bit. There is no need to duplicate the left operand.
*/
lType = TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
switch (lType) {
case IntJitType(int size) when size == Integer.BYTES -> rv.visitInsn(DUP);
case IntJitType lt -> {
}
case LongJitType(int size) when size == Long.BYTES -> rv.visitInsn(DUP2);
case LongJitType lt -> {
}
case MpIntJitType lt -> {
}
default -> throw new AssertionError();
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
if (type == IntJitType.I4) {
return em
.emit(Op::dup_x1) // r l r
.emit(Op::iadd)
.emit(Op::swap)
.emit(Op::invokestatic, TR_INTEGER, "compareUnsigned", MDESC_INTEGER__COMPARE,
false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
// sum < l iff sign bit is 1
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::iushr);
}
return lType;
// Just add and extract the carry bit
return em
.emit(Op::iadd)
.emit(Op::ldc__i, type.size() * Byte.SIZE)
.emit(Op::ishr)
// LATER: This mask may not be necessary....
.emit(Op::ldc__i, 1)
.emit(Op::iand);
}
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntCarryOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
switch (rType) {
case IntJitType(int size) when size == Integer.BYTES -> {
// [l,l,r]
rv.visitInsn(IADD);
// [l,sum]
rv.visitInsn(SWAP); // spare an LDC,XOR
// [sum,l]
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "compareUnsigned",
MDESC_INTEGER__COMPARE, false);
// [cmpU(sum,l)] sum < l iff sign bit is 1
rv.visitLdcInsn(31);
rv.visitInsn(IUSHR);
return IntJitType.I1;
}
case IntJitType(int size) -> {
// Just add and extract the carry bit
rv.visitInsn(IADD);
rv.visitLdcInsn(size * Byte.SIZE);
rv.visitInsn(ISHR);
rv.visitLdcInsn(1);
rv.visitInsn(IAND);
return IntJitType.I1;
}
case LongJitType(int size) when size == Long.BYTES -> {
// [l:LONG,l:LONG,r:LONG]
rv.visitInsn(LADD);
// [l:LONG,sum:LONG]
rv.visitInsn(DUP2_X2);
rv.visitInsn(POP2);
// [sum:LONG,l:LONG]
rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "compareUnsigned",
MDESC_LONG__COMPARE_UNSIGNED, false);
// [cmpU(sum,l):INT] sum < l iff sign bit is 1
rv.visitLdcInsn(31);
rv.visitInsn(IUSHR);
return IntJitType.I1;
}
case LongJitType(int size) -> {
// Just add and extract the carry bit
rv.visitInsn(LADD);
rv.visitLdcInsn(size * Byte.SIZE);
rv.visitInsn(LSHR);
rv.visitInsn(L2I);
// TODO: This mask may not be necessary
rv.visitLdcInsn(1);
rv.visitInsn(IAND);
return IntJitType.I1;
}
case MpIntJitType t when t.size() == lType.size() -> {
generateMpIntCarry(gen, t, rv);
return IntJitType.I1;
}
case MpIntJitType t -> {
return TODO("MpInt of differing sizes");
}
default -> throw new AssertionError();
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
if (type == LongJitType.I8) {
return em
.emit(Op::dup2_x2_22) // r l r
.emit(Op::ladd)
.emit(Op::dup2_x2_22)
.emit(Op::pop2__2)
.emit(Op::invokestatic, TR_LONG, "compareUnsigned", MDESC_LONG__COMPARE,
false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
// sum < l iff sign bit is 1
.emit(Op::ldc__i, Integer.SIZE - 1)
.emit(Op::iushr);
}
// Just add and extract the carry bit
return em
.emit(Op::ladd)
.emit(Op::ldc__i, type.size() * Byte.SIZE)
.emit(Op::lshr)
.emit(Op::l2i)
// LATER: This mask may not be necessary....
.emit(Op::ldc__i, 1)
.emit(Op::iand);
}
@Override
public <THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntCarryOp op,
MpIntJitType type, Scope scope) {
/**
* Similar strategy as for INT_ADD. In fact, we call its per-leg logic.
*/
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
em = right.em();
var lLegs = left.opnd().type().castLegsLE(left.opnd());
assert lLegs.size() >= 2;
var rLegs = right.opnd().type().castLegsLE(right.opnd());
int legCount = type.legsAlloc();
var first = IntAddOpGen.genMpIntLegAddGivesCarry(em, lLegs.getFirst(), rLegs.getFirst(),
false, scope);
var emCarry = first.em();
for (int i = 1; i < legCount; i++) {
var result = IntAddOpGen.genMpIntLegAddTakesAndGivesCarry(emCarry, lLegs.get(i),
rLegs.get(i), false, scope);
emCarry = result.em();
}
// carry on top of stack is really [carry][sum] in long
if (type.partialSize() == 0) {
// The last leg was full, so extract the carry bit
return emCarry
.emit(Op::ldc__i, Integer.SIZE)
.emit(Op::lushr)
.emit(Op::l2i);
}
// The last leg was partial, so just get the bit one to the right
return emCarry
.emit(Op::ldc__i, type.partialSize() * Byte.SIZE)
.emit(Op::lushr)
.emit(Op::l2i)
// LATER: This mask probably not needed
.emit(Op::ldc__i, 1)
.emit(Op::iand);
}
}

View File

@@ -0,0 +1,222 @@
/* ###
* 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 java.util.function.Function;
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
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.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitIntTestOp;
/**
* An extension for integer comparison operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntCompareBinOpGen<T extends JitIntTestOp> extends IntPredBinOpGen<T> {
/**
* Invert the boolean on top of the stack
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the boolean on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
static <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> not(Emitter<N0> em) {
return em
// Where this is used, the previous subroutines have already emitted &1
//.emit(Op::ldc__i, 1)
//.emit(Op::iand)
.emit(Op::ldc__i, 1)
.emit(Op::ixor);
}
/**
* Assuming a conditional jump bytecode was just emitted, emit bytecode to push 0 (false) onto
* the stack for the fall-through case, or 1 (true) onto the stack for the taken case.
*
* @param <N> the incoming stack, i.e., that after emitting the conditional jump
* @param lblEmTrue the target label of the conditional jump just emitted, and the emitter typed
* with the incoming stack
* @return the emitter with the resulting stack, i.e., having pushed the boolean result
*/
default <N extends Next> Emitter<Ent<N, TInt>> genBool(LblEm<N, N> lblEmTrue) {
var lblEmDone = lblEmTrue.em()
.emit(Op::ldc__i, 0)
.emit(Op::goto_);
return lblEmDone.em()
.emit(Lbl::placeDead, lblEmTrue.lbl())
.emit(Op::ldc__i, 1)
.emit(Lbl::place, lblEmDone.lbl());
}
/**
* An implementation for (unsigned) int operands that invokes
* {@link Integer#compareUnsigned(int, int)} and then emits the given {@code if<cond>} jump.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
* jump
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
default <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>> genIntViaUcmpThenIf(Emitter<N0> em,
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
return em
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "compareUnsigned",
GenConsts.MDESC_INTEGER__COMPARE, false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(this::genIntViaIf, opIf);
}
/**
* An implementation for (signed) int operands that simply emits the given {@code if_icmp<cond>}
* jump.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param opIfIcmp a method reference, e.g., to {@link Op#if_icmpge(Emitter, Lbl)} for the
* conditional jump
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
default <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
Emitter<Ent<N2, TInt>>
genIntViaIfIcmp(Emitter<N0> em, Function<Emitter<N0>, LblEm<N2, N2>> opIfIcmp) {
return genBool(opIfIcmp.apply(em));
}
/**
* A utility that emits the given {@code if<cond>} along with the logic that pushes the correct
* result depending on whether or not the jump is taken.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack including the predicate, which is compared with 0
* @param em the emitter typed with the incoming stack
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
* jump
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
default <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
genIntViaIf(Emitter<N0> em, Function<Emitter<N0>, LblEm<N1, N1>> opIf) {
return genBool(opIf.apply(em));
}
/**
* An implementation for (signed) long operands that emits {@link Op#lcmp(Emitter) lcmp} and
* then emits the given {@code if<cond>} jump.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
* jump
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
default <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> genLongViaLcmpThenIf(Emitter<N0> em,
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
return em
.emit(Op::lcmp)
.emit(this::genIntViaIf, opIf);
}
/**
* An implementation for (unsigned) long operands that invokes
* {@link Long#compareUnsigned(long, long)} and then emits the given {@code if<cond>} jump.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
* jump
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
default <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> genLongViaUcmpThenIf(Emitter<N0> em,
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
return em
.emit(Op::invokestatic, GenConsts.TR_LONG, "compareUnsigned",
GenConsts.MDESC_LONG__COMPARE, false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(this::genIntViaIf, opIf);
}
/**
* {@inheritDoc}
* <p>
* The strategy for multi-precision comparison can be applied to all comparisons: Start with the
* most-significant legs and compare <em>for equality</em> until we find the first not-equal
* pair. Then, apply {@link #opForInt(Emitter, IntJitType)} to determine the overall result.
* There is no need to load or compare any legs beyond the most-significant not-equal pair. If
* we reach the final (least-significant) pair, we need not check them for equality. Just
* delegate to {@link #opForInt(Emitter, IntJitType)}.
*/
@Override
default <THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
Scope scope) {
/**
* Need to examine from most-significant to least-significant. Stop at the first pair of
* legs which are not equal, or on the least-significant leg.
*/
int legCount = type.legsAlloc();
Local<TInt> localLLeg = scope.decl(Types.T_INT, "lLeg");
Local<TInt> localRLeg = scope.decl(Types.T_INT, "rLeg");
Lbl<Bot> lblDone = Lbl.create();
for (int i = legCount - 1; i > 0; i--) { // Yes, stop one before 0, so use >, not >=
em = em
.emit(gen::genReadLegToStack, localThis, op.l(), type, i, ext())
.emit(Op::dup)
.emit(Op::istore, localLLeg)
.emit(gen::genReadLegToStack, localThis, op.r(), type, i, ext())
.emit(Op::dup)
.emit(Op::istore, localRLeg)
.emit(IntNotEqualOpGen.GEN::opForInt, IntJitType.I4)
.emit(Op::ifne, lblDone);
}
// We've reached the last leg. Just load them onto the stack
var lblEmStaged = em
.emit(gen::genReadLegToStack, localThis, op.l(), type, 0, ext())
.emit(gen::genReadLegToStack, localThis, op.r(), type, 0, ext())
.emit(Op::goto_);
return lblEmStaged.em()
.emit(Lbl::placeDead, lblDone)
.emit(Op::iload, localLLeg)
.emit(Op::iload, localRLeg)
.emit(Lbl::place, lblEmStaged.lbl())
.emit(this::opForInt, IntJitType.I4);
}
}

View File

@@ -0,0 +1,95 @@
/* ###
* 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 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.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitUnOp;
/**
* An extension for unary integer operators that count bits
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntCountUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
/**
* Emit the JVM bytecode to perform the operator with int operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @param type the p-code type of the input operand
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> opForInt(Emitter<N0> em,
IntJitType type);
/**
* Emit the JVM bytecode to perform the operator with long operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @param type the p-code type of the input operand
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TInt>> opForLong(Emitter<N0> em,
LongJitType type);
/**
* Emit the JVM bytecode to perform the operator with mp-int operands.
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param op the p-code op
* @param type the p-code type of the input operand
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the resulting stack, i.e., with only the result pushed
*/
<THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
Scope scope);
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
var emResult = switch (uType) {
case IntJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForInt, t);
case LongJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForLong, t);
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
default -> throw new AssertionError();
};
return new LiveOpResult(
gen.genWriteFromStack(emResult, localThis, op.out(), IntJitType.I4, ext(), scope));
}
}

View File

@@ -15,27 +15,29 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
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.gen.GenConsts;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
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.Methods.Inv;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitIntAddOp;
import ghidra.pcode.emu.jit.op.JitIntDivOp;
/**
* The generator for a {@link JitIntAddOp int_add}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #INVOKESTATIC} on
* {@link Integer#divideUnsigned(int, int)} or {@link Long#divideUnsigned(long, long)} depending on
* the type.
* This uses the binary operator generator and simply emits
* {@link Op#invokestatic(Emitter, TRef, String, ghidra.pcode.emu.jit.gen.util.Methods.MthDesc, boolean)
* invokestatic} on {@link Integer#divideUnsigned(int, int)} or *
* {@link Long#divideUnsigned(long, long)} depending on the type.
* <p>
* For multi-precision division, this emits code to invoke
* {@link JitCompiledPassage#mpIntDivide(int[], int[], int[])}.
*/
public enum IntDivOpGen implements IntBinOpGen<JitIntDivOp> {
public enum IntDivOpGen implements IntOpBinOpGen<JitIntDivOp> {
/** The generator singleton */
GEN;
@@ -44,31 +46,33 @@ public enum IntDivOpGen implements IntBinOpGen<JitIntDivOp> {
return false;
}
private void generateMpIntDiv(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntDivide", mv, 1, TakeOut.OUT);
@Override
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 em
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "divideUnsigned",
GenConsts.MDESC_$INT_BINOP, false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
@Override
public JitType afterLeft(JitCodeGenerator gen, JitIntDivOp op, JitType lType, JitType rType,
MethodVisitor rv) {
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
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 em
.emit(Op::invokestatic, GenConsts.TR_LONG, "divideUnsigned",
GenConsts.MDESC_$LONG_BINOP, false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret);
}
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntDivOp op, JitBlock block,
JitType lType, JitType rType, MethodVisitor rv) {
rType = TypeConversions.forceUniform(gen, rType, lType, rExt(), rv);
switch (rType) {
case IntJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "divideUnsigned",
MDESC_$INT_BINOP, false);
case LongJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "divideUnsigned",
MDESC_$LONG_BINOP, false);
case MpIntJitType t when t.size() == lType.size() -> generateMpIntDiv(gen, t, rv);
// FIXME: forceUniform shouldn't have to enforce the same size....
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
default -> throw new AssertionError();
}
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
return rType;
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntDivOp op,
MpIntJitType type, Scope scope) {
return genMpDelegationToStaticMethod(em, gen, localThis, type, "mpIntDivide", op, 1,
TakeOut.OUT, scope);
}
}

View File

@@ -15,16 +15,23 @@
*/
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.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
import ghidra.pcode.emu.jit.op.JitIntEqualOp;
/**
* The generator for a {@link JitIntEqualOp int_equal}.
*
* <p>
* This uses the integer comparison operator generator and simply emits {@link #IF_ICMPEQ} or
* {@link #IFEQ} depending on the type.
* To avoid jumps, this delegates to {@link Integer#compare(int, int)}, which is signed, and then
* inverts the result.
*/
public enum IntEqualOpGen implements CompareIntBinOpGen<JitIntEqualOp> {
public enum IntEqualOpGen implements IntCompareBinOpGen<JitIntEqualOp> {
/** The generator singleton */
GEN;
@@ -34,12 +41,18 @@ public enum IntEqualOpGen implements CompareIntBinOpGen<JitIntEqualOp> {
}
@Override
public int icmpOpcode() {
return IF_ICMPEQ;
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 em
.emit(IntNotEqualOpGen.GEN::opForInt, type)
.emit(IntCompareBinOpGen::not);
}
@Override
public int ifOpcode() {
return IFEQ;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
return em
.emit(IntNotEqualOpGen.GEN::opForLong, type)
.emit(IntCompareBinOpGen::not);
}
}

View File

@@ -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.gen.op;
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.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
import ghidra.pcode.emu.jit.op.JitUnOp;
/**
* An extension for unary integer extension operators
* <p>
* The strategy here is to do nothing more than invoke the readers and writers. Because those are
* responsible for converting between the types, with the appropriate signedness, the work of
* extension is already done. We need only to know whether or not the operators should be treated as
* signed or unsigned. Thankfully, that method is already required by a super interface.
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntExtUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
JitType oType = gen.resolveType(op.out(), op.type());
JitType minType = JitType.unifyLeast(uType, oType);
return new LiveOpResult(switch (minType) {
case IntJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
case LongJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
// Need floats for COPY
case FloatJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
case DoubleJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
case MpIntJitType t -> {
var result = em
.emit(gen::genReadToOpnd, localThis, op.u(), t, ext(), scope);
yield result.em()
.emit(gen::genWriteFromOpnd, localThis, op.out(), result.opnd(), ext(),
scope);
}
default -> throw new AssertionError();
});
}
}

View File

@@ -25,7 +25,7 @@ import ghidra.pcode.emu.jit.op.JitIntLeftOp;
* This uses the integer shift operator generator and simply invokes
* {@link JitCompiledPassage#intLeft(int, int)}, etc. depending on the types.
*/
public enum IntLeftOpGen implements ShiftIntBinOpGen<JitIntLeftOp> {
public enum IntLeftOpGen implements IntShiftBinOpGen<JitIntLeftOp> {
/** The generator singleton */
GEN;

View File

@@ -15,15 +15,24 @@
*/
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.JitIntLessEqualOp;
/**
* The generator for a {@link JitIntLessEqualOp int_lessequal}.
*
* <p>
* This uses the integer comparison operator generator and simply emits {@link #IFLE}.
* This uses the (unsigned) integer comparison operator generator and simply emits
* {@link Op#ifle(Emitter) ifle}.
*/
public enum IntLessEqualOpGen implements CompareIntBinOpGen<JitIntLessEqualOp> {
public enum IntLessEqualOpGen implements IntCompareBinOpGen<JitIntLessEqualOp> {
/** The generator singleton */
GEN;
@@ -33,12 +42,14 @@ public enum IntLessEqualOpGen implements CompareIntBinOpGen<JitIntLessEqualOp> {
}
@Override
public int icmpOpcode() {
throw new AssertionError();
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 genIntViaUcmpThenIf(em, Op::ifle);
}
@Override
public int ifOpcode() {
return IFLE;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
return genLongViaUcmpThenIf(em, Op::ifle);
}
}

View File

@@ -15,15 +15,24 @@
*/
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.JitIntLessOp;
/**
* The generator for a {@link JitIntLessOp int_less}.
*
* <p>
* This uses the integer comparison operator generator and simply emits {@link #IFLT}.
* This uses the (unsigned) integer comparison operator generator and simply emits
* {@link Op#iflt(Emitter) iflt}.
*/
public enum IntLessOpGen implements CompareIntBinOpGen<JitIntLessOp> {
public enum IntLessOpGen implements IntCompareBinOpGen<JitIntLessOp> {
/** The generator singleton */
GEN;
@@ -33,12 +42,14 @@ public enum IntLessOpGen implements CompareIntBinOpGen<JitIntLessOp> {
}
@Override
public int icmpOpcode() {
throw new AssertionError();
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 genIntViaUcmpThenIf(em, Op::iflt);
}
@Override
public int ifOpcode() {
return IFLT;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
return genLongViaUcmpThenIf(em, Op::iflt);
}
}

View File

@@ -15,24 +15,25 @@
*/
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.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
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.JitIntMultOp;
/**
* The generator for a {@link JitIntMultOp int_mult}.
*
* <p>
* This uses the binary operator generator and simply emits {@link #IMUL} or {@link #LMUL} depending
* on the type.
* This uses the binary operator generator and simply emits {@link Op#imul(Emitter) imul} or
* {@link Op#lmul(Emitter) lmul} depending on the type.
* <p>
* For multi-precision multiplication, this emits code to invoke
* {@link JitCompiledPassage#mpIntMultiply(int[], int[], int[])}
*/
public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
public enum IntMultOpGen implements IntOpBinOpGen<JitIntMultOp> {
/** The generator singleton */
GEN;
@@ -41,6 +42,18 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
return false;
}
@Override
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.imul(em);
}
@Override
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.lmul(em);
}
/**
* Generate the mp-int multiply code.
* <p>
@@ -55,7 +68,8 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
* That ensures all the operand sizes match, which is often (at least conventionally) required
* by the Sleigh compiler. However, if r1 and r2 are each only 64 bits, and I can keep track of
* that fact, then I could perform about half as many multiplies and adds. It also be nice if I
* can look ahead and see that only 64 bits of temp is actually used.
* can look ahead and see that only 64 bits of temp is actually used. The same is true of
* {@link IntDivOpGen}, {@link IntRemOpGen}, {@link IntSDivOpGen}, and {@link IntSRemOpGen}.
* <p>
* <b>IDEA:</b> It would be quite a change, but perhaps generating a temporary JVM-level DFG
* would be useful for culling. The difficulty here is knowing whether or not a temp (unique) is
@@ -63,32 +77,18 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
* additional Sleigh compiler support. If used, I should not cull any computations, so that the
* retired value is the full value.
*
* @param em the code emitter with an empty stack
* @param localThis a handle to the owning compiled passage
* @param gen the code generator
* @param op the p-code op
* @param type the (uniform) type of the inputs and output operands
* @param mv the method visitor
* @param scope a scope for op-temporary variables
*/
private void generateMpIntMult(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntMultiply", mv, 0, TakeOut.OUT);
}
@Override
public JitType afterLeft(JitCodeGenerator gen, JitIntMultOp op, JitType lType, JitType rType,
MethodVisitor rv) {
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
}
@Override
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntMultOp 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(IMUL);
case LongJitType t -> rv.visitInsn(LMUL);
case MpIntJitType t when t.size() == lType.size() -> generateMpIntMult(gen, t, rv);
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
default -> throw new AssertionError();
}
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
return rType;
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntMultOp op,
MpIntJitType type, Scope scope) {
return genMpDelegationToStaticMethod(em, gen, localThis, type, "mpIntMultiply", op, 0,
TakeOut.OUT, scope);
}
}

View File

@@ -15,24 +15,28 @@
*/
package ghidra.pcode.emu.jit.gen.op;
import org.objectweb.asm.MethodVisitor;
import java.util.ArrayList;
import java.util.List;
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.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.MpIntLocalOpnd;
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.*;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitIntNegateOp;
/**
* The generator for a {@link JitIntNegateOp int_negate}.
*
* <p>
* There is no bitwise "not" operator in the JVM. We borrow the pattern we see output by the Java
* compiler for <code>int negate(n) {return ~n;}</code>. It XORs the input with a register of 1s.
* This uses the unary operator generator and emits the equivalent code.
*/
public enum IntNegateOpGen implements IntUnOpGen<JitIntNegateOp> {
public enum IntNegateOpGen implements IntOpUnOpGen<JitIntNegateOp> {
/** The generator singleton */
GEN;
@@ -41,38 +45,41 @@ public enum IntNegateOpGen implements IntUnOpGen<JitIntNegateOp> {
return false; // TODO: Is it? Test with 3-byte operands to figure it out.
}
private void generateMpIntNegate(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
int legCount = type.legsAlloc();
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", legCount)) {
for (int i = 0; i < legCount; i++) {
mv.visitVarInsn(ISTORE, temp.idx(i));
// NOTE: More significant legs have higher indices (reverse of stack)
}
// Compute and push back in reverse order
int i = legCount;
for (SimpleJitType t : type.legTypes()) {
mv.visitVarInsn(ILOAD, temp.idx(--i));
mv.visitLdcInsn(-1 >>> (Integer.SIZE - t.size() * Byte.SIZE));
mv.visitInsn(IXOR);
}
}
@Override
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 JitType generateUnOpRunCode(JitCodeGenerator gen, JitIntNegateOp op, JitBlock block,
JitType uType, MethodVisitor rv) {
switch (uType) {
case IntJitType t -> {
rv.visitLdcInsn(-1 >>> (Integer.SIZE - t.size() * Byte.SIZE));
rv.visitInsn(IXOR);
}
case LongJitType t -> {
rv.visitLdcInsn(-1L >>> (Long.SIZE - t.size() * Byte.SIZE));
rv.visitInsn(LXOR);
}
case MpIntJitType t -> generateMpIntNegate(gen, t, rv);
default -> throw new AssertionError();
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
opForLong(Emitter<N0> em) {
return em
.emit(Op::ldc__l, -1L)
.emit(Op::lxor);
}
@Override
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntNegateOp op,
MpIntJitType type, Scope scope) {
var opnd = gen.genReadToOpnd(em, localThis, op.u(), type, ext(), scope);
em = opnd.em();
var legs = opnd.opnd().type().castLegsLE(opnd.opnd());
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
int legCount = type.legsAlloc();
for (int i = 0; i < legCount; i++) {
var result = em
.emit(legs.get(i)::read)
.emit(this::opForInt)
.emit(legs.get(i)::write, scope);
em = result.em();
outLegs.add(result.opnd());
}
return uType;
var out = MpIntLocalOpnd.of(type, "out", outLegs);
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
}
}

View File

@@ -15,16 +15,26 @@
*/
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.GenConsts;
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.Methods.Inv;
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.JitIntNotEqualOp;
/**
* The generator for a {@link JitIntNotEqualOp int_notequal}.
*
* <p>
* This uses the integer comparison operator generator and simply emits {@link #IF_ICMPNE} or
* {@link #IFNE} depending on the type.
* To avoid jumps, this delegates to {@link Integer#compare(int, int)}, which is signed, and then
* masks the result.
*/
public enum IntNotEqualOpGen implements CompareIntBinOpGen<JitIntNotEqualOp> {
public enum IntNotEqualOpGen implements IntCompareBinOpGen<JitIntNotEqualOp> {
/** The generator singleton */
GEN;
@@ -34,12 +44,26 @@ public enum IntNotEqualOpGen implements CompareIntBinOpGen<JitIntNotEqualOp> {
}
@Override
public int icmpOpcode() {
return IF_ICMPNE;
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 em
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "compare",
GenConsts.MDESC_INTEGER__COMPARE, false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ldc__i, 1)
.emit(Op::iand);
// LATER: Can probably remove this mask 1. Tests check it, but still.
}
@Override
public int ifOpcode() {
return IFNE;
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
return em
.emit(Op::lcmp)
.emit(Op::ldc__i, 1)
.emit(Op::iand);
// LATER: Can probably remove this mask 1. Tests check it, but still.
}
}

View File

@@ -0,0 +1,102 @@
/* ###
* 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 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.opnd.Opnd.Ext;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitBinOp;
/**
* An extension for integer binary operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntOpBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
/**
* Emit the JVM bytecode to perform the operator with intF operands on the stack.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param type the p-code type of the operands
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>> Emitter<Ent<N2, TInt>>
opForInt(Emitter<N0> em, IntJitType type);
/**
* Emit the JVM bytecode to perform the operator with long operands on the stack.
*
* @param <N2> the tail of the incoming stack
* @param <N1> the tail of the incoming stack including the right operand
* @param <N0> the incoming stack with the right and left operands on top
* @param em the emitter typed with the incoming stack
* @param type the p-code type of the operands
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>> Emitter<Ent<N2, TLong>>
opForLong(Emitter<N0> em, LongJitType type);
/**
* Emit the JVM bytecode to perform the operator with multi-precision operands.
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param op the p-code op
* @param type the p-code type of the operands
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the empty stack
*/
<THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
Scope scope);
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType lType = gen.resolveType(op.l(), op.lType());
JitType rType = gen.resolveType(op.r(), op.rType());
JitType uType = JitType.unify(lType, rType);
return new LiveOpResult(switch (uType) {
case IntJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
.emit(this::opForInt, t)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case LongJitType t -> em
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
.emit(this::opForLong, t)
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
default -> throw new AssertionError();
});
}
}

View File

@@ -0,0 +1,91 @@
/* ###
* 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 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.tgt.JitCompiledPassage.EntryPoint;
import ghidra.pcode.emu.jit.gen.util.*;
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
import ghidra.pcode.emu.jit.gen.util.Types.*;
import ghidra.pcode.emu.jit.op.JitUnOp;
/**
* An extension for integer unary operators
*
* @param <T> the class of p-code op node in the use-def graph
*/
public interface IntOpUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
/**
* Emit the JVM bytecode to perform the operator with int operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> opForInt(Emitter<N0> em);
/**
* Emit the JVM bytecode to perform the operator with long operands on the stack.
*
* @param <N1> the tail of the incoming stack
* @param <N0> the incoming stack with the input operand on top
* @param em the emitter typed with the incoming stack
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
*/
<N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>> opForLong(Emitter<N0> em);
/**
* Emit the JVM bytecode to perform the operator with multi-precision operands.
*
* @param <THIS> the type of the generated passage
* @param em the emitter typed with the empty stack
* @param localThis a handle to the local holding the {@code this} reference
* @param gen the code generator
* @param op the p-code op
* @param type the p-code type of the operands
* @param scope a scope for generating temporary local storage
* @return the emitter typed with the empty stack
*/
<THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
Scope scope);
@Override
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
JitType uType = gen.resolveType(op.u(), op.uType());
return new LiveOpResult(switch (uType) {
case IntJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForInt)
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
case LongJitType t -> em
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
.emit(this::opForLong)
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
default -> throw new AssertionError();
});
}
}

View File

@@ -15,26 +15,36 @@
*/
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.JitIntOrOp;
/**
* The generator for a {@link JitIntOrOp int_or}.
*
* <p>
* This uses the bitwise binary operator and emits {@link #IOR} or {@link #LOR} depending on the
* type.
* This uses the bitwise binary operator and emits {@link Op#ior(Emitter) ior} or
* {@link Op#lor(Emitter) lor} depending on the type.
*/
public enum IntOrOpGen implements BitwiseBinOpGen<JitIntOrOp> {
public enum IntOrOpGen implements IntBitwiseBinOpGen<JitIntOrOp> {
/** 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);
}
}

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