diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitCompiler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitCompiler.java
index d6d99d2ef6..b03fa6a114 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitCompiler.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitCompiler.java
@@ -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();
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitJvmTypeUtils.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitJvmTypeUtils.java
index 29e1a02825..4e7912a655 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitJvmTypeUtils.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitJvmTypeUtils.java
@@ -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
+ *
+ * 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();
+ };
+ }
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitPcodeEmulator.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitPcodeEmulator.java
index 59a1ab9757..41a66e4264 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitPcodeEmulator.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/JitPcodeEmulator.java
@@ -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;
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/AlignedMpIntHandler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/AlignedMpIntHandler.java
new file mode 100644
index 0000000000..b043de6f0b
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/AlignedMpIntHandler.java
@@ -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.
+ *
+ * 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> 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> legs,
+ MpIntJitType type, Varnode vn) {
+ List> opndLegs = new ArrayList<>();
+ for (JvmLocal 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> legs,
+ MpIntJitType type, Varnode vn) {
+ List> opndLegs = new ArrayList<>();
+ for (JvmLocal 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> legs, MpIntJitType type,
+ Varnode vn) {
+ this(legs, type, vn, createOpnd(legs, type, vn), createRoOpnd(legs, type, vn));
+ }
+
+ @Override
+ public , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter 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 OpndEm genLoadToOpnd(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType to, Ext ext, Scope scope) {
+ return MpIntToMpInt.INSTANCE.convertOpndToOpnd(em, roOpnd, to, ext, scope);
+ }
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter 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 Emitter>> genLoadToArray(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType to, Ext ext, Scope scope, int slack) {
+ return MpIntToMpInt.INSTANCE.convertOpndToArray(em, opnd, to, ext, scope, slack);
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter em,
+ JitCodeGenerator> gen) {
+ var result = em
+ .emit(legs.get(0)::genLoadToStack, gen, IntJitType.I4, Ext.ZERO);
+ for (JvmLocal 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 the tail of the incoming stack
+ * @param 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 > Emitter doGenStoreInt(Emitter em,
+ JitCodeGenerator> gen, IntJitType type, JvmLocal 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.
+ *
+ * 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 the tail of the incoming stack
+ * @param 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 > Emitter>
+ doGenStoreIntAndSign(Emitter em, JitCodeGenerator> gen, IntJitType type,
+ JvmLocal 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 the tail of the incoming stack
+ * @param 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 > Emitter doGenStoreLong(
+ Emitter em, JitCodeGenerator> gen, LongJitType type,
+ JvmLocal lower, JvmLocal 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.
+ *
+ * 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 the tail of the incoming stack
+ * @param 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 > Emitter>
+ doGenStoreLongAndSign(Emitter em, JitCodeGenerator> gen, LongJitType type,
+ JvmLocal lower, JvmLocal 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 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 Emitter doGenZeroFill(Emitter em, JitCodeGenerator> gen,
+ List> locals, Scope scope) {
+ for (JvmLocal 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 the tail of the incoming stack
+ * @param 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 > Emitter doGenSignFill(Emitter em,
+ JitCodeGenerator> gen, List> locals, Scope scope) {
+ for (JvmLocal local : locals.subList(0, locals.size() - 1)) {
+ em = em
+ .emit(Op::dup)
+ .emit(local::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
+ }
+ JvmLocal last = locals.getLast();
+ return em
+ .emit(last::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
+ }
+
+ @Override
+ public , FJT extends SimpleJitType, N1 extends Next,
+ N0 extends Ent> Emitter genStoreFromStack(Emitter 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 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 Emitter genExt(Emitter 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 Emitter genStoreFromOpnd(Emitter em, JitCodeGenerator> gen,
+ Opnd from, Ext ext, Scope scope) {
+ List> fromLegs = from.type().castLegsLE(from);
+ List> 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 fromLeg = fromLegs.get(i);
+ SimpleOpnd 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 >> Emitter genStoreFromArray(
+ Emitter em, JitCodeGenerator> gen, MpIntJitType from, Ext ext, Scope scope) {
+ List fromLegTypes = from.legTypesLE();
+ List> 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 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 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> 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> 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/DoubleVarAlloc.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/DoubleVarAlloc.java
new file mode 100644
index 0000000000..b515de4c1d
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/DoubleVarAlloc.java
@@ -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 local, DoubleJitType type)
+ implements SimpleVarHandler {
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, int leg, Ext ext) {
+ return genLoadLegToStackC2(em, gen, type, leg, ext);
+ }
+
+ @Override
+ public MpToStackConv getConvToStack() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter 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?");
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/FloatVarAlloc.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/FloatVarAlloc.java
new file mode 100644
index 0000000000..837f1723e3
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/FloatVarAlloc.java
@@ -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 local, FloatJitType type)
+ implements SimpleVarHandler {
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, int leg, Ext ext) {
+ return genLoadLegToStackC1(em, gen, type, leg, ext);
+ }
+
+ @Override
+ public MpToStackConv getConvToStack() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter 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?");
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInIntHandler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInIntHandler.java
new file mode 100644
index 0000000000..c4d4370db4
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInIntHandler.java
@@ -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 local, IntJitType type, Varnode vn,
+ int byteShift) implements SubVarHandler {
+
+ @SuppressWarnings("javadoc")
+ public IntInIntHandler {
+ assertShiftFits(byteShift, type, local);
+ }
+
+ @Override
+ public MpToStackConv getConvToSub() {
+ return MpIntToInt.INSTANCE;
+ }
+
+ @Override
+ public , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter 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 Emitter> genLoadLegToStack(Emitter 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 Emitter> genLoadToBool(Emitter 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 , FJT extends SimpleJitType, N1 extends Next,
+ N0 extends Ent> Emitter genStoreFromStack(Emitter 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInLongHandler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInLongHandler.java
new file mode 100644
index 0000000000..00a24d13ec
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntInLongHandler.java
@@ -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 local, IntJitType type, Varnode vn,
+ int byteShift) implements SubInLongHandler {
+
+ @SuppressWarnings("javadoc")
+ public IntInLongHandler {
+ assertShiftFits(byteShift, type, local);
+ }
+
+ @Override
+ public MpToStackConv getConvToSub() {
+ return MpIntToInt.INSTANCE;
+ }
+
+ @Override
+ public , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter 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 Emitter> genLoadLegToStack(Emitter 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntVarAlloc.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntVarAlloc.java
new file mode 100644
index 0000000000..3b54fe069d
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/IntVarAlloc.java
@@ -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 local, IntJitType type)
+ implements SimpleVarHandler {
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, int leg, Ext ext) {
+ return genLoadLegToStackC1(em, gen, type, leg, ext);
+ }
+
+ @Override
+ public OpndEm genLoadToOpnd(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, Ext ext, Scope scope) {
+ return IntToMpInt.INSTANCE.doConvert(em, local.opnd(), local.name(), type, ext, scope);
+ }
+
+ @Override
+ public MpToStackConv getConvToStack() {
+ return MpIntToInt.INSTANCE;
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/JvmLocal.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/JvmLocal.java
new file mode 100644
index 0000000000..304e6e88c1
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/JvmLocal.java
@@ -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 the JVM type of this local
+ * @param 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, JT extends SimpleJitType>(Local local,
+ JT type, Varnode vn, SimpleOpnd opnd) {
+
+ /**
+ * Create a {@link JvmLocal} with the given local, type, and varnode
+ *
+ * @param the JVM type of the local
+ * @param 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 , JT extends SimpleJitType> JvmLocal
+ of(Local local, JT type, Varnode vn) {
+ SimpleOpnd 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
+ *
+ * This will verify at runtime that the types are in fact identical.
+ *
+ * @param the "to" JVM type
+ * @param the "to" p-code type
+ * @param type the "to" p-code type
+ * @return this local as the given type
+ */
+ @SuppressWarnings("unchecked")
+ public , TJT extends SimpleJitType> JvmLocal
+ 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) this;
+ }
+
+ /**
+ * Emit bytecode into the class constructor needed to access the varnode's actual value from the
+ * underlying {@link PcodeExecutorState}.
+ *
+ * @param 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 Emitter genInit(Emitter 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 the desired JVM type
+ * @param the desired p-code type
+ * @param 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 , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter 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 the JVM type of the value on the stack
+ * @param the p-code type of the value on the stack
+ * @param the tail of the incoming stack
+ * @param 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 , FJT extends SimpleJitType, N1 extends Next,
+ N0 extends Ent> Emitter genStoreFromStack(Emitter 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.
+ *
+ *
+ * This will copy the value from the {@link JitBytesPcodeExecutorState state} into the local
+ * variable.
+ *
+ * @param the type of the compiled passage
+ * @param 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 Emitter genBirthCode(Emitter em,
+ Local> localThis, JitCodeGenerator gen) {
+ return em
+ .emit(VarGen::genReadValDirectToStack, localThis, gen, type, vn)
+ .emit(opnd::writeDirect);
+ }
+
+ /**
+ * Emit bytecode to take this varnode out of scope.
+ *
+ *
+ * This will copy the value from the local variable into the {@link JitBytesPcodeExecutorState
+ * state}.
+ *
+ * @param the type of the compiled passage
+ * @param 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 Emitter genRetireCode(Emitter em,
+ Local> localThis, JitCodeGenerator 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongInLongHandler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongInLongHandler.java
new file mode 100644
index 0000000000..c1398d7274
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongInLongHandler.java
@@ -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 local, LongJitType type, Varnode vn,
+ int byteShift) implements SubInLongHandler {
+
+ @SuppressWarnings("javadoc")
+ public LongInLongHandler {
+ assertShiftFits(byteShift, type, local);
+ }
+
+ @Override
+ public MpToStackConv getConvToSub() {
+ return MpIntToLong.INSTANCE;
+ }
+
+ @Override
+ public , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter 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 Emitter> genLoadLegToStack(Emitter 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongVarAlloc.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongVarAlloc.java
new file mode 100644
index 0000000000..e5d1595e36
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/LongVarAlloc.java
@@ -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 local, LongJitType type)
+ implements SimpleVarHandler {
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, int leg, Ext ext) {
+ return genLoadLegToStackC2(em, gen, type, leg, ext);
+ }
+
+ @Override
+ public MpToStackConv getConvToStack() {
+ return MpIntToLong.INSTANCE;
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter 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);
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/NoHandler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/NoHandler.java
new file mode 100644
index 0000000000..6ac13301a1
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/alloc/NoHandler.java
@@ -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
+ *
+ * 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 , TJT extends SimpleJitType, N extends Next>
+ Emitter>
+ genLoadToStack(Emitter em, JitCodeGenerator> gen, TJT type, Ext ext) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public OpndEm genLoadToOpnd(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, Ext ext, Scope scope) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter> genLoadLegToStack(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, int leg, Ext ext) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter>> genLoadToArray(Emitter em,
+ JitCodeGenerator> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter> genLoadToBool(Emitter em,
+ JitCodeGenerator> gen) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public , FJT extends SimpleJitType, N1 extends Next,
+ N0 extends Ent> Emitter genStoreFromStack(Emitter em,
+ JitCodeGenerator> gen, FJT type, Ext ext, Scope scope) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Emitter genStoreFromOpnd(Emitter em, JitCodeGenerator> gen,
+ Opnd opnd, Ext ext, Scope scope) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public