mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 20:33:11 +08:00
GP-5980: Optimize and clean code for mp-int in p-code emu.
This commit is contained in:
@@ -262,7 +262,8 @@ public class JitCompiler {
|
||||
oum.dumpResult();
|
||||
}
|
||||
|
||||
JitCodeGenerator gen = new JitCodeGenerator(lookup, context, cfm, dfm, vsm, tm, am, oum);
|
||||
JitCodeGenerator<?> gen =
|
||||
new JitCodeGenerator<>(lookup, context, cfm, dfm, vsm, tm, am, oum);
|
||||
return gen.load();
|
||||
}
|
||||
|
||||
|
||||
@@ -116,4 +116,39 @@ public enum JitJvmTypeUtils {
|
||||
default -> throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the erasure of a type variable with the given upper bounds
|
||||
*
|
||||
* @param bounds the upper bounds
|
||||
* @return the erasure
|
||||
*/
|
||||
public static Class<?> eraseBounds(Type[] bounds) {
|
||||
if (bounds.length == 0) {
|
||||
return Object.class;
|
||||
}
|
||||
return erase(bounds[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the erasure of the given type
|
||||
* <p>
|
||||
* For a class, this is just the same class. For an array, it is the array of the erasure of the
|
||||
* element type. For a parameterized type, we take the erasure of the raw type, which should in
|
||||
* turn be a class. For a wildcard, we take the erasure of its first upper bound.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the erasure
|
||||
*/
|
||||
public static Class<?> erase(Type type) {
|
||||
return switch (type) {
|
||||
case Class<?> cls -> cls;
|
||||
case GenericArrayType arr -> Array.newInstance(erase(arr.getGenericComponentType()), 0)
|
||||
.getClass();
|
||||
case ParameterizedType pt -> erase(pt.getRawType());
|
||||
case TypeVariable<?> tv -> eraseBounds(tv.getBounds());
|
||||
case WildcardType wt -> eraseBounds(wt.getUpperBounds());
|
||||
default -> throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,12 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.objectweb.asm.MethodTooLargeException;
|
||||
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.emu.PcodeMachine.AccessKind;
|
||||
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowModel;
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
||||
import ghidra.pcode.emu.jit.decode.JitPassageDecoder;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPointPrototype;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
|
||||
import ghidra.pcode.emu.jit.var.JitVal;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
|
||||
+470
@@ -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);
|
||||
}
|
||||
}
|
||||
+63
@@ -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?");
|
||||
}
|
||||
}
|
||||
+63
@@ -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?");
|
||||
}
|
||||
}
|
||||
+125
@@ -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);
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for an {@link IntJitType int} p-code variable stored in part of a JVM {@code long}.
|
||||
*
|
||||
* @param local see {@link #local()}
|
||||
* @param type see {@link #type()}
|
||||
* @param vn see {@link #vn()} (wrt. the sub variable)
|
||||
* @param byteShift see {@link #byteShift()}
|
||||
*/
|
||||
public record IntInLongHandler(JvmLocal<TLong, LongJitType> local, IntJitType type, Varnode vn,
|
||||
int byteShift) implements SubInLongHandler<TInt, IntJitType> {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public IntInLongHandler {
|
||||
assertShiftFits(byteShift, type, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToSub() {
|
||||
return MpIntToInt.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lushr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
return genLoadToStack(em, gen, type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> {
|
||||
int msb = (this.type.size() + byteShift) * Byte.SIZE;
|
||||
if (msb > Integer.SIZE) {
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, msb - Integer.SIZE)
|
||||
.emit(Op::lshr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
if (msb == Integer.SIZE) {
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - msb)
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code int}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record IntVarAlloc(JvmLocal<TInt, IntJitType> local, IntJitType type)
|
||||
implements SimpleVarHandler<TInt, IntJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC1(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return IntToMpInt.INSTANCE.doConvert(em, local.opnd(), local.name(), type, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToStack() {
|
||||
return MpIntToInt.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Opnd::intToBool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
|
||||
Math.min(type.size(), maxByteSize));
|
||||
if (byteOffset == 0) {
|
||||
return new IntVarAlloc(local, IntJitType.forSize(subVn.getSize()));
|
||||
}
|
||||
return new IntInIntHandler(local, IntJitType.forSize(subVn.getSize()), subVn, byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An allocated JVM local
|
||||
*
|
||||
* @param <T> the JVM type of this local
|
||||
* @param <JT> the p-code type of this local
|
||||
* @param local the declared local this wraps
|
||||
* @param type a type for this local
|
||||
* @param vn the varnode whose value this local holds
|
||||
* @param opnd this local as an operand
|
||||
*/
|
||||
public record JvmLocal<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>(Local<T> local,
|
||||
JT type, Varnode vn, SimpleOpnd<T, JT> opnd) {
|
||||
|
||||
/**
|
||||
* Create a {@link JvmLocal} with the given local, type, and varnode
|
||||
*
|
||||
* @param <T> the JVM type of the local
|
||||
* @param <JT> the p-code type of the local
|
||||
* @param local the local
|
||||
* @param type the p-code type of the local
|
||||
* @param vn the varnode to assign to the local
|
||||
* @return the new local (wrapper)
|
||||
*/
|
||||
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> JvmLocal<T, JT>
|
||||
of(Local<T> local, JT type, Varnode vn) {
|
||||
SimpleOpnd<T, JT> opnd = SimpleOpnd.of(type, local);
|
||||
return new JvmLocal<>(local, type, vn, opnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the wrapped local
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String name() {
|
||||
return local.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast this local to satisfy checkers when a type variable is known to be of a given type
|
||||
* <p>
|
||||
* This will verify at runtime that the types are in fact identical.
|
||||
*
|
||||
* @param <TT> the "to" JVM type
|
||||
* @param <TJT> the "to" p-code type
|
||||
* @param type the "to" p-code type
|
||||
* @return this local as the given type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>> JvmLocal<TT, TJT>
|
||||
castOf(TJT type) {
|
||||
if (this.type != type) {
|
||||
throw new ClassCastException(
|
||||
"JvmLocal is not of the given type: this is %s. Requested is %s."
|
||||
.formatted(this.type, type));
|
||||
}
|
||||
return (JvmLocal<TT, TJT>) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode into the class constructor needed to access the varnode's actual value from the
|
||||
* underlying {@link PcodeExecutorState}.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <N extends Next> Emitter<N> genInit(Emitter<N> em, JitCodeGenerator<?> gen) {
|
||||
return VarGen.genVarnodeInit(em, gen, vn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load this local's value onto the JVM stack as the given type
|
||||
*
|
||||
* @param <TT> the desired JVM type
|
||||
* @param <TJT> the desired p-code type
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the desired p-code type
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(opnd::read)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store the value on the JVM stack into the local
|
||||
*
|
||||
* @param <FT> the JVM type of the value on the stack
|
||||
* @param <FJT> the p-code type of the value on the stack
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the value on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(Opnd::convert, type, this.type, ext)
|
||||
.emit(opnd::writeDirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to bring this varnode into scope.
|
||||
*
|
||||
* <p>
|
||||
* This will copy the value from the {@link JitBytesPcodeExecutorState state} into the local
|
||||
* variable.
|
||||
*
|
||||
* @param <THIS> the type of the compiled passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genBirthCode(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(VarGen::genReadValDirectToStack, localThis, gen, type, vn)
|
||||
.emit(opnd::writeDirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to take this varnode out of scope.
|
||||
*
|
||||
* <p>
|
||||
* This will copy the value from the local variable into the {@link JitBytesPcodeExecutorState
|
||||
* state}.
|
||||
*
|
||||
* @param <THIS> the type of the compiled passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genRetireCode(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(opnd::read)
|
||||
.emit(VarGen::genWriteValDirectFromStack, localThis, gen, type, vn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the maximum address that would be occupied by the full primitive type}
|
||||
*/
|
||||
public Address maxPrimAddr() {
|
||||
return vn.getAddress().add(type.ext().size() - 1);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for an {@link LongJitType long} p-code variable stored in part of a JVM {@code long}.
|
||||
*
|
||||
* @param local see {@link #local()}
|
||||
* @param type see {@link #type()}
|
||||
* @param vn see {@link #vn()} (wrt. the sub variable)
|
||||
* @param byteShift see {@link #byteShift()}
|
||||
*/
|
||||
public record LongInLongHandler(JvmLocal<TLong, LongJitType> local, LongJitType type, Varnode vn,
|
||||
int byteShift) implements SubInLongHandler<TLong, LongJitType> {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public LongInLongHandler {
|
||||
assertShiftFits(byteShift, type, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToSub() {
|
||||
return MpIntToLong.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lushr)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
|
||||
vn, 0, Integer.BYTES);
|
||||
return new IntInLongHandler(local, IntJitType.I4, subVn, byteShift)
|
||||
.genLoadLegToStack(em, gen, type, leg, ext);
|
||||
}
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
|
||||
vn, Integer.BYTES, vn.getSize() - Integer.BYTES);
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + Integer.BYTES).genLoadLegToStack(em, gen, type, leg - 1, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
|
||||
if (subVn.getSize() <= Integer.BYTES) {
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code long}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record LongVarAlloc(JvmLocal<TLong, LongJitType> local, LongJitType type)
|
||||
implements SimpleVarHandler<TLong, LongJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC2(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToStack() {
|
||||
return MpIntToLong.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Op::ldc__l, 0)
|
||||
.emit(Op::lcmp); // Outputs -1, 0, or 1. So long as lsb is set, it's true.
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
|
||||
Math.min(type.size(), maxByteSize));
|
||||
if (byteOffset == 0) {
|
||||
return new LongVarAlloc(local, LongJitType.forSize(subVn.getSize()));
|
||||
}
|
||||
if (subVn.getSize() <= Integer.BYTES) {
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteOffset);
|
||||
}
|
||||
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
|
||||
byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A dummy handler for values/variables that are not allocated in JVM locals
|
||||
* <p>
|
||||
* Every operation on this handler will throw an exception at code generation time.
|
||||
*/
|
||||
public enum NoHandler implements VarHandler {
|
||||
/** Singleton */
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Varnode vn() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType type() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+607
File diff suppressed because it is too large
Load Diff
+192
@@ -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);
|
||||
}
|
||||
}
|
||||
+66
@@ -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());
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
|
||||
/**
|
||||
* A handler to p-code variables stored in just a portion of a single JVM local variable.
|
||||
*
|
||||
* @param <ST> the JVM type of the sub variable
|
||||
* @param <SJT> the p-code type of the sub variable
|
||||
* @param <WT> the JVM type of the containing variable
|
||||
* @param <WJT> the p-code type of the containing variable
|
||||
*/
|
||||
public interface SubVarHandler<ST extends BPrim<?>, SJT extends SimpleJitType<ST, SJT>,
|
||||
WT extends BPrim<?>, WJT extends SimpleJitType<WT, WJT>> extends VarHandler {
|
||||
|
||||
/**
|
||||
* Verify that the sub variable as shifted actually fits in the containing variable
|
||||
*
|
||||
* @param byteShift the number of unused bytes in the container variable to the right of the sub
|
||||
* variable
|
||||
* @param type the type of the sub variable
|
||||
* @param local the containing local variable
|
||||
*/
|
||||
default void assertShiftFits(int byteShift, SJT type, JvmLocal<WT, WJT> local) {
|
||||
assert byteShift >= 0 && byteShift + type.size() <= local.type().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the number of unused bytes in the container variable to the right of the sub
|
||||
* variable}
|
||||
*/
|
||||
int byteShift();
|
||||
|
||||
/**
|
||||
* {@return the number of bits in the sub variable}
|
||||
*/
|
||||
default int bitSize() {
|
||||
return type().size() * Byte.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the number of unused bits in the container variable to the right of the sub
|
||||
* variable}
|
||||
*/
|
||||
default int bitShift() {
|
||||
return byteShift() * Byte.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the mask indicating which parts of the {@code int} containing variable are within
|
||||
* the sub variable}
|
||||
*/
|
||||
default int intMask() {
|
||||
return (-1 >>> (Integer.SIZE - bitSize())) << bitShift();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the mask indicating which parts of the {@code long} containing variable are within
|
||||
* the sub variable}
|
||||
*/
|
||||
default long longMask() {
|
||||
return (-1L >>> (Long.SIZE - bitSize())) << bitShift();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return The containing local variable}
|
||||
*/
|
||||
JvmLocal<WT, WJT> local();
|
||||
|
||||
@Override
|
||||
SJT type();
|
||||
|
||||
/**
|
||||
* Get the converter of multi-precision integers to the type of the sub variable.
|
||||
* <p>
|
||||
* The converter need not worry about positioning or masking wrt. the sub variable. It should
|
||||
* extract from the multi-precision integer the minimum number of legs needed to fill the sub
|
||||
* variable, i.e., it need only consider the sub variable's size. This handler will then mask
|
||||
* and position it within the containing variable for storage.
|
||||
*
|
||||
* @return the converter
|
||||
*/
|
||||
MpToStackConv<TInt, IntJitType, MpIntJitType, ST, SJT> getConvToSub();
|
||||
|
||||
@Override
|
||||
default <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, this.type(), ext)
|
||||
.emit(Opnd::convertToOpnd, this.type(), name(), type, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, this.type(), ext)
|
||||
.emit(Opnd::convertToArray, this.type(), name(), type, ext, scope, slack);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToSub()::convertOpndToStack, opnd, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToSub()::convertArrayToStack, type, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.op.SubPieceOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A handler that knows how to load and store variables' values from local storage.
|
||||
* <p>
|
||||
* Some variables are hosted in a single JVM local of compatible type. Others, notably
|
||||
* multi-precision integers, are allocated among two or more JVM local integers. Each such integer
|
||||
* is called a "leg" of the multi-precision integer. Other literature may call these "digits" (whose
|
||||
* value is in [0, 0xffffffff]). Depending on the operator implementation, value may need to be
|
||||
* loaded with alternative types or in different forms. e.g., any float operator will need to
|
||||
* convert its inputs into the appropriate float type, even if the operands were allocated as an int
|
||||
* type. Similarly, some operators are implement their multi-precision computations by invoking
|
||||
* static methods whose parameters are {@code int[]}, and so they will load and store the array
|
||||
* forms instead of accessing the legs' locals. This interface provides generators for the various
|
||||
* circumstances. Each subclass provides the implementations for various allocations.
|
||||
*/
|
||||
public interface VarHandler {
|
||||
|
||||
/**
|
||||
* Generate a name for the variable representing the given varnode
|
||||
* <p>
|
||||
* These are for debugging purposes. When dumping generating bytecode, the declared local
|
||||
* variables and their scopes are often also dumped. This provides a human with the local
|
||||
* variable index for various varnodes.
|
||||
*
|
||||
* @param vn the varnode
|
||||
* @return the name
|
||||
*/
|
||||
static String nameVn(Varnode vn) {
|
||||
return "var_%s_%x_%d".formatted(
|
||||
vn.getAddress().getAddressSpace().getName(),
|
||||
vn.getOffset(),
|
||||
vn.getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete varnode accessible to this handler
|
||||
*
|
||||
* @return the varnode
|
||||
*/
|
||||
Varnode vn();
|
||||
|
||||
/**
|
||||
* Get the name for this handler's local variable, named after the varnode is represents.
|
||||
*
|
||||
* @return the name of the local variable
|
||||
*/
|
||||
default String name() {
|
||||
return nameVn(vn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the p-code type of the local variable this handler uses.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
JitType type();
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value onto the JVM stack.
|
||||
*
|
||||
* @param <TT> the JVM type of the value to load onto the stack
|
||||
* @param <TJT> the p-code type of the value to load onto the stack
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
|
||||
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||
* @return the emitter typed with the resulting stack
|
||||
*/
|
||||
<TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next> Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into several locals.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the operand containing the locals, and the emitter typed with the incoming stack
|
||||
*/
|
||||
<N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
MpIntJitType type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load one leg of a multi-precision value from the varnode onto the JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param leg the index of the leg to load, 0 being least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
MpIntJitType type, int leg, Ext ext);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into an integer array in little-endian order,
|
||||
* pushing its ref onto the JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @param slack the number of additional, more significant, elements to allocate in the array
|
||||
* @return the emitter typed with the resulting stack, i.e., having the ref pushed onto it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value, interpreted as a boolean, as an integer onto the
|
||||
* JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int boolean pushed onto
|
||||
* it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em, JitCodeGenerator<?> gen);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a value into a variable from the JVM stack.
|
||||
*
|
||||
* @param <FT> the JVM type of the value on the stack
|
||||
* @param <FJT> the p-code type of the value on the stack
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the value to store on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
<FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a varnode's value from several locals.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param opnd the operand whose locals contain the value to be stored
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
<N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a varnode's value from an array of integer legs, in little endian
|
||||
* order
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the array ref on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the array
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Create a handler for a {@link PcodeOp#SUBPIECE} of a value.
|
||||
* <p>
|
||||
* To implement {@link SubPieceOpGen subpiece}, we could load the entire varnode and then
|
||||
* extract the designated portion. Or, we could load only the designated portion, averting any
|
||||
* code and execution cost of loading the un-designated portions. We accomplish this by
|
||||
* re-writing the subpiece op and a load of the sub-varnode.
|
||||
*
|
||||
* @param endian the endianness of the emulation target
|
||||
* @param byteOffset the number of least-significant bytes to remove
|
||||
* @param maxByteSize the maximum size of the resulting variable. In general, a subpiece should
|
||||
* never exceed the size of the parent varnode, but if it does, this will truncate
|
||||
* that excess.
|
||||
* @return the resulting subpiece handler
|
||||
*/
|
||||
VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize);
|
||||
}
|
||||
+128
-898
File diff suppressed because it is too large
Load Diff
+36
-8
@@ -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);
|
||||
|
||||
+214
-117
@@ -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();
|
||||
}
|
||||
|
||||
+2
-3
@@ -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>
|
||||
|
||||
+18
-18
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-27
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-25
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-28
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-25
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-27
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-14
@@ -19,10 +19,14 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
@@ -37,7 +41,7 @@ import ghidra.program.model.pcode.Varnode;
|
||||
* @param vn the varnode to pre-construct
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
public record FieldForVarnode(Varnode vn) implements StaticFieldReq<TRef<Varnode>> {
|
||||
@Override
|
||||
public String name() {
|
||||
Address addr = vn.getAddress();
|
||||
@@ -56,16 +60,22 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv) {
|
||||
cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, name(), TDESC_VARNODE, null, null);
|
||||
|
||||
sv.visitFieldInsn(GETSTATIC, gen.nameThis, "ADDRESS_FACTORY", TDESC_ADDRESS_FACTORY);
|
||||
sv.visitLdcInsn(vn.getAddress().getAddressSpace().getName());
|
||||
sv.visitLdcInsn(vn.getAddress().getOffset());
|
||||
sv.visitLdcInsn(vn.getSize());
|
||||
sv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "createVarnode",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true);
|
||||
sv.visitFieldInsn(PUTSTATIC, gen.nameThis, name(), TDESC_VARNODE);
|
||||
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, T_VARNODE, name());
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, "ADDRESS_FACTORY", T_ADDRESS_FACTORY)
|
||||
.emit(Op::ldc__a, vn.getAddress().getAddressSpace().getName())
|
||||
.emit(Op::ldc__l, vn.getAddress().getOffset())
|
||||
.emit(Op::ldc__i, vn.getSize())
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createVarnode",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putstatic, gen.typeThis, name(), T_VARNODE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +89,9 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
rv.visitFieldInsn(GETSTATIC, gen.nameThis, name(), TDESC_VARNODE);
|
||||
public <N extends Next> Emitter<Ent<N, TRef<Varnode>>> genLoad(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, name(), T_VARNODE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
|
||||
|
||||
/**
|
||||
* A field request for a pre-fetched or pre-constructed element
|
||||
*
|
||||
* @param <T> the type of the field
|
||||
*/
|
||||
public interface FieldReq {
|
||||
public interface FieldReq<T extends BNonVoid> {
|
||||
/**
|
||||
* Derive a suitable name for the field
|
||||
*
|
||||
@@ -30,12 +30,4 @@ public interface FieldReq {
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Emit code to load the field onto the JVM stack
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param rv the visitor often for the {@link JitCompiledPassage#run(int) run} method, but could
|
||||
* be the static initializer or constructor
|
||||
*/
|
||||
void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv);
|
||||
}
|
||||
|
||||
+368
-190
File diff suppressed because it is too large
Load Diff
+31
-4
@@ -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);
|
||||
}
|
||||
|
||||
+657
-381
File diff suppressed because it is too large
Load Diff
+23
-4
@@ -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);
|
||||
}
|
||||
|
||||
+177
@@ -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);
|
||||
}
|
||||
}
|
||||
+68
@@ -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);
|
||||
}
|
||||
}
|
||||
+110
@@ -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);
|
||||
}
|
||||
}
|
||||
+67
@@ -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);
|
||||
}
|
||||
}
|
||||
+265
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
+299
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
+14
-5
@@ -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);
|
||||
}
|
||||
+106
@@ -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);
|
||||
}
|
||||
+194
@@ -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;
|
||||
}
|
||||
}
|
||||
+76
@@ -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);
|
||||
}
|
||||
+40
-123
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
* An extension for bitwise binary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface BitwiseBinOpGen<T extends JitBinOp> extends IntBinOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM opcode to implement this operator with int operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int intOpcode();
|
||||
|
||||
/**
|
||||
* The JVM opcode to implement this operator with long operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int longOpcode();
|
||||
|
||||
/**
|
||||
* The implementation for multi-precision ints.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param type the type of each operand, including the reuslt
|
||||
* @param mv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
default void generateMpIntBinOp(JitCodeGenerator gen, MpIntJitType type,
|
||||
MethodVisitor mv) {
|
||||
/**
|
||||
* We need temp locals to get things in order. Read in right operand, do the op as we pop
|
||||
* each left op. Then push it all back.
|
||||
*
|
||||
* No masking of the result is required, since both operands should already be masked, and
|
||||
* the bitwise op cannot generate bits of more significance.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
// [lleg1,...,llegN:INT]
|
||||
mv.visitVarInsn(ILOAD, result.idx(i));
|
||||
// [lleg1,...,llegN:INT,rlegN:INT]
|
||||
mv.visitInsn(intOpcode());
|
||||
// [lleg1,...,olegN:INT]
|
||||
mv.visitVarInsn(ISTORE, result.idx(i));
|
||||
// [lleg1,...]
|
||||
}
|
||||
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This implementation reduces the need to just the JVM opcode. We simply ensure both operands
|
||||
* have the same size and JVM type, select and emit the correct opcode, and return the type of
|
||||
* the result.
|
||||
*/
|
||||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(intOpcode());
|
||||
case LongJitType t -> rv.visitInsn(longOpcode());
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntBinOp(gen, t, rv);
|
||||
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
+15
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+40
-26
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-5
@@ -15,6 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitBoolXorOp;
|
||||
import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
|
||||
|
||||
@@ -24,18 +32,23 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
|
||||
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
||||
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
||||
* just like {@link OpBehaviorBoolXor}. Thus, this is identical to {@link IntXorOpGen}.
|
||||
* @implNote Because having bits other than the least significant set in the inputs is "undefined
|
||||
* behavior," we could technically optimize this by only ANDing the least significant leg
|
||||
* when we're dealing with mp-ints.
|
||||
*/
|
||||
public enum BoolXorOpGen implements BitwiseBinOpGen<JitBoolXorOp> {
|
||||
public enum BoolXorOpGen implements IntBitwiseBinOpGen<JitBoolXorOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IXOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.ixor(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LXOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.lxor(em);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user