mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-07 21:24:00 -05:00
Merge remote-tracking branch 'origin/GP-5980_Dan_jitMpIntMore--SQUASHED'
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;
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler used for a varnode requiring allocation of multiple integers, where those integers
|
||||
* correspond exactly to the variable's legs.
|
||||
* <p>
|
||||
* In this case, we can usually give the operators direct access to the underlying mp-int operand.
|
||||
* We do need to be careful that we don't unintentionally permit the operator to use the variable's
|
||||
* storage for intermediate values. Thus, we have some provision for saying each leg is read-only,
|
||||
* which will cause attempts to store into them to instead generate a writable temporary local. Such
|
||||
* intermediate results will get written only by a call to
|
||||
* {@link #genStoreFromOpnd(Emitter, JitCodeGenerator, Opnd, Ext, Scope)}.
|
||||
*
|
||||
* @param legs the list of legs in little-endian order
|
||||
* @param type the type of the full multi-precision integer variable
|
||||
* @param vn the complete varnode accessible to this handler
|
||||
* @param opnd the (writable) list of local operands (in LE order)
|
||||
* @param roOpnd the read-only version of {@code opnd}.
|
||||
*/
|
||||
public record AlignedMpIntHandler(List<JvmLocal<TInt, IntJitType>> legs, MpIntJitType type,
|
||||
Varnode vn, MpIntLocalOpnd opnd, MpIntLocalOpnd roOpnd) implements VarHandler {
|
||||
|
||||
/**
|
||||
* Static utility for the {@link #AlignedMpIntHandler(List, MpIntJitType, Varnode)} constructor
|
||||
*
|
||||
* @param legs the list o legs in little-endian order
|
||||
* @param type the type of the full mp-int variable
|
||||
* @param vn the complete varnode
|
||||
* @return the writable operand
|
||||
*/
|
||||
private static MpIntLocalOpnd createOpnd(List<JvmLocal<TInt, IntJitType>> legs,
|
||||
MpIntJitType type, Varnode vn) {
|
||||
List<SimpleOpnd<TInt, IntJitType>> opndLegs = new ArrayList<>();
|
||||
for (JvmLocal<TInt, IntJitType> leg : legs) {
|
||||
opndLegs.add(leg.opnd());
|
||||
}
|
||||
return MpIntLocalOpnd.of(type, VarHandler.nameVn(vn), opndLegs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static utility for the {@link #AlignedMpIntHandler(List, MpIntJitType, Varnode)} constructor
|
||||
*
|
||||
* @param legs the list o legs in little-endian order
|
||||
* @param type the type of the full mp-int variable
|
||||
* @param vn the complete varnode
|
||||
* @return the read-only operand
|
||||
*/
|
||||
private static MpIntLocalOpnd createRoOpnd(List<JvmLocal<TInt, IntJitType>> legs,
|
||||
MpIntJitType type, Varnode vn) {
|
||||
List<SimpleOpnd<TInt, IntJitType>> opndLegs = new ArrayList<>();
|
||||
for (JvmLocal<TInt, IntJitType> leg : legs) {
|
||||
opndLegs.add(SimpleOpnd.ofIntReadOnly(leg.type(), leg.local()));
|
||||
}
|
||||
return MpIntLocalOpnd.of(type, VarHandler.nameVn(vn) + "_ro", opndLegs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preferred constructor
|
||||
*
|
||||
* @param legs the list o legs in little-endian order
|
||||
* @param type the type of the full muti-precision integer variable
|
||||
* @param vn the complete varnode accessible to this handler
|
||||
*/
|
||||
public AlignedMpIntHandler(List<JvmLocal<TInt, IntJitType>> legs, MpIntJitType type,
|
||||
Varnode vn) {
|
||||
this(legs, type, vn, createOpnd(legs, type, vn), createRoOpnd(legs, type, vn));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT to, Ext ext) {
|
||||
return switch (to) {
|
||||
case IntJitType t -> em
|
||||
.emit(legs.get(0)::genLoadToStack, gen, to, ext);
|
||||
case LongJitType t when legs.size() == 1 -> em
|
||||
.emit(legs.get(0)::genLoadToStack, gen, to, ext);
|
||||
case LongJitType t -> em
|
||||
.emit(legs.get(0)::genLoadToStack, gen, LongJitType.I8, Ext.ZERO)
|
||||
.emit(legs.get(1)::genLoadToStack, gen, LongJitType.I8, Ext.ZERO)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lshl)
|
||||
.emit(Op::lor)
|
||||
.emit(Opnd::convert, LongJitType.I8, to, ext);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope) {
|
||||
return MpIntToMpInt.INSTANCE.convertOpndToOpnd(em, roOpnd, to, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
IntJitType toType = type.legTypesLE().get(leg);
|
||||
if (leg >= legs.size()) {
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> em
|
||||
.emit(legs.getLast()::genLoadToStack, gen, toType, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
};
|
||||
}
|
||||
return em
|
||||
.emit(legs.get(leg)::genLoadToStack, gen, toType, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope, int slack) {
|
||||
return MpIntToMpInt.INSTANCE.convertOpndToArray(em, opnd, to, ext, scope, slack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
var result = em
|
||||
.emit(legs.get(0)::genLoadToStack, gen, IntJitType.I4, Ext.ZERO);
|
||||
for (JvmLocal<TInt, IntJitType> leg : legs) {
|
||||
result = result
|
||||
.emit(leg::genLoadToStack, gen, IntJitType.I4, Ext.ZERO)
|
||||
.emit(Op::ior);
|
||||
}
|
||||
return result.emit(Opnd::intToBool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a JVM int from the stack into the given local
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack, having the int on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the int on the stack
|
||||
* @param local the local to receive the value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the int
|
||||
*/
|
||||
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenStoreInt(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, IntJitType type, JvmLocal<TInt, IntJitType> local, Ext ext,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(local::genStoreFromStack, gen, type, ext, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to compute the sign of the int on the stack, and store that int into a given
|
||||
* local.
|
||||
* <p>
|
||||
* The int is copied and stored into the given local. Then, the sign of the int is computed and
|
||||
* remains on the stack. Signed extension is assumed.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack, having the int on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the int on the stack. Note that this type determines the
|
||||
* position of the sign bit.
|
||||
* @param local the local to receive the value
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the int and pushed
|
||||
* the sign
|
||||
*/
|
||||
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
doGenStoreIntAndSign(Emitter<N0> em, JitCodeGenerator<?> gen, IntJitType type,
|
||||
JvmLocal<TInt, IntJitType> local, Scope scope) {
|
||||
return em
|
||||
.emit(Op::dup)
|
||||
.emit(this::doGenStoreInt, gen, type, local, Ext.SIGN, scope)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a JVM long from the stack into two given locals
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack, having the long on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the int on the stack
|
||||
* @param lower the local to receive the lower 32 bits of the value
|
||||
* @param upper the local to receive the upper 32 bits of the value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the long
|
||||
*/
|
||||
protected <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<N1> doGenStoreLong(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, LongJitType type,
|
||||
JvmLocal<TInt, IntJitType> lower, JvmLocal<TInt, IntJitType> upper, Ext ext,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(lower::genStoreFromStack, gen, type, ext, scope)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(upper::genStoreFromStack, gen,
|
||||
LongJitType.forSize(type.size() - Integer.BYTES), ext, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to compute the sign of the long on the stack, and store that long into two
|
||||
* given locals.
|
||||
* <p>
|
||||
* The long is copied and stored into the given local. Then, the sign of the long is computed
|
||||
* and remains on the stack as an int. Signed extension is assumed.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack, having the long on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the long on the stack. Note that this type determines the
|
||||
* position of the sign bit.
|
||||
* @param lower the local to receive the lower 32 bits of the value
|
||||
* @param upper the local to receive the upper 32 bits of the value
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the long and pushed
|
||||
* the sign
|
||||
*/
|
||||
protected <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TInt>>
|
||||
doGenStoreLongAndSign(Emitter<N0> em, JitCodeGenerator<?> gen, LongJitType type,
|
||||
JvmLocal<TInt, IntJitType> lower, JvmLocal<TInt, IntJitType> upper,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(lower::genStoreFromStack, gen, type, Ext.SIGN, scope)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(Opnd::convert, type, IntJitType.I4, Ext.SIGN)
|
||||
.emit(Op::dup)
|
||||
.emit(upper::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to zero fill the given locals
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param locals the locals to zero fill
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
protected <N extends Next> Emitter<N> doGenZeroFill(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
List<JvmLocal<TInt, IntJitType>> locals, Scope scope) {
|
||||
for (JvmLocal<TInt, IntJitType> local : locals) {
|
||||
em = em
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(local::genStoreFromStack, gen, IntJitType.I4, Ext.ZERO, scope);
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to sign fill the given locals
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the sign int on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param locals the locals to sign fill
|
||||
* @param scope a scope for generating local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the sign
|
||||
*/
|
||||
protected <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenSignFill(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, List<JvmLocal<TInt, IntJitType>> locals, Scope scope) {
|
||||
for (JvmLocal<TInt, IntJitType> local : locals.subList(0, locals.size() - 1)) {
|
||||
em = em
|
||||
.emit(Op::dup)
|
||||
.emit(local::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
|
||||
}
|
||||
JvmLocal<TInt, IntJitType> last = locals.getLast();
|
||||
return em
|
||||
.emit(last::genStoreFromStack, gen, IntJitType.I4, Ext.SIGN, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT from, Ext ext, Scope scope) {
|
||||
return switch (from) {
|
||||
case IntJitType t when legs.size() == 1 -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreInt, gen, t, legs.get(0), ext, scope);
|
||||
case IntJitType t -> switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreInt, gen, t, legs.get(0), ext, scope)
|
||||
.emit(this::doGenZeroFill, gen, legs.subList(1, legs.size()), scope);
|
||||
case SIGN -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreIntAndSign, gen, t, legs.get(0), scope)
|
||||
.emit(this::doGenSignFill, gen, legs.subList(1, legs.size()), scope);
|
||||
};
|
||||
case LongJitType t when legs.size() == 1 -> em
|
||||
.emit(legs.get(0)::genStoreFromStack, gen, from, ext, scope);
|
||||
case LongJitType t when legs.size() == 2 -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreLong, gen, t, legs.get(0), legs.get(1), ext, scope);
|
||||
case LongJitType t -> switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreLong, gen, t, legs.get(0), legs.get(1), ext, scope)
|
||||
.emit(this::doGenZeroFill, gen, legs.subList(2, legs.size()), scope);
|
||||
case SIGN -> em
|
||||
.emit(Opnd::castStack1, from, t)
|
||||
.emit(this::doGenStoreLongAndSign, gen, t, legs.get(0), legs.get(1), scope)
|
||||
.emit(this::doGenSignFill, gen, legs.subList(2, legs.size()), scope);
|
||||
};
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to extend the value stored in our legs.
|
||||
*
|
||||
* @param <N> the tail of the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param defLegs the number of legs having the input value
|
||||
* @param legsOut the number of legs to receive the output value. If this is less than or equal
|
||||
* to {@code defLegs}, there is no extension to apply, so no code is emitted.
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
protected <N extends Next> Emitter<N> genExt(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
int defLegs, int legsOut, Ext ext, Scope scope) {
|
||||
if (legsOut <= defLegs) {
|
||||
return em;
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> doGenZeroFill(em, gen, legs.subList(defLegs, legsOut), scope);
|
||||
case SIGN -> em
|
||||
.emit(Op::iload, legs.get(defLegs - 1).local())
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr) // Signed
|
||||
.emit(this::doGenSignFill, gen, legs.subList(defLegs, legsOut), scope);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> from, Ext ext, Scope scope) {
|
||||
List<SimpleOpnd<TInt, IntJitType>> fromLegs = from.type().castLegsLE(from);
|
||||
List<SimpleOpnd<TInt, IntJitType>> toLegs = opnd.type().castLegsLE(opnd);
|
||||
int legsIn = fromLegs.size();
|
||||
int legsOut = toLegs.size();
|
||||
int defLegs = Integer.min(legsIn, legsOut);
|
||||
|
||||
for (int i = 0; i < defLegs; i++) {
|
||||
SimpleOpnd<TInt, IntJitType> fromLeg = fromLegs.get(i);
|
||||
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(i);
|
||||
em = em
|
||||
.emit(fromLeg::read)
|
||||
.emit(Opnd::convertIntToInt, fromLeg.type(), toLeg.type(), ext)
|
||||
.emit(toLeg::writeDirect);
|
||||
}
|
||||
return genExt(em, gen, defLegs, legsOut, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType from, Ext ext, Scope scope) {
|
||||
List<IntJitType> fromLegTypes = from.legTypesLE();
|
||||
List<SimpleOpnd<TInt, IntJitType>> toLegs = opnd.type().castLegsLE(opnd);
|
||||
int legsIn = fromLegTypes.size();
|
||||
int legsOut = toLegs.size();
|
||||
int defLegs = Integer.min(legsIn, legsOut);
|
||||
|
||||
for (int i = 0; i < defLegs - 1; i++) {
|
||||
IntJitType fromLegType = fromLegTypes.get(i);
|
||||
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(i);
|
||||
em = em
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, i)
|
||||
.emit(Op::iaload)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLeg.type(), ext)
|
||||
.emit(toLeg::writeDirect);
|
||||
}
|
||||
IntJitType fromLegType = fromLegTypes.get(defLegs - 1);
|
||||
SimpleOpnd<TInt, IntJitType> toLeg = toLegs.get(defLegs - 1);
|
||||
return em
|
||||
.emit(Op::ldc__i, defLegs - 1)
|
||||
.emit(Op::iaload)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLeg.type(), ext)
|
||||
.emit(toLeg::writeDirect)
|
||||
.emit(this::genExt, gen, defLegs, legsOut, ext, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility for implementing {@link #subpiece(Endian, int, int)}, also used by
|
||||
* {@link ShiftedMpIntHandler}.
|
||||
*
|
||||
* @param endian the endianness of the emulation target. Technically, this is only used in the
|
||||
* naming of any temporary local variables.
|
||||
* @param vn the varnode of the original handler
|
||||
* @param parts the parts (perhaps aligned to the legs) of the original handler
|
||||
* @param curShift if shifted, the number of bytes. If aligned, 0.
|
||||
* @param addShift the offset (in bytes) of the subpiece, i.e., additional shift
|
||||
* @param maxByteSize the size in bytes of the output operand, which indicate the maximum size
|
||||
* of the resulting handler's varnode.
|
||||
* @return the resulting handler
|
||||
*/
|
||||
static VarHandler subHandler(Endian endian, Varnode vn, List<JvmLocal<TInt, IntJitType>> parts,
|
||||
int curShift, int addShift, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, addShift, maxByteSize);
|
||||
int totalShift = curShift + addShift;
|
||||
int firstPart = totalShift / Integer.BYTES;
|
||||
int lastPartExcl = (totalShift + subVn.getSize() + Integer.BYTES - 1) / Integer.BYTES;
|
||||
List<JvmLocal<TInt, IntJitType>> subParts = parts.subList(firstPart, lastPartExcl);
|
||||
int subShift = totalShift % Integer.BYTES;
|
||||
|
||||
if (subParts.size() == 1) {
|
||||
IntJitType subType = IntJitType.forSize(subVn.getSize());
|
||||
if (subShift == 0) {
|
||||
return new IntVarAlloc(subParts.getFirst(), subType);
|
||||
}
|
||||
return new IntInIntHandler(subParts.getFirst(), subType, subVn, subShift);
|
||||
}
|
||||
MpIntJitType subType = MpIntJitType.forSize(subVn.getSize());
|
||||
if (subShift == 0) {
|
||||
return new AlignedMpIntHandler(subParts, subType, subVn);
|
||||
}
|
||||
return new ShiftedMpIntHandler(subParts, subType, subVn, subShift);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
return subHandler(endian, vn, legs, 0, byteOffset, maxByteSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.MpToStackConv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code double}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record DoubleVarAlloc(JvmLocal<TDouble, DoubleJitType> local, DoubleJitType type)
|
||||
implements SimpleVarHandler<TDouble, DoubleJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC2(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TDouble, DoubleJitType> getConvToStack() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Op::ldc__d, 0.0)
|
||||
.emit(Op::dcmpl); // So long as lsb is set, it's true
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
throw new AssertionError("Who's subpiecing a double?");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.MpToStackConv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code float}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record FloatVarAlloc(JvmLocal<TFloat, FloatJitType> local, FloatJitType type)
|
||||
implements SimpleVarHandler<TFloat, FloatJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC1(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TFloat, FloatJitType> getConvToStack() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Op::ldc__f, 0.0f)
|
||||
.emit(Op::fcmpl); // So long as lsb is set, it's true
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
throw new AssertionError("Who's subpiecing a float?");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for an {@link IntJitType int} p-code variable stored in part of a JVM {@code int}.
|
||||
*
|
||||
* @param local see {@link #local()}
|
||||
* @param type see {@link #type()}
|
||||
* @param vn see {@link #vn()} (wrt. the sub variable)
|
||||
* @param byteShift see {@link #byteShift()}
|
||||
*/
|
||||
public record IntInIntHandler(JvmLocal<TInt, IntJitType> local, IntJitType type, Varnode vn,
|
||||
int byteShift) implements SubVarHandler<TInt, IntJitType, TInt, IntJitType> {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public IntInIntHandler {
|
||||
assertShiftFits(byteShift, type, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToSub() {
|
||||
return MpIntToInt.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(Op::iload, local.local())
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::iushr)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
return genLoadToStack(em, gen, type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> {
|
||||
int msb = (this.type.size() + byteShift) * Byte.SIZE;
|
||||
if (msb == Integer.SIZE) {
|
||||
yield em
|
||||
.emit(Op::iload, local.local())
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
yield em
|
||||
.emit(Op::iload, local.local())
|
||||
.emit(Op::ldc__i, Integer.SIZE - msb)
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
int mask = intMask();
|
||||
return em
|
||||
.emit(Op::iload, local.local())
|
||||
.emit(Op::ldc__i, mask)
|
||||
.emit(Op::iand)
|
||||
.emit(Opnd::intToBool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
int mask = intMask();
|
||||
return em
|
||||
.emit(Opnd::convert, type, local.type(), ext)
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ldc__i, mask)
|
||||
.emit(Op::iand)
|
||||
.emit(Op::iload, local.local())
|
||||
.emit(Op::ldc__i, ~mask)
|
||||
.emit(Op::iand)
|
||||
.emit(Op::ior)
|
||||
.emit(Op::istore, local.local());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
|
||||
return new IntInIntHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for an {@link IntJitType int} p-code variable stored in part of a JVM {@code long}.
|
||||
*
|
||||
* @param local see {@link #local()}
|
||||
* @param type see {@link #type()}
|
||||
* @param vn see {@link #vn()} (wrt. the sub variable)
|
||||
* @param byteShift see {@link #byteShift()}
|
||||
*/
|
||||
public record IntInLongHandler(JvmLocal<TLong, LongJitType> local, IntJitType type, Varnode vn,
|
||||
int byteShift) implements SubInLongHandler<TInt, IntJitType> {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public IntInLongHandler {
|
||||
assertShiftFits(byteShift, type, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToSub() {
|
||||
return MpIntToInt.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lushr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
return genLoadToStack(em, gen, type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> {
|
||||
int msb = (this.type.size() + byteShift) * Byte.SIZE;
|
||||
if (msb > Integer.SIZE) {
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, msb - Integer.SIZE)
|
||||
.emit(Op::lshr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
if (msb == Integer.SIZE) {
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
yield em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - msb)
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code int}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record IntVarAlloc(JvmLocal<TInt, IntJitType> local, IntJitType type)
|
||||
implements SimpleVarHandler<TInt, IntJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC1(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return IntToMpInt.INSTANCE.doConvert(em, local.opnd(), local.name(), type, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TInt, IntJitType> getConvToStack() {
|
||||
return MpIntToInt.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Opnd::intToBool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
|
||||
Math.min(type.size(), maxByteSize));
|
||||
if (byteOffset == 0) {
|
||||
return new IntVarAlloc(local, IntJitType.forSize(subVn.getSize()));
|
||||
}
|
||||
return new IntInIntHandler(local, IntJitType.forSize(subVn.getSize()), subVn, byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An allocated JVM local
|
||||
*
|
||||
* @param <T> the JVM type of this local
|
||||
* @param <JT> the p-code type of this local
|
||||
* @param local the declared local this wraps
|
||||
* @param type a type for this local
|
||||
* @param vn the varnode whose value this local holds
|
||||
* @param opnd this local as an operand
|
||||
*/
|
||||
public record JvmLocal<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>(Local<T> local,
|
||||
JT type, Varnode vn, SimpleOpnd<T, JT> opnd) {
|
||||
|
||||
/**
|
||||
* Create a {@link JvmLocal} with the given local, type, and varnode
|
||||
*
|
||||
* @param <T> the JVM type of the local
|
||||
* @param <JT> the p-code type of the local
|
||||
* @param local the local
|
||||
* @param type the p-code type of the local
|
||||
* @param vn the varnode to assign to the local
|
||||
* @return the new local (wrapper)
|
||||
*/
|
||||
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> JvmLocal<T, JT>
|
||||
of(Local<T> local, JT type, Varnode vn) {
|
||||
SimpleOpnd<T, JT> opnd = SimpleOpnd.of(type, local);
|
||||
return new JvmLocal<>(local, type, vn, opnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the wrapped local
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String name() {
|
||||
return local.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast this local to satisfy checkers when a type variable is known to be of a given type
|
||||
* <p>
|
||||
* This will verify at runtime that the types are in fact identical.
|
||||
*
|
||||
* @param <TT> the "to" JVM type
|
||||
* @param <TJT> the "to" p-code type
|
||||
* @param type the "to" p-code type
|
||||
* @return this local as the given type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>> JvmLocal<TT, TJT>
|
||||
castOf(TJT type) {
|
||||
if (this.type != type) {
|
||||
throw new ClassCastException(
|
||||
"JvmLocal is not of the given type: this is %s. Requested is %s."
|
||||
.formatted(this.type, type));
|
||||
}
|
||||
return (JvmLocal<TT, TJT>) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode into the class constructor needed to access the varnode's actual value from the
|
||||
* underlying {@link PcodeExecutorState}.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <N extends Next> Emitter<N> genInit(Emitter<N> em, JitCodeGenerator<?> gen) {
|
||||
return VarGen.genVarnodeInit(em, gen, vn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load this local's value onto the JVM stack as the given type
|
||||
*
|
||||
* @param <TT> the desired JVM type
|
||||
* @param <TJT> the desired p-code type
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the desired p-code type
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(opnd::read)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store the value on the JVM stack into the local
|
||||
*
|
||||
* @param <FT> the JVM type of the value on the stack
|
||||
* @param <FJT> the p-code type of the value on the stack
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the value on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(Opnd::convert, type, this.type, ext)
|
||||
.emit(opnd::writeDirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to bring this varnode into scope.
|
||||
*
|
||||
* <p>
|
||||
* This will copy the value from the {@link JitBytesPcodeExecutorState state} into the local
|
||||
* variable.
|
||||
*
|
||||
* @param <THIS> the type of the compiled passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genBirthCode(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(VarGen::genReadValDirectToStack, localThis, gen, type, vn)
|
||||
.emit(opnd::writeDirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to take this varnode out of scope.
|
||||
*
|
||||
* <p>
|
||||
* This will copy the value from the local variable into the {@link JitBytesPcodeExecutorState
|
||||
* state}.
|
||||
*
|
||||
* @param <THIS> the type of the compiled passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genRetireCode(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(opnd::read)
|
||||
.emit(VarGen::genWriteValDirectFromStack, localThis, gen, type, vn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the maximum address that would be occupied by the full primitive type}
|
||||
*/
|
||||
public Address maxPrimAddr() {
|
||||
return vn.getAddress().add(type.ext().size() - 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for an {@link LongJitType long} p-code variable stored in part of a JVM {@code long}.
|
||||
*
|
||||
* @param local see {@link #local()}
|
||||
* @param type see {@link #type()}
|
||||
* @param vn see {@link #vn()} (wrt. the sub variable)
|
||||
* @param byteShift see {@link #byteShift()}
|
||||
*/
|
||||
public record LongInLongHandler(JvmLocal<TLong, LongJitType> local, LongJitType type, Varnode vn,
|
||||
int byteShift) implements SubInLongHandler<TLong, LongJitType> {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public LongInLongHandler {
|
||||
assertShiftFits(byteShift, type, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToSub() {
|
||||
return MpIntToLong.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return em
|
||||
.emit(Op::lload, local.local())
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lushr)
|
||||
.emit(Opnd::convert, this.type, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
|
||||
vn, 0, Integer.BYTES);
|
||||
return new IntInLongHandler(local, IntJitType.I4, subVn, byteShift)
|
||||
.genLoadLegToStack(em, gen, type, leg, ext);
|
||||
}
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(gen.getAnalysisContext().getEndian(),
|
||||
vn, Integer.BYTES, vn.getSize() - Integer.BYTES);
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + Integer.BYTES).genLoadLegToStack(em, gen, type, leg - 1, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, vn, byteOffset, maxByteSize);
|
||||
if (subVn.getSize() <= Integer.BYTES) {
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
|
||||
byteShift + byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler for a p-code variable allocated in one JVM {@code long}.
|
||||
*
|
||||
* @param local the JVM local
|
||||
* @param type the p-code type
|
||||
*/
|
||||
public record LongVarAlloc(JvmLocal<TLong, LongJitType> local, LongJitType type)
|
||||
implements SimpleVarHandler<TLong, LongJitType> {
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
return genLoadLegToStackC2(em, gen, type, leg, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpToStackConv<TInt, IntJitType, MpIntJitType, TLong, LongJitType> getConvToStack() {
|
||||
return MpIntToLong.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type, Ext.ZERO)
|
||||
.emit(Op::ldc__l, 0)
|
||||
.emit(Op::lcmp); // Outputs -1, 0, or 1. So long as lsb is set, it's true.
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
Varnode subVn = JitDataFlowArithmetic.subPieceVn(endian, local.vn(), byteOffset,
|
||||
Math.min(type.size(), maxByteSize));
|
||||
if (byteOffset == 0) {
|
||||
return new LongVarAlloc(local, LongJitType.forSize(subVn.getSize()));
|
||||
}
|
||||
if (subVn.getSize() <= Integer.BYTES) {
|
||||
return new IntInLongHandler(local, IntJitType.forSize(subVn.getSize()), subVn,
|
||||
byteOffset);
|
||||
}
|
||||
return new LongInLongHandler(local, LongJitType.forSize(subVn.getSize()), subVn,
|
||||
byteOffset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A dummy handler for values/variables that are not allocated in JVM locals
|
||||
* <p>
|
||||
* Every operation on this handler will throw an exception at code generation time.
|
||||
*/
|
||||
public enum NoHandler implements VarHandler {
|
||||
/** Singleton */
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Varnode vn() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType type() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd.SimpleOpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The handler used for a varnode requiring allocation of multiple integers, where those integers
|
||||
* <em>do not</em> align to the variable's legs.
|
||||
* <p>
|
||||
* The below diagram is an example shifted allocation, whose {@code byteShift} value is 3, and whose
|
||||
* varnode size is 11 (admittedly pathological, but made to illustrate a complicated example).
|
||||
*
|
||||
* <pre>
|
||||
* +--*--*--*--+--*--*--*--+--*--*--*--+--*--*--*--+
|
||||
* | parts[3] | parts[2] | parts[1] | parts[0] |
|
||||
* +-----------+-----------+-----------+-----------+
|
||||
* +--*--*--+--*--*--*--+--*--*--*--+
|
||||
* | leg2 | leg1 | leg0 |
|
||||
* +--------+-----------+-----------+
|
||||
* </pre>
|
||||
* <p>
|
||||
* In the unaligned case, all loads and stores require copying the shifted value into a series of
|
||||
* temporary locals, representing the legs of the value. Because these are already temporary, the
|
||||
* operator may freely use the legs as temporary storage.
|
||||
*
|
||||
* @param parts the list of locals spanned by the variable, in little-endian order.
|
||||
* @param type the type of the multi-precision integer variable (only considering the varnode, not
|
||||
* the whole comprised of the spanned parts). In the diagram, this would be
|
||||
* {@link MpIntJitType}{@code (size=11)}.
|
||||
* @param vn the complete varnode accessible to this handler. NOTE: The handler must take care not
|
||||
* to modify or permit access to portions of the parts at either end not actually part of
|
||||
* its varnode. In the example, the lower 24 bits of {@code parts[0]} and the upper 16
|
||||
* bits of {@code parts[3]} cannot be accessed. Should a caller to
|
||||
* {@link #genLoadToOpnd(Emitter, JitCodeGenerator, MpIntJitType, Ext, Scope)} specify a
|
||||
* type larger than 11 bytes, only the 11-byte value is loaded, then extended to the
|
||||
* requested size. We do not load the more sigificant portion of {@code parts[3]}.
|
||||
* @param byteShift the number of least-significant bytes of the handler's least-significant part
|
||||
* that are <em>excluded</em> from the variable's least-significant leg. I.e., the number
|
||||
* of bytes to shift right when loading the value. In the example, this is 3.
|
||||
*/
|
||||
public record ShiftedMpIntHandler(List<JvmLocal<TInt, IntJitType>> parts, MpIntJitType type,
|
||||
Varnode vn, int byteShift) implements VarHandler {
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public ShiftedMpIntHandler {
|
||||
assert byteShift > 0 && byteShift < 4;
|
||||
assert parts.size() > 1;
|
||||
}
|
||||
|
||||
private int bitShift() {
|
||||
return byteShift * Byte.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the right portion of a given leg
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param i the index of the part, 0 being the least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the portion pushed onto it
|
||||
* positioned in an int
|
||||
*/
|
||||
private <N extends Next> Emitter<Ent<N, TInt>> doGenLoadIntRight(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, int i, Ext ext) {
|
||||
return em
|
||||
.emit(parts.get(i)::genLoadToStack, gen, IntJitType.I4, ext)
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::iushr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load and {@code or} in the left portion of a given leg
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the right portion of the same leg already on it
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param i the index of the part, 0 being the least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the incoming stack, though the top value has been modified.
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
doGenLoadOrIntLeft(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
|
||||
if (i >= parts.size()) {
|
||||
// TODO: This may require attention to ext
|
||||
return Misc.cast1(em);
|
||||
}
|
||||
return em
|
||||
.emit(parts.get(i)::genLoadToStack, gen, IntJitType.I4, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE - bitShift())
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ior);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the right portion of a long
|
||||
* <p>
|
||||
* This operates as if the long is positioned at the same byte offset as the least-sigificant
|
||||
* leg. Adapting the example from the class documentation:
|
||||
*
|
||||
* <pre>
|
||||
* +--*--*--*--+--*--*--*--+--*--*--*--+--*--*--*--+
|
||||
* | parts[3] | parts[2] | parts[1] | parts[0] |
|
||||
* +-----------+-----------+-----------+-----------+
|
||||
* +--*--*--+--*--*--*--+--*--*--*--+
|
||||
* | leg2 | leg1 | leg0 |
|
||||
* +--------+-----------+-----------+
|
||||
* +-----------------------+
|
||||
* | as long |
|
||||
* +-----------------------+
|
||||
* </pre>
|
||||
* <p>
|
||||
* Thus, to load the full long, we need to retrieve and shift into place the values from
|
||||
* {@code parts[0]}, {@code [1]}, and {@code [2]}. This method loads the right-most portion.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param i the index of the part, 0 being the least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the portion pushed onto it
|
||||
* positioned in a long
|
||||
*/
|
||||
private <N extends Next> Emitter<Ent<N, TLong>> doGenLoadLongRight(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, int i, Ext ext) {
|
||||
return em
|
||||
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lushr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load and {@code or} in the middle portion of a long
|
||||
*
|
||||
* @see #doGenLoadLongRight(Emitter, JitCodeGenerator, int, Ext)
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the right portion of the long already on it
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param i the index of the part, 0 being the least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the incoming stack, though the top value has been modified.
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
doGenLoadOrLongMiddle(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
|
||||
return em
|
||||
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE - bitShift())
|
||||
.emit(Op::lshl)
|
||||
.emit(Op::lor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load and {@code or} in the left portion of a long
|
||||
*
|
||||
* @see #doGenLoadLongRight(Emitter, JitCodeGenerator, int, Ext)
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the right and middle portions of the long already
|
||||
* {@code or}ed together on it
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param i the index of the part, 0 being least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the incoming stack, though the top value has been modified.
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
doGenLoadOrLongLeft(Emitter<N0> em, JitCodeGenerator<?> gen, int i, Ext ext) {
|
||||
return em
|
||||
.emit(parts.get(i)::genLoadToStack, gen, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE * 2 - bitShift())
|
||||
.emit(Op::lshl)
|
||||
.emit(Op::lor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return switch (type) {
|
||||
case IntJitType t when t.size() + byteShift <= Integer.BYTES -> em
|
||||
// We know at most 4 bytes (1 part) are involved
|
||||
.emit(this::doGenLoadIntRight, gen, 0, ext)
|
||||
.emit(Opnd::convert, IntJitType.forSize(Integer.BYTES - byteShift), type, ext);
|
||||
case IntJitType t -> em
|
||||
.emit(this::doGenLoadIntRight, gen, 0, ext)
|
||||
.emit(this::doGenLoadOrIntLeft, gen, 1, ext)
|
||||
.emit(Opnd::convert, IntJitType.I4, type, ext);
|
||||
case LongJitType t when t.size() + byteShift <= Long.BYTES -> em
|
||||
// We know at most 8 bytes (2 parts) are involved
|
||||
.emit(this::doGenLoadLongRight, gen, 0, ext)
|
||||
.emit(this::doGenLoadOrLongMiddle, gen, 1, ext)
|
||||
.emit(Opnd::convert, LongJitType.forSize(Long.BYTES - byteShift), type, ext);
|
||||
case LongJitType t -> em
|
||||
.emit(this::doGenLoadLongRight, gen, 0, ext)
|
||||
.emit(this::doGenLoadOrLongMiddle, gen, 1, ext)
|
||||
.emit(this::doGenLoadOrLongLeft, gen, 2, ext)
|
||||
.emit(Opnd::convert, LongJitType.I8, type, ext);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope) {
|
||||
/**
|
||||
* NOTE: Even though we can access more significant parts, we should not incorporate
|
||||
* anything beyond what is allowed by the type (which corresponds to the varnode size).
|
||||
*/
|
||||
List<IntJitType> fromLegTypes = type.legTypesLE();
|
||||
List<IntJitType> toLegTypes = to.legTypesLE();
|
||||
List<SimpleOpnd<TInt, IntJitType>> toLegs = new ArrayList<>();
|
||||
int legsOut = toLegTypes.size();
|
||||
int legsIn = fromLegTypes.size();
|
||||
int defLegs = Integer.min(legsIn, legsOut);
|
||||
|
||||
for (int i = 0; i < defLegs; i++) {
|
||||
IntJitType fromLegType = fromLegTypes.get(i);
|
||||
IntJitType toLegType = toLegTypes.get(i);
|
||||
var result = em
|
||||
.emit(this::doGenLoadIntRight, gen, i, ext)
|
||||
.emit(this::doGenLoadOrIntLeft, gen, i + 1, ext)
|
||||
// This chained convert should blot out anything outside the varnode
|
||||
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
|
||||
.emit(Opnd::createInt, toLegType, "%s_leg%d".formatted(name(), i), scope);
|
||||
em = result.em();
|
||||
toLegs.add(result.opnd());
|
||||
}
|
||||
if (legsOut > defLegs) {
|
||||
var sign = switch (ext) {
|
||||
case ZERO -> new SimpleOpndEm<>(IntConstOpnd.ZERO_I4, em);
|
||||
case SIGN -> em
|
||||
.emit(toLegs.getLast()::read)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr) // Signed
|
||||
.emit(Opnd::createIntReadOnly, IntJitType.I4,
|
||||
"%s_sign".formatted(name()), scope);
|
||||
};
|
||||
em = sign.em();
|
||||
for (int i = defLegs; i < legsOut; i++) {
|
||||
toLegs.add(sign.opnd());
|
||||
}
|
||||
}
|
||||
return new OpndEm<>(MpIntLocalOpnd.of(to, name(), toLegs), em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
List<IntJitType> fromLegTypes = type.legTypesLE();
|
||||
int fromLegCount = fromLegTypes.size();
|
||||
if (leg >= fromLegCount) {
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> em
|
||||
.emit(this::doGenLoadIntRight, gen, fromLegCount - 1, ext) // Remove ???
|
||||
.emit(this::doGenLoadOrIntLeft, gen, fromLegCount, ext)
|
||||
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegTypes.getLast(), ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
};
|
||||
}
|
||||
IntJitType fromLegType = fromLegTypes.get(leg);
|
||||
IntJitType toLegType = type.legTypesLE().get(leg);
|
||||
return em
|
||||
.emit(this::doGenLoadIntRight, gen, leg, ext)
|
||||
.emit(this::doGenLoadOrIntLeft, gen, leg + 1, ext)
|
||||
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType to, Ext ext, Scope scope, int slack) {
|
||||
List<IntJitType> fromLegTypes = type.legTypesLE();
|
||||
List<IntJitType> toLegTypes = to.legTypesLE();
|
||||
int legsOut = toLegTypes.size();
|
||||
int legsIn = fromLegTypes.size();
|
||||
int defLegs = Integer.min(legsIn, legsOut);
|
||||
|
||||
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR, name());
|
||||
em = em
|
||||
.emit(Op::ldc__i, legsOut + slack)
|
||||
.emit(Op::newarray, Types.T_INT)
|
||||
.emit(Op::astore, arr);
|
||||
|
||||
for (int i = 0; i < defLegs; i++) {
|
||||
IntJitType fromLegType = fromLegTypes.get(i);
|
||||
IntJitType toLegType = toLegTypes.get(i);
|
||||
em = em
|
||||
.emit(Op::aload, arr)
|
||||
.emit(Op::ldc__i, i)
|
||||
.emit(this::doGenLoadIntRight, gen, i, ext)
|
||||
.emit(this::doGenLoadOrIntLeft, gen, i + 1, ext)
|
||||
.emit(Opnd::convertIntToInt, IntJitType.I4, fromLegType, ext)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
|
||||
.emit(Op::iastore);
|
||||
}
|
||||
return em
|
||||
.emit(MpIntToMpInt::doGenArrExt, arr, legsOut, defLegs, ext, scope)
|
||||
.emit(Op::aload, arr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
int maskRight = -1 << bitShift();
|
||||
var result = em
|
||||
.emit(Op::iload, parts.get(0).local())
|
||||
.emit(Op::ldc__i, maskRight)
|
||||
.emit(Op::iand);
|
||||
for (int i = 0; i < parts.size(); i++) {
|
||||
int bytesLeft = type.size() - byteShift - i * Integer.SIZE;
|
||||
var twoInts = result
|
||||
.emit(Op::iload, parts.get(i).local());
|
||||
if (bytesLeft < Integer.SIZE) {
|
||||
int maskLeft = -1 >>> (Integer.SIZE - bytesLeft);
|
||||
twoInts = twoInts
|
||||
.emit(Op::ldc__i, maskLeft)
|
||||
.emit(Op::iand);
|
||||
}
|
||||
result = twoInts
|
||||
.emit(Op::ior);
|
||||
}
|
||||
return result.emit(Opnd::intToBool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store from the stack into a given part
|
||||
* <p>
|
||||
* This will combined the existing value and the positioned value using a mask of the accessible
|
||||
* portion of the given part. This code will compute that mask and emit the bytecode to apply
|
||||
* it.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the value on top, positioned in an int
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param i the index of the part, 0 being the least significant
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1> doGenStoreInt(Emitter<N0> em,
|
||||
int i) {
|
||||
int bitShift = byteShift * Byte.SIZE;
|
||||
int mask = -1;
|
||||
if (i == 0) {
|
||||
mask &= -1 << bitShift;
|
||||
}
|
||||
// bytes we'd exceed to the left
|
||||
int bytesToRight = Integer.BYTES * (i + 1) - type.size() - byteShift;
|
||||
if (bytesToRight > 0) {
|
||||
mask &= -1 >>> (bytesToRight * Byte.SIZE);
|
||||
}
|
||||
JvmLocal<TInt, IntJitType> part = parts.get(i);
|
||||
assert mask != 0;
|
||||
return mask == -1
|
||||
? em
|
||||
.emit(Op::istore, part.local())
|
||||
: em
|
||||
.emit(Op::ldc__i, mask)
|
||||
.emit(Op::iand)
|
||||
.emit(Op::iload, part.local())
|
||||
.emit(Op::ldc__i, ~mask)
|
||||
.emit(Op::iand)
|
||||
.emit(Op::ior)
|
||||
.emit(Op::istore, part.local());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT from, Ext ext, Scope scope) {
|
||||
int bitShift = byteShift * Byte.SIZE;
|
||||
return switch (from) {
|
||||
case IntJitType t -> {
|
||||
var emConvPos = em
|
||||
.emit(Opnd::convert, from, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, bitShift)
|
||||
.emit(Op::lshl);
|
||||
for (int i = 0; i < parts.size() - 1; i++) {
|
||||
emConvPos = emConvPos
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, i)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Opnd::lextshr, ext);
|
||||
}
|
||||
yield emConvPos
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, parts.size() - 1);
|
||||
}
|
||||
case LongJitType t -> {
|
||||
var emConvPos = em
|
||||
.emit(Opnd::convert, from, LongJitType.I8, ext)
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, bitShift)
|
||||
.emit(Op::ishl)
|
||||
.emit(this::doGenStoreInt, 0)
|
||||
.emit(Op::ldc__i, Integer.SIZE - bitShift)
|
||||
.emit(Opnd::lextshr, ext);
|
||||
for (int i = 1; i < parts.size() - 1; i++) {
|
||||
emConvPos = emConvPos
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, i)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Opnd::lextshr, ext);
|
||||
}
|
||||
yield emConvPos
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, parts.size() - 1);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load a leg from the source operand and position it within a long on the
|
||||
* stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param leg the leg to load
|
||||
* @param bitShift the number of bits to shift left
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the long
|
||||
*/
|
||||
private <N extends Next> Emitter<Ent<N, TLong>> positionOpndLeg(Emitter<N> em,
|
||||
SimpleOpnd<TInt, IntJitType> leg, int bitShift, Ext ext) {
|
||||
return em
|
||||
.emit(leg::read)
|
||||
.emit(Opnd::convert, leg.type(), LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, bitShift)
|
||||
.emit(Op::lshl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a leg into its two overlapped parts, then load the next leg from the
|
||||
* source positioning it and the remainder into the long on the top of the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param fromLegs the list of source legs in little-endian order
|
||||
* @param i the index of the part to store into, 0 being the least significant
|
||||
* @param bitShift the number of bits to shift the next leg left
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the incoming stack, though the top value has been modified
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
storePartAndPositionNextOpndLeg(Emitter<N0> em,
|
||||
List<SimpleOpnd<TInt, IntJitType>> fromLegs, int i, int bitShift, Ext ext) {
|
||||
var emStored = em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, i);
|
||||
return i + 1 < fromLegs.size()
|
||||
? emStored
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(this::positionOpndLeg, fromLegs.get(i + 1), bitShift, ext)
|
||||
.emit(Op::lor)
|
||||
: emStored
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Opnd::lextshr, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The general strategy is to load the source operand one leg at a time. In order to put each
|
||||
* leg with the remaining portion of the previous leg in position, we use a long on the stack as
|
||||
* a temporary. This eases "gluing" the legs together and then writing the shifted portion into
|
||||
* each part.
|
||||
*/
|
||||
@Override
|
||||
public <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
List<SimpleOpnd<TInt, IntJitType>> fromLegs = opnd.type().castLegsLE(opnd);
|
||||
int bitShift = byteShift * Byte.SIZE;
|
||||
var emConvPos = em.emit(this::positionOpndLeg, fromLegs.get(0), bitShift, ext);
|
||||
for (int i = 0; i < parts.size() - 1; i++) {
|
||||
emConvPos =
|
||||
emConvPos.emit(this::storePartAndPositionNextOpndLeg, fromLegs, i, bitShift, ext);
|
||||
}
|
||||
return emConvPos
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, parts.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The analog to {@link #positionOpndLeg(Emitter, SimpleOpnd, int, Ext)}, but for a source value
|
||||
* in an array.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param arr a handle to the local holding the source array reference
|
||||
* @param legType the type of the leg being loaded
|
||||
* @param bitShift the number of bits to shift left
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the long
|
||||
*/
|
||||
private <N extends Next> Emitter<Ent<N, TLong>> positionArrLeg(Emitter<N> em,
|
||||
Local<TRef<int[]>> arr, IntJitType legType, int bitShift, Ext ext) {
|
||||
return em
|
||||
.emit(Op::aload, arr)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::iaload)
|
||||
.emit(Opnd::convert, legType, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, bitShift)
|
||||
.emit(Op::lshl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The analog to {@link #storePartAndPositionNextOpndLeg(Emitter, List, int, int, Ext)}, but for
|
||||
* a source value in an array.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param arr a handle to the local holding the source array reference
|
||||
* @param fromLegTypes the list of leg types in the source value, in little-endian order
|
||||
* @param i the index of the part to store into, 0 being the least significant
|
||||
* @param bitShift the number of bits to shift the next leg left
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the incoming stack, though the top value has been modified
|
||||
*/
|
||||
private <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
storePartAndPositionNextArrLeg(Emitter<N0> em, Local<TRef<int[]>> arr,
|
||||
List<IntJitType> fromLegTypes, int i, int bitShift, Ext ext) {
|
||||
var emStored = em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, i);
|
||||
return i + 1 < fromLegTypes.size()
|
||||
? emStored
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(this::positionArrLeg, arr, fromLegTypes.get(i + 1), bitShift, ext)
|
||||
.emit(Op::lor)
|
||||
: emStored
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Opnd::lextshr, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The strategy here is the same as for
|
||||
* {@link #genStoreFromOpnd(Emitter, JitCodeGenerator, Opnd, Ext, Scope)}
|
||||
*/
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType from, Ext ext, Scope scope) {
|
||||
try (SubScope ss = scope.sub()) {
|
||||
Local<TRef<int[]>> arr = ss.decl(Types.T_INT_ARR, "temp_arr");
|
||||
List<IntJitType> fromLegTypes = from.legTypesLE();
|
||||
int bitShift = byteShift + Byte.SIZE;
|
||||
var emConvPos = em
|
||||
.emit(Op::astore, arr)
|
||||
.emit(this::positionArrLeg, arr, fromLegTypes.get(0), bitShift, ext);
|
||||
for (int i = 0; i < parts.size() - 1; i++) {
|
||||
emConvPos = emConvPos.emit(this::storePartAndPositionNextArrLeg, arr, fromLegTypes,
|
||||
i, bitShift, ext);
|
||||
}
|
||||
return emConvPos
|
||||
.emit(Op::l2i)
|
||||
.emit(this::doGenStoreInt, parts.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize) {
|
||||
return AlignedMpIntHandler.subHandler(endian, vn, parts, byteShift, byteOffset,
|
||||
maxByteSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A handler for p-code variables composed of a single JVM local variable.
|
||||
*
|
||||
* @param <T> the JVM type of the variable
|
||||
* @param <JT> the p-code type of the variable
|
||||
*/
|
||||
public interface SimpleVarHandler<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
|
||||
extends VarHandler {
|
||||
/**
|
||||
* Get the local variable into which this p-code variable is allocated
|
||||
*
|
||||
* @return the local
|
||||
*/
|
||||
JvmLocal<T, JT> local();
|
||||
|
||||
@Override
|
||||
default Varnode vn() {
|
||||
return local().vn();
|
||||
}
|
||||
|
||||
@Override
|
||||
JT type();
|
||||
|
||||
@Override
|
||||
default <TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next>
|
||||
Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext) {
|
||||
return local().genLoadToStack(em, gen, type, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type(), ext)
|
||||
.emit(Opnd::convertToOpnd, type(), local().name(), type, ext, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* This provides the implementation of
|
||||
* {@link #genLoadLegToStack(Emitter, JitCodeGenerator, MpIntJitType, int, Ext)} for category-1
|
||||
* primitives, i.e., {@code int} and {@code float}.
|
||||
* <p>
|
||||
* Only leg 0 is meaningful for a category-1 primitive. Any other leg is just the extension of
|
||||
* the one leg.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param leg the index of the leg to load, 0 being least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
|
||||
*/
|
||||
default <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStackC1(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> {
|
||||
IntJitType intType = IntJitType.forSize(type().size());
|
||||
yield em
|
||||
.emit(this::genLoadToStack, gen, intType, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE - intType.size() * Byte.SIZE)
|
||||
.emit(Op::ishl)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This provides the implementation of
|
||||
* {@link #genLoadLegToStack(Emitter, JitCodeGenerator, MpIntJitType, int, Ext)} for category-2
|
||||
* primitives, i.e., {@code long} and {@code double}.
|
||||
* <p>
|
||||
* Only legs 0 and 1 are meaningful for a category-2 primitive. Any other leg is just the
|
||||
* extension of the upper leg.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param leg the index of the leg to load, 0 being least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
|
||||
*/
|
||||
default <N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStackC2(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, int leg, Ext ext) {
|
||||
if (leg == 0) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
if (leg == 1) {
|
||||
LongJitType longType = LongJitType.forSize(type().size());
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, longType, ext)
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lshr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Opnd::convertIntToInt, IntJitType.forSize(type().size() - Integer.BYTES),
|
||||
type.legTypesLE().get(leg), ext);
|
||||
}
|
||||
return switch (ext) {
|
||||
case ZERO -> em
|
||||
.emit(Op::ldc__i, 0);
|
||||
case SIGN -> {
|
||||
LongJitType longType = LongJitType.forSize(type().size());
|
||||
yield em
|
||||
.emit(this::genLoadToStack, gen, longType, ext)
|
||||
.emit(Op::ldc__i, longType.size() * Byte.SIZE - Integer.SIZE)
|
||||
.emit(Op::lshr)
|
||||
.emit(Op::l2i)
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::ishr); // FIXME: Is size conversion required here?
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, type(), ext)
|
||||
.emit(Opnd::convertToArray, type(), local().name(), type, ext, scope, slack);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
return local().genStoreFromStack(em, gen, type, ext, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the converter of multi-precision integers to the stack type of this handler's local.
|
||||
* <p>
|
||||
* Note that the converter need only extract the least 1 or 2 legs of the source multi-precision
|
||||
* int, depending on the category of the destination's type. The converter knows how to handle
|
||||
* both the operand (series of locals) and array forms.
|
||||
*
|
||||
* @return the converter
|
||||
*/
|
||||
MpToStackConv<TInt, IntJitType, MpIntJitType, T, JT> getConvToStack();
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToStack()::convertOpndToStack, opnd, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToStack()::convertArrayToStack, type, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
|
||||
/**
|
||||
* A handler for a p-code variable stored in part of a JVM {@code long}.
|
||||
*
|
||||
* @param <ST> the JVM type of the sub variable
|
||||
* @param <SJT> the p-code type of the sub variable
|
||||
*/
|
||||
public interface SubInLongHandler<ST extends BPrim<?>, SJT extends SimpleJitType<ST, SJT>>
|
||||
extends SubVarHandler<ST, SJT, TLong, LongJitType> {
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
long mask = longMask();
|
||||
return em
|
||||
.emit(Op::lload, local().local())
|
||||
.emit(Op::ldc__l, mask)
|
||||
.emit(Op::land)
|
||||
.emit(Op::ldc__l, 0)
|
||||
.emit(Op::lcmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope) {
|
||||
long mask = longMask();
|
||||
return em
|
||||
.emit(Opnd::convert, type, LongJitType.I8, ext)
|
||||
.emit(Op::ldc__i, bitShift())
|
||||
.emit(Op::lshl)
|
||||
.emit(Op::ldc__l, mask)
|
||||
.emit(Op::land)
|
||||
.emit(Op::lload, local().local())
|
||||
.emit(Op::ldc__l, ~mask)
|
||||
.emit(Op::land)
|
||||
.emit(Op::lor)
|
||||
.emit(Op::lstore, local().local());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
|
||||
/**
|
||||
* A handler to p-code variables stored in just a portion of a single JVM local variable.
|
||||
*
|
||||
* @param <ST> the JVM type of the sub variable
|
||||
* @param <SJT> the p-code type of the sub variable
|
||||
* @param <WT> the JVM type of the containing variable
|
||||
* @param <WJT> the p-code type of the containing variable
|
||||
*/
|
||||
public interface SubVarHandler<ST extends BPrim<?>, SJT extends SimpleJitType<ST, SJT>,
|
||||
WT extends BPrim<?>, WJT extends SimpleJitType<WT, WJT>> extends VarHandler {
|
||||
|
||||
/**
|
||||
* Verify that the sub variable as shifted actually fits in the containing variable
|
||||
*
|
||||
* @param byteShift the number of unused bytes in the container variable to the right of the sub
|
||||
* variable
|
||||
* @param type the type of the sub variable
|
||||
* @param local the containing local variable
|
||||
*/
|
||||
default void assertShiftFits(int byteShift, SJT type, JvmLocal<WT, WJT> local) {
|
||||
assert byteShift >= 0 && byteShift + type.size() <= local.type().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the number of unused bytes in the container variable to the right of the sub
|
||||
* variable}
|
||||
*/
|
||||
int byteShift();
|
||||
|
||||
/**
|
||||
* {@return the number of bits in the sub variable}
|
||||
*/
|
||||
default int bitSize() {
|
||||
return type().size() * Byte.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the number of unused bits in the container variable to the right of the sub
|
||||
* variable}
|
||||
*/
|
||||
default int bitShift() {
|
||||
return byteShift() * Byte.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the mask indicating which parts of the {@code int} containing variable are within
|
||||
* the sub variable}
|
||||
*/
|
||||
default int intMask() {
|
||||
return (-1 >>> (Integer.SIZE - bitSize())) << bitShift();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the mask indicating which parts of the {@code long} containing variable are within
|
||||
* the sub variable}
|
||||
*/
|
||||
default long longMask() {
|
||||
return (-1L >>> (Long.SIZE - bitSize())) << bitShift();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return The containing local variable}
|
||||
*/
|
||||
JvmLocal<WT, WJT> local();
|
||||
|
||||
@Override
|
||||
SJT type();
|
||||
|
||||
/**
|
||||
* Get the converter of multi-precision integers to the type of the sub variable.
|
||||
* <p>
|
||||
* The converter need not worry about positioning or masking wrt. the sub variable. It should
|
||||
* extract from the multi-precision integer the minimum number of legs needed to fill the sub
|
||||
* variable, i.e., it need only consider the sub variable's size. This handler will then mask
|
||||
* and position it within the containing variable for storage.
|
||||
*
|
||||
* @return the converter
|
||||
*/
|
||||
MpToStackConv<TInt, IntJitType, MpIntJitType, ST, SJT> getConvToSub();
|
||||
|
||||
@Override
|
||||
default <N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, this.type(), ext)
|
||||
.emit(Opnd::convertToOpnd, this.type(), name(), type, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
return em
|
||||
.emit(this::genLoadToStack, gen, this.type(), ext)
|
||||
.emit(Opnd::convertToArray, this.type(), name(), type, ext, scope, slack);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToSub()::convertOpndToStack, opnd, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(
|
||||
Emitter<N0> em, JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope) {
|
||||
return em
|
||||
.emit(getConvToSub()::convertArrayToStack, type, this.type(), ext)
|
||||
.emit(this::genStoreFromStack, gen, this.type(), ext, scope);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.alloc;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.op.SubPieceOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Scope;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A handler that knows how to load and store variables' values from local storage.
|
||||
* <p>
|
||||
* Some variables are hosted in a single JVM local of compatible type. Others, notably
|
||||
* multi-precision integers, are allocated among two or more JVM local integers. Each such integer
|
||||
* is called a "leg" of the multi-precision integer. Other literature may call these "digits" (whose
|
||||
* value is in [0, 0xffffffff]). Depending on the operator implementation, value may need to be
|
||||
* loaded with alternative types or in different forms. e.g., any float operator will need to
|
||||
* convert its inputs into the appropriate float type, even if the operands were allocated as an int
|
||||
* type. Similarly, some operators are implement their multi-precision computations by invoking
|
||||
* static methods whose parameters are {@code int[]}, and so they will load and store the array
|
||||
* forms instead of accessing the legs' locals. This interface provides generators for the various
|
||||
* circumstances. Each subclass provides the implementations for various allocations.
|
||||
*/
|
||||
public interface VarHandler {
|
||||
|
||||
/**
|
||||
* Generate a name for the variable representing the given varnode
|
||||
* <p>
|
||||
* These are for debugging purposes. When dumping generating bytecode, the declared local
|
||||
* variables and their scopes are often also dumped. This provides a human with the local
|
||||
* variable index for various varnodes.
|
||||
*
|
||||
* @param vn the varnode
|
||||
* @return the name
|
||||
*/
|
||||
static String nameVn(Varnode vn) {
|
||||
return "var_%s_%x_%d".formatted(
|
||||
vn.getAddress().getAddressSpace().getName(),
|
||||
vn.getOffset(),
|
||||
vn.getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete varnode accessible to this handler
|
||||
*
|
||||
* @return the varnode
|
||||
*/
|
||||
Varnode vn();
|
||||
|
||||
/**
|
||||
* Get the name for this handler's local variable, named after the varnode is represents.
|
||||
*
|
||||
* @return the name of the local variable
|
||||
*/
|
||||
default String name() {
|
||||
return nameVn(vn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the p-code type of the local variable this handler uses.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
JitType type();
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value onto the JVM stack.
|
||||
*
|
||||
* @param <TT> the JVM type of the value to load onto the stack
|
||||
* @param <TJT> the p-code type of the value to load onto the stack
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
|
||||
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||
* @return the emitter typed with the resulting stack
|
||||
*/
|
||||
<TT extends BPrim<?>, TJT extends SimpleJitType<TT, TJT>, N extends Next> Emitter<Ent<N, TT>>
|
||||
genLoadToStack(Emitter<N> em, JitCodeGenerator<?> gen, TJT type, Ext ext);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into several locals.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the JVM stack by the proceeding bytecode
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the operand containing the locals, and the emitter typed with the incoming stack
|
||||
*/
|
||||
<N extends Next> OpndEm<MpIntJitType, N> genLoadToOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
MpIntJitType type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load one leg of a multi-precision value from the varnode onto the JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param leg the index of the leg to load, 0 being least significant
|
||||
* @param ext the kind of extension to apply
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TInt>> genLoadLegToStack(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
MpIntJitType type, int leg, Ext ext);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into an integer array in little-endian order,
|
||||
* pushing its ref onto the JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the complete multi-precision value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @param slack the number of additional, more significant, elements to allocate in the array
|
||||
* @return the emitter typed with the resulting stack, i.e., having the ref pushed onto it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TRef<int[]>>> genLoadToArray(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope, int slack);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value, interpreted as a boolean, as an integer onto the
|
||||
* JVM stack.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the resulting stack, i.e., having the int boolean pushed onto
|
||||
* it
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, TInt>> genLoadToBool(Emitter<N> em, JitCodeGenerator<?> gen);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a value into a variable from the JVM stack.
|
||||
*
|
||||
* @param <FT> the JVM type of the value on the stack
|
||||
* @param <FJT> the p-code type of the value on the stack
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the value to store on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
<FT extends BPrim<?>, FJT extends SimpleJitType<FT, FJT>, N1 extends Next,
|
||||
N0 extends Ent<N1, FT>> Emitter<N1> genStoreFromStack(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, FJT type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a varnode's value from several locals.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param opnd the operand whose locals contain the value to be stored
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
<N extends Next> Emitter<N> genStoreFromOpnd(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
Opnd<MpIntJitType> opnd, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a varnode's value from an array of integer legs, in little endian
|
||||
* order
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the array ref on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value on the stack
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the array
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1> genStoreFromArray(Emitter<N0> em,
|
||||
JitCodeGenerator<?> gen, MpIntJitType type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Create a handler for a {@link PcodeOp#SUBPIECE} of a value.
|
||||
* <p>
|
||||
* To implement {@link SubPieceOpGen subpiece}, we could load the entire varnode and then
|
||||
* extract the designated portion. Or, we could load only the designated portion, averting any
|
||||
* code and execution cost of loading the un-designated portions. We accomplish this by
|
||||
* re-writing the subpiece op and a load of the sub-varnode.
|
||||
*
|
||||
* @param endian the endianness of the emulation target
|
||||
* @param byteOffset the number of least-significant bytes to remove
|
||||
* @param maxByteSize the maximum size of the resulting variable. In general, a subpiece should
|
||||
* never exceed the size of the parent varnode, but if it does, this will truncate
|
||||
* that excess.
|
||||
* @return the resulting subpiece handler
|
||||
*/
|
||||
VarHandler subpiece(Endian endian, int byteOffset, int maxByteSize);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,13 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
return endian;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given number of bytes from the higher-offset end of the varnode
|
||||
*
|
||||
* @param vn the varnode
|
||||
* @param amt the number of bytes to remove
|
||||
* @return the resulting varnode
|
||||
*/
|
||||
public Varnode truncVnFromRight(Varnode vn, int amt) {
|
||||
return new Varnode(vn.getAddress(), vn.getSize() - amt);
|
||||
}
|
||||
@@ -102,6 +109,13 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
return subpiece(outVn, endian.isBigEndian() ? amt : 0, in1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given number of bytes from the lower-offset end of the varnode
|
||||
*
|
||||
* @param vn the varnode
|
||||
* @param amt the number of bytes to remove
|
||||
* @return the resulting varnode
|
||||
*/
|
||||
public Varnode truncVnFromLeft(Varnode vn, int amt) {
|
||||
return new Varnode(vn.getAddress().add(amt), vn.getSize() - amt);
|
||||
}
|
||||
@@ -228,11 +242,25 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
return dfm.notifyOp(new JitSynthSubPieceOp(out, offset, v)).out();
|
||||
}
|
||||
|
||||
private Varnode subPieceVn(int size, int offset, Varnode whole) {
|
||||
if (endian.isBigEndian()) {
|
||||
return new Varnode(whole.getAddress().add(whole.getSize() - offset - size), size);
|
||||
/**
|
||||
* Compute the varnode representing a {@link JitSubPieceOp subpiece} of the given varnode
|
||||
*
|
||||
* @param endian the endianness of the emulation target
|
||||
* @param whole the whole varnode
|
||||
* @param offset the number of least-significant bytes to remove
|
||||
* @param size the size of the subpiece (maximum, since truncation may occur)
|
||||
* @return the resulting subpiece.
|
||||
*/
|
||||
public static Varnode subPieceVn(Endian endian, Varnode whole, int offset, int size) {
|
||||
int minSize = Math.min(whole.getSize() - offset, size);
|
||||
if (minSize < 1) {
|
||||
throw new AssertionError("subpiece would have non-positive size");
|
||||
}
|
||||
return new Varnode(whole.getAddress().add(offset), size);
|
||||
int addrOffset = switch (endian) {
|
||||
case BIG -> whole.getSize() - offset - minSize;
|
||||
case LITTLE -> offset;
|
||||
};
|
||||
return new Varnode(whole.getAddress().add(addrOffset), minSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,7 +274,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
* @return the output
|
||||
*/
|
||||
public JitVal shaveFromRight(int amt, JitVal in1) {
|
||||
return subpiece(in1.size() - amt, amt, in1);
|
||||
return subpiece(in1, amt, in1.size() - amt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +288,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
* @return the output
|
||||
*/
|
||||
public JitVal shaveFromLeft(int amt, JitVal in1) {
|
||||
return subpiece(in1.size() - amt, 0, in1);
|
||||
return subpiece(in1, 0, in1.size() - amt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,14 +314,14 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
* @param v the input value
|
||||
* @return the output value
|
||||
*/
|
||||
public JitVal subpiece(int size, int offset, JitVal v) {
|
||||
public JitVal subpiece(JitVal v, int offset, int size) {
|
||||
if (v instanceof JitConstVal c) {
|
||||
return new JitConstVal(size,
|
||||
OB_SUBPIECE.evaluateBinary(size, v.size(), c.value(), BigInteger.valueOf(offset)));
|
||||
}
|
||||
if (v instanceof JitVarnodeVar vv) {
|
||||
Varnode inVn = vv.varnode();
|
||||
Varnode outVn = subPieceVn(size, offset, inVn);
|
||||
Varnode outVn = subPieceVn(endian, inVn, offset, size);
|
||||
return subpiece(outVn, offset, v);
|
||||
}
|
||||
throw new UnsupportedOperationException("unsupported subpiece of " + v);
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.analysis;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
|
||||
/**
|
||||
* The p-code type of an operand.
|
||||
@@ -35,6 +37,42 @@ import ghidra.lifecycle.Unfinished;
|
||||
*/
|
||||
public interface JitType {
|
||||
|
||||
/**
|
||||
* Get the smallest type to which both of the given types can be converted without loss.
|
||||
* <p>
|
||||
* When the given types are a mix of integral and floating-point, this chooses an integral type
|
||||
* whose size is the greater of the two.
|
||||
*
|
||||
* @param a the first type
|
||||
* @param b the second type
|
||||
* @return the uniform type
|
||||
*/
|
||||
static JitType unify(JitType a, JitType b) {
|
||||
if (a == b) {
|
||||
return a;
|
||||
}
|
||||
int size = Math.max(a.size(), b.size());
|
||||
return JitTypeBehavior.INTEGER.type(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #unify(JitType, JitType)}, except that it takes the lesser size.
|
||||
* <p>
|
||||
* This is used when culling of unnecessary loads is desired and loss of precision is
|
||||
* acceptable.
|
||||
*
|
||||
* @param a the first type
|
||||
* @param b the second type
|
||||
* @return the uniform type
|
||||
*/
|
||||
static JitType unifyLeast(JitType a, JitType b) {
|
||||
if (a == b) {
|
||||
return a;
|
||||
}
|
||||
int size = Math.min(a.size(), b.size());
|
||||
return JitTypeBehavior.INTEGER.type(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two types by preference. The type with the more preferred behavior then smaller size
|
||||
* is preferred.
|
||||
@@ -67,54 +105,88 @@ public interface JitType {
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
public static JitType forJavaType(Class<?> cls) {
|
||||
if (cls == boolean.class) {
|
||||
return IntJitType.I4;
|
||||
return SimpleJitType.forJavaType(cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* A type comprising of legs, each of simple type
|
||||
*
|
||||
* @param <T> the JVM type of each leg
|
||||
* @param <LT> the p-code type of each leg
|
||||
*/
|
||||
public interface LeggedJitType<T extends BPrim<?>, LT extends SimpleJitType<T, LT>>
|
||||
extends JitType {
|
||||
|
||||
@Override
|
||||
List<? extends LT> legTypesBE();
|
||||
|
||||
/**
|
||||
* Cast the given operand's legs as having this type's leg type.
|
||||
* <p>
|
||||
* This is (sadly) necessary because of the loss of type information in {@link Opnd} when it
|
||||
* has a legged type.
|
||||
*
|
||||
* @param opnd the operand whose legs to cast
|
||||
* @return the legs in little-endian order.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
default List<SimpleOpnd<T, LT>> castLegsLE(Opnd<? extends LeggedJitType<?, ?>> opnd) {
|
||||
return (List) opnd.legsLE();
|
||||
}
|
||||
if (cls == byte.class) {
|
||||
return IntJitType.I1;
|
||||
}
|
||||
if (cls == short.class) {
|
||||
return IntJitType.I2;
|
||||
}
|
||||
if (cls == int.class) {
|
||||
return IntJitType.I4;
|
||||
}
|
||||
if (cls == long.class) {
|
||||
return LongJitType.I8;
|
||||
}
|
||||
if (cls == float.class) {
|
||||
return FloatJitType.F4;
|
||||
}
|
||||
if (cls == double.class) {
|
||||
return DoubleJitType.F8;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
/**
|
||||
* A p-code type that can be represented in a single JVM variable.
|
||||
*
|
||||
* @param <T> the JVM type for this JIT type
|
||||
* @param <JT> this JIT type (recursive)
|
||||
*/
|
||||
public interface SimpleJitType extends JitType {
|
||||
public interface SimpleJitType<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
|
||||
extends LeggedJitType<T, JT> {
|
||||
|
||||
/**
|
||||
* Identify the p-code type that is exactly represented by the given JVM type.
|
||||
*
|
||||
* <p>
|
||||
* This is used during Direct userop invocation to convert the arguments and return value.
|
||||
*
|
||||
* @param cls the primitive class (not boxed)
|
||||
* @return the p-code type
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> JT forJavaType(
|
||||
Class<?> cls) {
|
||||
if (cls == boolean.class) {
|
||||
return (JT) IntJitType.I1;
|
||||
}
|
||||
if (cls == byte.class) {
|
||||
return (JT) IntJitType.I1;
|
||||
}
|
||||
if (cls == short.class) {
|
||||
return (JT) IntJitType.I2;
|
||||
}
|
||||
if (cls == int.class) {
|
||||
return (JT) IntJitType.I4;
|
||||
}
|
||||
if (cls == long.class) {
|
||||
return (JT) LongJitType.I8;
|
||||
}
|
||||
if (cls == float.class) {
|
||||
return (JT) FloatJitType.F4;
|
||||
}
|
||||
if (cls == double.class) {
|
||||
return (JT) DoubleJitType.F8;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM type of the variable that can represent a p-code variable of this type
|
||||
*
|
||||
* @return the primitive class (not boxed)
|
||||
* @return the primitive type (not boxed)
|
||||
*/
|
||||
Class<?> javaType();
|
||||
|
||||
/**
|
||||
* The JVM opcode to load a local variable of this type onto the stack
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int opcodeLoad();
|
||||
|
||||
/**
|
||||
* The JVM opcode to store a local variable of this type from the stack
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int opcodeStore();
|
||||
T bType();
|
||||
|
||||
/**
|
||||
* Re-apply the {@link JitTypeBehavior#INTEGER integer} behavior to this type
|
||||
@@ -125,10 +197,10 @@ public interface JitType {
|
||||
*
|
||||
* @return this type as an int
|
||||
*/
|
||||
SimpleJitType asInt();
|
||||
SimpleJitType<?, ?> asInt();
|
||||
|
||||
@Override
|
||||
SimpleJitType ext();
|
||||
SimpleJitType<T, JT> ext();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +208,7 @@ public interface JitType {
|
||||
*
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
public record IntJitType(int size) implements SimpleJitType {
|
||||
public record IntJitType(int size) implements SimpleJitType<TInt, IntJitType> {
|
||||
/** {@code int1}: a 1-byte integer */
|
||||
public static final IntJitType I1 = new IntJitType(1);
|
||||
/** {@code int2}: a 2-byte integer */
|
||||
@@ -183,18 +255,8 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> javaType() {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeLoad() {
|
||||
return ILOAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeStore() {
|
||||
return ISTORE;
|
||||
public TInt bType() {
|
||||
return Types.T_INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,7 +270,12 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntJitType> legTypes() {
|
||||
public List<IntJitType> legTypesBE() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntJitType> legTypesLE() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
@@ -218,7 +285,7 @@ public interface JitType {
|
||||
*
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
public record LongJitType(int size) implements SimpleJitType {
|
||||
public record LongJitType(int size) implements SimpleJitType<TLong, LongJitType> {
|
||||
/** {@code int5}: a 5-byte integer */
|
||||
public static final LongJitType I5 = new LongJitType(5);
|
||||
/** {@code int6}: a 6-byte integer */
|
||||
@@ -228,6 +295,12 @@ public interface JitType {
|
||||
/** {@code int8}: a 8-byte integer */
|
||||
public static final LongJitType I8 = new LongJitType(8);
|
||||
|
||||
// These are needed only as intermediates during conversion
|
||||
public static final LongJitType I1 = new LongJitType(1);
|
||||
public static final LongJitType I2 = new LongJitType(2);
|
||||
public static final LongJitType I3 = new LongJitType(3);
|
||||
public static final LongJitType I4 = new LongJitType(4);
|
||||
|
||||
/**
|
||||
* Get the type for an integer of the given size 5 through 8
|
||||
*
|
||||
@@ -241,6 +314,11 @@ public interface JitType {
|
||||
case 6 -> I6;
|
||||
case 7 -> I7;
|
||||
case 8 -> I8;
|
||||
// For intermediate conversion only
|
||||
case 1 -> I1;
|
||||
case 2 -> I2;
|
||||
case 3 -> I3;
|
||||
case 4 -> I4;
|
||||
default -> throw new IllegalArgumentException("size:" + size);
|
||||
};
|
||||
}
|
||||
@@ -265,18 +343,8 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> javaType() {
|
||||
return long.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeLoad() {
|
||||
return LLOAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeStore() {
|
||||
return LSTORE;
|
||||
public TLong bType() {
|
||||
return Types.T_LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -290,7 +358,12 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LongJitType> legTypes() {
|
||||
public List<LongJitType> legTypesBE() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LongJitType> legTypesLE() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
@@ -298,7 +371,7 @@ public interface JitType {
|
||||
/**
|
||||
* The p-code type for floating-point of size 4, i.e., that fits in a JVM float.
|
||||
*/
|
||||
public enum FloatJitType implements SimpleJitType {
|
||||
public enum FloatJitType implements SimpleJitType<TFloat, FloatJitType> {
|
||||
/** {@code float4}: a 4-byte float */
|
||||
F4;
|
||||
|
||||
@@ -318,18 +391,8 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> javaType() {
|
||||
return float.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeLoad() {
|
||||
return FLOAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeStore() {
|
||||
return FSTORE;
|
||||
public TFloat bType() {
|
||||
return Types.T_FLOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -343,7 +406,12 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatJitType> legTypes() {
|
||||
public List<FloatJitType> legTypesBE() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatJitType> legTypesLE() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
@@ -351,7 +419,7 @@ public interface JitType {
|
||||
/**
|
||||
* The p-code type for floating-point of size 8, i.e., that fits in a JVM double.
|
||||
*/
|
||||
public enum DoubleJitType implements SimpleJitType {
|
||||
public enum DoubleJitType implements SimpleJitType<TDouble, DoubleJitType> {
|
||||
/** {@code float8}: a 8-byte float */
|
||||
F8;
|
||||
|
||||
@@ -371,18 +439,8 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> javaType() {
|
||||
return double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeLoad() {
|
||||
return DLOAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int opcodeStore() {
|
||||
return DSTORE;
|
||||
public TDouble bType() {
|
||||
return Types.T_DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -396,7 +454,12 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DoubleJitType> legTypes() {
|
||||
public List<DoubleJitType> legTypesBE() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DoubleJitType> legTypesLE() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
@@ -410,10 +473,33 @@ public interface JitType {
|
||||
* no matter the language endianness.
|
||||
*
|
||||
* @param size the size in bytes
|
||||
* @param legTypesBE the type of each leg, in big-endian order
|
||||
* @param legTypesLE the type of each leg, in little-endian order
|
||||
*/
|
||||
public record MpIntJitType(int size) implements JitType {
|
||||
public record MpIntJitType(int size, List<IntJitType> legTypesBE, List<IntJitType> legTypesLE)
|
||||
implements LeggedJitType<TInt, IntJitType> {
|
||||
private static final Map<Integer, MpIntJitType> FOR_SIZES = new HashMap<>();
|
||||
|
||||
private static int legsAlloc(int size) {
|
||||
return (size + Integer.BYTES - 1) / Integer.BYTES;
|
||||
}
|
||||
|
||||
private static int partialSize(int size) {
|
||||
return size % Integer.BYTES;
|
||||
}
|
||||
|
||||
private static List<IntJitType> computeLegTypesBE(int size) {
|
||||
IntJitType[] types = new IntJitType[legsAlloc(size)];
|
||||
int i = 0;
|
||||
if (partialSize(size) != 0) {
|
||||
types[i++] = IntJitType.forSize(partialSize(size));
|
||||
}
|
||||
for (; i < types.length; i++) {
|
||||
types[i] = IntJitType.I4;
|
||||
}
|
||||
return Arrays.asList(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type for an integer of the given size 9 or greater
|
||||
*
|
||||
@@ -425,6 +511,14 @@ public interface JitType {
|
||||
return FOR_SIZES.computeIfAbsent(size, MpIntJitType::new);
|
||||
}
|
||||
|
||||
private MpIntJitType(int size, List<IntJitType> legTypesBE) {
|
||||
this(size, legTypesBE, legTypesBE.reversed());
|
||||
}
|
||||
|
||||
private MpIntJitType(int size) {
|
||||
this(size, computeLegTypesBE(size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int pref() {
|
||||
return 4;
|
||||
@@ -441,7 +535,7 @@ public interface JitType {
|
||||
* @return the total number of legs
|
||||
*/
|
||||
public int legsAlloc() {
|
||||
return (size + Integer.BYTES - 1) / Integer.BYTES;
|
||||
return legsAlloc(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,20 +553,7 @@ public interface JitType {
|
||||
* @return the number of bytes in the partial leg, or 0 if all legs are whole
|
||||
*/
|
||||
public int partialSize() {
|
||||
return size % Integer.BYTES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntJitType> legTypes() {
|
||||
IntJitType[] types = new IntJitType[legsAlloc()];
|
||||
int i = 0;
|
||||
if (partialSize() != 0) {
|
||||
types[i++] = IntJitType.forSize(partialSize());
|
||||
}
|
||||
for (; i < types.length; i++) {
|
||||
types[i] = IntJitType.I4;
|
||||
}
|
||||
return Arrays.asList(types);
|
||||
return partialSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -486,7 +567,7 @@ public interface JitType {
|
||||
*
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
public record MpFloatJitType(int size) implements JitType {
|
||||
public record MpFloatJitType(int size) implements LeggedJitType<TInt, IntJitType> {
|
||||
private static final Map<Integer, MpFloatJitType> FOR_SIZES = new HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -516,7 +597,12 @@ public interface JitType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SimpleJitType> legTypes() {
|
||||
public List<IntJitType> legTypesBE() {
|
||||
return Unfinished.TODO("MpFloat");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntJitType> legTypesLE() {
|
||||
return Unfinished.TODO("MpFloat");
|
||||
}
|
||||
}
|
||||
@@ -563,5 +649,16 @@ public interface JitType {
|
||||
*
|
||||
* @return the list of types, each fitting in a JVM int, in big-endian order.
|
||||
*/
|
||||
List<? extends SimpleJitType> legTypes();
|
||||
List<? extends SimpleJitType<?, ?>> legTypesBE();
|
||||
|
||||
/**
|
||||
* Get the p-code type that describes the part of the variable in each leg
|
||||
*
|
||||
* <p>
|
||||
* Each whole leg will have the type {@link IntJitType#I4}, and the partial leg, if applicable,
|
||||
* will have its appropriate smaller integer type.
|
||||
*
|
||||
* @return the list of types, each fitting in a JVM int, in little-endian order.
|
||||
*/
|
||||
List<? extends SimpleJitType<?, ?>> legTypesLE();
|
||||
}
|
||||
|
||||
@@ -69,9 +69,8 @@ import ghidra.program.model.pcode.PcodeOp;
|
||||
* 16-byte quadruple-precision, and 32-byte octuple-precision. Some p-code types map precisely to
|
||||
* JVM counterparts: The 4- and 8-byte integer types map precisely to the JVM's {@code int} and
|
||||
* {@code long} types. Similarly, the 4- and 8-byte float types map precisely to {@code float} and
|
||||
* {@code double}. <b>TODO</b>: The JIT translator does not currently support integral types greater
|
||||
* than 8 bytes (64 bits) in size nor floating-point types other than 4 and 8 bytes (single and
|
||||
* double precision) in size.
|
||||
* {@code double}. <b>TODO</b>: The JIT translator does not currently support floating-point types
|
||||
* other than 4 and 8 bytes (single and double precision) in size.
|
||||
*
|
||||
* <h3>Signedness</h3>
|
||||
* <p>
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.ATHROW;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
/**
|
||||
@@ -42,9 +41,9 @@ import ghidra.program.model.pcode.PcodeOp;
|
||||
*
|
||||
* @param op the op which may cause an exception
|
||||
* @param block the block containing the op
|
||||
* @param label the label at the start of the handler
|
||||
* @param lbl the label at the start of the handler
|
||||
*/
|
||||
public record ExceptionHandler(PcodeOp op, JitBlock block, Label label) {
|
||||
public record ExceptionHandler(PcodeOp op, JitBlock block, Lbl<Ent<Bot, TRef<Throwable>>> lbl) {
|
||||
/**
|
||||
* Construct a handler, generating a new label
|
||||
*
|
||||
@@ -52,23 +51,24 @@ public record ExceptionHandler(PcodeOp op, JitBlock block, Label label) {
|
||||
* @param block the block containing the op
|
||||
*/
|
||||
public ExceptionHandler(PcodeOp op, JitBlock block) {
|
||||
this(op, block, new Label());
|
||||
this(op, block, Lbl.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the handler's code into the {@link JitCompiledPassage#run(int) run} method.
|
||||
*
|
||||
* @param <THIS> the type of the compiled passage
|
||||
* @param em the dead emitter
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
* @return the dead emitter
|
||||
*/
|
||||
public void generateRunCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
rv.visitLabel(label);
|
||||
// [exc]
|
||||
gen.generatePassageExit(block, () -> {
|
||||
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
|
||||
}, gen.getExitContext(op), rv);
|
||||
// [exc]
|
||||
rv.visitInsn(ATHROW);
|
||||
// []
|
||||
public <THIS extends JitCompiledPassage> Emitter<Dead> genRun(Emitter<Dead> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Lbl::placeDead, lbl)
|
||||
.emit(gen::genExit, localThis, block, PcGen.loadOffset(gen.getAddressForOp(op)),
|
||||
gen.getExitContext(op))
|
||||
.emit(Op::athrow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,18 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
@@ -35,7 +38,7 @@ import ghidra.program.model.address.Address;
|
||||
*
|
||||
* @param address the address contained by the page to pre-fetch
|
||||
*/
|
||||
public record FieldForArrDirect(Address address) implements InstanceFieldReq {
|
||||
public record FieldForArrDirect(Address address) implements InstanceFieldReq<TRef<byte[]>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "arrDir_%s_%x".formatted(address.getAddressSpace().getName(),
|
||||
@@ -61,29 +64,27 @@ public record FieldForArrDirect(Address address) implements InstanceFieldReq {
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
|
||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_BYTE_ARR, null, null);
|
||||
|
||||
// [...]
|
||||
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||
// [...,this]
|
||||
gen.generateLoadJitStateSpace(address.getAddressSpace(), iv);
|
||||
// [...,jitspace]
|
||||
iv.visitLdcInsn(address.getOffset());
|
||||
// [...,arr]
|
||||
iv.visitMethodInsn(INVOKEVIRTUAL, NAME_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE,
|
||||
"getDirect", MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT, false);
|
||||
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_BYTE_ARR);
|
||||
// [...]
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, Types.T_BYTE_ARR, name());
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(gen::genLoadJitStateSpace, localThis, address.getAddressSpace())
|
||||
.emit(Op::ldc__l, address.getOffset())
|
||||
.emit(Op::invokevirtual, GenConsts.T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE,
|
||||
"getDirect", GenConsts.MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT,
|
||||
false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putfield, gen.typeThis, name(), Types.T_BYTE_ARR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
// [...]
|
||||
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||
// [...,this]
|
||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
||||
TDESC_BYTE_ARR);
|
||||
// [...,arr]
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<byte[]>>>
|
||||
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::getfield, gen.typeThis, name(), Types.T_BYTE_ARR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,20 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
|
||||
/**
|
||||
* A field request for pre-constructed contextreg value
|
||||
*/
|
||||
record FieldForContext(RegisterValue ctx) implements StaticFieldReq {
|
||||
record FieldForContext(RegisterValue ctx) implements StaticFieldReq<TRef<RegisterValue>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "CTX_%s".formatted(ctx.getUnsignedValue().toString(16));
|
||||
@@ -45,33 +49,28 @@ record FieldForContext(RegisterValue ctx) implements StaticFieldReq {
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv) {
|
||||
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
ClassVisitor cv) {
|
||||
if (ctx == null) {
|
||||
return;
|
||||
return em;
|
||||
}
|
||||
cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, name(), TDESC_REGISTER_VALUE, null,
|
||||
null);
|
||||
|
||||
// []
|
||||
sv.visitFieldInsn(GETSTATIC, gen.nameThis, "LANGUAGE", TDESC_LANGUAGE);
|
||||
// [language]
|
||||
sv.visitLdcInsn(ctx.getUnsignedValue().toString(16));
|
||||
// [language,ctx:STR]
|
||||
sv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "createContext",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT, true);
|
||||
// [ctx:RV]
|
||||
sv.visitFieldInsn(PUTSTATIC, gen.nameThis, name(), TDESC_REGISTER_VALUE);
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, T_REGISTER_VALUE, name());
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, "LANGUAGE", T_LANGUAGE)
|
||||
.emit(Op::ldc__a, ctx.getUnsignedValue().toString(16))
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createContext",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putstatic, gen.typeThis, name(), T_REGISTER_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
// [...]
|
||||
if (ctx == null) {
|
||||
rv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
else {
|
||||
rv.visitFieldInsn(GETSTATIC, gen.nameThis, name(), TDESC_REGISTER_VALUE);
|
||||
}
|
||||
// [...,ctx]
|
||||
public <N extends Next> Emitter<Ent<N, TRef<RegisterValue>>> genLoad(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return ctx == null
|
||||
? em.emit(Op::aconst_null, T_REGISTER_VALUE)
|
||||
: em.emit(Op::getstatic, gen.typeThis, name(), T_REGISTER_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,22 @@
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
||||
import ghidra.pcode.emu.jit.JitPassage.ExtBranch;
|
||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
|
||||
/**
|
||||
@@ -44,7 +47,7 @@ import ghidra.program.model.lang.RegisterValue;
|
||||
*
|
||||
* @param target the target address-contextreg pair of the branch exiting via this slot
|
||||
*/
|
||||
public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
|
||||
public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq<TRef<ExitSlot>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "exit_%x_%s".formatted(target.address.getOffset(), target.biCtx.toString(16));
|
||||
@@ -72,32 +75,29 @@ public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
|
||||
* as needed.
|
||||
*/
|
||||
@Override
|
||||
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
|
||||
FieldForContext ctxField = gen.requestStaticFieldForContext(target.rvCtx);
|
||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_EXIT_SLOT, null, null);
|
||||
|
||||
// []
|
||||
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||
// [this]
|
||||
iv.visitInsn(DUP);
|
||||
// [this,this]
|
||||
iv.visitLdcInsn(target.address.getOffset());
|
||||
// [this,this,target:LONG]
|
||||
ctxField.generateLoadCode(gen, iv);
|
||||
// [this,this,target:LONG,ctx:RV]
|
||||
iv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "createExitSlot",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT, true);
|
||||
// [this,slot]
|
||||
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
|
||||
// []
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_EXIT_SLOT, name());
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__l, target.address.getOffset())
|
||||
.emit(ctxField::genLoad, gen)
|
||||
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "createExitSlot",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putfield, gen.typeThis, name(), T_EXIT_SLOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
// []
|
||||
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||
// [this]
|
||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
|
||||
// [slot]
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<ExitSlot>>>
|
||||
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::getfield, gen.typeThis, name(), T_EXIT_SLOT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,18 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE;
|
||||
import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
@@ -36,7 +39,8 @@ import ghidra.program.model.address.AddressSpace;
|
||||
*
|
||||
* @param space the address space of the state space to pre-fetch
|
||||
*/
|
||||
public record FieldForSpaceIndirect(AddressSpace space) implements InstanceFieldReq {
|
||||
public record FieldForSpaceIndirect(AddressSpace space)
|
||||
implements InstanceFieldReq<TRef<JitBytesPcodeExecutorStateSpace>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "spaceInd_" + space.getName();
|
||||
@@ -60,27 +64,21 @@ public record FieldForSpaceIndirect(AddressSpace space) implements InstanceField
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
|
||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(),
|
||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, null, null);
|
||||
|
||||
// [...]
|
||||
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||
// [...,this]
|
||||
gen.generateLoadJitStateSpace(space, iv);
|
||||
// [...,this,jitspace]
|
||||
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(),
|
||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
||||
// [...]
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, name());
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(gen::genLoadJitStateSpace, localThis, space)
|
||||
.emit(Op::putfield, gen.typeThis, name(), T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
// [...]
|
||||
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||
// [...,this]
|
||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
||||
// [...,jitspace]
|
||||
public <THIS extends JitCompiledPassage, N extends Next>
|
||||
Emitter<Ent<N, TRef<JitBytesPcodeExecutorStateSpace>>>
|
||||
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::getfield, gen.typeThis, name(), T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
|
||||
/**
|
||||
@@ -36,7 +39,8 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
* @param userop the definition to pre-fetch
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
public record FieldForUserop(PcodeUseropDefinition<?> userop) implements InstanceFieldReq {
|
||||
public record FieldForUserop(PcodeUseropDefinition<?> userop)
|
||||
implements InstanceFieldReq<TRef<PcodeUseropDefinition<?>>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "userop_" + userop.getName();
|
||||
@@ -60,30 +64,27 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop) implements Instanc
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateInitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor iv) {
|
||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_PCODE_USEROP_DEFINITION, null,
|
||||
null);
|
||||
|
||||
// []
|
||||
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||
// [this]
|
||||
iv.visitInsn(DUP);
|
||||
// [this,this]
|
||||
iv.visitLdcInsn(userop.getName());
|
||||
// [this,this,name]
|
||||
iv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "getUseropDefinition",
|
||||
MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION, true);
|
||||
// [this,userop]
|
||||
iv.visitFieldInsn(PUTFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
|
||||
// []
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genInit(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_FINAL, T_PCODE_USEROP_DEFINITION, name());
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__a, userop.getName())
|
||||
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "getUseropDefinition",
|
||||
MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
// []
|
||||
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||
// [this]
|
||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
|
||||
// [userop]
|
||||
public <THIS extends JitCompiledPassage, N extends Next>
|
||||
Emitter<Ent<N, TRef<PcodeUseropDefinition<?>>>>
|
||||
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::getfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
@@ -37,7 +41,7 @@ import ghidra.program.model.pcode.Varnode;
|
||||
* @param vn the varnode to pre-construct
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
public record FieldForVarnode(Varnode vn) implements StaticFieldReq<TRef<Varnode>> {
|
||||
@Override
|
||||
public String name() {
|
||||
Address addr = vn.getAddress();
|
||||
@@ -56,16 +60,22 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv) {
|
||||
cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, name(), TDESC_VARNODE, null, null);
|
||||
|
||||
sv.visitFieldInsn(GETSTATIC, gen.nameThis, "ADDRESS_FACTORY", TDESC_ADDRESS_FACTORY);
|
||||
sv.visitLdcInsn(vn.getAddress().getAddressSpace().getName());
|
||||
sv.visitLdcInsn(vn.getAddress().getOffset());
|
||||
sv.visitLdcInsn(vn.getSize());
|
||||
sv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "createVarnode",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true);
|
||||
sv.visitFieldInsn(PUTSTATIC, gen.nameThis, name(), TDESC_VARNODE);
|
||||
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, T_VARNODE, name());
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, "ADDRESS_FACTORY", T_ADDRESS_FACTORY)
|
||||
.emit(Op::ldc__a, vn.getAddress().getAddressSpace().getName())
|
||||
.emit(Op::ldc__l, vn.getAddress().getOffset())
|
||||
.emit(Op::ldc__i, vn.getSize())
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createVarnode",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putstatic, gen.typeThis, name(), T_VARNODE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +89,9 @@ public record FieldForVarnode(Varnode vn) implements StaticFieldReq {
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||
rv.visitFieldInsn(GETSTATIC, gen.nameThis, name(), TDESC_VARNODE);
|
||||
public <N extends Next> Emitter<Ent<N, TRef<Varnode>>> genLoad(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, name(), T_VARNODE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
|
||||
|
||||
/**
|
||||
* A field request for a pre-fetched or pre-constructed element
|
||||
*
|
||||
* @param <T> the type of the field
|
||||
*/
|
||||
public interface FieldReq {
|
||||
public interface FieldReq<T extends BNonVoid> {
|
||||
/**
|
||||
* Derive a suitable name for the field
|
||||
*
|
||||
@@ -30,12 +30,4 @@ public interface FieldReq {
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Emit code to load the field onto the JVM stack
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param rv the visitor often for the {@link JitCompiledPassage#run(int) run} method, but could
|
||||
* be the static initializer or constructor
|
||||
*/
|
||||
void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv);
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.reflect.TypeLiteral;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
@@ -28,6 +28,11 @@ import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.MthDesc;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.error.LowlevelError;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
@@ -44,201 +49,374 @@ import ghidra.program.model.pcode.Varnode;
|
||||
public interface GenConsts {
|
||||
public static final int BLOCK_SIZE = SemisparseByteArray.BLOCK_SIZE;
|
||||
|
||||
public static final String TDESC_ADDRESS = Type.getDescriptor(Address.class);
|
||||
public static final String TDESC_ADDRESS_FACTORY = Type.getDescriptor(AddressFactory.class);
|
||||
public static final String TDESC_ADDRESS_SPACE = Type.getDescriptor(AddressSpace.class);
|
||||
public static final String TDESC_BYTE_ARR = Type.getDescriptor(byte[].class);
|
||||
public static final String TDESC_EXIT_SLOT = Type.getDescriptor(ExitSlot.class);
|
||||
public static final String TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE =
|
||||
Type.getDescriptor(JitBytesPcodeExecutorState.class);
|
||||
public static final String TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE =
|
||||
Type.getDescriptor(JitBytesPcodeExecutorStateSpace.class);
|
||||
public static final String TDESC_JIT_PCODE_THREAD = Type.getDescriptor(JitPcodeThread.class);
|
||||
public static final String TDESC_LANGUAGE = Type.getDescriptor(Language.class);
|
||||
public static final String TDESC_LIST = Type.getDescriptor(List.class);
|
||||
public static final String TDESC_PCODE_USEROP_DEFINITION =
|
||||
Type.getDescriptor(PcodeUseropDefinition.class);
|
||||
public static final String TDESC_REGISTER_VALUE = Type.getDescriptor(RegisterValue.class);
|
||||
public static final String TDESC_STRING = Type.getDescriptor(String.class);
|
||||
public static final String TDESC_VARNODE = Type.getDescriptor(Varnode.class);
|
||||
public static final TRef<Object[]> TARR_OBJECT = Types.refOf(Object[].class);
|
||||
public static final TRef<Varnode[]> TARR_VARNODE = Types.refOf(Varnode[].class);
|
||||
public static final TRef<Double> TR_DOUBLE = Types.refOf(Double.class);
|
||||
public static final TRef<Float> TR_FLOAT = Types.refOf(Float.class);
|
||||
public static final TRef<Integer> TR_INTEGER = Types.refOf(Integer.class);
|
||||
public static final TRef<Long> TR_LONG = Types.refOf(Long.class);
|
||||
public static final TRef<Address> T_ADDRESS = Types.refOf(Address.class);
|
||||
public static final TRef<AddressFactory> T_ADDRESS_FACTORY =
|
||||
Types.refOf(AddressFactory.class);
|
||||
public static final TRef<AddressSpace> T_ADDRESS_SPACE = Types.refOf(AddressSpace.class);
|
||||
public static final TRef<AddrCtx> T_ADDR_CTX = Types.refOf(AddrCtx.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<ArrayList<?>> T_ARRAY_LIST = (TRef) Types.refOf(ArrayList.class);
|
||||
public static final TRef<AssertionError> T_ASSERTION_ERROR =
|
||||
Types.refOf(AssertionError.class);
|
||||
public static final TRef<DecodePcodeExecutionException> T_DECODE_PCODE_EXECUTION_EXCEPTION =
|
||||
Types.refOf(DecodePcodeExecutionException.class);
|
||||
public static final TRef<EntryPoint> T_ENTRY_POINT = Types.refOf(EntryPoint.class);
|
||||
public static final TRef<ExitSlot> T_EXIT_SLOT = Types.refOf(ExitSlot.class);
|
||||
public static final TRef<IllegalArgumentException> T_ILLEGAL_ARGUMENT_EXCEPTION =
|
||||
Types.refOf(IllegalArgumentException.class);
|
||||
public static final TRef<JitBytesPcodeExecutorState> T_JIT_BYTES_PCODE_EXECUTOR_STATE =
|
||||
Types.refOf(JitBytesPcodeExecutorState.class);
|
||||
public static final TRef<JitBytesPcodeExecutorStateSpace> //
|
||||
T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE = Types.refOf(JitBytesPcodeExecutorStateSpace.class);
|
||||
public static final TRef<JitCompiledPassage> T_JIT_COMPILED_PASSAGE =
|
||||
Types.refOf(JitCompiledPassage.class);
|
||||
public static final TRef<JitPcodeThread> T_JIT_PCODE_THREAD =
|
||||
Types.refOf(JitPcodeThread.class);
|
||||
public static final TRef<JitThreadBytesPcodeExecutorState> //
|
||||
T_JIT_THREAD_BYTES_PCODE_EXECUTOR_STATE = Types.refOf(JitThreadBytesPcodeExecutorState.class);
|
||||
public static final TRef<Language> T_LANGUAGE = Types.refOf(Language.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<List<?>> T_LIST = (TRef) Types.refOf(List.class);
|
||||
public static final TRef<LowlevelError> T_LOWLEVEL_ERROR = Types.refOf(LowlevelError.class);
|
||||
public static final TRef<Math> T_MATH = Types.refOf(Math.class);
|
||||
public static final TRef<Object> T_OBJECT = Types.refOf(Object.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<PcodeUseropDefinition<?>> T_PCODE_USEROP_DEFINITION =
|
||||
(TRef) Types.refOf(PcodeUseropDefinition.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<PcodeUseropLibrary<?>> T_PCODE_USEROP_LIBRARY =
|
||||
(TRef) Types.refOf(PcodeUseropLibrary.class);
|
||||
public static final TRef<PrintStream> T_PRINT_STREAM = Types.refOf(PrintStream.class);
|
||||
public static final TRef<RegisterValue> T_REGISTER_VALUE = Types.refOf(RegisterValue.class);
|
||||
public static final TRef<SleighLinkException> T_SLEIGH_LINK_EXCEPTION =
|
||||
Types.refOf(SleighLinkException.class);
|
||||
public static final TRef<String> T_STRING = Types.refOf(String.class);
|
||||
public static final TRef<System> T_SYSTEM = Types.refOf(System.class);
|
||||
public static final TRef<Throwable> T_THROWABLE = Types.refOf(Throwable.class);
|
||||
public static final TRef<Varnode> T_VARNODE = Types.refOf(Varnode.class);
|
||||
|
||||
public static final String TSIG_LIST_ADDRCTX =
|
||||
JitJvmTypeUtils.typeToSignature(new TypeLiteral<List<AddrCtx>>() {}.value);
|
||||
|
||||
public static final String MDESC_ADDR_CTX__$INIT = Type.getMethodDescriptor(Type.VOID_TYPE,
|
||||
Type.getType(RegisterValue.class), Type.getType(Address.class));
|
||||
public static final String MDESC_ADDRESS_FACTORY__GET_ADDRESS_SPACE =
|
||||
Type.getMethodDescriptor(Type.getType(AddressSpace.class), Type.INT_TYPE);
|
||||
public static final String MDESC_ADDRESS_SPACE__GET_ADDRESS =
|
||||
Type.getMethodDescriptor(Type.getType(Address.class), Type.LONG_TYPE);
|
||||
public static final String MDESC_ARRAY_LIST__$INIT = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Bot, TRef<RegisterValue>>, TRef<Address>>> MDESC_ADDR_CTX__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).param(T_REGISTER_VALUE).param(T_ADDRESS).build();
|
||||
public static final MthDesc<TRef<AddressSpace>,
|
||||
Ent<Bot, TInt>> MDESC_ADDRESS_FACTORY__GET_ADDRESS_SPACE =
|
||||
MthDesc.returns(T_ADDRESS_SPACE).param(Types.T_INT).build();
|
||||
public static final MthDesc<TRef<Address>, Ent<Bot, TLong>> MDESC_ADDRESS_SPACE__GET_ADDRESS =
|
||||
MthDesc.returns(T_ADDRESS).param(Types.T_LONG).build();
|
||||
public static final MthDesc<TVoid, Bot> MDESC_ARRAY_LIST__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).build();
|
||||
// NOTE: The void (String) form is private....
|
||||
public static final String MDESC_ASSERTION_ERROR__$INIT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class));
|
||||
public static final String MDESC_DOUBLE__DOUBLE_TO_RAW_LONG_BITS =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.DOUBLE_TYPE);
|
||||
public static final String MDESC_DOUBLE__IS_NAN =
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE);
|
||||
public static final String MDESC_DOUBLE__LONG_BITS_TO_DOUBLE =
|
||||
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_FLOAT__FLOAT_TO_RAW_INT_BITS =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.FLOAT_TYPE);
|
||||
public static final String MDESC_FLOAT__INT_BITS_TO_FLOAT =
|
||||
Type.getMethodDescriptor(Type.FLOAT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_FLOAT__IS_NAN =
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.FLOAT_TYPE);
|
||||
public static final String MDESC_ILLEGAL_ARGUMENT_EXCEPTION__$INIT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
||||
public static final String MDESC_INTEGER__BIT_COUNT =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_INTEGER__COMPARE =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_INTEGER__TO_UNSIGNED_LONG =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.INT_TYPE);
|
||||
public static final MthDesc<TVoid, Ent<Bot, TRef<Object>>> MDESC_ASSERTION_ERROR__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).param(T_OBJECT).build();
|
||||
public static final MthDesc<TLong, Ent<Bot, TDouble>> MDESC_DOUBLE__DOUBLE_TO_RAW_LONG_BITS =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_DOUBLE).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TDouble>> MDESC_DOUBLE__IS_NAN =
|
||||
MthDesc.<Boolean, Double> derive(d -> Double.isNaN(d))
|
||||
.check(MthDesc::returns, Types.T_BOOL)
|
||||
.check(MthDesc::param, Types.T_DOUBLE)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TDouble, Ent<Bot, TLong>> MDESC_DOUBLE__LONG_BITS_TO_DOUBLE =
|
||||
MthDesc.returns(Types.T_DOUBLE).param(Types.T_LONG).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TFloat>> MDESC_FLOAT__FLOAT_TO_RAW_INT_BITS =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_FLOAT).build();
|
||||
public static final MthDesc<TFloat, Ent<Bot, TInt>> MDESC_FLOAT__INT_BITS_TO_FLOAT =
|
||||
MthDesc.returns(Types.T_FLOAT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TFloat>> MDESC_FLOAT__IS_NAN =
|
||||
MthDesc.<Boolean, Float> derive(d -> Float.isNaN(d))
|
||||
.check(MthDesc::returns, Types.T_BOOL)
|
||||
.check(MthDesc::param, Types.T_FLOAT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Bot, TRef<String>>> MDESC_ILLEGAL_ARGUMENT_EXCEPTION__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TInt>> MDESC_INTEGER__BIT_COUNT =
|
||||
MthDesc.derive(Integer::bitCount)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_INTEGER__COMPARE =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TInt>> MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
|
||||
MthDesc.derive(Integer::numberOfLeadingZeros)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TLong, Ent<Bot, TInt>> MDESC_INTEGER__TO_UNSIGNED_LONG =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_INT).build();
|
||||
public static final MthDesc<TRef<Integer>, Ent<Bot, TInt>> MDESC_INTEGER__VALUE_OF =
|
||||
MthDesc.returns(TR_INTEGER).param(Types.T_INT).build();
|
||||
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_LANGUAGE =
|
||||
Type.getMethodDescriptor(Type.getType(Language.class));
|
||||
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_SPACE_FOR =
|
||||
Type.getMethodDescriptor(Type.getType(JitBytesPcodeExecutorStateSpace.class),
|
||||
Type.getType(AddressSpace.class));
|
||||
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT =
|
||||
Type.getMethodDescriptor(Type.getType(byte[].class), Type.LONG_TYPE);
|
||||
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__READ =
|
||||
Type.getMethodDescriptor(Type.getType(byte[].class), Type.LONG_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__WRITE =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(byte[].class),
|
||||
Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__COUNT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT =
|
||||
Type.getMethodDescriptor(Type.getType(RegisterValue.class), Type.getType(Language.class),
|
||||
Type.getType(String.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_DECODE_ERROR =
|
||||
Type.getMethodDescriptor(Type.getType(DecodePcodeExecutionException.class),
|
||||
Type.getType(String.class), Type.LONG_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT =
|
||||
Type.getMethodDescriptor(Type.getType(ExitSlot.class), Type.LONG_TYPE,
|
||||
Type.getType(RegisterValue.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
|
||||
Type.getMethodDescriptor(Type.getType(Varnode.class), Type.getType(AddressFactory.class),
|
||||
Type.getType(String.class), Type.LONG_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED =
|
||||
Type.getMethodDescriptor(Type.getType(EntryPoint.class), Type.getType(ExitSlot.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
|
||||
Type.getMethodDescriptor(Type.getType(Language.class), Type.getType(String.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION =
|
||||
Type.getMethodDescriptor(Type.getType(PcodeUseropDefinition.class),
|
||||
Type.getType(String.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PcodeUseropDefinition.class),
|
||||
Type.getType(Varnode.class), Type.getType(Varnode[].class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class),
|
||||
Type.getType(int[].class), Type.getType(int[].class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_COUNTER_AND_CONTEXT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__SET_COUNTER_AND_CONTEXT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_INT_RAW =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_LONG_RAW =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_MP_INT =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(int[].class),
|
||||
Type.getType(int[].class), Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(byte[].class),
|
||||
Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(byte[].class),
|
||||
Type.INT_TYPE);
|
||||
public static final String MDESC_JIT_PCODE_THREAD__GET_STATE =
|
||||
Type.getMethodDescriptor(Type.getType(JitThreadBytesPcodeExecutorState.class));
|
||||
public static final String MDESC_LANGUAGE__GET_ADDRESS_FACTORY =
|
||||
Type.getMethodDescriptor(Type.getType(AddressFactory.class));
|
||||
public static final MthDesc<TRef<JitBytesPcodeExecutorStateSpace>,
|
||||
Ent<Bot, TRef<AddressSpace>>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE__GET_SPACE_FOR =
|
||||
MthDesc.returns(T_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE).param(T_ADDRESS_SPACE).build();
|
||||
public static final MthDesc<TRef<byte[]>,
|
||||
Ent<Bot, TLong>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__GET_DIRECT =
|
||||
MthDesc.returns(Types.T_BYTE_ARR).param(Types.T_LONG).build();
|
||||
public static final MthDesc<TRef<byte[]>,
|
||||
Ent<Ent<Bot, TLong>, TInt>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__READ =
|
||||
MthDesc.returns(Types.T_BYTE_ARR).param(Types.T_LONG).param(Types.T_INT).build();
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Ent<Bot, TLong>, TRef<byte[]>>, TInt>,
|
||||
TInt>> MDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE__WRITE =
|
||||
MthDesc.returns(Types.T_VOID)
|
||||
.param(Types.T_LONG)
|
||||
.param(Types.T_BYTE_ARR)
|
||||
.param(Types.T_INT)
|
||||
.param(Types.T_INT)
|
||||
.build();
|
||||
public static final MthDesc<TLong,
|
||||
Ent<Ent<Bot, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_INT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Bot, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__COUNT =
|
||||
MthDesc.returns(Types.T_VOID).param(Types.T_INT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TRef<RegisterValue>,
|
||||
Ent<Ent<Bot, TRef<Language>>, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_CONTEXT =
|
||||
MthDesc.returns(T_REGISTER_VALUE).param(T_LANGUAGE).param(T_STRING).build();
|
||||
public static final MthDesc<TRef<DecodePcodeExecutionException>,
|
||||
Ent<Ent<Bot, TRef<String>>, TLong>> MDESC_JIT_COMPILED_PASSAGE__CREATE_DECODE_ERROR =
|
||||
MthDesc.returns(T_DECODE_PCODE_EXECUTION_EXCEPTION)
|
||||
.param(T_STRING)
|
||||
.param(Types.T_LONG)
|
||||
.build();
|
||||
public static final MthDesc<TRef<ExitSlot>,
|
||||
Ent<Ent<Bot, TLong>, TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT =
|
||||
MthDesc.returns(T_EXIT_SLOT).param(Types.T_LONG).param(T_REGISTER_VALUE).build();
|
||||
public static final MthDesc<TRef<Varnode>,
|
||||
Ent<Ent<Ent<Ent<Bot, TRef<AddressFactory>>, TRef<String>>, TLong>,
|
||||
TInt>> MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
|
||||
MthDesc.returns(T_VARNODE)
|
||||
.param(T_ADDRESS_FACTORY)
|
||||
.param(T_STRING)
|
||||
.param(Types.T_LONG)
|
||||
.param(Types.T_INT)
|
||||
.build();
|
||||
public static final MthDesc<TRef<EntryPoint>,
|
||||
Ent<Bot, TRef<ExitSlot>>> MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED =
|
||||
MthDesc.returns(T_ENTRY_POINT).param(T_EXIT_SLOT).build();
|
||||
public static final MthDesc<TRef<Language>,
|
||||
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
|
||||
MthDesc.returns(T_LANGUAGE).param(T_STRING).build();
|
||||
public static final MthDesc<TRef<PcodeUseropDefinition<?>>,
|
||||
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION =
|
||||
MthDesc.returns(T_PCODE_USEROP_DEFINITION).param(T_STRING).build();
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TRef<PcodeUseropDefinition<?>>>, TRef<Varnode>>,
|
||||
TRef<Varnode[]>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
||||
MthDesc.returns(Types.T_VOID)
|
||||
.param(T_PCODE_USEROP_DEFINITION)
|
||||
.param(T_VARNODE)
|
||||
.param(TARR_VARNODE)
|
||||
.build();
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
|
||||
TRef<int[]>>> MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
|
||||
MthDesc.returns(Types.T_VOID)
|
||||
.param(Types.T_INT_ARR)
|
||||
.param(Types.T_INT_ARR)
|
||||
.param(Types.T_INT_ARR)
|
||||
.build();
|
||||
public static final MthDesc<TInt,
|
||||
Ent<Ent<Ent<Bot, TRef<byte[]>>, TInt>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N =
|
||||
MthDesc.returns(Types.T_BOOL)
|
||||
.param(Types.T_BYTE_ARR)
|
||||
.param(Types.T_INT)
|
||||
.param(Types.T_INT)
|
||||
.build();
|
||||
public static final MthDesc<TInt,
|
||||
Ent<Ent<Bot, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_BYTE_ARR).param(Types.T_INT).build();
|
||||
public static final MthDesc<TLong,
|
||||
Ent<Ent<Bot, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_BYTE_ARR).param(Types.T_INT).build();
|
||||
public static final MthDesc<TVoid, Ent<Ent<Bot, TLong>,
|
||||
TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__SET_$OR_WRITE_COUNTER_AND_CONTEXT =
|
||||
MthDesc.returns(Types.T_VOID).param(Types.T_LONG).param(T_REGISTER_VALUE).build();
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>,
|
||||
TInt>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_INT_RAW =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>,
|
||||
TLong>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_LONG_RAW =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_LONG).param(Types.T_LONG).build();
|
||||
public static final MthDesc<TInt,
|
||||
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
|
||||
TInt>> MDESC_JIT_COMPILED_PASSAGE__$FLAGBIT_MP_INT =
|
||||
MthDesc.returns(Types.T_INT)
|
||||
.param(Types.T_INT_ARR)
|
||||
.param(Types.T_INT_ARR)
|
||||
.param(Types.T_INT)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* This is just to assure all the methods referred to below have the same signature. The fields
|
||||
* here should not be used in any code, written, generated, or otherwise.
|
||||
*/
|
||||
interface WriteIntX {
|
||||
WriteIntX AE1 = JitCompiledPassage::writeInt1;
|
||||
WriteIntX BE2 = JitCompiledPassage::writeIntBE2;
|
||||
WriteIntX BE3 = JitCompiledPassage::writeIntBE3;
|
||||
WriteIntX BE4 = JitCompiledPassage::writeIntBE4;
|
||||
|
||||
WriteIntX LE2 = JitCompiledPassage::writeIntLE2;
|
||||
WriteIntX LE3 = JitCompiledPassage::writeIntLE3;
|
||||
WriteIntX LE4 = JitCompiledPassage::writeIntLE4;
|
||||
|
||||
void writeIntX(int value, byte[] arr, int offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just to assure all the methods referred to below have the same signature. The fields
|
||||
* here should not be used in any code, written, generated, or otherwise.
|
||||
*/
|
||||
interface WriteLongX {
|
||||
WriteLongX AE1 = JitCompiledPassage::writeLong1;
|
||||
WriteLongX BE2 = JitCompiledPassage::writeLongBE2;
|
||||
WriteLongX BE3 = JitCompiledPassage::writeLongBE3;
|
||||
WriteLongX BE4 = JitCompiledPassage::writeLongBE4;
|
||||
WriteLongX BE5 = JitCompiledPassage::writeLongBE5;
|
||||
WriteLongX BE6 = JitCompiledPassage::writeLongBE6;
|
||||
WriteLongX BE7 = JitCompiledPassage::writeLongBE7;
|
||||
WriteLongX BE8 = JitCompiledPassage::writeLongBE8;
|
||||
|
||||
WriteLongX LE2 = JitCompiledPassage::writeLongLE2;
|
||||
WriteLongX LE3 = JitCompiledPassage::writeLongLE3;
|
||||
WriteLongX LE4 = JitCompiledPassage::writeLongLE4;
|
||||
WriteLongX LE5 = JitCompiledPassage::writeLongLE5;
|
||||
WriteLongX LE6 = JitCompiledPassage::writeLongLE6;
|
||||
WriteLongX LE7 = JitCompiledPassage::writeLongLE7;
|
||||
WriteLongX LE8 = JitCompiledPassage::writeLongLE8;
|
||||
|
||||
void writeLongX(long value, byte[] arr, int offset);
|
||||
}
|
||||
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TInt>, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
|
||||
MthDesc.derive(JitCompiledPassage::writeInt1)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_BYTE_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TLong>, TRef<byte[]>>, TInt>> MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX =
|
||||
MthDesc.derive(JitCompiledPassage::writeLong1)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_BYTE_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TRef<JitThreadBytesPcodeExecutorState>,
|
||||
Bot> MDESC_JIT_PCODE_THREAD__GET_STATE =
|
||||
MthDesc.returns(T_JIT_THREAD_BYTES_PCODE_EXECUTOR_STATE).build();
|
||||
public static final MthDesc<TRef<AddressFactory>, Bot> MDESC_LANGUAGE__GET_ADDRESS_FACTORY =
|
||||
MthDesc.returns(T_ADDRESS_FACTORY).build();
|
||||
public static final String MDESC_LANGUAGE__GET_DEFAULT_SPACE =
|
||||
Type.getMethodDescriptor(Type.getType(AddressSpace.class));
|
||||
public static final String MDESC_LIST__ADD =
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class));
|
||||
public static final String MDESC_LONG__BIT_COUNT =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_LONG__COMPARE_UNSIGNED =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_LONG__NUMBER_OF_LEADING_ZEROS =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_LOW_LEVEL_ERROR__$INIT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
||||
public static final String MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY =
|
||||
Type.getMethodDescriptor(Type.getType(PcodeUseropLibrary.class));
|
||||
public static final String MDESC_SLEIGH_LINK_EXCEPTION__$INIT =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
||||
public static final MthDesc<TInt, Ent<Bot, TRef<Object>>> MDESC_LIST__ADD =
|
||||
MthDesc.returns(Types.T_BOOL).param(T_OBJECT).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TLong>> MDESC_LONG__BIT_COUNT =
|
||||
MthDesc.derive(Long::bitCount)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TLong>, TLong>> MDESC_LONG__COMPARE =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_LONG).param(Types.T_LONG).build();
|
||||
public static final MthDesc<TInt, Ent<Bot, TLong>> MDESC_LONG__NUMBER_OF_LEADING_ZEROS =
|
||||
MthDesc.derive(Long::numberOfLeadingZeros)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_LOWLEVEL_ERROR__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
|
||||
public static final MthDesc<TVoid, Bot> MDESC_OBJECT__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).build();
|
||||
public static final MthDesc<TRef<PcodeUseropLibrary<?>>,
|
||||
Bot> MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY =
|
||||
MthDesc.returns(T_PCODE_USEROP_LIBRARY).build();
|
||||
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_PRINT_STREAM__PRINTLN =
|
||||
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
|
||||
public static final MthDesc<TVoid, Ent<Bot, TRef<String>>> MDESC_SLEIGH_LINK_EXCEPTION__$INIT =
|
||||
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
|
||||
public static final MthDesc<TRef<String>, Ent<Bot, TRef<Object[]>>> MDESC_STRING__FORMATTED =
|
||||
MthDesc.returns(T_STRING).param(TARR_OBJECT).build();
|
||||
|
||||
public static final String MDESC_$DOUBLE_UNOP =
|
||||
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
|
||||
public static final String MDESC_$FLOAT_UNOP =
|
||||
Type.getMethodDescriptor(Type.FLOAT_TYPE, Type.FLOAT_TYPE);
|
||||
public static final String MDESC_$INT_BINOP =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_$LONG_BINOP =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_$SHIFT_AA =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||
Type.getType(int[].class), Type.getType(int[].class));
|
||||
public static final String MDESC_$SHIFT_AJ =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||
Type.getType(int[].class), Type.LONG_TYPE);
|
||||
public static final String MDESC_$SHIFT_AI =
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||
Type.getType(int[].class), Type.INT_TYPE);
|
||||
public static final String MDESC_$SHIFT_JA =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.getType(int[].class));
|
||||
public static final String MDESC_$SHIFT_JJ =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_$SHIFT_JI =
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.INT_TYPE);
|
||||
public static final String MDESC_$SHIFT_IA =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.getType(int[].class));
|
||||
public static final String MDESC_$SHIFT_IJ =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
|
||||
public static final String MDESC_$SHIFT_II =
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||
public static final MthDesc<TDouble, Ent<Bot, TDouble>> MDESC_$DOUBLE_UNOP =
|
||||
MthDesc.returns(Types.T_DOUBLE).param(Types.T_DOUBLE).build();
|
||||
public static final MthDesc<TFloat, Ent<Bot, TFloat>> MDESC_$FLOAT_UNOP =
|
||||
MthDesc.returns(Types.T_FLOAT).param(Types.T_FLOAT).build();
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_$INT_BINOP =
|
||||
MthDesc.returns(Types.T_INT).param(Types.T_INT).param(Types.T_INT).build();
|
||||
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TLong>> MDESC_$LONG_BINOP =
|
||||
MthDesc.returns(Types.T_LONG).param(Types.T_LONG).param(Types.T_LONG).build();
|
||||
|
||||
public static final String NAME_ADDR_CTX = Type.getInternalName(AddrCtx.class);
|
||||
public static final String NAME_ADDRESS = Type.getInternalName(Address.class);
|
||||
public static final String NAME_ADDRESS_FACTORY = Type.getInternalName(AddressFactory.class);
|
||||
public static final String NAME_ADDRESS_SPACE = Type.getInternalName(AddressSpace.class);
|
||||
public static final String NAME_ARRAY_LIST = Type.getInternalName(ArrayList.class);
|
||||
public static final String NAME_ASSERTION_ERROR = Type.getInternalName(AssertionError.class);
|
||||
public static final String NAME_DOUBLE = Type.getInternalName(Double.class);
|
||||
public static final String NAME_EXIT_SLOT = Type.getInternalName(ExitSlot.class);
|
||||
public static final String NAME_FLOAT = Type.getInternalName(Float.class);
|
||||
public static final String NAME_ILLEGAL_ARGUMENT_EXCEPTION =
|
||||
Type.getInternalName(IllegalArgumentException.class);
|
||||
public static final String NAME_INTEGER = Type.getInternalName(Integer.class);
|
||||
public static final String NAME_JIT_BYTES_PCODE_EXECUTOR_STATE =
|
||||
Type.getInternalName(JitBytesPcodeExecutorState.class);
|
||||
public static final String NAME_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE =
|
||||
Type.getInternalName(JitBytesPcodeExecutorStateSpace.class);
|
||||
public static final String NAME_JIT_COMPILED_PASSAGE =
|
||||
Type.getInternalName(JitCompiledPassage.class);
|
||||
public static final String NAME_JIT_PCODE_THREAD = Type.getInternalName(JitPcodeThread.class);
|
||||
public static final String NAME_LANGUAGE = Type.getInternalName(Language.class);
|
||||
public static final String NAME_LIST = Type.getInternalName(List.class);
|
||||
public static final String NAME_LONG = Type.getInternalName(Long.class);
|
||||
public static final String NAME_LOW_LEVEL_ERROR = Type.getInternalName(LowlevelError.class);
|
||||
public static final String NAME_MATH = Type.getInternalName(Math.class);
|
||||
public static final String NAME_OBJECT = Type.getInternalName(Object.class);
|
||||
public static final String NAME_PCODE_USEROP_DEFINITION =
|
||||
Type.getInternalName(PcodeUseropDefinition.class);
|
||||
public static final String NAME_SLEIGH_LINK_EXCEPTION =
|
||||
Type.getInternalName(SleighLinkException.class);
|
||||
public static final String NAME_THROWABLE = Type.getInternalName(Throwable.class);
|
||||
public static final String NAME_VARNODE = Type.getInternalName(Varnode.class);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TRef<int[]>>> MDESC_$SHIFT_AA =
|
||||
MthDesc.<int[], Integer, int[], int[]> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TLong>> MDESC_$SHIFT_AJ =
|
||||
MthDesc.<int[], Integer, int[], Long> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Ent<Bot, TRef<int[]>>, TInt>, TRef<int[]>>, TInt>> MDESC_$SHIFT_AI =
|
||||
MthDesc.<int[], Integer, int[], Integer> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TRef<int[]>>> MDESC_$SHIFT_JA =
|
||||
MthDesc.<Long, Long, int[]> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TLong>> MDESC_$SHIFT_JJ =
|
||||
MthDesc.<Long, Long, Long> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TLong, Ent<Ent<Bot, TLong>, TInt>> MDESC_$SHIFT_JI =
|
||||
MthDesc.<Long, Long, Integer> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TRef<int[]>>> MDESC_$SHIFT_IA =
|
||||
MthDesc.<Integer, Integer, int[]> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT_ARR)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TLong>> MDESC_$SHIFT_IJ =
|
||||
MthDesc.<Integer, Integer, Long> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_LONG)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TInt, Ent<Ent<Bot, TInt>, TInt>> MDESC_$SHIFT_II =
|
||||
MthDesc.<Integer, Integer, Integer> derive(JitCompiledPassage::intLeft)
|
||||
.check(MthDesc::returns, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::build);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,12 +16,18 @@
|
||||
package ghidra.pcode.emu.jit.gen;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
|
||||
|
||||
/**
|
||||
* A static field request initialized in the class initializer
|
||||
*
|
||||
* @param <T> the JVM type of the field
|
||||
*/
|
||||
public interface StaticFieldReq extends FieldReq {
|
||||
public interface StaticFieldReq<T extends BNonVoid> extends FieldReq<T> {
|
||||
/**
|
||||
* Emit the field declaration and its initialization bytecode
|
||||
*
|
||||
@@ -29,9 +35,22 @@ public interface StaticFieldReq extends FieldReq {
|
||||
* The declaration is emitted into the class definition, and the initialization code is emitted
|
||||
* into the class initializer.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @param cv the visitor for the class definition
|
||||
* @param sv the visitor for the class (static) initializer
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
void generateClinitCode(JitCodeGenerator gen, ClassVisitor cv, MethodVisitor sv);
|
||||
<N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
ClassVisitor cv);
|
||||
|
||||
/**
|
||||
* Emit code to load the field onto the JVM stack
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param gen the code generator
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
<N extends Next> Emitter<Ent<N, T>> genLoad(Emitter<N> em, JitCodeGenerator<?> gen);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitLoadOp;
|
||||
import ghidra.pcode.emu.jit.op.JitStoreOp;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A generator to emit code that accesses variables of various size in a
|
||||
* {@link JitBytesPcodeExecutorState state}, for a specific type and byte order.
|
||||
* <p>
|
||||
* This is used by variable birthing and retirement as well as direct memory accesses. Dynamic
|
||||
* memory accesses, i.e., {@link JitStoreOp store} and {@link JitLoadOp load} do not use this,
|
||||
* though they may borrow some portions.
|
||||
*
|
||||
* @param <JT> the JIT type of the operand
|
||||
*/
|
||||
public interface AccessGen<JT extends JitType> {
|
||||
|
||||
/**
|
||||
* Lookup the generator for accessing variables for the given type and byte order
|
||||
*
|
||||
* @param endian the byte order
|
||||
* @param type the p-code type of the variable
|
||||
* @return the access generator
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends JitType> AccessGen<T> lookup(Endian endian, T type) {
|
||||
return (AccessGen<T>) switch (endian) {
|
||||
case BIG -> switch (type) {
|
||||
case IntJitType t -> IntAccessGen.BE;
|
||||
case LongJitType t -> LongAccessGen.BE;
|
||||
case FloatJitType t -> FloatAccessGen.BE;
|
||||
case DoubleJitType t -> DoubleAccessGen.BE;
|
||||
case MpIntJitType t -> MpIntAccessGen.BE;
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case LITTLE -> switch (type) {
|
||||
case IntJitType t -> IntAccessGen.LE;
|
||||
case LongJitType t -> LongAccessGen.LE;
|
||||
case FloatJitType t -> FloatAccessGen.LE;
|
||||
case DoubleJitType t -> DoubleAccessGen.LE;
|
||||
case MpIntJitType t -> MpIntAccessGen.LE;
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the generator for accessing variables of simple types and the given byte order
|
||||
*
|
||||
* @param <T> the JVM type of the variable
|
||||
* @param <JT> the p-code type of the variable
|
||||
* @param endian the byte order
|
||||
* @param type the p-code type of the variable
|
||||
* @return the access generator
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> SimpleAccessGen<T, JT>
|
||||
lookupSimple(Endian endian, JT type) {
|
||||
return (SimpleAccessGen<T, JT>) switch (endian) {
|
||||
case BIG -> switch (type) {
|
||||
case IntJitType t -> IntAccessGen.BE;
|
||||
case LongJitType t -> LongAccessGen.BE;
|
||||
case FloatJitType t -> FloatAccessGen.BE;
|
||||
case DoubleJitType t -> DoubleAccessGen.BE;
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case LITTLE -> switch (type) {
|
||||
case IntJitType t -> IntAccessGen.LE;
|
||||
case LongJitType t -> LongAccessGen.LE;
|
||||
case FloatJitType t -> FloatAccessGen.LE;
|
||||
case DoubleJitType t -> DoubleAccessGen.LE;
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the generator for accessing variables of multi-precision integer type and the given
|
||||
* byte order
|
||||
*
|
||||
* @param endian the byte order
|
||||
* @return the access generator
|
||||
*/
|
||||
public static MpIntAccessGen lookupMp(Endian endian) {
|
||||
return switch (endian) {
|
||||
case BIG -> MpIntAccessGen.BE;
|
||||
case LITTLE -> MpIntAccessGen.LE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to read the given varnode onto the stack as a p-code bool (JVM int)
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
public static <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
|
||||
genReadToBool(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
Varnode vn) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size < BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::ldc__i, size)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::ldc__i, BLOCK_SIZE - off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "readBoolN",
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_BOOL_N, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ior);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.DoubleJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Local;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The generator for accessing doubles
|
||||
*
|
||||
* <p>
|
||||
* This is accomplished by delegating to the long access generator with type conversion.
|
||||
*/
|
||||
public enum DoubleAccessGen implements SimpleAccessGen<TDouble, DoubleJitType> {
|
||||
/** The big-endian instance */
|
||||
BE(LongAccessGen.BE),
|
||||
/** The little-endian instance */
|
||||
LE(LongAccessGen.LE);
|
||||
|
||||
final LongAccessGen longGen;
|
||||
|
||||
private DoubleAccessGen(LongAccessGen longGen) {
|
||||
this.longGen = longGen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TDouble>>
|
||||
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
Varnode vn) {
|
||||
return em
|
||||
.emit(longGen::genReadToStack, localThis, gen, vn)
|
||||
.emit(LongToDouble.INSTANCE::convertStackToStack, LongJitType.I8, DoubleJitType.F8,
|
||||
Ext.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TDouble>>
|
||||
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
return em
|
||||
.emit(DoubleToLong.INSTANCE::convertStackToStack, DoubleJitType.F8, LongJitType.I8,
|
||||
Ext.ZERO)
|
||||
.emit(longGen::genWriteFromStack, localThis, gen, vn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.BLOCK_SIZE;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Local;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* A generator that exports part of its implementation for use in a {@link MpIntAccessGen}.
|
||||
*
|
||||
* <p>
|
||||
* This really just avoids the re-creation of {@link Varnode} objects for each leg of a large
|
||||
* varnode. The method instead takes the (space,offset,size) triple as well as the offset of the
|
||||
* block containing its start.
|
||||
*/
|
||||
public interface ExportsLegAccessGen extends SimpleAccessGen<TInt, IntJitType> {
|
||||
/**
|
||||
* Emit code to read one JVM int, either a whole variable or one leg of a multi-precision int
|
||||
* variable.
|
||||
*
|
||||
* <p>
|
||||
* Legs that span blocks are handled as in
|
||||
* {@link #genReadToStack(Emitter, Local, JitCodeGenerator, Varnode)}
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param space the address space of the varnode
|
||||
* @param block the block offset containing the varnode (or leg)
|
||||
* @param off the offset of the varnode (or leg)
|
||||
* @param size the size of the varnode in bytes (or leg)
|
||||
* @return the emitter typed with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>> genReadLegToStack(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
AddressSpace space, long block, int off, int size);
|
||||
|
||||
/**
|
||||
* Emit code to write one JVM int, either a whole variable or one leg of a multi-precision int
|
||||
* variable.
|
||||
*
|
||||
* <p>
|
||||
* Legs that span blocks are handled as in
|
||||
* {@link #genWriteFromStack(Emitter, Local, JitCodeGenerator, Varnode)}
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the value on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param space the address space of the varnode
|
||||
* @param block the block offset containing the varnode (or leg)
|
||||
* @param off the offset of the varnode (or leg)
|
||||
* @param size the size of the varnode in bytes (or leg)
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1>
|
||||
genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off, int size);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>> genReadToStack(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
return genReadLegToStack(em, localThis, gen, space, block, off, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<N1>
|
||||
genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
return genWriteLegFromStack(em, localThis, gen, space, block, off, size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.FloatJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Local;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The generator for writing floats
|
||||
*
|
||||
* <p>
|
||||
* This is accomplished by delegating to the int access generator with type conversion.
|
||||
*/
|
||||
public enum FloatAccessGen implements SimpleAccessGen<TFloat, FloatJitType> {
|
||||
/** The big-endian instance */
|
||||
BE(IntAccessGen.BE),
|
||||
/** The little-endian instance */
|
||||
LE(IntAccessGen.LE);
|
||||
|
||||
final IntAccessGen intGen;
|
||||
|
||||
private FloatAccessGen(IntAccessGen intGen) {
|
||||
this.intGen = intGen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TFloat>> genReadToStack(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
return em
|
||||
.emit(intGen::genReadToStack, localThis, gen, vn)
|
||||
.emit(IntToFloat.INSTANCE::convertStackToStack, IntJitType.I4, FloatJitType.F4,
|
||||
Ext.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TFloat>>
|
||||
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
return em
|
||||
.emit(FloatToInt.INSTANCE::convertStackToStack, FloatJitType.F4, IntJitType.I4,
|
||||
Ext.ZERO)
|
||||
.emit(intGen::genWriteFromStack, localThis, gen, vn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
/**
|
||||
* The generator for writing integers.
|
||||
*/
|
||||
public enum IntAccessGen implements MethodAccessGen, ExportsLegAccessGen {
|
||||
/** The big-endian instance */
|
||||
BE {
|
||||
@Override
|
||||
public String chooseReadName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "readInt1";
|
||||
case 2 -> "readIntBE2";
|
||||
case 3 -> "readIntBE3";
|
||||
case 4 -> "readIntBE4";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
|
||||
genReadLegToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
|
||||
int size) {
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
|
||||
.emit(Op::ishl)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ior);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseWriteName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "writeInt1";
|
||||
case 2 -> "writeIntBE2";
|
||||
case 3 -> "writeIntBE3";
|
||||
case 4 -> "writeIntBE4";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>>
|
||||
Emitter<N1> genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
|
||||
int size) {
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
|
||||
.emit(Op::iushr)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
},
|
||||
/** The little-endian instance */
|
||||
LE {
|
||||
@Override
|
||||
public String chooseReadName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "readInt1";
|
||||
case 2 -> "readIntLE2";
|
||||
case 3 -> "readIntLE3";
|
||||
case 4 -> "readIntLE4";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TInt>>
|
||||
genReadLegToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
|
||||
int size) {
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ior);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseWriteName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "writeInt1";
|
||||
case 2 -> "writeIntLE2";
|
||||
case 3 -> "writeIntLE3";
|
||||
case 4 -> "writeIntLE4";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TInt>>
|
||||
Emitter<N1> genWriteLegFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, AddressSpace space, long block, int off,
|
||||
int size) {
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, BLOCK_SIZE - off)
|
||||
.emit(Op::iushr)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the {@code int} access generator for the given byte order
|
||||
*
|
||||
* @param endian the byte order
|
||||
* @return the access generator
|
||||
*/
|
||||
public static IntAccessGen forEndian(Endian endian) {
|
||||
return switch (endian) {
|
||||
case BIG -> BE;
|
||||
case LITTLE -> LE;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.FieldForArrDirect;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* Bytes writer for longs in big endian order.
|
||||
*/
|
||||
public enum LongAccessGen implements MethodAccessGen, SimpleAccessGen<TLong, LongJitType> {
|
||||
/** The big-endian instance */
|
||||
BE {
|
||||
@Override
|
||||
public String chooseReadName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "readLong1";
|
||||
case 2 -> "readLongBE2";
|
||||
case 3 -> "readLongBE3";
|
||||
case 4 -> "readLongBE4";
|
||||
case 5 -> "readLongBE5";
|
||||
case 6 -> "readLongBE6";
|
||||
case 7 -> "readLongBE7";
|
||||
case 8 -> "readLongBE8";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TLong>>
|
||||
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
|
||||
.emit(Op::lshl)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::lor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseWriteName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "writeLong1";
|
||||
case 2 -> "writeLongBE2";
|
||||
case 3 -> "writeLongBE3";
|
||||
case 4 -> "writeLongBE4";
|
||||
case 5 -> "writeLongBE5";
|
||||
case 6 -> "writeLongBE6";
|
||||
case 7 -> "writeLongBE7";
|
||||
case 8 -> "writeLongBE8";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TLong>>
|
||||
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::ldc__i, off + size - BLOCK_SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
},
|
||||
/** The little-endian instance */
|
||||
LE {
|
||||
@Override
|
||||
public String chooseReadName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "readLong1";
|
||||
case 2 -> "readLongLE2";
|
||||
case 3 -> "readLongLE3";
|
||||
case 4 -> "readLongLE4";
|
||||
case 5 -> "readLongLE5";
|
||||
case 6 -> "readLongLE6";
|
||||
case 7 -> "readLongLE7";
|
||||
case 8 -> "readLongLE8";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TLong>>
|
||||
genReadToStack(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseReadName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__READ_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::lor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseWriteName(int size) {
|
||||
return switch (size) {
|
||||
case 1 -> "writeLong1";
|
||||
case 2 -> "writeLongLE2";
|
||||
case 3 -> "writeLongLE3";
|
||||
case 4 -> "writeLongLE4";
|
||||
case 5 -> "writeLongLE5";
|
||||
case 6 -> "writeLongLE6";
|
||||
case 7 -> "writeLongLE7";
|
||||
case 8 -> "writeLongLE8";
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TLong>>
|
||||
Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
long offset = vn.getOffset();
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int size = vn.getSize();
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
FieldForArrDirect blkField = gen.requestFieldForArrDirect(space.getAddress(block));
|
||||
if (off + size <= BLOCK_SIZE) {
|
||||
return em
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(size),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
FieldForArrDirect nxtField =
|
||||
gen.requestFieldForArrDirect(space.getAddress(block + BLOCK_SIZE));
|
||||
return em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::ldc__i, BLOCK_SIZE - off)
|
||||
.emit(Op::lushr)
|
||||
.emit(nxtField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(off + size - BLOCK_SIZE),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid)
|
||||
.emit(blkField::genLoad, localThis, gen)
|
||||
.emit(Op::ldc__i, off)
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE,
|
||||
chooseWriteName(BLOCK_SIZE - off),
|
||||
MDESC_JIT_COMPILED_PASSAGE__WRITE_LONGX, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the {@code long} access generator for the given byte order
|
||||
*
|
||||
* @param endian the byte order
|
||||
* @return the access generator
|
||||
*/
|
||||
public static LongAccessGen forEndian(Endian endian) {
|
||||
return switch (endian) {
|
||||
case BIG -> BE;
|
||||
case LITTLE -> LE;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.type;
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.op.LoadOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.StoreOpGen;
|
||||
@@ -26,13 +26,22 @@ import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
* <p>
|
||||
* This is needed by {@link LoadOpGen} and {@link StoreOpGen}.
|
||||
*/
|
||||
public interface MethodAccessGen extends TypedAccessGen {
|
||||
public interface MethodAccessGen {
|
||||
/**
|
||||
* Choose the name of a method, e.g. {@link JitCompiledPassage#readInt1(byte[], int)} to use for
|
||||
* the given variable size.
|
||||
* Choose the name of the read method, e.g. {@link JitCompiledPassage#readInt1(byte[], int)} to
|
||||
* use for the given variable size.
|
||||
*
|
||||
* @param size the size in bytes
|
||||
* @return the name of the method
|
||||
*/
|
||||
String chooseName(int size);
|
||||
String chooseReadName(int size);
|
||||
|
||||
/**
|
||||
* Choose the name of the write method, e.g.
|
||||
* {@link JitCompiledPassage#writeInt1(int,byte[], int)} to use for the given variable size.
|
||||
*
|
||||
* @param size the size in bytes
|
||||
* @return the name of the method
|
||||
*/
|
||||
String chooseWriteName(int size);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.OpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An access generator for a multi-precision integer variable
|
||||
*/
|
||||
public interface MpAccessGen extends AccessGen<MpIntJitType> {
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into several locals.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @param type desired the p-code type of the value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the operand containing the locals, and the emitter typed with the incoming stack
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N extends Next> OpndEm<MpIntJitType, N> genReadToOpnd(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
|
||||
MpIntJitType type, Ext ext, Scope scope);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value into an integer array in little-endian order,
|
||||
* pushing its ref onto the JVM stack.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @param type desired the p-code type of the value
|
||||
* @param ext the kind of extension to apply
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @param slack the number of additional, more significant, elements to allocate in the array
|
||||
* @return the emitter typed with the resulting stack, i.e., having the ref pushed onto it
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<int[]>>> genReadToArray(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
|
||||
MpIntJitType type, Ext ext, Scope scope, int slack);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a value into a variable from the JVM stack.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param opnd the operand whose locals contain the value to be stored
|
||||
* @param vn the varnode
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N extends Next> Emitter<N> genWriteFromOpnd(Emitter<N> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Opnd<MpIntJitType> opnd,
|
||||
Varnode vn);
|
||||
|
||||
/**
|
||||
* Emit bytecode to store a varnode's value from an array of integer legs, in little endian
|
||||
* order
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the array ref on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., having popped the array
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TRef<int[]>>> Emitter<N1>
|
||||
genWriteFromArray(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn, Scope scope);
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.BLOCK_SIZE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.*;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The generator for writing multi-precision ints.
|
||||
*/
|
||||
public enum MpIntAccessGen implements MpAccessGen {
|
||||
/** The big-endian instance */
|
||||
BE(IntAccessGen.BE) {
|
||||
@Override
|
||||
protected List<IntJitType> orderedLegTypes(MpIntJitType type) {
|
||||
return type.legTypesBE();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd) {
|
||||
return opnd.type().castLegsLE(opnd).reversed();
|
||||
}
|
||||
},
|
||||
/** The little-endian instance */
|
||||
LE(IntAccessGen.LE) {
|
||||
@Override
|
||||
protected List<IntJitType> orderedLegTypes(MpIntJitType type) {
|
||||
return type.legTypesLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd) {
|
||||
return opnd.type().castLegsLE(opnd);
|
||||
}
|
||||
};
|
||||
|
||||
final IntAccessGen legGen;
|
||||
|
||||
private MpIntAccessGen(IntAccessGen legGen) {
|
||||
this.legGen = legGen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arrange the leg types so that the least-significant one is first
|
||||
*
|
||||
* @param type the mp-int type
|
||||
* @return the leg types in little-endian order
|
||||
*/
|
||||
protected abstract List<IntJitType> orderedLegTypes(MpIntJitType type);
|
||||
|
||||
/**
|
||||
* Arrange the operand legs so that the least-significant one is first
|
||||
*
|
||||
* @param opnd the mp-int operand
|
||||
* @return the legs in little-endian order
|
||||
*/
|
||||
protected abstract List<SimpleOpnd<TInt, IntJitType>> orderedLegs(Opnd<MpIntJitType> opnd);
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> OpndEm<MpIntJitType, N> genReadToOpnd(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn,
|
||||
MpIntJitType type, Ext ext, Scope scope) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
List<SimpleOpnd<TInt, IntJitType>> legs = new ArrayList<>();
|
||||
MpIntJitType fromType = MpIntJitType.forSize(vn.getSize());
|
||||
long offset = vn.getOffset();
|
||||
for (IntJitType t : orderedLegTypes(fromType)) {
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
String name = "mpl_%s_%x_%d_leg%x".formatted(space.getName(), vn.getOffset(),
|
||||
vn.getSize(), offset);
|
||||
int legSize = t.size();
|
||||
var result = em
|
||||
.emit(legGen::genReadLegToStack, localThis, gen, space, block, off, legSize)
|
||||
.emit(Opnd::createInt, t, name, scope);
|
||||
legs.add(result.opnd());
|
||||
em = result.em();
|
||||
offset += legSize;
|
||||
}
|
||||
MpIntLocalOpnd temp = MpIntLocalOpnd.of(fromType,
|
||||
"mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()), legs);
|
||||
return MpIntToMpInt.INSTANCE.convertOpndToOpnd(em, temp, type, ext, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<int[]>>>
|
||||
genReadToArray(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
Varnode vn, MpIntJitType type, Ext ext, Scope scope, int slack) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR,
|
||||
"mpa_mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()));
|
||||
List<IntJitType> fromLegTypes = orderedLegTypes(MpIntJitType.forSize(vn.getSize()));
|
||||
List<IntJitType> toLegTypes = orderedLegTypes(type);
|
||||
int legsOut = toLegTypes.size();
|
||||
int legsIn = fromLegTypes.size();
|
||||
int defLegs = Integer.min(legsIn, legsOut);
|
||||
em = em
|
||||
.emit(Op::ldc__i, defLegs + slack)
|
||||
.emit(Op::newarray, Types.T_INT)
|
||||
.emit(Op::astore, arr);
|
||||
long offset = vn.getOffset();
|
||||
for (int i = 0; i < defLegs; i++) {
|
||||
IntJitType fromLegType = fromLegTypes.get(i);
|
||||
IntJitType toLegType = toLegTypes.get(i);
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int legSize = fromLegType.size();
|
||||
em = em
|
||||
.emit(Op::aload, arr)
|
||||
.emit(Op::ldc__i, i)
|
||||
.emit(legGen::genReadLegToStack, localThis, gen, space, block, off, legSize)
|
||||
.emit(Opnd::convertIntToInt, fromLegType, toLegType, ext)
|
||||
.emit(Op::iastore);
|
||||
offset += legSize;
|
||||
}
|
||||
return em
|
||||
.emit(MpIntToMpInt::doGenArrExt, arr, legsOut, defLegs, ext, scope)
|
||||
.emit(Op::aload, arr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next> Emitter<N> genWriteFromOpnd(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
Opnd<MpIntJitType> opnd, Varnode vn) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
long offset = vn.getOffset();
|
||||
for (SimpleOpnd<TInt, IntJitType> leg : orderedLegs(opnd)) {
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int legSize = leg.type().size();
|
||||
em = em
|
||||
.emit(leg::read)
|
||||
.emit(legGen::genWriteLegFromStack, localThis, gen, space, block, off, legSize);
|
||||
offset += legSize;
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, TRef<int[]>>>
|
||||
Emitter<N1> genWriteFromArray(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn, Scope scope) {
|
||||
AddressSpace space = vn.getAddress().getAddressSpace();
|
||||
Local<TRef<int[]>> arr = scope.decl(Types.T_INT_ARR,
|
||||
"mpa_mem_%s_%x_%d".formatted(space.getName(), vn.getOffset(), vn.getSize()));
|
||||
var em1 = em
|
||||
.emit(Op::astore, arr);
|
||||
List<IntJitType> legTypes = orderedLegTypes(MpIntJitType.forSize(vn.getSize()));
|
||||
final int legCount = legTypes.size();
|
||||
long offset = vn.getOffset();
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
IntJitType t = legTypes.get(i);
|
||||
long block = offset / BLOCK_SIZE * BLOCK_SIZE;
|
||||
int off = (int) (offset - block);
|
||||
int legSize = t.size();
|
||||
em1 = em1
|
||||
.emit(Op::aload, arr)
|
||||
.emit(Op::ldc__i, i)
|
||||
.emit(Op::iaload)
|
||||
.emit(legGen::genWriteLegFromStack, localThis, gen, space, block, off, legSize);
|
||||
offset += legSize;
|
||||
}
|
||||
return em1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.access;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Local;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An access generator for simple-typed variables
|
||||
*
|
||||
* @param <T> the JVM type of the variable
|
||||
* @param <JT> the p-code type of the variable
|
||||
*/
|
||||
public interface SimpleAccessGen<T extends BPrim<?>, JT extends SimpleJitType<T, JT>>
|
||||
extends AccessGen<JT> {
|
||||
|
||||
/**
|
||||
* Emit code to read a varnode
|
||||
* <p>
|
||||
* If the varnode fits completely in the block (the common case), then this accesses the bytes
|
||||
* from the one block, using the method chosen by size. If the varnode extends into the next
|
||||
* block, then this will split the varnode into two portions according to machine byte order.
|
||||
* Each portion is accessed using the method for the size of that portion. The results are
|
||||
* reassembled into a single operand.
|
||||
*
|
||||
* @param <N> the incoming stack
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @return the code generator with the resulting stack, i.e., having pushed the value
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, T>> genReadToStack(
|
||||
Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Varnode vn);
|
||||
|
||||
/**
|
||||
* Emit code to write a varnode
|
||||
* <p>
|
||||
* If the varnode fits completely in the block (the common case), then this accesses the bytes
|
||||
* from the one block, using the method chosen by size. If the varnode extends into the next
|
||||
* block, then this will split the varnode into two portions according to machine byte order.
|
||||
* Each portion is accessed using the method for the size of that portion.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack having the value on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param vn the varnode
|
||||
* @return the code generator with the resulting stack, i.e., having popped the value
|
||||
*/
|
||||
<THIS extends JitCompiledPassage, N1 extends Next, N0 extends Ent<N1, T>> Emitter<N1>
|
||||
genWriteFromStack(Emitter<N0> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn);
|
||||
}
|
||||
@@ -15,19 +15,15 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_JIT_COMPILED_PASSAGE;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
@@ -66,74 +62,54 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||
* arrays, invoke the method, and then place the result legs on the stack, least-significant leg
|
||||
* on top.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param gen the code generator
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param type the type of the operands
|
||||
* @param methodName the name of the method in {@link JitCompiledPassage} to invoke
|
||||
* @param mv the method visitor
|
||||
* @param overProvisionLeft the number of extra ints to allocate for the left operand's array.
|
||||
* This is to facilitate Knuth's division algorithm, which may require an extra
|
||||
* leading leg in the dividend after normalization.
|
||||
* @param op the p-code op
|
||||
* @param slackLeft the number of extra ints to allocate for the left operand's array. This is
|
||||
* to facilitate Knuth's division algorithm, which may require an extra leading leg
|
||||
* in the dividend after normalization.
|
||||
* @param takeOut indicates which operand of the static method to actually take for the output.
|
||||
* This is to facilitate the remainder operator, because Knuth's algorithm leaves the
|
||||
* remainder where there dividend was.
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the empty stack
|
||||
*/
|
||||
static void generateMpDelegationToStaticMethod(JitCodeGenerator gen, MpIntJitType type,
|
||||
String methodName, MethodVisitor mv, int overProvisionLeft, TakeOut takeOut) {
|
||||
default <THIS extends JitCompiledPassage> Emitter<Bot> genMpDelegationToStaticMethod(
|
||||
Emitter<Bot> em, JitCodeGenerator<THIS> gen, Local<TRef<THIS>> localThis,
|
||||
MpIntJitType type, String methodName, JitBinOp op, int slackLeft, TakeOut takeOut,
|
||||
Scope scope) {
|
||||
/**
|
||||
* The strategy here will be to allocate an array for each of the operands (output and 2
|
||||
* inputs) and then invoke a static method to do the actual operation. It might be nice to
|
||||
* generate inline code for small multiplications, but we're going to leave that for later.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,...,rlegN]
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
int legCount = type.legsAlloc();
|
||||
try (
|
||||
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", legCount);
|
||||
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", legCount)) {
|
||||
// [rleg1,...,rlegN,lleg1,...,llegN]
|
||||
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||
// [lleg1,...,llegN]
|
||||
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||
// []
|
||||
|
||||
switch (takeOut) {
|
||||
case OUT -> {
|
||||
// []
|
||||
mv.visitLdcInsn(legCount);
|
||||
// [count:INT]
|
||||
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||
// [out:INT[count]]
|
||||
mv.visitInsn(DUP);
|
||||
// [out,out]
|
||||
|
||||
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
|
||||
// [inL,out,out]
|
||||
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||
// [inR,inL,out,out]
|
||||
}
|
||||
case LEFT -> {
|
||||
// []
|
||||
mv.visitLdcInsn(legCount);
|
||||
// [count:INT]
|
||||
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||
// [out]
|
||||
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
|
||||
// [inL,out]
|
||||
mv.visitInsn(DUP_X1);
|
||||
// [inL,out,inL]
|
||||
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||
// [inR,inL,out,inL]
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName,
|
||||
MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP, true);
|
||||
// [out||inL:INT[count]]
|
||||
|
||||
// Push the result back, in reverse order
|
||||
OpGen.generateMpLegsFromArray(legCount, mv);
|
||||
var emParams = switch (takeOut) {
|
||||
case OUT -> em
|
||||
.emit(Op::ldc__i, legCount)
|
||||
.emit(Op::newarray, Types.T_INT)
|
||||
.emit(Op::dup)
|
||||
.emit(gen::genReadToArray, localThis, op.l(), type, ext(), scope, slackLeft)
|
||||
.emit(gen::genReadToArray, localThis, op.r(), type, rExt(), scope, 0);
|
||||
case LEFT -> em
|
||||
.emit(Op::aconst_null, Types.T_INT_ARR)
|
||||
.emit(gen::genReadToArray, localThis, op.l(), type, ext(), scope, slackLeft)
|
||||
.emit(Op::dup_x1)
|
||||
.emit(gen::genReadToArray, localThis, op.r(), type, rExt(), scope, 0);
|
||||
};
|
||||
return emParams
|
||||
.emit(Op::invokestatic, GenConsts.T_JIT_COMPILED_PASSAGE, methodName,
|
||||
GenConsts.MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::retVoid)
|
||||
.emit(gen::genWriteFromArray, localThis, op.out(), type, ext(), scope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,63 +140,4 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||
default Ext rExt() {
|
||||
return ext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code between reading the left and right operands
|
||||
*
|
||||
* <p>
|
||||
* This is invoked immediately after emitting code to push the left operand onto the stack,
|
||||
* giving the implementation an opportunity to perform any manipulations of that operand
|
||||
* necessary to set up the operation, before code to push the right operand is emitted.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param op the operator
|
||||
* @param lType the actual type of the left operand
|
||||
* @param rType the actual type of the right operand
|
||||
* @param rv the method visitor
|
||||
* @return the new actual type of the left operand
|
||||
*/
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return lType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code for the binary operator
|
||||
*
|
||||
* <p>
|
||||
* At this point both operands are on the stack. After this returns, code to write the result
|
||||
* from the stack into the destination operand will be emitted.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param op the operator
|
||||
* @param block the block containing the operator
|
||||
* @param lType the actual type of the left operand
|
||||
* @param rType the actual type of the right operand
|
||||
* @param rv the method visitor
|
||||
* @return the actual type of the result
|
||||
*/
|
||||
JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This default implementation emits code to load the left operand, invokes the
|
||||
* {@link #afterLeft(JitCodeGenerator, JitBinOp, JitType, JitType, MethodVisitor) after-left}
|
||||
* hook point, emits code to load the right operand, invokes
|
||||
* {@link #generateBinOpRunCode(JitCodeGenerator, JitBinOp, JitBlock, JitType, JitType, MethodVisitor)
|
||||
* generate-binop}, and finally emits code to write the destination operand.
|
||||
*/
|
||||
@Override
|
||||
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
|
||||
JitType lType = gen.generateValReadCode(op.l(), op.lType(), ext());
|
||||
JitType rType = op.rType().resolve(gen.getTypeModel().typeOf(op.r()));
|
||||
lType = afterLeft(gen, op, lType, rType, rv);
|
||||
JitType checkRType = gen.generateValReadCode(op.r(), op.rType(), rExt());
|
||||
assert checkRType == rType;
|
||||
JitType outType = generateBinOpRunCode(gen, op, block, lType, rType, rv);
|
||||
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
* An extension for bitwise binary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface BitwiseBinOpGen<T extends JitBinOp> extends IntBinOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM opcode to implement this operator with int operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int intOpcode();
|
||||
|
||||
/**
|
||||
* The JVM opcode to implement this operator with long operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int longOpcode();
|
||||
|
||||
/**
|
||||
* The implementation for multi-precision ints.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param type the type of each operand, including the reuslt
|
||||
* @param mv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
default void generateMpIntBinOp(JitCodeGenerator gen, MpIntJitType type,
|
||||
MethodVisitor mv) {
|
||||
/**
|
||||
* We need temp locals to get things in order. Read in right operand, do the op as we pop
|
||||
* each left op. Then push it all back.
|
||||
*
|
||||
* No masking of the result is required, since both operands should already be masked, and
|
||||
* the bitwise op cannot generate bits of more significance.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
// [lleg1,...,llegN:INT]
|
||||
mv.visitVarInsn(ILOAD, result.idx(i));
|
||||
// [lleg1,...,llegN:INT,rlegN:INT]
|
||||
mv.visitInsn(intOpcode());
|
||||
// [lleg1,...,olegN:INT]
|
||||
mv.visitVarInsn(ISTORE, result.idx(i));
|
||||
// [lleg1,...]
|
||||
}
|
||||
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This implementation reduces the need to just the JVM opcode. We simply ensure both operands
|
||||
* have the same size and JVM type, select and emit the correct opcode, and return the type of
|
||||
* the result.
|
||||
*/
|
||||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(intOpcode());
|
||||
case LongJitType t -> rv.visitInsn(longOpcode());
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntBinOp(gen, t, rv);
|
||||
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitBoolAndOp;
|
||||
import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
|
||||
|
||||
@@ -28,17 +36,19 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
|
||||
* behavior," we could technically optimize this by only ANDing the least significant leg
|
||||
* when we're dealing with mp-ints.
|
||||
*/
|
||||
public enum BoolAndOpGen implements BitwiseBinOpGen<JitBoolAndOp> {
|
||||
public enum BoolAndOpGen implements IntBitwiseBinOpGen<JitBoolAndOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IAND;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.iand(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LAND;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.land(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,23 +15,27 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitBoolNegateOp;
|
||||
import ghidra.pcode.opbehavior.OpBehaviorBoolNegate;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitBoolNegateOp bool_negate}.
|
||||
* <p>
|
||||
* This emits ^1, as observed in code emitted by {@code javac}. For multi-precision, we perform that
|
||||
* operation only on the least-significant leg.
|
||||
*
|
||||
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
||||
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
||||
* just like {@link OpBehaviorBoolNegate}.
|
||||
* just like {@link OpBehaviorBoolNegate}. Additionally, boolean operands ought to be a
|
||||
* byte, but certainly no larger than an int (4 bytes).
|
||||
*/
|
||||
public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||
public enum BoolNegateOpGen implements IntOpUnOpGen<JitBoolNegateOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -41,24 +45,34 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitBoolNegateOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case IntJitType t -> {
|
||||
rv.visitLdcInsn(1);
|
||||
rv.visitInsn(IXOR);
|
||||
}
|
||||
case LongJitType t -> {
|
||||
rv.visitLdcInsn(1L);
|
||||
rv.visitInsn(LXOR);
|
||||
}
|
||||
case MpIntJitType t -> {
|
||||
// Least-sig leg is on top, and it's an int.
|
||||
rv.visitLdcInsn(1);
|
||||
rv.visitInsn(IXOR);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
opForInt(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::ixor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
opForLong(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__l, 1)
|
||||
.emit(Op::lxor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitBoolNegateOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
/**
|
||||
* NOTE: This will needlessly overwrite the upper legs of the mp-int output. That said,
|
||||
* Sleigh-spec authors should keep "boolean" operands no larger than an int, preferably a
|
||||
* byte.
|
||||
*/
|
||||
return em
|
||||
.emit(gen::genReadLegToStack, localThis, op.u(), type, 0, ext())
|
||||
.emit(this::opForInt)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), type.legTypesLE().getFirst(),
|
||||
ext(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitBoolOrOp;
|
||||
import ghidra.pcode.opbehavior.OpBehaviorBoolOr;
|
||||
|
||||
@@ -24,18 +32,23 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolOr;
|
||||
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
||||
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
||||
* just like {@link OpBehaviorBoolOr}. Thus, this is identical to {@link IntOrOpGen}.
|
||||
* @implNote Because having bits other than the least significant set in the inputs is "undefined
|
||||
* behavior," we could technically optimize this by only ANDing the least significant leg
|
||||
* when we're dealing with mp-ints.
|
||||
*/
|
||||
public enum BoolOrOpGen implements BitwiseBinOpGen<JitBoolOrOp> {
|
||||
public enum BoolOrOpGen implements IntBitwiseBinOpGen<JitBoolOrOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.ior(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.lor(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitBoolXorOp;
|
||||
import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
|
||||
|
||||
@@ -24,18 +32,23 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolXor;
|
||||
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
||||
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
||||
* just like {@link OpBehaviorBoolXor}. Thus, this is identical to {@link IntXorOpGen}.
|
||||
* @implNote Because having bits other than the least significant set in the inputs is "undefined
|
||||
* behavior," we could technically optimize this by only ANDing the least significant leg
|
||||
* when we're dealing with mp-ints.
|
||||
*/
|
||||
public enum BoolXorOpGen implements BitwiseBinOpGen<JitBoolXorOp> {
|
||||
public enum BoolXorOpGen implements IntBitwiseBinOpGen<JitBoolXorOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IXOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.ixor(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LXOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.lxor(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,21 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitPassage.RIndBranch;
|
||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.BranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.UBranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Dead;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
@@ -52,48 +56,49 @@ public enum BranchIndOpGen implements OpGen<JitBranchIndOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the run method visitor
|
||||
*/
|
||||
static void generateExitCode(JitCodeGenerator gen, JitBranchIndOp op, RegisterValue ctx,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
gen.generatePassageExit(block, () -> {
|
||||
// [...]
|
||||
JitType targetType = gen.generateValReadCode(op.target(), op.targetType(), Ext.ZERO);
|
||||
// [...,target:?]
|
||||
TypeConversions.generateToLong(targetType, LongJitType.I8, Ext.ZERO, rv);
|
||||
// [...,target:LONG]
|
||||
}, ctx, rv);
|
||||
|
||||
rv.visitInsn(ACONST_NULL);
|
||||
rv.visitInsn(ARETURN);
|
||||
static <THIS extends JitCompiledPassage> Emitter<Dead> genExit(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitBranchIndOp op, RegisterValue ctx, JitBlock block) {
|
||||
PcGen tgtGen = PcGen.loadTarget(localThis, gen, op.target());
|
||||
return em
|
||||
.emit(gen::genExit, localThis, block, tgtGen, ctx)
|
||||
.emit(Op::aconst_null, GenConsts.T_ENTRY_POINT)
|
||||
.emit(Op::areturn, retReq);
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator for indirect branches
|
||||
*/
|
||||
static class IndBranchGen extends BranchGen<RIndBranch, JitBranchIndOp> {
|
||||
static class IndBranchGen extends UBranchGen<RIndBranch, JitBranchIndOp> {
|
||||
/** Singleton */
|
||||
static final IndBranchGen IND = new IndBranchGen();
|
||||
|
||||
@Override
|
||||
Address exit(JitCodeGenerator gen, RIndBranch branch) {
|
||||
Address exit(JitCodeGenerator<?> gen, RIndBranch branch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitBranchIndOp op, RIndBranch branch,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
generateExitCode(gen, op, branch.flowCtx(), block, rv);
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitBranchIndOp op, RIndBranch branch, JitBlock block) {
|
||||
return genExit(em, localThis, retReq, gen, op, branch.flowCtx(), block);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithCtxmod(JitCodeGenerator gen, JitBranchIndOp op, Address exit,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
generateExitCode(gen, op, null, block, rv);
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitBranchIndOp op,
|
||||
Address exit, JitBlock block) {
|
||||
return genExit(em, localThis, retReq, gen, op, null, block);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitBranchIndOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
IndBranchGen.IND.generateCode(gen, op, op.branch(), block, rv);
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitBranchIndOp op, JitBlock block, Scope scope) {
|
||||
return new DeadOpResult(IndBranchGen.IND.genRun(
|
||||
em, localThis, localCtxmod, retReq, gen, op, op.branch(), block));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,21 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.gen.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.CBranchOpGen.ExtCBranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.CBranchOpGen.IntCBranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.op.JitBranchOp;
|
||||
import ghidra.pcode.emu.jit.op.JitOp;
|
||||
@@ -35,7 +41,7 @@ import ghidra.program.model.lang.RegisterValue;
|
||||
*
|
||||
* <p>
|
||||
* With an {@link IntBranch} record, this simply looks up the label for the target block and emits a
|
||||
* block transition followed by a {@link #GOTO goto}.
|
||||
* block transition followed by a {@link Op#goto_(Emitter) goto}.
|
||||
*
|
||||
* <p>
|
||||
* With an {@link ExtBranch} record, this emits code to retire the target to the program counter,
|
||||
@@ -56,13 +62,10 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the run method visitor
|
||||
*/
|
||||
static void generateRetireCode(JitCodeGenerator gen, Address exit, RegisterValue ctx,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
gen.generatePassageExit(block, () -> {
|
||||
// [...]
|
||||
rv.visitLdcInsn(exit.getOffset());
|
||||
// [...,target:LONG]
|
||||
}, ctx, rv);
|
||||
static <THIS extends JitCompiledPassage> Emitter<Bot> genRetire(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, Address exit,
|
||||
RegisterValue ctx, JitBlock block) {
|
||||
return gen.genExit(em, localThis, block, PcGen.loadOffset(exit), ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,20 +79,24 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the run method visitor
|
||||
*/
|
||||
static void generateExitCode(JitCodeGenerator gen, Address exit, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
generateRetireCode(gen, exit, null, block, rv);
|
||||
rv.visitInsn(ACONST_NULL);
|
||||
rv.visitInsn(ARETURN);
|
||||
static <THIS extends JitCompiledPassage> Emitter<Dead> genExit(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, Address exit, JitBlock block) {
|
||||
return em
|
||||
.emit(BranchOpGen::genRetire, localThis, gen, exit, (RegisterValue) null, block)
|
||||
.emit(Op::aconst_null, GenConsts.T_ENTRY_POINT)
|
||||
.emit(Op::areturn, retReq);
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator
|
||||
*
|
||||
* @param <NR> the stack after the JVM branch bytecode (may be {@link Dead})
|
||||
* @param <NI> the stack before the JVM branch bytecode (cannot be {@link Dead})
|
||||
* @param <TB> the type of branch
|
||||
* @param <TO> the type of op
|
||||
*/
|
||||
static abstract class BranchGen<TB extends RBranch, TO extends JitOp> {
|
||||
static abstract class BranchGen<NR, NI extends Next, TB extends RBranch, TO extends JitOp> {
|
||||
/**
|
||||
* Get the target address of the branch
|
||||
*
|
||||
@@ -97,7 +104,7 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param branch the branch
|
||||
* @return the target address
|
||||
*/
|
||||
abstract Address exit(JitCodeGenerator gen, TB branch);
|
||||
abstract Address exit(JitCodeGenerator<?> gen, TB branch);
|
||||
|
||||
/**
|
||||
* Generate code for the branch in the case a context modification has not occurred.
|
||||
@@ -111,8 +118,9 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
abstract void generateCodeWithoutCtxmod(JitCodeGenerator gen, TO op, TB branch,
|
||||
JitBlock block, MethodVisitor rv);
|
||||
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRunWithoutCtxmod(Emitter<NI> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, TO op, TB branch, JitBlock block);
|
||||
|
||||
/**
|
||||
* Generate code for the branch in the case a context modification may have occurred.
|
||||
@@ -127,8 +135,10 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
abstract void generateCodeWithCtxmod(JitCodeGenerator gen, TO op, Address exit,
|
||||
JitBlock block, MethodVisitor rv);
|
||||
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRunWithCtxmod(Emitter<NI> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, Address exit,
|
||||
JitBlock block);
|
||||
|
||||
/**
|
||||
* Emit code that jumps or exits via a direct branch
|
||||
@@ -139,95 +149,140 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
||||
* @param block the block containing the op
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
void generateCode(JitCodeGenerator gen, TO op, TB branch, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
switch (branch.reach()) {
|
||||
case WITH_CTXMOD -> generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
|
||||
case WITHOUT_CTXMOD -> generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||
abstract <THIS extends JitCompiledPassage> Emitter<NR> genRun(Emitter<NI> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
|
||||
JitBlock block);
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract branch code generator for unconditional branches.
|
||||
*
|
||||
* @param <TB> the type of branch
|
||||
* @param <TO> the type of op
|
||||
*/
|
||||
abstract static class UBranchGen<TB extends RBranch, TO extends JitOp>
|
||||
extends BranchGen<Dead, Bot, TB, TO> {
|
||||
/**
|
||||
* Emit code that jumps or exits via a direct branch
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param op the branch op
|
||||
* @param branch the branch from the op
|
||||
* @param block the block containing the op
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
@Override
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
|
||||
JitBlock block) {
|
||||
return switch (branch.reach()) {
|
||||
case WITH_CTXMOD -> genRunWithCtxmod(em, localThis, localCtxmod, retReq, gen, op,
|
||||
exit(gen, branch), block);
|
||||
case WITHOUT_CTXMOD -> genRunWithoutCtxmod(em, localThis, retReq, gen, op, branch,
|
||||
block);
|
||||
case MAYBE_CTXMOD -> {
|
||||
Label withModctx = new Label();
|
||||
RunFixedLocal.CTXMOD.generateLoadCode(rv);
|
||||
rv.visitJumpInsn(IFNE, withModctx);
|
||||
generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||
rv.visitLabel(withModctx);
|
||||
generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
|
||||
var emIf = em
|
||||
.emit(Op::iload, localCtxmod)
|
||||
.emit(Op::ifne);
|
||||
yield emIf.em()
|
||||
.emit(this::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
|
||||
block)
|
||||
.emit(Lbl::placeDead, emIf.lbl())
|
||||
// NB. genRun is already branching. No need for if-else construct.
|
||||
.emit(this::genRunWithCtxmod, localThis, localCtxmod, retReq, gen, op,
|
||||
exit(gen, branch), block);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator for internal branches
|
||||
*
|
||||
* @implNote We leave {@code TO:=}{@link JitOp} here, because we want {@link IntCBranchGen} to
|
||||
* be able to delegate to this instance.
|
||||
*/
|
||||
static class IntBranchGen extends BranchGen<RIntBranch, JitOp> {
|
||||
static class IntBranchGen extends UBranchGen<RIntBranch, JitOp> {
|
||||
/** Singleton */
|
||||
static final IntBranchGen INT = new IntBranchGen();
|
||||
|
||||
@Override
|
||||
Address exit(JitCodeGenerator gen, RIntBranch branch) {
|
||||
Address exit(JitCodeGenerator<?> gen, RIntBranch branch) {
|
||||
return gen.getAddressForOp(branch.to());
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitOp op, RIntBranch branch, JitBlock block) {
|
||||
JitBlock target = block.getTargetBlock(branch);
|
||||
Label label = gen.labelForBlock(target);
|
||||
VarGen.computeBlockTransition(gen, block, target).generate(rv);
|
||||
rv.visitJumpInsn(GOTO, label);
|
||||
Lbl<Bot> label = gen.labelForBlock(target);
|
||||
return em
|
||||
.emit(VarGen.computeBlockTransition(localThis, gen, block, target)::genFwd)
|
||||
.emit(Op::goto_, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
generateExitCode(gen, exit, block, rv);
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitOp op, Address exit,
|
||||
JitBlock block) {
|
||||
return genExit(em, localThis, retReq, gen, exit, block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator for external branches
|
||||
*
|
||||
* @implNote We leave {@code TO:=}{@link JitOp} here, because we want {@link ExtCBranchGen} to
|
||||
* be able to delegate to this instance.
|
||||
*/
|
||||
static class ExtBranchGen extends BranchGen<RExtBranch, JitOp> {
|
||||
static class ExtBranchGen extends UBranchGen<RExtBranch, JitOp> {
|
||||
/** Singleton */
|
||||
static final ExtBranchGen EXT = new ExtBranchGen();
|
||||
|
||||
@Override
|
||||
Address exit(JitCodeGenerator gen, RExtBranch branch) {
|
||||
Address exit(JitCodeGenerator<?> gen, RExtBranch branch) {
|
||||
return branch.to().address;
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithoutCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitOp op, RExtBranch branch, JitBlock block) {
|
||||
AddrCtx exit = branch.to();
|
||||
FieldForExitSlot slotField = gen.requestFieldForExitSlot(exit);
|
||||
|
||||
generateRetireCode(gen, exit.address, exit.rvCtx, block, rv);
|
||||
|
||||
// []
|
||||
slotField.generateLoadCode(gen, rv);
|
||||
// [slot]
|
||||
rv.visitMethodInsn(INVOKESTATIC, GenConsts.NAME_JIT_COMPILED_PASSAGE, "getChained",
|
||||
GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true);
|
||||
// [chained:ENTRY]
|
||||
rv.visitInsn(ARETURN);
|
||||
return em
|
||||
.emit(BranchOpGen::genRetire, localThis, gen, exit.address, exit.rvCtx, block)
|
||||
.emit(slotField::genLoad, localThis, gen)
|
||||
.emit(Op::invokestatic, GenConsts.T_JIT_COMPILED_PASSAGE, "getChained",
|
||||
GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::areturn, retReq);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
generateExitCode(gen, exit, block, rv);
|
||||
<THIS extends JitCompiledPassage> Emitter<Dead> genRunWithCtxmod(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitOp op, Address exit,
|
||||
JitBlock block) {
|
||||
return genExit(em, localThis, retReq, gen, exit, block);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitBranchOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
switch (op.branch()) {
|
||||
case RIntBranch ib -> IntBranchGen.INT.generateCode(gen, op, ib, block, rv);
|
||||
case RExtBranch eb -> ExtBranchGen.EXT.generateCode(gen, op, eb, block, rv);
|
||||
public <THIS extends JitCompiledPassage> DeadOpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitBranchOp op, JitBlock block, Scope scope) {
|
||||
return new DeadOpResult(switch (op.branch()) {
|
||||
case RIntBranch ib -> IntBranchGen.INT.genRun(em, localThis, localCtxmod, retReq, gen,
|
||||
op, ib, block);
|
||||
case RExtBranch eb -> ExtBranchGen.EXT.genRun(em, localThis, localCtxmod, retReq, gen,
|
||||
op, eb, block);
|
||||
default -> throw new AssertionError("Branch type confusion");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,23 +15,22 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowModel;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.ExtBranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.IntBranchGen;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.*;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
||||
import ghidra.pcode.emu.jit.op.JitOp;
|
||||
import ghidra.pcode.emu.jit.var.JitFailVal;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
@@ -44,76 +43,138 @@ import ghidra.program.model.pcode.PcodeOp;
|
||||
*
|
||||
* <p>
|
||||
* With an {@link IntBranch} record, this looks up the label for the target block and checks if a
|
||||
* transition is necessary. If one is necessary, it emits an {@link #IFEQ ifeq} with the transition
|
||||
* and {@link #GOTO goto} it guards. The {@code ifeq} skips to the fall-through case. If a
|
||||
* transition is not necessary, it simply emits an {@link #IFNE ifne} to the target label.
|
||||
* transition is necessary. If one is necessary, it emits an {@link Op#ifeq(Emitter) ifeq} with the
|
||||
* transition and {@link Op#goto_(Emitter) goto} it guards. The {@code ifeq} skips to the
|
||||
* fall-through case. If a transition is not necessary, it simply emits an {@link Op#ifne(Emitter)
|
||||
* ifne} to the target label.
|
||||
*
|
||||
* <p>
|
||||
* With an {@link ExtBranch} record, this does the same as {@link BranchOpGen} but guarded by an
|
||||
* {@link #IFEQ ifeq} that skips to the fall-through case.
|
||||
* {@link Op#ifeq(Emitter) ifeq} that skips to the fall-through case.
|
||||
*/
|
||||
public enum CBranchOpGen implements OpGen<JitCBranchOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
/**
|
||||
* An abstract branch code generator for conditional branches.
|
||||
*
|
||||
* @param <TB> the type of branch
|
||||
* @param <TO> the type of op
|
||||
*/
|
||||
abstract static class CBranchGen<TB extends RBranch, TO extends JitCBranchOp>
|
||||
extends BranchGen<Bot, Ent<Bot, TInt>, TB, TO> {
|
||||
@Override
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRun(Emitter<Ent<Bot, TInt>> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, TO op, TB branch,
|
||||
JitBlock block) {
|
||||
return switch (branch.reach()) {
|
||||
case WITH_CTXMOD -> genRunWithCtxmod(em, localThis, localCtxmod, retReq, gen, op,
|
||||
exit(gen, branch), block);
|
||||
case WITHOUT_CTXMOD -> genRunWithoutCtxmod(em, localThis, retReq, gen, op, branch,
|
||||
block);
|
||||
case MAYBE_CTXMOD -> {
|
||||
var lblIf = em.emit(Op::iload, localCtxmod)
|
||||
.emit(Op::ifne);
|
||||
var lblGoto = lblIf.em()
|
||||
.emit(this::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
|
||||
block)
|
||||
.emit(Op::goto_);
|
||||
yield lblGoto.em()
|
||||
.emit(Lbl::placeDead, lblIf.lbl())
|
||||
.emit(this::genRunWithCtxmod, localThis, localCtxmod, retReq, gen, op,
|
||||
exit(gen, branch), block)
|
||||
.emit(Lbl::place, lblGoto.lbl());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator for internal conditional branches
|
||||
*/
|
||||
static class IntCBranchGen extends IntBranchGen {
|
||||
static class IntCBranchGen extends CBranchGen<RIntBranch, JitCBranchOp> {
|
||||
/** Singleton */
|
||||
static final IntCBranchGen C_INT = new IntCBranchGen();
|
||||
|
||||
@Override
|
||||
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
JitBlock target = block.getTargetBlock(branch);
|
||||
Label label = gen.labelForBlock(target);
|
||||
BlockTransition transition = VarGen.computeBlockTransition(gen, block, target);
|
||||
if (transition.needed()) {
|
||||
Label fall = new Label();
|
||||
rv.visitJumpInsn(IFEQ, fall);
|
||||
transition.generate(rv);
|
||||
rv.visitJumpInsn(GOTO, label);
|
||||
rv.visitLabel(fall);
|
||||
}
|
||||
else {
|
||||
rv.visitJumpInsn(IFNE, label);
|
||||
}
|
||||
Address exit(JitCodeGenerator<?> gen, RIntBranch branch) {
|
||||
return IntBranchGen.INT.exit(gen, branch);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
Label fall = new Label();
|
||||
rv.visitJumpInsn(IFEQ, fall);
|
||||
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
|
||||
rv.visitLabel(fall);
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithoutCtxmod(
|
||||
Emitter<Ent<Bot, TInt>> em, Local<TRef<THIS>> localThis,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
|
||||
RIntBranch branch, JitBlock block) {
|
||||
JitBlock target = block.getTargetBlock(branch);
|
||||
Lbl<Bot> label = gen.labelForBlock(target);
|
||||
BlockTransition<THIS> transition =
|
||||
VarGen.computeBlockTransition(localThis, gen, block, target);
|
||||
|
||||
if (!transition.needed()) {
|
||||
return em
|
||||
.emit(Op::ifne, label);
|
||||
}
|
||||
var lblFall = em
|
||||
.emit(Op::ifeq);
|
||||
return lblFall.em()
|
||||
.emit(transition::genFwd)
|
||||
.emit(Op::goto_, label)
|
||||
.emit(Lbl::placeDead, lblFall.lbl());
|
||||
}
|
||||
|
||||
@Override
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithCtxmod(Emitter<Ent<Bot, TInt>> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
|
||||
Address exit, JitBlock block) {
|
||||
var lblFall = em
|
||||
.emit(Op::ifeq);
|
||||
return lblFall.em()
|
||||
.emit(IntBranchGen.INT::genRunWithCtxmod, localThis, localCtxmod, retReq, gen,
|
||||
op, exit, block)
|
||||
.emit(Lbl::placeDead, lblFall.lbl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch code generator for external conditional branches
|
||||
*/
|
||||
static class ExtCBranchGen extends ExtBranchGen {
|
||||
static class ExtCBranchGen extends CBranchGen<RExtBranch, JitCBranchOp> {
|
||||
/** Singleton */
|
||||
static final ExtCBranchGen C_EXT = new ExtCBranchGen();
|
||||
|
||||
@Override
|
||||
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
Label fall = new Label();
|
||||
rv.visitJumpInsn(IFEQ, fall);
|
||||
super.generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||
rv.visitLabel(fall);
|
||||
Address exit(JitCodeGenerator<?> gen, RExtBranch branch) {
|
||||
return ExtBranchGen.EXT.exit(gen, branch);
|
||||
}
|
||||
|
||||
@Override
|
||||
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
Label fall = new Label();
|
||||
rv.visitJumpInsn(IFEQ, fall);
|
||||
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
|
||||
rv.visitLabel(fall);
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithoutCtxmod(
|
||||
Emitter<Ent<Bot, TInt>> em, Local<TRef<THIS>> localThis,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
|
||||
RExtBranch branch, JitBlock block) {
|
||||
var lblFall = em
|
||||
.emit(Op::ifeq);
|
||||
return lblFall.em()
|
||||
.emit(ExtBranchGen.EXT::genRunWithoutCtxmod, localThis, retReq, gen, op, branch,
|
||||
block)
|
||||
.emit(Lbl::placeDead, lblFall.lbl());
|
||||
}
|
||||
|
||||
@Override
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunWithCtxmod(Emitter<Ent<Bot, TInt>> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod,
|
||||
RetReq<TRef<EntryPoint>> retReq, JitCodeGenerator<THIS> gen, JitCBranchOp op,
|
||||
Address exit, JitBlock block) {
|
||||
var lblFall = em
|
||||
.emit(Op::ifeq);
|
||||
return lblFall.em()
|
||||
.emit(ExtBranchGen.EXT::genRunWithCtxmod, localThis, localCtxmod, retReq, gen,
|
||||
op, exit, block)
|
||||
.emit(Lbl::placeDead, lblFall.lbl());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,24 +196,27 @@ public enum CBranchOpGen implements OpGen<JitCBranchOp> {
|
||||
* which will ensure we apply special handling here.
|
||||
*/
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitCBranchOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
public <THIS extends JitCompiledPassage> LiveOpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitCBranchOp op, JitBlock block, Scope scope) {
|
||||
if (op.op() instanceof ExitPcodeOp && op.branch() instanceof RExtBranch eb) {
|
||||
assert eb.reach() == Reachability.MAYBE_CTXMOD;
|
||||
Label fall = new Label();
|
||||
RunFixedLocal.CTXMOD.generateLoadCode(rv);
|
||||
rv.visitJumpInsn(IFEQ, fall);
|
||||
BranchOpGen.generateExitCode(gen, eb.to().address, block, rv);
|
||||
rv.visitLabel(fall);
|
||||
return;
|
||||
var lblFall = em
|
||||
.emit(Op::iload, localCtxmod)
|
||||
.emit(Op::ifeq);
|
||||
return new LiveOpResult(lblFall.em()
|
||||
.emit(BranchOpGen::genExit, localThis, retReq, gen, eb.to().address, block)
|
||||
.emit(Lbl::placeDead, lblFall.lbl()));
|
||||
}
|
||||
|
||||
JitType cType = gen.generateValReadCode(op.cond(), op.condType(), Ext.ZERO);
|
||||
TypeConversions.generateIntToBool(cType, rv);
|
||||
switch (op.branch()) {
|
||||
case RIntBranch ib -> IntCBranchGen.C_INT.generateCode(gen, op, ib, block, rv);
|
||||
case RExtBranch eb -> ExtCBranchGen.C_EXT.generateCode(gen, op, eb, block, rv);
|
||||
default -> throw new AssertionError("Branch type confusion");
|
||||
}
|
||||
var emBool = gen.genReadToBool(em, localThis, op.cond());
|
||||
|
||||
return new LiveOpResult(switch (op.branch()) {
|
||||
case RIntBranch ib -> IntCBranchGen.C_INT.genRun(emBool, localThis, localCtxmod, retReq,
|
||||
gen, op, ib, block);
|
||||
case RExtBranch eb -> ExtCBranchGen.C_EXT.genRun(emBool, localThis, localCtxmod, retReq,
|
||||
gen, op, eb, block);
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,19 @@
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_SLEIGH_LINK_EXCEPTION__$INIT;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_SLEIGH_LINK_EXCEPTION;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_SLEIGH_LINK_EXCEPTION;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitCallOtherMissingOp;
|
||||
import ghidra.pcode.exec.SleighLinkException;
|
||||
|
||||
@@ -37,23 +44,21 @@ public enum CallOtherMissingOpGen implements OpGen<JitCallOtherMissingOp> {
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitCallOtherMissingOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
gen.generatePassageExit(block, () -> {
|
||||
rv.visitLdcInsn(gen.getAddressForOp(op.op()).getOffset());
|
||||
}, gen.getExitContext(op.op()), rv);
|
||||
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitCallOtherMissingOp op, JitBlock block, Scope scope) {
|
||||
String message = gen.getErrorMessage(op.op());
|
||||
// [...]
|
||||
rv.visitTypeInsn(NEW, NAME_SLEIGH_LINK_EXCEPTION);
|
||||
// [...,error:NEW]
|
||||
rv.visitInsn(DUP);
|
||||
// [...,error:NEW,error:NEW]
|
||||
rv.visitLdcInsn(message);
|
||||
// [...,error:NEW,error:NEW,message]
|
||||
rv.visitMethodInsn(INVOKESPECIAL, NAME_SLEIGH_LINK_EXCEPTION, "<init>",
|
||||
MDESC_SLEIGH_LINK_EXCEPTION__$INIT, false);
|
||||
// [...,error]
|
||||
rv.visitInsn(ATHROW);
|
||||
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op.op()));
|
||||
return new DeadOpResult(em
|
||||
.emit(gen::genExit, localThis, block, pcGen, gen.getExitContext(op.op()))
|
||||
.emit(Op::new_, T_SLEIGH_LINK_EXCEPTION)
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__a, message)
|
||||
.emit(Op::invokespecial, T_SLEIGH_LINK_EXCEPTION, "<init>",
|
||||
MDESC_SLEIGH_LINK_EXCEPTION__$INIT, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::retVoid)
|
||||
.emit(Op::athrow));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,31 +19,33 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
||||
import ghidra.pcode.emu.jit.analysis.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcGen;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||
import ghidra.pcode.emu.jit.op.JitCallOtherDefOp;
|
||||
import ghidra.pcode.emu.jit.op.JitCallOtherOpIf;
|
||||
import ghidra.pcode.emu.jit.var.JitVal;
|
||||
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.OpOutput;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
@@ -53,9 +55,9 @@ import ghidra.program.model.pcode.Varnode;
|
||||
*
|
||||
* <p>
|
||||
* The checks if Direct invocation is possible. If so, it emits code using
|
||||
* {@link #generateRunCodeUsingDirectStrategy(JitCodeGenerator, JitCallOtherOpIf, JitBlock, MethodVisitor)}.
|
||||
* {@link #genRunDirectStrategy(Emitter, Local, JitCodeGenerator, JitCallOtherOpIf, JitBlock, Scope)}.
|
||||
* If not, it emits code using
|
||||
* {@link #generateRunCodeUsingRetirementStrategy(JitCodeGenerator, PcodeOp, JitBlock, PcodeUseropDefinition, MethodVisitor)}.
|
||||
* {@link #genRunRetirementStrategy(Emitter, Local, JitCodeGenerator, PcodeOp, JitBlock, PcodeUseropDefinition)}.
|
||||
* Direct invocation is possible when the userop is {@link PcodeUseropDefinition#isFunctional()
|
||||
* functional} and all of its parameters and return type have a supported primitive type.
|
||||
* ({@code char} is not supported.) Regarding the invocation strategies, see
|
||||
@@ -83,82 +85,80 @@ import ghidra.program.model.pcode.Varnode;
|
||||
* stack. We then emit the invocation of the Java method, guarded by the exception handler. We then
|
||||
* have to consider whether the userop has an output operand and whether its definition returns a
|
||||
* value. If both are true, we emit code to write the result. If neither is true, we're done. If a
|
||||
* result is returned, but no output operand is provided, we <em>must</em> still emit a {@link #POP
|
||||
* pop}.
|
||||
* result is returned, but no output operand is provided, we <em>must</em> still emit a
|
||||
* {@link Op#pop(Emitter) pop}.
|
||||
*/
|
||||
public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
private static <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<Varnode>>>
|
||||
genLoadVarnodeOrNull(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
if (vn == null) {
|
||||
return em.emit(Op::aconst_null, T_VARNODE);
|
||||
}
|
||||
FieldForVarnode field = gen.requestStaticFieldForVarnode(vn);
|
||||
return em.emit(field::genLoad, gen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code to implement the Standard strategy (see the class documentation)
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param block the block containing the op
|
||||
* @param userop the userop definition, wrapped by the {@link JitDataFlowUseropLibrary}
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
* @return the result of emitting the userop's bytecode
|
||||
*/
|
||||
public static void generateRunCodeUsingRetirementStrategy(JitCodeGenerator gen, PcodeOp op,
|
||||
JitBlock block, PcodeUseropDefinition<?> userop, MethodVisitor rv) {
|
||||
public static <THIS extends JitCompiledPassage> OpResult genRunRetirementStrategy(
|
||||
Emitter<Bot> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen,
|
||||
PcodeOp op, JitBlock block, PcodeUseropDefinition<?> userop) {
|
||||
/**
|
||||
* This is about the simplest (laziest) approach we could take for the moment, but it should
|
||||
* suffice, depending on the frequency of CALLOTHER executions. We immediately retire all
|
||||
* variables, then invoke the userop as it would be by the p-code interpreter. It can access
|
||||
* its variables in the usual fashion. Although not ideal, it can also feed the executor
|
||||
* (interpreter) ops to execute --- they won't be jitted here. Then, we liven the variables
|
||||
* back.
|
||||
*
|
||||
* This is about the simplest (laziest) approach we could take, but it should suffice when
|
||||
* we cannot invoke directly. We immediately retire all variables, then invoke the userop as
|
||||
* it would be by the p-code interpreter. It can access its variables in the usual fashion.
|
||||
* Although not ideal, it can also feed the executor (interpreter) ops to execute --- they
|
||||
* won't be jitted here. Then, we liven the variables back.
|
||||
* <p>
|
||||
* NOTE: The output variable should be "alive", so we need not store it into a local. It'll
|
||||
* be made alive in the return block transition.
|
||||
*
|
||||
* TODO: Implement direct invocation for functional userops. NOTE: Cannot avoid block
|
||||
* retirement and re-birth unless I also do direct invocation. Otherwise, the parameters are
|
||||
* read from the state instead of from the local variables.
|
||||
*/
|
||||
BlockTransition transition = VarGen.computeBlockTransition(gen, block, null);
|
||||
transition.generate(rv);
|
||||
BlockTransition<THIS> transition =
|
||||
VarGen.computeBlockTransition(localThis, gen, block, null);
|
||||
|
||||
gen.generateRetirePcCtx(() -> {
|
||||
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
|
||||
}, gen.getExitContext(op), RetireMode.SET, rv);
|
||||
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op));
|
||||
|
||||
// []
|
||||
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||
// [this]
|
||||
gen.requestFieldForUserop(userop).generateLoadCode(gen, rv);
|
||||
// [this,userop]
|
||||
var emArr = em
|
||||
.emit(transition::genFwd)
|
||||
.emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op),
|
||||
RetireMode.SET)
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(gen.requestFieldForUserop(userop)::genLoad, localThis, gen)
|
||||
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getOutput())
|
||||
.emit(Op::ldc__i, op.getNumInputs() - 1)
|
||||
.emit(Op::anewarray, T_VARNODE);
|
||||
|
||||
if (op.getOutput() == null) {
|
||||
rv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
else {
|
||||
gen.requestStaticFieldForVarnode(op.getOutput()).generateLoadCode(gen, rv);
|
||||
}
|
||||
// [this,userop,outVn]
|
||||
|
||||
rv.visitLdcInsn(op.getNumInputs() - 1);
|
||||
rv.visitTypeInsn(ANEWARRAY, NAME_VARNODE);
|
||||
// [this,userop,outVn,inVns:ARR]
|
||||
for (int i = 1; i < op.getNumInputs(); i++) {
|
||||
// [this,userop,outVn,inVns:ARR]
|
||||
rv.visitInsn(DUP);
|
||||
// [this,userop,outVn,inVns:ARR,inVns:ARR]
|
||||
rv.visitLdcInsn(i - 1);
|
||||
// [this,userop,outVn,inVns:ARR,inVns:ARR,index]
|
||||
// Yes, including constants :/
|
||||
Varnode input = op.getInput(i);
|
||||
gen.requestStaticFieldForVarnode(input).generateLoadCode(gen, rv);
|
||||
// [this,userop,outVn,inVns:ARR,inVns:ARR,index,inVn]
|
||||
rv.visitInsn(AASTORE);
|
||||
// [this,userop,outVn,inVns:ARR]
|
||||
emArr = emArr
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, i - 1)
|
||||
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getInput(i))
|
||||
.emit(Op::aastore);
|
||||
}
|
||||
// [this,userop,outVn,inVns:ARR]
|
||||
|
||||
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "invokeUserop",
|
||||
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP, true);
|
||||
|
||||
transition.generateInv(rv);
|
||||
return new LiveOpResult(emArr
|
||||
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "invokeUserop",
|
||||
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::retVoid)
|
||||
.emit(transition::genInv));
|
||||
}
|
||||
|
||||
static Parameter findOutputParameter(Parameter[] parameters, Method method) {
|
||||
@@ -189,13 +189,19 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
/**
|
||||
* Emit code to implement the Direct strategy (see the class documentation)
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op use-def node
|
||||
* @param block the block containing the op
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the result of emitting the userop's bytecode
|
||||
*/
|
||||
public static void generateRunCodeUsingDirectStrategy(JitCodeGenerator gen,
|
||||
JitCallOtherOpIf op, JitBlock block, MethodVisitor rv) {
|
||||
public static <THIS extends JitCompiledPassage, LIB extends PcodeUseropLibrary<?>> OpResult
|
||||
genRunDirectStrategy(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitCallOtherOpIf op,
|
||||
JitBlock block, Scope scope) {
|
||||
FieldForUserop useropField = gen.requestFieldForUserop(op.userop());
|
||||
|
||||
// Set<Varnode> live = gen.vsm.getLiveVars(block);
|
||||
@@ -203,23 +209,9 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
* NOTE: It doesn't matter if there are live variables. We still have to "retire" the
|
||||
* program counter and contextreg if the userop throws an exception.
|
||||
*/
|
||||
final Label tryStart = new Label();
|
||||
final Label tryEnd = new Label();
|
||||
rv.visitTryCatchBlock(tryStart, tryEnd,
|
||||
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).label(), NAME_THROWABLE);
|
||||
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
|
||||
// []
|
||||
useropField.generateLoadCode(gen, rv);
|
||||
// [userop]
|
||||
rv.visitMethodInsn(INVOKEINTERFACE, NAME_PCODE_USEROP_DEFINITION, "getDefiningLibrary",
|
||||
MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY, true);
|
||||
// [library:PcodeUseropLibrary]
|
||||
Method method = op.userop().getJavaMethod();
|
||||
String owningLibName = Type.getInternalName(method.getDeclaringClass());
|
||||
rv.visitTypeInsn(CHECKCAST, owningLibName);
|
||||
// [library:OWNING_TYPE]
|
||||
|
||||
Parameter[] parameters = method.getParameters();
|
||||
Parameter outputParameter = findOutputParameter(parameters, method);
|
||||
if (outputParameter != null && method.getReturnType() != void.class) {
|
||||
@@ -228,100 +220,138 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
It's applied to %s of %s""".formatted(
|
||||
OpOutput.class.getSimpleName(), outputParameter, method.getName()));
|
||||
}
|
||||
try (JvmTempAlloc out =
|
||||
am.allocateTemp(rv, "out", int[].class, outputParameter == null ? 0 : 1)) {
|
||||
MpIntJitType outMpType;
|
||||
if (outputParameter != null) {
|
||||
if (!(op instanceof JitCallOtherDefOp defOp)) {
|
||||
outMpType = null;
|
||||
rv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
else {
|
||||
outMpType = MpIntJitType.forSize(defOp.out().size());
|
||||
rv.visitLdcInsn(outMpType.legsAlloc());
|
||||
rv.visitIntInsn(NEWARRAY, T_INT);
|
||||
}
|
||||
rv.visitVarInsn(ASTORE, out.idx(0));
|
||||
|
||||
Local<TRef<int[]>> localOut;
|
||||
MpIntJitType outMpType;
|
||||
|
||||
if (outputParameter != null) {
|
||||
localOut = scope.decl(Types.T_INT_ARR, "out");
|
||||
if (op instanceof JitCallOtherDefOp defOp) {
|
||||
outMpType = MpIntJitType.forSize(defOp.out().size());
|
||||
em = em
|
||||
.emit(Op::ldc__i, outMpType.legsAlloc())
|
||||
.emit(Op::newarray, Types.T_INT)
|
||||
.emit(Op::astore, localOut);
|
||||
}
|
||||
else {
|
||||
outMpType = null;
|
||||
}
|
||||
|
||||
int argIdx = 0;
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
Parameter p = parameters[i];
|
||||
|
||||
if (p == outputParameter) {
|
||||
rv.visitVarInsn(ALOAD, out.idx(0));
|
||||
continue;
|
||||
}
|
||||
|
||||
JitVal arg = op.args().get(argIdx++);
|
||||
|
||||
// TODO: Should this always be zero extension?
|
||||
JitType type = gen.generateValReadCode(arg, JitTypeBehavior.ANY, Ext.ZERO);
|
||||
if (p.getType() == boolean.class) {
|
||||
TypeConversions.generateIntToBool(type, rv);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p.getType() == int[].class) {
|
||||
MpIntJitType mpType = MpIntJitType.forSize(type.size());
|
||||
// NOTE: Would be nice to have annotation specify signedness
|
||||
TypeConversions.generate(gen, type, mpType, Ext.ZERO, rv);
|
||||
int legCount = mpType.legsAlloc();
|
||||
try (JvmTempAlloc temp = am.allocateTemp(rv, "temp", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(temp, legCount, rv);
|
||||
OpGen.generateMpLegsIntoArray(temp, legCount, legCount, rv);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some primitive/simple type
|
||||
// TODO: Should this always be zero extension? Can annotation specify?
|
||||
TypeConversions.generate(gen, type, JitType.forJavaType(p.getType()), Ext.ZERO,
|
||||
rv);
|
||||
}
|
||||
// [library,params...]
|
||||
rv.visitLabel(tryStart);
|
||||
rv.visitMethodInsn(INVOKEVIRTUAL, owningLibName, method.getName(),
|
||||
Type.getMethodDescriptor(method), false);
|
||||
// [return?]
|
||||
rv.visitLabel(tryEnd);
|
||||
if (outputParameter != null) {
|
||||
if (outMpType != null && op instanceof JitCallOtherDefOp defOp) {
|
||||
rv.visitVarInsn(ALOAD, out.idx(0));
|
||||
OpGen.generateMpLegsFromArray(outMpType.legsAlloc(), rv);
|
||||
// NOTE: Want annotation to specify signedness
|
||||
gen.generateVarWriteCode(defOp.out(), outMpType, Ext.ZERO);
|
||||
}
|
||||
// Else there's either no @OpOutput or the output operand is absent
|
||||
}
|
||||
else if (op instanceof JitCallOtherDefOp defOp) {
|
||||
// TODO: Can annotation specify signedness of return value?
|
||||
gen.generateVarWriteCode(defOp.out(), JitType.forJavaType(method.getReturnType()),
|
||||
Ext.ZERO);
|
||||
}
|
||||
else if (method.getReturnType() != void.class) {
|
||||
TypeConversions.generatePop(JitType.forJavaType(method.getReturnType()), rv);
|
||||
em = em
|
||||
.emit(Op::aconst_null, Types.T_INT_ARR)
|
||||
.emit(Op::astore, localOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
outMpType = null;
|
||||
localOut = null;
|
||||
}
|
||||
|
||||
static class ResourceGroup implements AutoCloseable {
|
||||
private final List<AutoCloseable> resources = new ArrayList<>();
|
||||
TRef<LIB> libType = Types.refExtends(T_PCODE_USEROP_LIBRARY, method.getDeclaringClass());
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
for (AutoCloseable r : resources) {
|
||||
r.close();
|
||||
var rec = new Object() {
|
||||
<N extends Next> Emitter<? extends Ent<N, ?>> doReadArg(Emitter<N> em, JitVal arg,
|
||||
Parameter param) {
|
||||
if (param.getType() == boolean.class) {
|
||||
return gen.genReadToBool(em, localThis, arg);
|
||||
}
|
||||
if (param.getType() == int[].class) {
|
||||
MpIntJitType t = MpIntJitType.forSize(arg.size());
|
||||
// TODO: Annotation/attribute to specify slack?
|
||||
return gen.genReadToArray(em, localThis, arg, t, Ext.ZERO, scope, 0);
|
||||
}
|
||||
return switch (JitType.forJavaType(param.getType())) {
|
||||
case IntJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
|
||||
case LongJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
|
||||
case FloatJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
|
||||
case DoubleJitType t -> gen.genReadToStack(em, localThis, arg, t, Ext.ZERO);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends AutoCloseable> T add(T resource) {
|
||||
resources.add(resource);
|
||||
return resource;
|
||||
<N extends Next> ObjInv<?, LIB, N, ?> doInv(Emitter<N> em, List<JitVal> args,
|
||||
List<Parameter> params) {
|
||||
if (params.isEmpty()) {
|
||||
/**
|
||||
* NOTE: Can't put try-catch block here because the handler's all expect
|
||||
* Ent<Bot,Throwable>
|
||||
*/
|
||||
return Op.invokevirtual(em, libType, method.getName(), MthDesc.reflect(method),
|
||||
false);
|
||||
}
|
||||
Parameter param = params.getFirst();
|
||||
if (param == outputParameter) {
|
||||
var emOut = em.emit(Op::aload, localOut);
|
||||
var inv = doInv(emOut, args, params.subList(1, params.size()));
|
||||
return Inv.takeQArg(inv);
|
||||
}
|
||||
JitVal arg = args.getFirst();
|
||||
// TODO: Annotation/attribute to describe signedness?
|
||||
// TODO: Or a way to receive the byte size
|
||||
var emRead = doReadArg(em, arg, param);
|
||||
var inv =
|
||||
doInv(emRead, args.subList(1, args.size()), params.subList(1, params.size()));
|
||||
return Inv.takeQArg(inv);
|
||||
}
|
||||
};
|
||||
|
||||
var tryCatchBlock = Misc.tryCatch(em, Lbl.create(),
|
||||
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).lbl(),
|
||||
GenConsts.T_THROWABLE);
|
||||
em = tryCatchBlock.em();
|
||||
|
||||
var emLib = em
|
||||
.emit(useropField::genLoad, localThis, gen)
|
||||
.emit(Op::invokeinterface, T_PCODE_USEROP_DEFINITION, "getDefiningLibrary",
|
||||
MDESC_PCODE_USEROP_DEFINITION__GET_DEFINING_LIBRARY)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::checkcast, libType);
|
||||
var inv = rec
|
||||
.doInv(emLib, op.args(), Arrays.asList(parameters))
|
||||
.step(Inv::takeQObjRef);
|
||||
|
||||
if (outputParameter != null) {
|
||||
if (outMpType != null && op instanceof JitCallOtherDefOp defOp) {
|
||||
em = inv
|
||||
.step(Inv::retQVoid)
|
||||
.emit(Op::aload, localOut)
|
||||
.emit(gen::genWriteFromArray, localThis, defOp.out(), outMpType, Ext.ZERO,
|
||||
scope);
|
||||
}
|
||||
// Else there's either no @OpOutput or the output operand is absent
|
||||
}
|
||||
else if (op instanceof JitCallOtherDefOp defOp) {
|
||||
// TODO: Can annotation specify signedness of return value?
|
||||
var write = new Object() {
|
||||
public <T extends BPrim<?>, JT extends SimpleJitType<T, JT>> Emitter<Bot> doWrite(
|
||||
Inv<?, Bot, Bot> inv, Class<?> returnType) {
|
||||
JT type = SimpleJitType.forJavaType(returnType);
|
||||
return inv
|
||||
.step(Inv::retQ, type.bType())
|
||||
.emit(gen::genWriteFromStack, localThis, defOp.out(), type, Ext.ZERO,
|
||||
scope);
|
||||
}
|
||||
};
|
||||
em = inv.step(write::doWrite, method.getReturnType());
|
||||
}
|
||||
else if (method.getReturnType() != void.class) {
|
||||
em = switch (JitType.forJavaType(method.getReturnType())) {
|
||||
case IntJitType t -> inv
|
||||
.step(Inv::retQ, t.bType())
|
||||
.emit(Op::pop);
|
||||
case LongJitType t -> inv
|
||||
.step(Inv::retQ, t.bType())
|
||||
.emit(Op::pop2__2);
|
||||
case FloatJitType t -> inv
|
||||
.step(Inv::retQ, t.bType())
|
||||
.emit(Op::pop);
|
||||
case DoubleJitType t -> inv
|
||||
.step(Inv::retQ, t.bType())
|
||||
.emit(Op::pop2__2);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
return new LiveOpResult(em
|
||||
.emit(Lbl::place, tryCatchBlock.end()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,17 +380,17 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitCallOtherOpIf op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitCallOtherOpIf op, JitBlock block, Scope scope) {
|
||||
if (op.userop().modifiesContext()) {
|
||||
rv.visitLdcInsn(1);
|
||||
RunFixedLocal.CTXMOD.generateStoreCode(rv);
|
||||
em = em
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::istore, localCtxmod);
|
||||
}
|
||||
if (canDoDirectInvocation(op)) {
|
||||
generateRunCodeUsingDirectStrategy(gen, op, block, rv);
|
||||
}
|
||||
else {
|
||||
generateRunCodeUsingRetirementStrategy(gen, op.op(), block, op.userop(), rv);
|
||||
return genRunDirectStrategy(em, localThis, gen, op, block, scope);
|
||||
}
|
||||
return genRunRetirementStrategy(em, localThis, gen, op.op(), block, op.userop());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitVarScopeModel;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitCatenateOp;
|
||||
|
||||
/**
|
||||
@@ -36,8 +41,9 @@ public enum CatenateOpGen implements OpGen<JitCatenateOp> {
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitCatenateOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitCatenateOp op, JitBlock block, Scope scope) {
|
||||
throw new AssertionError("Cannnot generate synthetic op");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatTestOp;
|
||||
|
||||
/**
|
||||
* An extension for float comparison operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends FloatBinOpGen<T> {
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the comparison with float operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int fcmpOpcode();
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the comparison with double operands on the stack.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int dcmpOpcode();
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the conditional jump.
|
||||
*
|
||||
* <p>
|
||||
* The condition should correspond to the true case of the p-code operator.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int condOpcode();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This implementation reduces the need to just a few opcodes: 1) the opcode for comparing in
|
||||
* case of JVM {@code float}, 2) the opcode for comparing in the case of JVM {@code double}, and
|
||||
* 3) the conditional jump on the result of that comparison. First, the comparison opcode is
|
||||
* emitted. It should result in and int <0, ==0, or >0 on the stack, depending on whether
|
||||
* L<R, L==R, or L>R, respectively. Then the conditional jump is emitted. We place labels
|
||||
* in an if-else pattern to place either a 1 (true) or 0 (false) value of the appropriate p-code
|
||||
* type on the stack.
|
||||
*
|
||||
* @implNote This template is consistently generated by the Java compiler (Adoptium OpenJDK 21),
|
||||
* despite there being possible branchless implementations. That could indicate one of
|
||||
* a few things: 1) the HotSpot JIT knows how to optimize this pattern, perhaps using
|
||||
* branchless native instructions, 2) branchless optimizations don't yield the speedup
|
||||
* here we might expect, or 3) they didn't care to optimize. <b>TODO</b>: Investigate
|
||||
* in case it's thing 3. We might like to see if branchless JVM bytecodes can improve
|
||||
* performance.
|
||||
*/
|
||||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
assert rType == lType;
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
Label lblTrue = new Label();
|
||||
Label lblDone = new Label();
|
||||
switch (rType) {
|
||||
case FloatJitType t -> rv.visitInsn(fcmpOpcode());
|
||||
case DoubleJitType t -> rv.visitInsn(dcmpOpcode());
|
||||
case MpFloatJitType t -> Unfinished.TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
rv.visitJumpInsn(condOpcode(), lblTrue);
|
||||
TypeConversions.generateLdcFalse(outType, rv);
|
||||
rv.visitJumpInsn(GOTO, lblDone);
|
||||
rv.visitLabel(lblTrue);
|
||||
TypeConversions.generateLdcTrue(outType, rv);
|
||||
rv.visitLabel(lblDone);
|
||||
|
||||
return outType;
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.op.JitIntTestOp;
|
||||
|
||||
/**
|
||||
* An extension for integer comparison operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends IntBinOpGen<T> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* If the comparison is unsigned, we will emit invocations of
|
||||
* {@link Integer#compareUnsigned(int, int)} or {@link Long#compareUnsigned(long, long)},
|
||||
* followed by a conditional jump corresponding to this p-code comparison op. If the comparison
|
||||
* is signed, and the type fits in a JVM int, we emit the conditional jump of ints directly
|
||||
* implementing this p-code comparison op. If the type requires a JVM long, we first emit an
|
||||
* {@link #LCMP lcmp}, followed by the same opcode that would be used in the unsigned case.
|
||||
*
|
||||
* @return true if signed, false if not
|
||||
*/
|
||||
@Override
|
||||
boolean isSigned();
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the conditional jump for signed integers.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int icmpOpcode();
|
||||
|
||||
default void generateIntCmp(String methodName, MethodVisitor rv) {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, methodName, MDESC_INTEGER__COMPARE,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits bytecode for the JVM int case
|
||||
*
|
||||
* @param lblTrue the target bytecode label for the true case
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
default void generateIntJump(Label lblTrue, MethodVisitor rv) {
|
||||
if (isSigned()) {
|
||||
rv.visitJumpInsn(icmpOpcode(), lblTrue);
|
||||
}
|
||||
else {
|
||||
generateIntCmp("compareUnsigned", rv);
|
||||
rv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits bytecode for the JVM long case
|
||||
*
|
||||
* @param lblTrue the target bytecode label for the true case
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
default void generateLongJump(Label lblTrue, MethodVisitor rv) {
|
||||
if (isSigned()) {
|
||||
rv.visitInsn(LCMP);
|
||||
}
|
||||
else {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "compareUnsigned",
|
||||
MDESC_LONG__COMPARE_UNSIGNED, false);
|
||||
}
|
||||
rv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the conditional jump for unsigned or long integers.
|
||||
* <p>
|
||||
* This is emitted <em>after</em> the application of {@link #LCMP} or the comparator method.
|
||||
*
|
||||
* @return the opcode
|
||||
*/
|
||||
int ifOpcode();
|
||||
|
||||
@Override
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
}
|
||||
|
||||
default JitType generateMpIntCmp(JitCodeGenerator gen, MpIntJitType type, Label lblTrue,
|
||||
MethodVisitor mv) {
|
||||
int legCount = type.legsAlloc();
|
||||
Label lblDone = new Label();
|
||||
// Need two temps, because comparison is from *most* to least-significant
|
||||
try (
|
||||
JvmTempAlloc tmpL = gen.getAllocationModel().allocateTemp(mv, "tmpL", legCount);
|
||||
JvmTempAlloc tmpR = gen.getAllocationModel().allocateTemp(mv, "tmpR", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ILOAD, tmpL.idx(legCount - i - 1));
|
||||
mv.visitVarInsn(ILOAD, tmpR.idx(legCount - i - 1));
|
||||
//OpGen.generateSyserrInts(gen, 2, mv);
|
||||
generateIntCmp(i == 0 ? "compare" : "compareUnsigned", mv);
|
||||
if (i != legCount - 1) {
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitJumpInsn(IFNE, lblDone);
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
}
|
||||
}
|
||||
mv.visitLabel(lblDone);
|
||||
mv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||
return IntJitType.I4;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This reduces the implementation to a flag for signedness, the opcode for the conditional jump
|
||||
* on integer operands, and the opcode for a conditional jump after the comparison of longs. The
|
||||
* JVM, does not provide conditional jumps on long operands, so we must first compare the longs,
|
||||
* pushing an int onto the stack, and then conditionally jumping on that. This pattern is
|
||||
* similar for unsigned comparison of integers.
|
||||
*/
|
||||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
Label lblTrue = new Label();
|
||||
Label lblDone = new Label();
|
||||
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> generateIntJump(lblTrue, rv);
|
||||
case LongJitType t -> generateLongJump(lblTrue, rv);
|
||||
case MpIntJitType t -> generateMpIntCmp(gen, t, lblTrue, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
TypeConversions.generateLdcFalse(outType, rv);
|
||||
rv.visitJumpInsn(GOTO, lblDone);
|
||||
rv.visitLabel(lblTrue);
|
||||
TypeConversions.generateLdcTrue(outType, rv);
|
||||
rv.visitLabel(lblDone);
|
||||
|
||||
return outType;
|
||||
}
|
||||
}
|
||||
@@ -15,22 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.op.JitCopyOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitCopyOp copy}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits nothing extra. The unary generator template will
|
||||
* emit code to load the input operand, this emits nothing, and then the template emits code to
|
||||
* write the output operand, effecting a simple copy.
|
||||
* This is identical to {@link IntZExtOpGen}, except that we expect (require?) the output and input
|
||||
* operand to agree in size, and so we don't actually expect any extension. In the event that is not
|
||||
* the case, it seems agreeable that zero extension is applied.
|
||||
*/
|
||||
public enum CopyOpGen implements UnOpGen<JitCopyOp> {
|
||||
public enum CopyOpGen implements IntExtUnOpGen<JitCopyOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -38,10 +32,4 @@ public enum CopyOpGen implements UnOpGen<JitCopyOp> {
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitCopyOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
return uType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatAbsOp;
|
||||
|
||||
/**
|
||||
@@ -33,21 +33,25 @@ import ghidra.pcode.emu.jit.op.JitFloatAbsOp;
|
||||
* This uses the unary operator generator and emits an invocation of {@link Math#abs(float)} or
|
||||
* {@link Math#abs(double)}, depending on the type.
|
||||
*/
|
||||
public enum FloatAbsOpGen implements FloatUnOpGen<JitFloatAbsOp> {
|
||||
public enum FloatAbsOpGen implements FloatOpUnOpGen<JitFloatAbsOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatAbsOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "abs",
|
||||
MDESC_$FLOAT_UNOP, false);
|
||||
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "abs",
|
||||
MDESC_$DOUBLE_UNOP, false);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::invokestatic, T_MATH, "abs", MDESC_$FLOAT_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::invokestatic, T_MATH, "abs", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,37 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatAddOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatAddOp float_add}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #FADD} or {@link #DADD} depending
|
||||
* on the type.
|
||||
* This uses the binary operator generator and simply emits {@link Op#fadd(Emitter) fadd} or
|
||||
* {@link Op#dadd(Emitter) dadd} depending on the type.
|
||||
*/
|
||||
public enum FloatAddOpGen implements FloatBinOpGen<JitFloatAddOp> {
|
||||
public enum FloatAddOpGen implements FloatOpBinOpGen<JitFloatAddOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatAddOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
assert rType == lType;
|
||||
switch (lType) {
|
||||
case FloatJitType t -> rv.visitInsn(FADD);
|
||||
case DoubleJitType t -> rv.visitInsn(DADD);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
|
||||
return Op.fadd(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
|
||||
return Op.dadd(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatCeilOp;
|
||||
|
||||
/**
|
||||
@@ -34,25 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatCeilOp;
|
||||
* This uses the unary operator generator and emits an invocation of {@link Math#ceil(double)},
|
||||
* possibly surrounding it with conversions from and to float.
|
||||
*/
|
||||
public enum FloatCeilOpGen implements FloatUnOpGen<JitFloatCeilOp> {
|
||||
public enum FloatCeilOpGen implements FloatOpUnOpGen<JitFloatCeilOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatCeilOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> {
|
||||
// There apparently is no Math.ceil(float)???
|
||||
rv.visitInsn(F2D);
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "ceil", MDESC_$DOUBLE_UNOP, false);
|
||||
rv.visitInsn(D2F);
|
||||
}
|
||||
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "ceil",
|
||||
MDESC_$DOUBLE_UNOP, false);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::f2d)
|
||||
.emit(this::opForDouble)
|
||||
.emit(Op::d2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::invokestatic, T_MATH, "ceil", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatTestOp;
|
||||
|
||||
/**
|
||||
* An extension for float comparison operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface FloatCompareBinOpGen<T extends JitFloatTestOp> extends BinOpGen<T> {
|
||||
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the comparison with float operands on the stack.
|
||||
* <p>
|
||||
* The result should be as defined by {@link Comparator#compare(Object, Object)}.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>>
|
||||
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the comparison with double operands on the stack.
|
||||
* <p>
|
||||
* The result should be as defined by {@link Comparator#compare(Object, Object)}.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>>
|
||||
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM opcode to perform the conditional jump.
|
||||
* <p>
|
||||
* The condition should correspond to the true case of the p-code operator.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack including the comparison result
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the target label and emitter typed with the resulting stack, i.e., with the
|
||||
* comparison result popped
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This implementation reduces the need to just a few opcodes: 1) the opcode for comparing in
|
||||
* case of JVM {@code float}, 2) the opcode for comparing in the case of JVM {@code double}, and
|
||||
* 3) the conditional jump on the result of that comparison. First, the comparison opcode is
|
||||
* emitted. It should result in and int <0, ==0, or >0 on the stack, depending on whether
|
||||
* L<R, L==R, or L>R, respectively. Then the conditional jump is emitted. We place labels
|
||||
* in an if-else pattern to place either a 1 (true) or 0 (false) value of the appropriate p-code
|
||||
* type on the stack.
|
||||
*
|
||||
* @implNote This template is consistently generated by the Java compiler (Adoptium OpenJDK 21),
|
||||
* despite there being possible branchless implementations. That could indicate one of
|
||||
* a few things: 1) the HotSpot JIT knows how to optimize this pattern, perhaps using
|
||||
* branchless native instructions, 2) branchless optimizations don't yield the speedup
|
||||
* here we might expect, or 3) they didn't care to optimize. <b>TODO</b>: Investigate
|
||||
* in case it's thing 3. We might like to see if branchless JVM bytecodes can improve
|
||||
* performance.
|
||||
*/
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType lType = gen.resolveType(op.l(), op.lType());
|
||||
JitType rType = gen.resolveType(op.r(), op.rType());
|
||||
assert rType == lType;
|
||||
Emitter<Ent<Bot, TInt>> emCmp = switch (lType) {
|
||||
case FloatJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
|
||||
.emit(this::opForFloatCmp);
|
||||
case DoubleJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, Ext.ZERO)
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, Ext.ZERO)
|
||||
.emit(this::opForDoubleCmp);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
// LATER: Given the type conversion during write, we may not need a jump at all.
|
||||
var lblTrue = emCmp
|
||||
.emit(this::opForCondJump);
|
||||
var lblDone = lblTrue.em()
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::goto_);
|
||||
return new LiveOpResult(lblDone.em()
|
||||
.emit(Lbl::placeDead, lblTrue.lbl())
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Lbl::place, lblDone.lbl())
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, Ext.ZERO, scope));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.BPrim;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitUnOp;
|
||||
|
||||
/**
|
||||
* An extension for float conversion operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface FloatConvertUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
|
||||
|
||||
/**
|
||||
* An implementation based on a given bytecode op
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param <UT> the JVM type of the input operand
|
||||
* @param <UJT> the p-code type of the input operand
|
||||
* @param <OT> the JVM type of the output operand
|
||||
* @param <OJT> the p-code type of the output operand
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param ut the p-code type of the input operand
|
||||
* @param ot the p-code type of the output operand
|
||||
* @param opcode a method reference, e.g., to {@link Op#f2d(Emitter)} for the conversion
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the incoming stack
|
||||
*/
|
||||
default <THIS extends JitCompiledPassage, UT extends BPrim<?>,
|
||||
UJT extends SimpleJitType<UT, UJT>, OT extends BPrim<?>,
|
||||
OJT extends SimpleJitType<OT, OJT>> Emitter<Bot> gen(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, UJT ut, OJT ot,
|
||||
Function<? super Emitter<Ent<Bot, UT>>, Emitter<Ent<Bot, OT>>> opcode,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), ut, ext())
|
||||
.emit(opcode)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), ot, ext(), scope);
|
||||
}
|
||||
}
|
||||
@@ -15,37 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatDivOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatDivOp float_div}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #FDIV} or {@link #DDIV} depending
|
||||
* on the type.
|
||||
* This uses the binary operator generator and simply emits {@link Op#fdiv(Emitter) fdiv} or
|
||||
* {@link Op#ddiv(Emitter) ddiv} depending on the type.
|
||||
*/
|
||||
public enum FloatDivOpGen implements FloatBinOpGen<JitFloatDivOp> {
|
||||
public enum FloatDivOpGen implements FloatOpBinOpGen<JitFloatDivOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatDivOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
assert rType == lType;
|
||||
switch (lType) {
|
||||
case FloatJitType t -> rv.visitInsn(FDIV);
|
||||
case DoubleJitType t -> rv.visitInsn(DDIV);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
|
||||
return Op.fdiv(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
|
||||
return Op.ddiv(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,40 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatEqualOp float_equal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the float comparison operator generator and simply emits {@link #FCMPL} or
|
||||
* {@link #DCMPL} depending on the type and then {@link #IFEQ}.
|
||||
* This uses the float comparison operator generator and simply emits {@link Op#fcmpl(Emitter)
|
||||
* fcmpl} or {@link Op#dcmpl(Emitter) dcmpl} depending on the type and then {@link Op#ifeq(Emitter)
|
||||
* ifeq}.
|
||||
*/
|
||||
public enum FloatEqualOpGen implements CompareFloatOpGen<JitFloatEqualOp> {
|
||||
public enum FloatEqualOpGen implements FloatCompareBinOpGen<JitFloatEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int fcmpOpcode() {
|
||||
return FCMPL;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
|
||||
return Op.fcmpl(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dcmpOpcode() {
|
||||
return DCMPL;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
|
||||
return Op.dcmpl(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int condOpcode() {
|
||||
return IFEQ;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
|
||||
return Op.ifeq(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,48 +17,56 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatFloat2FloatOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatFloat2FloatOp float_float2float}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #F2D} or {@link #D2F}.
|
||||
* This uses the unary operator generator and emits {@link Op#f2d(Emitter) f2d} or
|
||||
* {@link Op#d2f(Emitter) d2f}.
|
||||
*/
|
||||
public enum FloatFloat2FloatOpGen implements FloatUnOpGen<JitFloatFloat2FloatOp> {
|
||||
public enum FloatFloat2FloatOpGen implements FloatConvertUnOpGen<JitFloatFloat2FloatOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
|
||||
rv.visitInsn(opcode);
|
||||
return type;
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatFloat2FloatOp op,
|
||||
JitBlock block, JitType uType, MethodVisitor rv) {
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
return switch (uType) {
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitFloatFloat2FloatOp op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
JitType outType = gen.resolveType(op.out(), op.type());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case FloatJitType ut -> switch (outType) {
|
||||
case FloatJitType ot -> ot;
|
||||
case DoubleJitType ot -> gen(rv, F2D, ot);
|
||||
case FloatJitType ot -> em;
|
||||
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2d, scope);
|
||||
case MpFloatJitType ot -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case DoubleJitType ut -> switch (outType) {
|
||||
case FloatJitType ot -> gen(rv, D2F, ot);
|
||||
case DoubleJitType ot -> ot;
|
||||
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2f, scope);
|
||||
case DoubleJitType ot -> em;
|
||||
case MpFloatJitType ot -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpFloatJitType ot -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatFloorOp;
|
||||
|
||||
/**
|
||||
@@ -34,25 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatFloorOp;
|
||||
* This uses the unary operator generator and emits an invocation of {@link Math#floor(double)},
|
||||
* possibly surrounding it with conversions from and to float.
|
||||
*/
|
||||
public enum FloatFloorOpGen implements FloatUnOpGen<JitFloatFloorOp> {
|
||||
public enum FloatFloorOpGen implements FloatOpUnOpGen<JitFloatFloorOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatFloorOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> {
|
||||
// There apparently is no Math.floor(float)???
|
||||
rv.visitInsn(F2D);
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
|
||||
rv.visitInsn(D2F);
|
||||
}
|
||||
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor",
|
||||
MDESC_$DOUBLE_UNOP, false);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::f2d)
|
||||
.emit(this::opForDouble)
|
||||
.emit(Op::d2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,22 +17,27 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatInt2FloatOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatInt2FloatOp float_int2float}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #I2F}, {@link #I2D}, {@link #L2F}, or
|
||||
* {@link #L2D}.
|
||||
* This uses the unary operator generator and emits {@link Op#i2f(Emitter) i2f},
|
||||
* {@link Op#i2d(Emitter) i2d}, {@link Op#l2f(Emitter) l2f}, or {@link Op#l2d(Emitter) l2d}.
|
||||
*/
|
||||
public enum FloatInt2FloatOpGen implements UnOpGen<JitFloatInt2FloatOp> {
|
||||
public enum FloatInt2FloatOpGen implements FloatConvertUnOpGen<JitFloatInt2FloatOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -41,30 +46,27 @@ public enum FloatInt2FloatOpGen implements UnOpGen<JitFloatInt2FloatOp> {
|
||||
return false; // TODO: Is it signed? Test to figure it out.
|
||||
}
|
||||
|
||||
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
|
||||
rv.visitInsn(opcode);
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatInt2FloatOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
return switch (uType) {
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitFloatInt2FloatOp op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
JitType outType = gen.resolveType(op.out(), op.type());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case IntJitType ut -> switch (outType) {
|
||||
case FloatJitType ot -> gen(rv, I2F, ot);
|
||||
case DoubleJitType ot -> gen(rv, I2D, ot);
|
||||
case MpFloatJitType ot -> TODO("MpInt/Float");
|
||||
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::i2f, scope);
|
||||
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::i2d, scope);
|
||||
case MpFloatJitType ot -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case LongJitType ut -> switch (outType) {
|
||||
case FloatJitType ot -> gen(rv, L2F, ot);
|
||||
case DoubleJitType ot -> gen(rv, L2D, ot);
|
||||
case MpFloatJitType ot -> TODO("MpInt/Float");
|
||||
case FloatJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::l2f, scope);
|
||||
case DoubleJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::l2d, scope);
|
||||
case MpFloatJitType ot -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpIntJitType ut -> TODO("MpInt/Float");
|
||||
case MpIntJitType ut -> TODO("MpInt->Float");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,40 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatLessEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatLessEqualOp float_lessequal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the float comparison operator generator and simply emits {@link #FCMPG} or
|
||||
* {@link #DCMPG} depending on the type and then {@link #IFLE}.
|
||||
* This uses the float comparison operator generator and simply emits {@link Op#fcmpg(Emitter)
|
||||
* fcmpg} or {@link Op#dcmpg(Emitter) dcmpg} depending on the type and then {@link Op#ifle(Emitter)
|
||||
* ifle}.
|
||||
*/
|
||||
public enum FloatLessEqualOpGen implements CompareFloatOpGen<JitFloatLessEqualOp> {
|
||||
public enum FloatLessEqualOpGen implements FloatCompareBinOpGen<JitFloatLessEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int fcmpOpcode() {
|
||||
return FCMPG;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
|
||||
return Op.fcmpg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dcmpOpcode() {
|
||||
return DCMPG;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
|
||||
return Op.dcmpg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int condOpcode() {
|
||||
return IFLE;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
|
||||
return Op.ifle(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,40 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatLessOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatLessOp float_less}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the float comparison operator generator and simply emits {@link #FCMPG} or
|
||||
* {@link #DCMPG} depending on the type and then {@link #IFLT}.
|
||||
* This uses the float comparison operator generator and simply emits {@link Op#fcmpg(Emitter)
|
||||
* fcmpg} or {@link Op#dcmpg(Emitter) dcmpg} depending on the type and then {@link Op#iflt(Emitter)
|
||||
* iflt}.
|
||||
*/
|
||||
public enum FloatLessOpGen implements CompareFloatOpGen<JitFloatLessOp> {
|
||||
public enum FloatLessOpGen implements FloatCompareBinOpGen<JitFloatLessOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int fcmpOpcode() {
|
||||
return FCMPG;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
|
||||
return Op.fcmpg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dcmpOpcode() {
|
||||
return DCMPG;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
|
||||
return Op.dcmpg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int condOpcode() {
|
||||
return IFLT;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
|
||||
return Op.iflt(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,37 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatMultOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatMultOp float_mult}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #FMUL} or {@link #DMUL} depending
|
||||
* on the type.
|
||||
* This uses the binary operator generator and simply emits {@link Op#fmul(Emitter) fmul} or
|
||||
* {@link Op#dmul(Emitter) dmul} depending on the type.
|
||||
*/
|
||||
public enum FloatMultOpGen implements FloatBinOpGen<JitFloatMultOp> {
|
||||
public enum FloatMultOpGen implements FloatOpBinOpGen<JitFloatMultOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatMultOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
assert rType == lType;
|
||||
switch (lType) {
|
||||
case FloatJitType t -> rv.visitInsn(FMUL);
|
||||
case DoubleJitType t -> rv.visitInsn(DMUL);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
|
||||
return Op.fmul(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
|
||||
return Op.dmul(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,18 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatNaNOp;
|
||||
|
||||
/**
|
||||
@@ -33,21 +39,35 @@ import ghidra.pcode.emu.jit.op.JitFloatNaNOp;
|
||||
* This uses the unary operator generator and emits an invocation of {@link Float#isNaN(float)} or
|
||||
* {@link Double#isNaN(double)}, depending on the type.
|
||||
*/
|
||||
public enum FloatNaNOpGen implements FloatUnOpGen<JitFloatNaNOp> {
|
||||
public enum FloatNaNOpGen implements UnOpGen<JitFloatNaNOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatNaNOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_FLOAT, "isNaN",
|
||||
MDESC_FLOAT__IS_NAN, false);
|
||||
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_DOUBLE, "isNaN",
|
||||
MDESC_DOUBLE__IS_NAN, false);
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitFloatNaNOp op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case FloatJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(Op::invokestatic, TR_FLOAT, "isNaN", MDESC_FLOAT__IS_NAN, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, ext(), scope);
|
||||
case DoubleJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(Op::invokestatic, TR_DOUBLE, "isNaN", MDESC_DOUBLE__IS_NAN, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), IntJitType.I4, ext(), scope);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return IntJitType.I4;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,35 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatNegOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatNegOp float_neg}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #FNEG} or {@link #DNEG}.
|
||||
* This uses the unary operator generator and emits {@link Op#fneg(Emitter) fneg} or
|
||||
* {@link Op#dneg(Emitter) dneg}.
|
||||
*/
|
||||
public enum FloatNegOpGen implements FloatUnOpGen<JitFloatNegOp> {
|
||||
public enum FloatNegOpGen implements FloatOpUnOpGen<JitFloatNegOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatNegOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> rv.visitInsn(FNEG);
|
||||
case DoubleJitType t -> rv.visitInsn(DNEG);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return Op.fneg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return Op.dneg(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,40 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatNotEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatNotEqualOp float_notequal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the float comparison operator generator and simply emits {@link #FCMPL} or
|
||||
* {@link #DCMPL} depending on the type and then {@link #IFNE}.
|
||||
* This uses the float comparison operator generator and simply emits {@link Op#fcmpl(Emitter)
|
||||
* fcmpl} or {@link Op#dcmpl(Emitter) dcmpl} depending on the type and then {@link Op#ifne(Emitter)
|
||||
* ifne}.
|
||||
*/
|
||||
public enum FloatNotEqualOpGen implements CompareFloatOpGen<JitFloatNotEqualOp> {
|
||||
public enum FloatNotEqualOpGen implements FloatCompareBinOpGen<JitFloatNotEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int fcmpOpcode() {
|
||||
return FCMPL;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TInt>> opForFloatCmp(Emitter<N0> em) {
|
||||
return Op.fcmpl(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dcmpOpcode() {
|
||||
return DCMPL;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TInt>> opForDoubleCmp(Emitter<N0> em) {
|
||||
return Op.dcmpl(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int condOpcode() {
|
||||
return IFNE;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> LblEm<N1, N1> opForCondJump(Emitter<N0> em) {
|
||||
return Op.ifne(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatBinOp;
|
||||
|
||||
/**
|
||||
* An extension for floating-point binary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface FloatOpBinOpGen<T extends JitFloatBinOp> extends BinOpGen<T> {
|
||||
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with float operands on the stack.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>>
|
||||
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with double operands on the stack.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>>
|
||||
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType lType = gen.resolveType(op.l(), op.lType());
|
||||
JitType rType = gen.resolveType(op.r(), op.rType());
|
||||
assert rType == lType;
|
||||
return new LiveOpResult(switch (lType) {
|
||||
case FloatJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
|
||||
.emit(this::opForFloat)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case DoubleJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, Ext.ZERO)
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, Ext.ZERO)
|
||||
.emit(this::opForDouble)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatUnOp;
|
||||
|
||||
/**
|
||||
* An extension for floating-point unary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface FloatOpUnOpGen<T extends JitFloatUnOp> extends UnOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with float operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with double operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case FloatJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForFloat)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case DoubleJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForDouble)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case MpFloatJitType t -> Unfinished.TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatRoundOp;
|
||||
|
||||
/**
|
||||
@@ -37,30 +37,31 @@ import ghidra.pcode.emu.jit.op.JitFloatRoundOp;
|
||||
* {@code round(x) = floor(x + 0.5)}. This uses the unary operator generator and emits the bytecode
|
||||
* to implement that definition, applying type conversions as needed.
|
||||
*/
|
||||
public enum FloatRoundOpGen implements FloatUnOpGen<JitFloatRoundOp> {
|
||||
public enum FloatRoundOpGen implements FloatOpUnOpGen<JitFloatRoundOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatRoundOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
// Math.round also converts to int/long
|
||||
case FloatJitType t -> {
|
||||
rv.visitLdcInsn(0.5f);
|
||||
rv.visitInsn(FADD);
|
||||
rv.visitInsn(F2D);
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
|
||||
rv.visitInsn(D2F);
|
||||
}
|
||||
case DoubleJitType t -> {
|
||||
rv.visitLdcInsn(0.5d);
|
||||
rv.visitInsn(DADD);
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "floor", MDESC_$DOUBLE_UNOP, false);
|
||||
}
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__f, 0.5f)
|
||||
.emit(Op::fadd)
|
||||
.emit(Op::f2d)
|
||||
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::d2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__d, 0.5)
|
||||
.emit(Op::dadd)
|
||||
.emit(Op::invokestatic, T_MATH, "floor", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_$DOUBLE_UNOP;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_MATH;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.T_MATH;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatSqrtOp;
|
||||
|
||||
/**
|
||||
@@ -34,24 +34,25 @@ import ghidra.pcode.emu.jit.op.JitFloatSqrtOp;
|
||||
* This uses the unary operator generator and emits an invocation of {@link Math#sqrt(double)},
|
||||
* possibly surrounding it with conversions from and to float.
|
||||
*/
|
||||
public enum FloatSqrtOpGen implements FloatUnOpGen<JitFloatSqrtOp> {
|
||||
public enum FloatSqrtOpGen implements FloatOpUnOpGen<JitFloatSqrtOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatSqrtOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case FloatJitType t -> {
|
||||
rv.visitInsn(F2D);
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "sqrt", MDESC_$DOUBLE_UNOP, false);
|
||||
rv.visitInsn(D2F);
|
||||
}
|
||||
case DoubleJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_MATH, "sqrt",
|
||||
MDESC_$DOUBLE_UNOP, false);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
public <N1 extends Next, N0 extends Ent<N1, TFloat>> Emitter<Ent<N1, TFloat>>
|
||||
opForFloat(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::f2d)
|
||||
.emit(this::opForDouble)
|
||||
.emit(Op::d2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TDouble>> Emitter<Ent<N1, TDouble>>
|
||||
opForDouble(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::invokestatic, T_MATH, "sqrt", MDESC_$DOUBLE_UNOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,37 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TDouble;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TFloat;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatSubOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatSubOp float_sub}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #FSUB} or {@link #DSUB} depending
|
||||
* on the type.
|
||||
* This uses the binary operator generator and simply emits {@link Op#fsub(Emitter) fsub} or
|
||||
* {@link Op#dsub(Emitter) dsub} depending on the type.
|
||||
*/
|
||||
public enum FloatSubOpGen implements FloatBinOpGen<JitFloatSubOp> {
|
||||
public enum FloatSubOpGen implements FloatOpBinOpGen<JitFloatSubOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitFloatSubOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
assert rType == lType;
|
||||
switch (lType) {
|
||||
case FloatJitType t -> rv.visitInsn(FSUB);
|
||||
case DoubleJitType t -> rv.visitInsn(DSUB);
|
||||
case MpFloatJitType t -> TODO("MpFloat");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TFloat>, N0 extends Ent<N1, TFloat>> //
|
||||
Emitter<Ent<N2, TFloat>> opForFloat(Emitter<N0> em) {
|
||||
return Op.fsub(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TDouble>, N0 extends Ent<N1, TDouble>> //
|
||||
Emitter<Ent<N2, TDouble>> opForDouble(Emitter<N0> em) {
|
||||
return Op.dsub(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,49 +17,56 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitFloatTruncOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitFloatTruncOp float_trunc}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #F2I}, {@link #F2L}, {@link #D2I}, or
|
||||
* {@link #D2L}.
|
||||
* This uses the unary operator generator and emits {@link Op#f2i(Emitter) f2i},
|
||||
* {@link Op#f2l(Emitter) f2l}, {@link Op#d2i(Emitter) d2i}, or {@link Op#d2l(Emitter) d2l}.
|
||||
*/
|
||||
public enum FloatTruncOpGen implements FloatUnOpGen<JitFloatTruncOp> {
|
||||
public enum FloatTruncOpGen implements FloatConvertUnOpGen<JitFloatTruncOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
private JitType gen(MethodVisitor rv, int opcode, JitType type) {
|
||||
rv.visitInsn(opcode);
|
||||
return type;
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false; // TODO: Is it signed? Test to figure it out.
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitFloatTruncOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
return switch (uType) {
|
||||
public <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, JitFloatTruncOp op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
JitType outType = gen.resolveType(op.out(), op.type());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case FloatJitType ut -> switch (outType) {
|
||||
case IntJitType ot -> gen(rv, F2I, ot);
|
||||
case LongJitType ot -> gen(rv, F2L, ot);
|
||||
case MpIntJitType ot -> TODO("MpFloat/Int");
|
||||
case IntJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2i, scope);
|
||||
case LongJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::f2l, scope);
|
||||
case MpIntJitType ot -> TODO("Float->MpInt");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case DoubleJitType ut -> switch (outType) {
|
||||
case IntJitType ot -> gen(rv, D2I, ot);
|
||||
case LongJitType ot -> gen(rv, D2L, ot);
|
||||
case MpIntJitType ot -> TODO("MpFloat/Int");
|
||||
case IntJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2i, scope);
|
||||
case LongJitType ot -> gen(em, localThis, gen, op, ut, ot, Op::d2l, scope);
|
||||
case MpIntJitType ot -> TODO("Float->MpInt");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpIntJitType ot -> TODO("MpFloat/Int");
|
||||
case MpFloatJitType ut -> TODO("MpInt->Float");
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,35 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd.SimpleOpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitInt2CompOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitInt2CompOp int_2comp}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #INEG} or {@link #LNEG}, depending on
|
||||
* type.
|
||||
* This uses the unary operator generator and emits {@link Op#ineg(Emitter) ineg} or
|
||||
* {@link Op#lneg(Emitter) lneg}, depending on type.
|
||||
* <p>
|
||||
* The multi-precision logic is similar to {@link IntAddOpGen}. We follow the process "flip the bits
|
||||
* and add 1", so for each leg, we consider that it may have a carry in. We then invert all the bits
|
||||
* using ^-1 and then add that carry in. The least significant leg is assumed to have a carry in,
|
||||
* effecting the +1.
|
||||
*/
|
||||
public enum Int2CompOpGen implements IntUnOpGen<JitInt2CompOp> {
|
||||
public enum Int2CompOpGen implements IntOpUnOpGen<JitInt2CompOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -44,62 +52,139 @@ public enum Int2CompOpGen implements IntUnOpGen<JitInt2CompOp> {
|
||||
return false; // TODO: Is it? Test with 3-byte operands to figure it out.
|
||||
}
|
||||
|
||||
private void generateMpIntLeg2Cmp(int idx, IntJitType type, boolean givesCarry,
|
||||
MethodVisitor mv) {
|
||||
// [carryN-1:LONG]
|
||||
mv.visitVarInsn(ILOAD, idx);
|
||||
// [legN:INT,carry:LONG]
|
||||
mv.visitLdcInsn(-1 >>> (Integer.SIZE - type.size() * Byte.SIZE));
|
||||
// [ff:INT,legN:INT,carry:LONG]
|
||||
mv.visitInsn(IXOR);
|
||||
// [invN:INT,carry:LONG]
|
||||
TypeConversions.generateIntToLong(type, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [invN:LONG,carry:LONG]
|
||||
mv.visitInsn(LADD);
|
||||
// [carry|2cmpN:LONG]
|
||||
if (givesCarry) {
|
||||
mv.visitInsn(DUP2);
|
||||
// [carry|2cmpN:LONG,carry|2cmpN:LONG]
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, type, Ext.ZERO, mv);
|
||||
// [2cmpN:INT,carry|2cmpN:LONG]
|
||||
mv.visitVarInsn(ISTORE, idx);
|
||||
// [carry|2cmpN:LONG]
|
||||
mv.visitLdcInsn(Integer.SIZE);
|
||||
// [32:INT, carry:LONG]
|
||||
mv.visitInsn(LUSHR);
|
||||
// [carryN:LONG]
|
||||
}
|
||||
else {
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, type, Ext.ZERO, mv);
|
||||
// [2cmpN:INT]
|
||||
mv.visitVarInsn(ISTORE, idx);
|
||||
// []
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMpInt2Comp(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
int legCount = type.legsAlloc();
|
||||
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
|
||||
List<IntJitType> types = type.legTypes().reversed();
|
||||
mv.visitLdcInsn(1L); // Seed the "carry in" with the 1 to add
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean isLast = i == legCount - 1;
|
||||
generateMpIntLeg2Cmp(result.idx(i), types.get(i), !isLast, mv);
|
||||
}
|
||||
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||
}
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
opForInt(Emitter<N0> em) {
|
||||
return Op.ineg(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitInt2CompOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case IntJitType t -> rv.visitInsn(INEG);
|
||||
case LongJitType t -> rv.visitInsn(LNEG);
|
||||
case MpIntJitType t -> generateMpInt2Comp(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
opForLong(Emitter<N0> em) {
|
||||
return Op.lneg(em);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to process a leg of the input operand with the carry in
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry in
|
||||
* @param opnd the operand containing the leg from the input multi-precision operand
|
||||
* @param shiftCarry indicates whether or not the long needs to be shifted to position the carry
|
||||
* bit
|
||||
* @return the emitter typed with a stack of one long, the inverted leg and carry out
|
||||
*/
|
||||
static Emitter<Ent<Bot, TLong>> prepOpndAndCarry(Emitter<Ent<Bot, TLong>> em,
|
||||
SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry) {
|
||||
if (shiftCarry) {
|
||||
em = em
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr);
|
||||
}
|
||||
return uType;
|
||||
return em
|
||||
.emit(opnd::read)
|
||||
.emit(Op::ldc__i, -1 >>> (Integer.SIZE - opnd.type().size() * Byte.SIZE))
|
||||
.emit(Op::ixor)
|
||||
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "toUnsignedLong",
|
||||
GenConsts.MDESC_INTEGER__TO_UNSIGNED_LONG, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ladd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to invert the given leg of the input operand, leaving the carry out on the
|
||||
* stack.
|
||||
* <p>
|
||||
* This assumes the stack has a carry from the previous leg's complement. It is either in bit
|
||||
* position 32, if {@code shiftCarry} is set, or bit position 0, otherwise. It shifts, if
|
||||
* needed, the carry bit into position 0 and then adds the inverted leg. It writes the lower 32
|
||||
* bits of that, i.e., the resulting complement, into an output operand, and then leaves the
|
||||
* carry out in bit position 32 of the long on the stack.
|
||||
* <p>
|
||||
* If {@code opnd} is writable, the result is written there and that operand returned.
|
||||
* Otherwise, a new operand is generated. The caller must em>always</em> use the returned
|
||||
* operand to construct the legs of the final multi-precision output operand. It must
|
||||
* <em>never</em> use {@code opnd}, nor the multi-precision operand containing it, as the final
|
||||
* output.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry out of the previous leg's
|
||||
* complement, i.e., the carry in for this leg's complement.
|
||||
* @param opnd the leg for the input multi-precision operand
|
||||
* @param shiftCarry true to indicate the carry bit must be shifted from position 32 to 0.
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand and the emitter typed with a stack of one long whose value is the
|
||||
* carry out for this leg's complement.
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLeg2CmpTakesAndGivesCarry(
|
||||
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(Int2CompOpGen::prepOpndAndCarry, opnd, shiftCarry)
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(opnd::write, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode as in
|
||||
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} except that
|
||||
* we do not leave a carry out on the stack.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry out of the previous leg's
|
||||
* complement, i.e., the carry in for this leg's complement.
|
||||
* @param opnd the leg for the input multi-precision operand
|
||||
* @param shiftCarry true to indicate the carry bit must be shifted from position 32 to 0.
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand and the emitter typed with the empty stack.
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Bot> genMpIntLeg2CmpTakesCarry(
|
||||
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> opnd, boolean shiftCarry,
|
||||
Scope scope) {
|
||||
return em
|
||||
.emit(Int2CompOpGen::prepOpndAndCarry, opnd, shiftCarry)
|
||||
.emit(Op::l2i)
|
||||
.emit(opnd::write, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The strategy here follows after
|
||||
* {@link IntAddOpGen#genRunMpInt(Emitter, Local, JitCodeGenerator, ghidra.pcode.emu.jit.op.JitIntAddOp, MpIntJitType, Scope)}.
|
||||
* We no longer need to assert a minimum length, since we provide a "carry in" of 1 for the
|
||||
* initial leg. We initialize the complement by loading that 1 onto the stack and invoking
|
||||
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} with
|
||||
* {@code shiftCarry} cleared. We terminate the operation with
|
||||
* {@link #genMpIntLeg2CmpTakesCarry(Emitter, SimpleOpnd, boolean, Scope)}. We use
|
||||
* {@link #genMpIntLeg2CmpTakesAndGivesCarry(Emitter, SimpleOpnd, boolean, Scope)} with
|
||||
* {@code shiftCarry} set for each middle leg. The resulting legs are all appended to form the
|
||||
* final multi-precision output.
|
||||
*/
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitInt2CompOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
var opnd = gen.genReadToOpnd(em, localThis, op.u(), type, ext(), scope);
|
||||
em = opnd.em();
|
||||
var legs = opnd.opnd().type().castLegsLE(opnd.opnd());
|
||||
|
||||
int legCount = type.legsAlloc();
|
||||
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
|
||||
var emCarry = em
|
||||
.emit(Op::ldc__l, 1);
|
||||
for (int i = 0; i < legCount - 1; i++) {
|
||||
var result = emCarry
|
||||
.emit(Int2CompOpGen::genMpIntLeg2CmpTakesAndGivesCarry, legs.get(i), i != 0,
|
||||
scope);
|
||||
emCarry = result.em();
|
||||
outLegs.add(result.opnd());
|
||||
}
|
||||
var result = emCarry
|
||||
.emit(Int2CompOpGen::genMpIntLeg2CmpTakesCarry, legs.getLast(), true, scope);
|
||||
em = result.em();
|
||||
outLegs.add(result.opnd());
|
||||
|
||||
var out = MpIntLocalOpnd.of(type, "out", outLegs);
|
||||
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,120 +15,261 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd.SimpleOpndEm;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntAddOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntAddOp int_add}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #IADD} or {@link #LADD} depending
|
||||
* on the type.
|
||||
*
|
||||
* This uses the binary operator generator and simply emits {@link Op#iadd(Emitter) iadd} or
|
||||
* {@link Op#ladd(Emitter) ladd} depending on the type.
|
||||
* <p>
|
||||
* NOTE: The multi-precision integer parts of this are a work in progress.
|
||||
* The multi-precision integer logic is not such a simple matter.
|
||||
*/
|
||||
public enum IntAddOpGen implements IntBinOpGen<JitIntAddOp> {
|
||||
public enum IntAddOpGen implements IntOpBinOpGen<JitIntAddOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
static void generateMpIntLegAdd(JitCodeGenerator gen, int idx, boolean takesCarry,
|
||||
boolean givesCarry, boolean storesResult, MethodVisitor mv) {
|
||||
if (takesCarry) {
|
||||
// [...,llegN:INT,olegN+1:LONG]
|
||||
mv.visitLdcInsn(32);
|
||||
mv.visitInsn(LUSHR);
|
||||
// [...,lleg1...,carryinN:LONG]
|
||||
mv.visitInsn(DUP2_X1);
|
||||
mv.visitInsn(POP2);
|
||||
// [...,carryinN:LONG,llegN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,carryinN:LONG,llegN:LONG]
|
||||
mv.visitInsn(LADD);
|
||||
// [...,sumpartN:LONG]
|
||||
}
|
||||
else {
|
||||
// [...,llegN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,sumpartN:LONG] (legN + 0)
|
||||
}
|
||||
mv.visitVarInsn(ILOAD, idx);
|
||||
// [...,sumpartN:LONG,rlegN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,sumpartN:LONG,rlegN:LONG]
|
||||
mv.visitInsn(LADD);
|
||||
// [...,olegN:LONG]
|
||||
if (storesResult) {
|
||||
if (givesCarry) {
|
||||
mv.visitInsn(DUP2);
|
||||
}
|
||||
// [...,(olegN:LONG),olegN:LONG]
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, Ext.ZERO, mv);
|
||||
// [...,(olegN:LONG),olegN:INT]
|
||||
/** NB. The store will perform the masking */
|
||||
mv.visitVarInsn(ISTORE, idx);
|
||||
// [...,(olegN:LONG)]
|
||||
}
|
||||
else {
|
||||
if (!givesCarry) {
|
||||
mv.visitInsn(POP2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMpIntAdd(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
/**
|
||||
* The strategy is to allocate a temp local for each leg of the result. First, we'll pop the
|
||||
* right operand into the temp. Then, as we work with each leg of the left operand, we'll
|
||||
* execute the algorithm. Convert both right and left legs to a long and add them (along
|
||||
* with a possible carry in). Store the result back into the temp locals. Shift the leg
|
||||
* right 32 to get the carry out, then continue to the next leg up. The final carry out can
|
||||
* be dropped (overflow). The result legs are then pushed back to the stack.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean isLast = i == legCount - 1;
|
||||
boolean takesCarry = i != 0; // not first
|
||||
generateMpIntLegAdd(gen, result.idx(i), takesCarry, !isLast, true, mv);
|
||||
}
|
||||
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntAddOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.iadd(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntAddOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(IADD);
|
||||
case LongJitType t -> rv.visitInsn(LADD);
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntAdd(gen, t, rv);
|
||||
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
|
||||
default -> throw new AssertionError();
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.ladd(em);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load a leg of the left operand and combine it with the carry in
|
||||
* <p>
|
||||
* The carry in is a long where bit 32 has the value 0 or 1. The lower 32 bits, which are
|
||||
* actually the result of the previous legs' addition, are ignored. We use a long because it can
|
||||
* hold the full result of addition, where the carry out winds up at bit 32 of the long. This
|
||||
* routine emits bytecode to shift the previous sum out, so that the carry bit is now at bit 0,
|
||||
* i.e., the long how has the value 0 or 1. It then loads and adds the left leg into that long.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry in
|
||||
* @param left the operand containing the leg from the left multi-precision operand
|
||||
* @return the emitter typed with a stack of one long, the summed left and carry in
|
||||
*/
|
||||
static Emitter<Ent<Bot, TLong>> prepLeftAndCarry(Emitter<Ent<Bot, TLong>> em,
|
||||
SimpleOpnd<TInt, IntJitType> left) {
|
||||
return em
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(left::read)
|
||||
.emit(Op::i2l)
|
||||
.emit(Op::ladd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to load a leg of the right operand and add it to the summed left and carry in
|
||||
* <p>
|
||||
* This completes the addition of the left and right legs along with the carry in. The long on
|
||||
* the stack is now the result, with the carry out in bit position 32.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the summed left and carry in
|
||||
* @param right the operand containing the leg from the right multi-precision operand
|
||||
* @return the emitter typed with a stack of one long, the result and carry out
|
||||
*/
|
||||
static Emitter<Ent<Bot, TLong>> addRight(Emitter<Ent<Bot, TLong>> em,
|
||||
SimpleOpnd<TInt, IntJitType> right) {
|
||||
return em
|
||||
.emit(right::read)
|
||||
.emit(Op::i2l)
|
||||
.emit(Op::ladd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally emit bytecode to store the result into a leg of the output operand
|
||||
* <p>
|
||||
* Other operators may borrow some components of this mp-int addition routine, e.g., to compute
|
||||
* only the output carry bit. In those cases, storing the actual result of the addition is not
|
||||
* necessary, but we still need to keep the intermediate carry bit. Thus, we provide this
|
||||
* routine to conditionally tee the sum (lower 32-bits of the long on the stack) for a leg into
|
||||
* a result leg. Whether or not we emit such bytecode, we ensure the value on the stack remains
|
||||
* unchanged.
|
||||
* <p>
|
||||
* See
|
||||
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
|
||||
* regarding the returned record.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the result and carry out for a given
|
||||
* leg
|
||||
* @param into the output leg operand for the multi-precision output operand
|
||||
* @param store true to emit the bytecode, false to emit nothing
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand or null, and the emitter typed with a stack of one long whose
|
||||
* value is unchanged.
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> maybeStore(Emitter<Ent<Bot, TLong>> em,
|
||||
SimpleOpnd<TInt, IntJitType> into, boolean store, Scope scope) {
|
||||
if (!store) {
|
||||
return new SimpleOpndEm<>(null, em);
|
||||
}
|
||||
return rType;
|
||||
return em
|
||||
.emit(Op::dup2__2)
|
||||
.emit(Op::l2i)
|
||||
.emit(into::write, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to add two corresponding legs of the operands, leaving the carry out on the
|
||||
* stack.
|
||||
* <p>
|
||||
* This assumes the stack has a carry from the previous legs' sum in bit position 32. It shifts
|
||||
* it into position and then adds in the two given legs. It conditionally writes the lower 32
|
||||
* bits of that, i.e., the resulting sum, into an output operand, and then leaves the carry out
|
||||
* in bit position 32 of the long on the stack.
|
||||
* <p>
|
||||
* The returned value is always a non-null record, but the value of the operand may vary. If
|
||||
* {@code store} is false, the operand is always null. This will be the case, e.g., for
|
||||
* computing the carry out of multi-precision addition, because the actual result is not needed.
|
||||
* If {@code store} is true, the the returned operand may or may not be identical to the given
|
||||
* {@code left} parameter, depending on whether or not that operand can be written. The caller
|
||||
* must <em>always</em> use the returned operand to construct the legs of the final
|
||||
* multi-precision output operand. It must <em>never</em> use {@code left}, nor the
|
||||
* multi-precision operand containing it, as the final output.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry out of the previous legs'
|
||||
* sum, i.e., the carry in for these legs' sum.
|
||||
* @param left the leg for the left multi-precision operand
|
||||
* @param right the leg for the right multi-precision operand
|
||||
* @param storesResult true to receive the leg for the output multi-precision operand
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand or null, and the emitter typed with a stack of one long whose
|
||||
* value is the carry out for these legs' sum
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLegAddTakesAndGivesCarry(
|
||||
Emitter<Ent<Bot, TLong>> em, SimpleOpnd<TInt, IntJitType> left,
|
||||
SimpleOpnd<TInt, IntJitType> right, boolean storesResult, Scope scope) {
|
||||
return em
|
||||
.emit(IntAddOpGen::prepLeftAndCarry, left)
|
||||
.emit(IntAddOpGen::addRight, right)
|
||||
.emit(IntAddOpGen::maybeStore, left, storesResult, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode as in
|
||||
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
|
||||
* except that we do not expect a carry in on the stack.
|
||||
* <p>
|
||||
* This should be used to initiate the addition, taking the least-significant legs of the input
|
||||
* multi-precision operands.
|
||||
*
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param left the leg for the left multi-precision operand
|
||||
* @param right the leg for the right multi-precision operand
|
||||
* @param storesResult true to receive the leg for the output multi-precision operand
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand or null, and the emitter typed with a stack of one long whose
|
||||
* value is the carry out for these legs' sum
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Ent<Bot, TLong>> genMpIntLegAddGivesCarry(Emitter<Bot> em,
|
||||
SimpleOpnd<TInt, IntJitType> left, SimpleOpnd<TInt, IntJitType> right,
|
||||
boolean storesResult, Scope scope) {
|
||||
return em
|
||||
.emit(left::read)
|
||||
.emit(Op::i2l)
|
||||
.emit(IntAddOpGen::addRight, right)
|
||||
.emit(IntAddOpGen::maybeStore, left, storesResult, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode as in
|
||||
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)}
|
||||
* except that we do not leave a carry out on the stack.
|
||||
* <p>
|
||||
* This should be used to finalize the addition, taking the most-significant legs of the input
|
||||
* multi-precision operands. Note that this always stores the result and returns an output
|
||||
* operand. Otherwise, this would give no output at all, since it does not leave a carry out on
|
||||
* the stack.
|
||||
*
|
||||
* @param em the emitter typed with a stack of one long, the carry out of the previous legs'
|
||||
* sum, i.e., the carry in for these legs' sum.
|
||||
* @param left the leg for the left multi-precision operand
|
||||
* @param right the leg for the right multi-precision operand
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the output operand and the emitter typed with the empty stack
|
||||
*/
|
||||
static SimpleOpndEm<TInt, IntJitType, Bot> genMpIntLegAddTakesCarry(Emitter<Ent<Bot, TLong>> em,
|
||||
SimpleOpnd<TInt, IntJitType> left, SimpleOpnd<TInt, IntJitType> right, Scope scope) {
|
||||
return em
|
||||
.emit(IntAddOpGen::prepLeftAndCarry, left)
|
||||
.emit(IntAddOpGen::addRight, right)
|
||||
.emit(Op::l2i)
|
||||
.emit(left::write, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The strategy here follows from grade-school long addition. We assert that there are at least
|
||||
* two legs, otherwise we would have just emitted a single add bytecode. This allows us to
|
||||
* unconditionally initialize the addition with
|
||||
* {@link #genMpIntLegAddGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)} and
|
||||
* terminate it with {@link #genMpIntLegAddTakesCarry(Emitter, SimpleOpnd, SimpleOpnd, Scope)}.
|
||||
* When there are more than 2 legs, we use
|
||||
* {@link #genMpIntLegAddTakesAndGivesCarry(Emitter, SimpleOpnd, SimpleOpnd, boolean, Scope)} as
|
||||
* many times as necessary in the middle. For all legs, we store the result and append it as a
|
||||
* leg to the final output.
|
||||
*/
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntAddOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
/**
|
||||
* The strategy here is fairly simple. Ensure both operands are stored in locals. One of the
|
||||
* operands <em>must</em> be in temporaries to ensure we don't modify an input in place.
|
||||
* <p>
|
||||
* For each leg position, convert both right and left legs to a long and add them (along
|
||||
* with a possible carry in). Store the result back into the temp locals. Shift the long sum
|
||||
* right 32 to get the carry out, then continue to the next leg up. The final carry out can
|
||||
* be dropped (overflow).
|
||||
*/
|
||||
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
|
||||
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
|
||||
em = right.em();
|
||||
var lLegs = left.opnd().type().castLegsLE(left.opnd());
|
||||
assert lLegs.size() >= 2;
|
||||
var rLegs = right.opnd().type().castLegsLE(right.opnd());
|
||||
|
||||
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
|
||||
int legCount = type.legsAlloc();
|
||||
|
||||
var first = genMpIntLegAddGivesCarry(em, lLegs.getFirst(), rLegs.getFirst(), true, scope);
|
||||
var emCarry = first.em();
|
||||
outLegs.add(first.opnd());
|
||||
for (int i = 1; i < legCount - 1; i++) {
|
||||
var result =
|
||||
genMpIntLegAddTakesAndGivesCarry(emCarry, lLegs.get(i), rLegs.get(i), true, scope);
|
||||
emCarry = result.em();
|
||||
outLegs.add(result.opnd());
|
||||
}
|
||||
var last = genMpIntLegAddTakesCarry(emCarry, lLegs.getLast(), rLegs.getLast(), scope);
|
||||
em = last.em();
|
||||
outLegs.add(last.opnd());
|
||||
|
||||
var out = MpIntLocalOpnd.of(type, "out", outLegs);
|
||||
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,26 +15,36 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntAndOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntAndOp int_and}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the bitwise binary operator and emits {@link #IAND} or {@link #LAND} depending on the
|
||||
* type.
|
||||
* This uses the bitwise binary operator and emits {@link Op#iand(Emitter) iand} or
|
||||
* {@link Op#land(Emitter) land} depending on the type.
|
||||
*/
|
||||
public enum IntAndOpGen implements BitwiseBinOpGen<JitIntAndOp> {
|
||||
public enum IntAndOpGen implements IntBitwiseBinOpGen<JitIntAndOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IAND;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.iand(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LAND;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.land(em);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
* An extension for bitwise binary operators
|
||||
* <p>
|
||||
* This provides a simple strategy for multi-precision integer implementation. Since all bit
|
||||
* positions are considered independently, we just apply the same
|
||||
* {@link #opForInt(Emitter, IntJitType)} operator to each pair of corresponding legs independently
|
||||
* to compute each corresponding output leg.
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntBitwiseBinOpGen<T extends JitBinOp> extends IntOpBinOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
|
||||
Scope scope) {
|
||||
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
|
||||
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
|
||||
em = right.em();
|
||||
var lLegs = left.opnd().type().castLegsLE(left.opnd());
|
||||
var rLegs = right.opnd().type().castLegsLE(right.opnd());
|
||||
|
||||
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
|
||||
int legCount = type.legsAlloc();
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
var result = em
|
||||
.emit(lLegs.get(i)::read)
|
||||
.emit(rLegs.get(i)::read)
|
||||
.emit(this::opForInt, IntJitType.I4)
|
||||
.emit(lLegs.get(i)::write, scope);
|
||||
em = result.em();
|
||||
outLegs.add(result.opnd());
|
||||
}
|
||||
var out = MpIntLocalOpnd.of(type, "out", outLegs);
|
||||
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
|
||||
}
|
||||
}
|
||||
@@ -15,171 +15,142 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntCarryOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntCarryOp int_carry}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator. First we have to consider which strategy we are going to
|
||||
* use. If the p-code type is strictly smaller than its host JVM type, we can simply add the two
|
||||
* operands and examine the next bit up. This is accomplished by emitting {@link #IADD} or
|
||||
* {@link #LADD}, depending on the type, followed by a shift right and a mask.
|
||||
*
|
||||
* This uses the integer predicate operator generator. First we have to consider which strategy we
|
||||
* are going to use. If the p-code type is strictly smaller than its host JVM type, we can simply
|
||||
* add the two operands and examine the next bit up. This is accomplished by emitting
|
||||
* {@link Op#iadd(Emitter) iadd} or {@link Op#ladd(Emitter) ladd}, depending on the type, followed
|
||||
* by a shift right and a mask.
|
||||
* <p>
|
||||
* If the p-code type exactly fits its host JVM type, we still add, but we will need to compare the
|
||||
* result to one of the operands. Thus, we override
|
||||
* {@link #afterLeft(JitCodeGenerator, JitIntCarryOp, JitType, JitType, MethodVisitor) afterLeft}
|
||||
* and emit code to duplicate the left operand. We can then add and invoke
|
||||
* {@link Integer#compareUnsigned(int, int)} to determine whether there was overflow. If there was,
|
||||
* then we know the carry bit would have been set. We can spare the conditional flow by just
|
||||
* shifting the sign bit into the 1's place.
|
||||
*
|
||||
* result to one of the operands. Thus, we emit code to duplicate the left operand. We can then add
|
||||
* and invoke {@link Integer#compareUnsigned(int, int)} (or similar for longs) to determine whether
|
||||
* there was overflow. If there was, then we know the carry bit would have been set. We can spare
|
||||
* the conditional flow by just shifting the sign bit into the 1's place.
|
||||
* <p>
|
||||
* NOTE: The multi-precision integer parts of this are a work in progress.
|
||||
* For multi-precision integers, we invoke the subroutines in {@link IntAddOpGen}, but do not store
|
||||
* the results, because we only need the carry. When we reach the end, we take advantage of the fact
|
||||
* that the final stack result is actually the full 33-bit result for the last leg. We can just
|
||||
* shift it the required number of bytes (depending on the type of the input operands) and mask for
|
||||
* the desired carry bit.
|
||||
*/
|
||||
public enum IntCarryOpGen implements IntBinOpGen<JitIntCarryOp> {
|
||||
public enum IntCarryOpGen implements IntPredBinOpGen<JitIntCarryOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
private void generateMpIntCarry(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
/**
|
||||
* Similar strategy as for INT_ADD. In fact, we call its per-leg logic.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
int remSize = type.partialSize();
|
||||
|
||||
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", legCount)) {
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ISTORE, temp.idx(i));
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
}
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean takesCarry = i != 0; // not first
|
||||
IntAddOpGen.generateMpIntLegAdd(gen, temp.idx(i), takesCarry, true, false, mv);
|
||||
}
|
||||
// [olegN:LONG]
|
||||
if (remSize == 0) {
|
||||
// The last leg was full, so extract bit 32
|
||||
mv.visitLdcInsn(32);
|
||||
}
|
||||
else {
|
||||
// The last leg was partial, so get the next more significant bit
|
||||
mv.visitLdcInsn(remSize * Byte.SIZE);
|
||||
}
|
||||
mv.visitInsn(LUSHR);
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, Ext.ZERO, mv);
|
||||
mv.visitLdcInsn(1);
|
||||
mv.visitInsn(IAND);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntCarryOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
/**
|
||||
* There are two strategies to use here depending on whether or not there's room to capture
|
||||
* the carry bit. If there's not room, we have to compare the sum to one of the input
|
||||
* operands. If the sum is less, then we can conclude there was a carry. For that strategy,
|
||||
* we will need to keep a copy of the left operand, so duplicate it.
|
||||
*
|
||||
* On the other hand, if there is room to capture the carry, we can just add the two
|
||||
* operands and extract the carry bit. There is no need to duplicate the left operand.
|
||||
*/
|
||||
lType = TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
switch (lType) {
|
||||
case IntJitType(int size) when size == Integer.BYTES -> rv.visitInsn(DUP);
|
||||
case IntJitType lt -> {
|
||||
}
|
||||
case LongJitType(int size) when size == Long.BYTES -> rv.visitInsn(DUP2);
|
||||
case LongJitType lt -> {
|
||||
}
|
||||
case MpIntJitType lt -> {
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
if (type == IntJitType.I4) {
|
||||
return em
|
||||
.emit(Op::dup_x1) // r l r
|
||||
.emit(Op::iadd)
|
||||
.emit(Op::swap)
|
||||
.emit(Op::invokestatic, TR_INTEGER, "compareUnsigned", MDESC_INTEGER__COMPARE,
|
||||
false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
// sum < l iff sign bit is 1
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::iushr);
|
||||
}
|
||||
return lType;
|
||||
// Just add and extract the carry bit
|
||||
return em
|
||||
.emit(Op::iadd)
|
||||
.emit(Op::ldc__i, type.size() * Byte.SIZE)
|
||||
.emit(Op::ishr)
|
||||
// LATER: This mask may not be necessary....
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::iand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntCarryOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType(int size) when size == Integer.BYTES -> {
|
||||
// [l,l,r]
|
||||
rv.visitInsn(IADD);
|
||||
// [l,sum]
|
||||
rv.visitInsn(SWAP); // spare an LDC,XOR
|
||||
// [sum,l]
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "compareUnsigned",
|
||||
MDESC_INTEGER__COMPARE, false);
|
||||
// [cmpU(sum,l)] sum < l iff sign bit is 1
|
||||
rv.visitLdcInsn(31);
|
||||
rv.visitInsn(IUSHR);
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case IntJitType(int size) -> {
|
||||
// Just add and extract the carry bit
|
||||
rv.visitInsn(IADD);
|
||||
rv.visitLdcInsn(size * Byte.SIZE);
|
||||
rv.visitInsn(ISHR);
|
||||
rv.visitLdcInsn(1);
|
||||
rv.visitInsn(IAND);
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case LongJitType(int size) when size == Long.BYTES -> {
|
||||
// [l:LONG,l:LONG,r:LONG]
|
||||
rv.visitInsn(LADD);
|
||||
// [l:LONG,sum:LONG]
|
||||
rv.visitInsn(DUP2_X2);
|
||||
rv.visitInsn(POP2);
|
||||
// [sum:LONG,l:LONG]
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "compareUnsigned",
|
||||
MDESC_LONG__COMPARE_UNSIGNED, false);
|
||||
// [cmpU(sum,l):INT] sum < l iff sign bit is 1
|
||||
rv.visitLdcInsn(31);
|
||||
rv.visitInsn(IUSHR);
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case LongJitType(int size) -> {
|
||||
// Just add and extract the carry bit
|
||||
rv.visitInsn(LADD);
|
||||
rv.visitLdcInsn(size * Byte.SIZE);
|
||||
rv.visitInsn(LSHR);
|
||||
rv.visitInsn(L2I);
|
||||
// TODO: This mask may not be necessary
|
||||
rv.visitLdcInsn(1);
|
||||
rv.visitInsn(IAND);
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case MpIntJitType t when t.size() == lType.size() -> {
|
||||
generateMpIntCarry(gen, t, rv);
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case MpIntJitType t -> {
|
||||
return TODO("MpInt of differing sizes");
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
if (type == LongJitType.I8) {
|
||||
return em
|
||||
.emit(Op::dup2_x2_22) // r l r
|
||||
.emit(Op::ladd)
|
||||
.emit(Op::dup2_x2_22)
|
||||
.emit(Op::pop2__2)
|
||||
.emit(Op::invokestatic, TR_LONG, "compareUnsigned", MDESC_LONG__COMPARE,
|
||||
false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
// sum < l iff sign bit is 1
|
||||
.emit(Op::ldc__i, Integer.SIZE - 1)
|
||||
.emit(Op::iushr);
|
||||
}
|
||||
// Just add and extract the carry bit
|
||||
return em
|
||||
.emit(Op::ladd)
|
||||
.emit(Op::ldc__i, type.size() * Byte.SIZE)
|
||||
.emit(Op::lshr)
|
||||
.emit(Op::l2i)
|
||||
// LATER: This mask may not be necessary....
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::iand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntCarryOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
/**
|
||||
* Similar strategy as for INT_ADD. In fact, we call its per-leg logic.
|
||||
*/
|
||||
var left = gen.genReadToOpnd(em, localThis, op.l(), type, ext(), scope);
|
||||
var right = gen.genReadToOpnd(left.em(), localThis, op.r(), type, rExt(), scope);
|
||||
em = right.em();
|
||||
var lLegs = left.opnd().type().castLegsLE(left.opnd());
|
||||
assert lLegs.size() >= 2;
|
||||
var rLegs = right.opnd().type().castLegsLE(right.opnd());
|
||||
|
||||
int legCount = type.legsAlloc();
|
||||
|
||||
var first = IntAddOpGen.genMpIntLegAddGivesCarry(em, lLegs.getFirst(), rLegs.getFirst(),
|
||||
false, scope);
|
||||
var emCarry = first.em();
|
||||
for (int i = 1; i < legCount; i++) {
|
||||
var result = IntAddOpGen.genMpIntLegAddTakesAndGivesCarry(emCarry, lLegs.get(i),
|
||||
rLegs.get(i), false, scope);
|
||||
emCarry = result.em();
|
||||
}
|
||||
// carry on top of stack is really [carry][sum] in long
|
||||
if (type.partialSize() == 0) {
|
||||
// The last leg was full, so extract the carry bit
|
||||
return emCarry
|
||||
.emit(Op::ldc__i, Integer.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(Op::l2i);
|
||||
}
|
||||
// The last leg was partial, so just get the bit one to the right
|
||||
return emCarry
|
||||
.emit(Op::ldc__i, type.partialSize() * Byte.SIZE)
|
||||
.emit(Op::lushr)
|
||||
.emit(Op::l2i)
|
||||
// LATER: This mask probably not needed
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::iand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Lbl.LblEm;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntTestOp;
|
||||
|
||||
/**
|
||||
* An extension for integer comparison operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntCompareBinOpGen<T extends JitIntTestOp> extends IntPredBinOpGen<T> {
|
||||
|
||||
/**
|
||||
* Invert the boolean on top of the stack
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the boolean on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
static <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> not(Emitter<N0> em) {
|
||||
return em
|
||||
// Where this is used, the previous subroutines have already emitted &1
|
||||
//.emit(Op::ldc__i, 1)
|
||||
//.emit(Op::iand)
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::ixor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming a conditional jump bytecode was just emitted, emit bytecode to push 0 (false) onto
|
||||
* the stack for the fall-through case, or 1 (true) onto the stack for the taken case.
|
||||
*
|
||||
* @param <N> the incoming stack, i.e., that after emitting the conditional jump
|
||||
* @param lblEmTrue the target label of the conditional jump just emitted, and the emitter typed
|
||||
* with the incoming stack
|
||||
* @return the emitter with the resulting stack, i.e., having pushed the boolean result
|
||||
*/
|
||||
default <N extends Next> Emitter<Ent<N, TInt>> genBool(LblEm<N, N> lblEmTrue) {
|
||||
var lblEmDone = lblEmTrue.em()
|
||||
.emit(Op::ldc__i, 0)
|
||||
.emit(Op::goto_);
|
||||
return lblEmDone.em()
|
||||
.emit(Lbl::placeDead, lblEmTrue.lbl())
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Lbl::place, lblEmDone.lbl());
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation for (unsigned) int operands that invokes
|
||||
* {@link Integer#compareUnsigned(int, int)} and then emits the given {@code if<cond>} jump.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
|
||||
* jump
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
default <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> genIntViaUcmpThenIf(Emitter<N0> em,
|
||||
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
|
||||
return em
|
||||
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "compareUnsigned",
|
||||
GenConsts.MDESC_INTEGER__COMPARE, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(this::genIntViaIf, opIf);
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation for (signed) int operands that simply emits the given {@code if_icmp<cond>}
|
||||
* jump.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param opIfIcmp a method reference, e.g., to {@link Op#if_icmpge(Emitter, Lbl)} for the
|
||||
* conditional jump
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
default <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>>
|
||||
genIntViaIfIcmp(Emitter<N0> em, Function<Emitter<N0>, LblEm<N2, N2>> opIfIcmp) {
|
||||
return genBool(opIfIcmp.apply(em));
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility that emits the given {@code if<cond>} along with the logic that pushes the correct
|
||||
* result depending on whether or not the jump is taken.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack including the predicate, which is compared with 0
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
|
||||
* jump
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
default <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
genIntViaIf(Emitter<N0> em, Function<Emitter<N0>, LblEm<N1, N1>> opIf) {
|
||||
return genBool(opIf.apply(em));
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation for (signed) long operands that emits {@link Op#lcmp(Emitter) lcmp} and
|
||||
* then emits the given {@code if<cond>} jump.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
|
||||
* jump
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
default <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> genLongViaLcmpThenIf(Emitter<N0> em,
|
||||
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
|
||||
return em
|
||||
.emit(Op::lcmp)
|
||||
.emit(this::genIntViaIf, opIf);
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation for (unsigned) long operands that invokes
|
||||
* {@link Long#compareUnsigned(long, long)} and then emits the given {@code if<cond>} jump.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param opIf a method reference, e.g., to {@link Op#ifge(Emitter, Lbl)} for the conditional
|
||||
* jump
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
default <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> genLongViaUcmpThenIf(Emitter<N0> em,
|
||||
Function<Emitter<Ent<N2, TInt>>, LblEm<N2, N2>> opIf) {
|
||||
return em
|
||||
.emit(Op::invokestatic, GenConsts.TR_LONG, "compareUnsigned",
|
||||
GenConsts.MDESC_LONG__COMPARE, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(this::genIntViaIf, opIf);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The strategy for multi-precision comparison can be applied to all comparisons: Start with the
|
||||
* most-significant legs and compare <em>for equality</em> until we find the first not-equal
|
||||
* pair. Then, apply {@link #opForInt(Emitter, IntJitType)} to determine the overall result.
|
||||
* There is no need to load or compare any legs beyond the most-significant not-equal pair. If
|
||||
* we reach the final (least-significant) pair, we need not check them for equality. Just
|
||||
* delegate to {@link #opForInt(Emitter, IntJitType)}.
|
||||
*/
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
|
||||
Scope scope) {
|
||||
/**
|
||||
* Need to examine from most-significant to least-significant. Stop at the first pair of
|
||||
* legs which are not equal, or on the least-significant leg.
|
||||
*/
|
||||
int legCount = type.legsAlloc();
|
||||
Local<TInt> localLLeg = scope.decl(Types.T_INT, "lLeg");
|
||||
Local<TInt> localRLeg = scope.decl(Types.T_INT, "rLeg");
|
||||
Lbl<Bot> lblDone = Lbl.create();
|
||||
for (int i = legCount - 1; i > 0; i--) { // Yes, stop one before 0, so use >, not >=
|
||||
em = em
|
||||
.emit(gen::genReadLegToStack, localThis, op.l(), type, i, ext())
|
||||
.emit(Op::dup)
|
||||
.emit(Op::istore, localLLeg)
|
||||
.emit(gen::genReadLegToStack, localThis, op.r(), type, i, ext())
|
||||
.emit(Op::dup)
|
||||
.emit(Op::istore, localRLeg)
|
||||
.emit(IntNotEqualOpGen.GEN::opForInt, IntJitType.I4)
|
||||
.emit(Op::ifne, lblDone);
|
||||
}
|
||||
// We've reached the last leg. Just load them onto the stack
|
||||
var lblEmStaged = em
|
||||
.emit(gen::genReadLegToStack, localThis, op.l(), type, 0, ext())
|
||||
.emit(gen::genReadLegToStack, localThis, op.r(), type, 0, ext())
|
||||
.emit(Op::goto_);
|
||||
return lblEmStaged.em()
|
||||
.emit(Lbl::placeDead, lblDone)
|
||||
.emit(Op::iload, localLLeg)
|
||||
.emit(Op::iload, localRLeg)
|
||||
.emit(Lbl::place, lblEmStaged.lbl())
|
||||
.emit(this::opForInt, IntJitType.I4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitUnOp;
|
||||
|
||||
/**
|
||||
* An extension for unary integer operators that count bits
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntCountUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with int operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param type the p-code type of the input operand
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> opForInt(Emitter<N0> em,
|
||||
IntJitType type);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with long operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param type the p-code type of the input operand
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TInt>> opForLong(Emitter<N0> em,
|
||||
LongJitType type);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with mp-int operands.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param type the p-code type of the input operand
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the resulting stack, i.e., with only the result pushed
|
||||
*/
|
||||
<THIS extends JitCompiledPassage> Emitter<Ent<Bot, TInt>> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
|
||||
Scope scope);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
var emResult = switch (uType) {
|
||||
case IntJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForInt, t);
|
||||
case LongJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForLong, t);
|
||||
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
return new LiveOpResult(
|
||||
gen.genWriteFromStack(emResult, localThis, op.out(), IntJitType.I4, ext(), scope));
|
||||
}
|
||||
}
|
||||
@@ -15,27 +15,29 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntAddOp;
|
||||
import ghidra.pcode.emu.jit.op.JitIntDivOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntAddOp int_add}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #INVOKESTATIC} on
|
||||
* {@link Integer#divideUnsigned(int, int)} or {@link Long#divideUnsigned(long, long)} depending on
|
||||
* the type.
|
||||
* This uses the binary operator generator and simply emits
|
||||
* {@link Op#invokestatic(Emitter, TRef, String, ghidra.pcode.emu.jit.gen.util.Methods.MthDesc, boolean)
|
||||
* invokestatic} on {@link Integer#divideUnsigned(int, int)} or *
|
||||
* {@link Long#divideUnsigned(long, long)} depending on the type.
|
||||
* <p>
|
||||
* For multi-precision division, this emits code to invoke
|
||||
* {@link JitCompiledPassage#mpIntDivide(int[], int[], int[])}.
|
||||
*/
|
||||
public enum IntDivOpGen implements IntBinOpGen<JitIntDivOp> {
|
||||
public enum IntDivOpGen implements IntOpBinOpGen<JitIntDivOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -44,31 +46,33 @@ public enum IntDivOpGen implements IntBinOpGen<JitIntDivOp> {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntDiv(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntDivide", mv, 1, TakeOut.OUT);
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return em
|
||||
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "divideUnsigned",
|
||||
GenConsts.MDESC_$INT_BINOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntDivOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return em
|
||||
.emit(Op::invokestatic, GenConsts.TR_LONG, "divideUnsigned",
|
||||
GenConsts.MDESC_$LONG_BINOP, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntDivOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, rExt(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "divideUnsigned",
|
||||
MDESC_$INT_BINOP, false);
|
||||
case LongJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "divideUnsigned",
|
||||
MDESC_$LONG_BINOP, false);
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntDiv(gen, t, rv);
|
||||
// FIXME: forceUniform shouldn't have to enforce the same size....
|
||||
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
|
||||
return rType;
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntDivOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
return genMpDelegationToStaticMethod(em, gen, localThis, type, "mpIntDivide", op, 1,
|
||||
TakeOut.OUT, scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,23 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntEqualOp int_equal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the integer comparison operator generator and simply emits {@link #IF_ICMPEQ} or
|
||||
* {@link #IFEQ} depending on the type.
|
||||
* To avoid jumps, this delegates to {@link Integer#compare(int, int)}, which is signed, and then
|
||||
* inverts the result.
|
||||
*/
|
||||
public enum IntEqualOpGen implements CompareIntBinOpGen<JitIntEqualOp> {
|
||||
public enum IntEqualOpGen implements IntCompareBinOpGen<JitIntEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -34,12 +41,18 @@ public enum IntEqualOpGen implements CompareIntBinOpGen<JitIntEqualOp> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int icmpOpcode() {
|
||||
return IF_ICMPEQ;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return em
|
||||
.emit(IntNotEqualOpGen.GEN::opForInt, type)
|
||||
.emit(IntCompareBinOpGen::not);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int ifOpcode() {
|
||||
return IFEQ;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return em
|
||||
.emit(IntNotEqualOpGen.GEN::opForLong, type)
|
||||
.emit(IntCompareBinOpGen::not);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Bot;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.pcode.emu.jit.op.JitUnOp;
|
||||
|
||||
/**
|
||||
* An extension for unary integer extension operators
|
||||
* <p>
|
||||
* The strategy here is to do nothing more than invoke the readers and writers. Because those are
|
||||
* responsible for converting between the types, with the appropriate signedness, the work of
|
||||
* extension is already done. We need only to know whether or not the operators should be treated as
|
||||
* signed or unsigned. Thankfully, that method is already required by a super interface.
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntExtUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
JitType oType = gen.resolveType(op.out(), op.type());
|
||||
JitType minType = JitType.unifyLeast(uType, oType);
|
||||
return new LiveOpResult(switch (minType) {
|
||||
case IntJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
case LongJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
// Need floats for COPY
|
||||
case FloatJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
case DoubleJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
case MpIntJitType t -> {
|
||||
var result = em
|
||||
.emit(gen::genReadToOpnd, localThis, op.u(), t, ext(), scope);
|
||||
yield result.em()
|
||||
.emit(gen::genWriteFromOpnd, localThis, op.out(), result.opnd(), ext(),
|
||||
scope);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import ghidra.pcode.emu.jit.op.JitIntLeftOp;
|
||||
* This uses the integer shift operator generator and simply invokes
|
||||
* {@link JitCompiledPassage#intLeft(int, int)}, etc. depending on the types.
|
||||
*/
|
||||
public enum IntLeftOpGen implements ShiftIntBinOpGen<JitIntLeftOp> {
|
||||
public enum IntLeftOpGen implements IntShiftBinOpGen<JitIntLeftOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
@@ -15,15 +15,24 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntLessEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntLessEqualOp int_lessequal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the integer comparison operator generator and simply emits {@link #IFLE}.
|
||||
* This uses the (unsigned) integer comparison operator generator and simply emits
|
||||
* {@link Op#ifle(Emitter) ifle}.
|
||||
*/
|
||||
public enum IntLessEqualOpGen implements CompareIntBinOpGen<JitIntLessEqualOp> {
|
||||
public enum IntLessEqualOpGen implements IntCompareBinOpGen<JitIntLessEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -33,12 +42,14 @@ public enum IntLessEqualOpGen implements CompareIntBinOpGen<JitIntLessEqualOp> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int icmpOpcode() {
|
||||
throw new AssertionError();
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return genIntViaUcmpThenIf(em, Op::ifle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int ifOpcode() {
|
||||
return IFLE;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return genLongViaUcmpThenIf(em, Op::ifle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,24 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntLessOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntLessOp int_less}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the integer comparison operator generator and simply emits {@link #IFLT}.
|
||||
* This uses the (unsigned) integer comparison operator generator and simply emits
|
||||
* {@link Op#iflt(Emitter) iflt}.
|
||||
*/
|
||||
public enum IntLessOpGen implements CompareIntBinOpGen<JitIntLessOp> {
|
||||
public enum IntLessOpGen implements IntCompareBinOpGen<JitIntLessOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -33,12 +42,14 @@ public enum IntLessOpGen implements CompareIntBinOpGen<JitIntLessOp> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int icmpOpcode() {
|
||||
throw new AssertionError();
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return genIntViaUcmpThenIf(em, Op::iflt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int ifOpcode() {
|
||||
return IFLT;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return genLongViaUcmpThenIf(em, Op::iflt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,24 +15,25 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntMultOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntMultOp int_mult}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the binary operator generator and simply emits {@link #IMUL} or {@link #LMUL} depending
|
||||
* on the type.
|
||||
* This uses the binary operator generator and simply emits {@link Op#imul(Emitter) imul} or
|
||||
* {@link Op#lmul(Emitter) lmul} depending on the type.
|
||||
* <p>
|
||||
* For multi-precision multiplication, this emits code to invoke
|
||||
* {@link JitCompiledPassage#mpIntMultiply(int[], int[], int[])}
|
||||
*/
|
||||
public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
|
||||
public enum IntMultOpGen implements IntOpBinOpGen<JitIntMultOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -41,6 +42,18 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.imul(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.lmul(em);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the mp-int multiply code.
|
||||
* <p>
|
||||
@@ -55,7 +68,8 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
|
||||
* That ensures all the operand sizes match, which is often (at least conventionally) required
|
||||
* by the Sleigh compiler. However, if r1 and r2 are each only 64 bits, and I can keep track of
|
||||
* that fact, then I could perform about half as many multiplies and adds. It also be nice if I
|
||||
* can look ahead and see that only 64 bits of temp is actually used.
|
||||
* can look ahead and see that only 64 bits of temp is actually used. The same is true of
|
||||
* {@link IntDivOpGen}, {@link IntRemOpGen}, {@link IntSDivOpGen}, and {@link IntSRemOpGen}.
|
||||
* <p>
|
||||
* <b>IDEA:</b> It would be quite a change, but perhaps generating a temporary JVM-level DFG
|
||||
* would be useful for culling. The difficulty here is knowing whether or not a temp (unique) is
|
||||
@@ -63,32 +77,18 @@ public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
|
||||
* additional Sleigh compiler support. If used, I should not cull any computations, so that the
|
||||
* retired value is the full value.
|
||||
*
|
||||
* @param em the code emitter with an empty stack
|
||||
* @param localThis a handle to the owning compiled passage
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param type the (uniform) type of the inputs and output operands
|
||||
* @param mv the method visitor
|
||||
* @param scope a scope for op-temporary variables
|
||||
*/
|
||||
private void generateMpIntMult(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntMultiply", mv, 0, TakeOut.OUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntMultOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntMultOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(IMUL);
|
||||
case LongJitType t -> rv.visitInsn(LMUL);
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntMult(gen, t, rv);
|
||||
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
|
||||
return rType;
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntMultOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
return genMpDelegationToStaticMethod(em, gen, localThis, type, "mpIntMultiply", op, 0,
|
||||
TakeOut.OUT, scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,24 +15,28 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.MpIntLocalOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.SimpleOpnd;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitIntNegateOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntNegateOp int_negate}.
|
||||
*
|
||||
* <p>
|
||||
* There is no bitwise "not" operator in the JVM. We borrow the pattern we see output by the Java
|
||||
* compiler for <code>int negate(n) {return ~n;}</code>. It XORs the input with a register of 1s.
|
||||
* This uses the unary operator generator and emits the equivalent code.
|
||||
*/
|
||||
public enum IntNegateOpGen implements IntUnOpGen<JitIntNegateOp> {
|
||||
public enum IntNegateOpGen implements IntOpUnOpGen<JitIntNegateOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -41,38 +45,41 @@ public enum IntNegateOpGen implements IntUnOpGen<JitIntNegateOp> {
|
||||
return false; // TODO: Is it? Test with 3-byte operands to figure it out.
|
||||
}
|
||||
|
||||
private void generateMpIntNegate(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
int legCount = type.legsAlloc();
|
||||
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", legCount)) {
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ISTORE, temp.idx(i));
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
}
|
||||
// Compute and push back in reverse order
|
||||
int i = legCount;
|
||||
for (SimpleJitType t : type.legTypes()) {
|
||||
mv.visitVarInsn(ILOAD, temp.idx(--i));
|
||||
mv.visitLdcInsn(-1 >>> (Integer.SIZE - t.size() * Byte.SIZE));
|
||||
mv.visitInsn(IXOR);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public <N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>>
|
||||
opForInt(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__i, -1)
|
||||
.emit(Op::ixor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitIntNegateOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case IntJitType t -> {
|
||||
rv.visitLdcInsn(-1 >>> (Integer.SIZE - t.size() * Byte.SIZE));
|
||||
rv.visitInsn(IXOR);
|
||||
}
|
||||
case LongJitType t -> {
|
||||
rv.visitLdcInsn(-1L >>> (Long.SIZE - t.size() * Byte.SIZE));
|
||||
rv.visitInsn(LXOR);
|
||||
}
|
||||
case MpIntJitType t -> generateMpIntNegate(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
public <N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>>
|
||||
opForLong(Emitter<N0> em) {
|
||||
return em
|
||||
.emit(Op::ldc__l, -1L)
|
||||
.emit(Op::lxor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitIntNegateOp op,
|
||||
MpIntJitType type, Scope scope) {
|
||||
var opnd = gen.genReadToOpnd(em, localThis, op.u(), type, ext(), scope);
|
||||
em = opnd.em();
|
||||
var legs = opnd.opnd().type().castLegsLE(opnd.opnd());
|
||||
|
||||
List<SimpleOpnd<TInt, IntJitType>> outLegs = new ArrayList<>();
|
||||
int legCount = type.legsAlloc();
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
var result = em
|
||||
.emit(legs.get(i)::read)
|
||||
.emit(this::opForInt)
|
||||
.emit(legs.get(i)::write, scope);
|
||||
em = result.em();
|
||||
outLegs.add(result.opnd());
|
||||
}
|
||||
return uType;
|
||||
var out = MpIntLocalOpnd.of(type, "out", outLegs);
|
||||
return gen.genWriteFromOpnd(em, localThis, op.out(), out, ext(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,26 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntNotEqualOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntNotEqualOp int_notequal}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the integer comparison operator generator and simply emits {@link #IF_ICMPNE} or
|
||||
* {@link #IFNE} depending on the type.
|
||||
* To avoid jumps, this delegates to {@link Integer#compare(int, int)}, which is signed, and then
|
||||
* masks the result.
|
||||
*/
|
||||
public enum IntNotEqualOpGen implements CompareIntBinOpGen<JitIntNotEqualOp> {
|
||||
public enum IntNotEqualOpGen implements IntCompareBinOpGen<JitIntNotEqualOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -34,12 +44,26 @@ public enum IntNotEqualOpGen implements CompareIntBinOpGen<JitIntNotEqualOp> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int icmpOpcode() {
|
||||
return IF_ICMPNE;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return em
|
||||
.emit(Op::invokestatic, GenConsts.TR_INTEGER, "compare",
|
||||
GenConsts.MDESC_INTEGER__COMPARE, false)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::iand);
|
||||
// LATER: Can probably remove this mask 1. Tests check it, but still.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int ifOpcode() {
|
||||
return IFNE;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TInt>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return em
|
||||
.emit(Op::lcmp)
|
||||
.emit(Op::ldc__i, 1)
|
||||
.emit(Op::iand);
|
||||
// LATER: Can probably remove this mask 1. Tests check it, but still.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.opnd.Opnd.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
* An extension for integer binary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntOpBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with intF operands on the stack.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param type the p-code type of the operands
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>> Emitter<Ent<N2, TInt>>
|
||||
opForInt(Emitter<N0> em, IntJitType type);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with long operands on the stack.
|
||||
*
|
||||
* @param <N2> the tail of the incoming stack
|
||||
* @param <N1> the tail of the incoming stack including the right operand
|
||||
* @param <N0> the incoming stack with the right and left operands on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @param type the p-code type of the operands
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>> Emitter<Ent<N2, TLong>>
|
||||
opForLong(Emitter<N0> em, LongJitType type);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with multi-precision operands.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param type the p-code type of the operands
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the empty stack
|
||||
*/
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
|
||||
Scope scope);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType lType = gen.resolveType(op.l(), op.lType());
|
||||
JitType rType = gen.resolveType(op.r(), op.rType());
|
||||
JitType uType = JitType.unify(lType, rType);
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case IntJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
|
||||
.emit(this::opForInt, t)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case LongJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.l(), t, ext())
|
||||
.emit(gen::genReadToStack, localThis, op.r(), t, rExt())
|
||||
.emit(this::opForLong, t)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, Ext.ZERO, scope);
|
||||
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.RetReq;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.*;
|
||||
import ghidra.pcode.emu.jit.op.JitUnOp;
|
||||
|
||||
/**
|
||||
* An extension for integer unary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntOpUnOpGen<T extends JitUnOp> extends UnOpGen<T> {
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with int operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TInt>> Emitter<Ent<N1, TInt>> opForInt(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with long operands on the stack.
|
||||
*
|
||||
* @param <N1> the tail of the incoming stack
|
||||
* @param <N0> the incoming stack with the input operand on top
|
||||
* @param em the emitter typed with the incoming stack
|
||||
* @return the emitter typed with the resulting stack, i.e., the tail with the result pushed
|
||||
*/
|
||||
<N1 extends Next, N0 extends Ent<N1, TLong>> Emitter<Ent<N1, TLong>> opForLong(Emitter<N0> em);
|
||||
|
||||
/**
|
||||
* Emit the JVM bytecode to perform the operator with multi-precision operands.
|
||||
*
|
||||
* @param <THIS> the type of the generated passage
|
||||
* @param em the emitter typed with the empty stack
|
||||
* @param localThis a handle to the local holding the {@code this} reference
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op
|
||||
* @param type the p-code type of the operands
|
||||
* @param scope a scope for generating temporary local storage
|
||||
* @return the emitter typed with the empty stack
|
||||
*/
|
||||
<THIS extends JitCompiledPassage> Emitter<Bot> genRunMpInt(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, T op, MpIntJitType type,
|
||||
Scope scope);
|
||||
|
||||
@Override
|
||||
default <THIS extends JitCompiledPassage> OpResult genRun(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, Local<TInt> localCtxmod, RetReq<TRef<EntryPoint>> retReq,
|
||||
JitCodeGenerator<THIS> gen, T op, JitBlock block, Scope scope) {
|
||||
JitType uType = gen.resolveType(op.u(), op.uType());
|
||||
return new LiveOpResult(switch (uType) {
|
||||
case IntJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForInt)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
case LongJitType t -> em
|
||||
.emit(gen::genReadToStack, localThis, op.u(), t, ext())
|
||||
.emit(this::opForLong)
|
||||
.emit(gen::genWriteFromStack, localThis, op.out(), t, ext(), scope);
|
||||
case MpIntJitType t -> genRunMpInt(em, localThis, gen, op, t, scope);
|
||||
default -> throw new AssertionError();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,26 +15,36 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Op;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TInt;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TLong;
|
||||
import ghidra.pcode.emu.jit.op.JitIntOrOp;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitIntOrOp int_or}.
|
||||
*
|
||||
* <p>
|
||||
* This uses the bitwise binary operator and emits {@link #IOR} or {@link #LOR} depending on the
|
||||
* type.
|
||||
* This uses the bitwise binary operator and emits {@link Op#ior(Emitter) ior} or
|
||||
* {@link Op#lor(Emitter) lor} depending on the type.
|
||||
*/
|
||||
public enum IntOrOpGen implements BitwiseBinOpGen<JitIntOrOp> {
|
||||
public enum IntOrOpGen implements IntBitwiseBinOpGen<JitIntOrOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public int intOpcode() {
|
||||
return IOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TInt>, N0 extends Ent<N1, TInt>>
|
||||
Emitter<Ent<N2, TInt>> opForInt(Emitter<N0> em, IntJitType type) {
|
||||
return Op.ior(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int longOpcode() {
|
||||
return LOR;
|
||||
public <N2 extends Next, N1 extends Ent<N2, TLong>, N0 extends Ent<N1, TLong>>
|
||||
Emitter<Ent<N2, TLong>> opForLong(Emitter<N0> em, LongJitType type) {
|
||||
return Op.lor(em);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user