mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-22 05:01:55 +08:00
GP-5214: Add support for multi-precision integer operations in the JIT-accelerated emulator.
This commit is contained in:
+5
@@ -191,6 +191,11 @@ public interface EmuSyscallLibrary<T> extends PcodeUseropLibrary<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return void.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeUseropLibrary<?> getDefiningLibrary() {
|
||||
return syslib;
|
||||
|
||||
@@ -145,6 +145,11 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return void.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getJavaMethod() {
|
||||
return null;
|
||||
|
||||
+248
-68
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.analysis;
|
||||
|
||||
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.*;
|
||||
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.maxAddr;
|
||||
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.overlapsLeft;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.collections4.iterators.ReverseListIterator;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
@@ -33,6 +33,7 @@ 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.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.var.*;
|
||||
import ghidra.program.model.address.*;
|
||||
@@ -258,6 +259,13 @@ public class JitAllocationModel {
|
||||
generateLoadCode(rv);
|
||||
VarGen.generateValWriteCodeDirect(gen, type, vn, rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the maximum address that would be occupied by the full primitive type}
|
||||
*/
|
||||
public Address maxPrimAddr() {
|
||||
return vn.getAddress().add(type.ext().size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,9 +304,10 @@ public class JitAllocationModel {
|
||||
* @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
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv);
|
||||
void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext, MethodVisitor rv);
|
||||
|
||||
/**
|
||||
* Emit bytecode to load the varnode's value onto the JVM stack.
|
||||
@@ -306,9 +315,10 @@ public class JitAllocationModel {
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value produced on the JVM stack by the preceding
|
||||
* bytecode
|
||||
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*/
|
||||
void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv);
|
||||
void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext, MethodVisitor rv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,14 +344,16 @@ public class JitAllocationModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
default void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
local().generateLoadCode(rv);
|
||||
TypeConversions.generate(gen, this.type(), type, rv);
|
||||
TypeConversions.generate(gen, this.type(), type, ext, rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
TypeConversions.generate(gen, type, this.type(), rv);
|
||||
default void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
TypeConversions.generate(gen, type, this.type(), ext, rv);
|
||||
local().generateStoreCode(rv);
|
||||
}
|
||||
}
|
||||
@@ -387,9 +399,9 @@ public class JitAllocationModel {
|
||||
* shifted to the right to place it into position.
|
||||
*
|
||||
* @param local the local variable allocated to this part
|
||||
* @param shift the number of bytes and direction to shift
|
||||
* @param shift the number of bytes and direction to shift (+ is right)
|
||||
*/
|
||||
public record MultiLocalPart(JvmLocal local, int shift) {
|
||||
public record MultiLocalSub(JvmLocal local, int shift) {
|
||||
private JitType chooseLargerType(JitType t1, JitType t2) {
|
||||
return t1.size() > t2.size() ? t1 : t2;
|
||||
}
|
||||
@@ -405,15 +417,17 @@ public class JitAllocationModel {
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the stack by the proceeding
|
||||
* bytecode, which may be to load additional parts
|
||||
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*
|
||||
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
||||
* expected type, otherwise bits may get dropped while positioning the value.
|
||||
*/
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
local.generateLoadCode(rv);
|
||||
JitType tempType = chooseLargerType(local.type, type);
|
||||
TypeConversions.generate(gen, local.type, tempType, rv);
|
||||
TypeConversions.generate(gen, local.type, tempType, ext, rv);
|
||||
if (shift > 0) {
|
||||
switch (tempType) {
|
||||
case IntJitType t -> {
|
||||
@@ -440,7 +454,7 @@ public class JitAllocationModel {
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
}
|
||||
TypeConversions.generate(gen, tempType, type, rv);
|
||||
TypeConversions.generate(gen, tempType, type, ext, rv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,14 +468,16 @@ public class JitAllocationModel {
|
||||
* @param gen the code generator
|
||||
* @param type the p-code type of the value expected on the stack by the proceeding
|
||||
* bytecode, which may be to load additional parts
|
||||
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
*
|
||||
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
||||
* expected type, otherwise bits may get dropped while positioning the value.
|
||||
*/
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
JitType tempType = chooseLargerType(local.type, type);
|
||||
TypeConversions.generate(gen, type, tempType, rv);
|
||||
TypeConversions.generate(gen, type, tempType, ext, rv);
|
||||
switch (tempType) {
|
||||
case IntJitType t -> {
|
||||
if (shift > 0) {
|
||||
@@ -483,9 +499,9 @@ public class JitAllocationModel {
|
||||
rv.visitInsn(LUSHR);
|
||||
}
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
default -> throw new AssertionError("tempType = " + tempType);
|
||||
}
|
||||
TypeConversions.generate(gen, tempType, local.type, rv);
|
||||
TypeConversions.generate(gen, tempType, local.type, ext, rv);
|
||||
switch (local.type) {
|
||||
case IntJitType t -> {
|
||||
int mask = -1 >>> (Integer.SIZE - Byte.SIZE * type.size());
|
||||
@@ -524,6 +540,34 @@ public class JitAllocationModel {
|
||||
}
|
||||
}
|
||||
|
||||
public record MultiLocalPart(List<MultiLocalSub> subs, SimpleJitType type) {
|
||||
public void generateLoadCode(JitCodeGenerator gen, Ext ext, MethodVisitor rv) {
|
||||
subs.get(0).generateLoadCode(gen, this.type, ext, rv);
|
||||
for (MultiLocalSub sub : subs.subList(1, subs.size())) {
|
||||
sub.generateLoadCode(gen, this.type, ext, rv);
|
||||
switch (this.type) {
|
||||
case IntJitType t -> rv.visitInsn(IOR);
|
||||
case LongJitType t -> rv.visitInsn(LOR);
|
||||
default -> throw new AssertionError("this.type = " + this.type);
|
||||
}
|
||||
}
|
||||
TypeConversions.generate(gen, this.type, type, ext, rv);
|
||||
}
|
||||
|
||||
public void generateStoreCode(JitCodeGenerator gen, Ext ext, MethodVisitor rv) {
|
||||
TypeConversions.generate(gen, type, this.type, ext, rv);
|
||||
for (MultiLocalSub sub : subs.subList(1, subs.size()).reversed()) {
|
||||
switch (this.type) {
|
||||
case IntJitType t -> rv.visitInsn(DUP);
|
||||
case LongJitType t -> rv.visitInsn(DUP2);
|
||||
default -> throw new AssertionError("this.type = " + this.type);
|
||||
}
|
||||
sub.generateStoreCode(gen, this.type, ext, rv);
|
||||
}
|
||||
subs.get(0).generateStoreCode(gen, this.type, ext, rv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The handler for a variable allocated in a composition of locals
|
||||
*
|
||||
@@ -537,6 +581,7 @@ public class JitAllocationModel {
|
||||
*/
|
||||
public record MultiLocalVarHandler(List<MultiLocalPart> parts, JitType type)
|
||||
implements VarHandler {
|
||||
|
||||
@Override
|
||||
public void generateInitCode(JitCodeGenerator gen, MethodVisitor iv) {
|
||||
// Generator calls local inits directly
|
||||
@@ -549,31 +594,23 @@ public class JitAllocationModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
parts.get(0).generateLoadCode(gen, this.type, rv);
|
||||
for (MultiLocalPart part : parts.subList(1, parts.size())) {
|
||||
part.generateLoadCode(gen, this.type, rv);
|
||||
switch (this.type) {
|
||||
case IntJitType t -> rv.visitInsn(IOR);
|
||||
case LongJitType t -> rv.visitInsn(LOR);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
for (MultiLocalPart part : parts) {
|
||||
part.generateLoadCode(gen, ext, rv);
|
||||
// TODO: Optimize case where last sub of cur is first sub of next
|
||||
}
|
||||
TypeConversions.generate(gen, this.type, type, rv);
|
||||
TypeConversions.generate(gen, this.type, type, ext, rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
TypeConversions.generate(gen, type, this.type, rv);
|
||||
for (MultiLocalPart part : parts.subList(1, parts.size()).reversed()) {
|
||||
switch (this.type) {
|
||||
case IntJitType t -> rv.visitInsn(DUP);
|
||||
case LongJitType t -> rv.visitInsn(DUP2);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
part.generateStoreCode(gen, this.type, rv);
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
TypeConversions.generate(gen, type, this.type, ext, rv);
|
||||
for (MultiLocalPart part : parts.reversed()) {
|
||||
part.generateStoreCode(gen, ext, rv);
|
||||
// TODO: Optimize case where last sub of cur is first sub of next
|
||||
}
|
||||
parts.get(0).generateStoreCode(gen, this.type, rv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,12 +636,14 @@ public class JitAllocationModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@@ -711,6 +750,15 @@ public class JitAllocationModel {
|
||||
index());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the initialization of this variable.
|
||||
*
|
||||
* @param mv the method visitor
|
||||
* @param nameThis the name of the class defining the containing method
|
||||
*/
|
||||
default void generateInitCode(MethodVisitor mv, String nameThis) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a load of this variable onto the JVM stack.
|
||||
*
|
||||
@@ -816,9 +864,7 @@ public class JitAllocationModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDeclCode(MethodVisitor mv, String nameThis, Label startLocals,
|
||||
Label endLocals) {
|
||||
super.generateDeclCode(mv, nameThis, startLocals, endLocals);
|
||||
public void generateInitCode(MethodVisitor mv, String nameThis) {
|
||||
mv.visitLdcInsn(0);
|
||||
mv.visitVarInsn(ISTORE, index());
|
||||
}
|
||||
@@ -860,6 +906,54 @@ public class JitAllocationModel {
|
||||
}
|
||||
}
|
||||
|
||||
public class JvmTempAlloc implements AutoCloseable {
|
||||
final MethodVisitor mv;
|
||||
final String prefix;
|
||||
final Class<?> primitiveType;
|
||||
final int startIndex;
|
||||
final int count;
|
||||
final int step;
|
||||
final Label start;
|
||||
final Label end;
|
||||
|
||||
JvmTempAlloc(MethodVisitor mv, String prefix, Class<?> primitiveType, int count,
|
||||
int startIndex, int step, Label start, Label end) {
|
||||
this.mv = mv;
|
||||
this.prefix = prefix;
|
||||
this.primitiveType = primitiveType;
|
||||
this.count = count;
|
||||
this.startIndex = startIndex;
|
||||
this.step = step;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int idx(int i) {
|
||||
if (i >= count) {
|
||||
throw new IndexOutOfBoundsException(i);
|
||||
}
|
||||
return startIndex + i * step;
|
||||
}
|
||||
|
||||
public void visitLocals() {
|
||||
mv.visitLabel(end);
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = count == 1 ? prefix : (prefix + i);
|
||||
mv.visitLocalVariable(name, Type.getDescriptor(primitiveType), null, start, end,
|
||||
startIndex + step * i);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
releaseTemp(this);
|
||||
}
|
||||
}
|
||||
|
||||
private final JitDataFlowModel dfm;
|
||||
private final JitVarScopeModel vsm;
|
||||
private final JitTypeModel tm;
|
||||
@@ -871,12 +965,13 @@ public class JitAllocationModel {
|
||||
private final Map<JitVal, VarHandler> handlers = new HashMap<>();
|
||||
private final Map<Varnode, VarHandler> handlersPerVarnode = new HashMap<>();
|
||||
private final NavigableMap<Address, JvmLocal> locals = new TreeMap<>();
|
||||
private final Deque<JvmTempAlloc> tempAllocs = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Construct the allocation model.
|
||||
*
|
||||
* @param context the analysis context
|
||||
* @param dfm the data flow moel
|
||||
* @param dfm the data flow model
|
||||
* @param vsm the variable scope model
|
||||
* @param tm the type model
|
||||
*/
|
||||
@@ -912,18 +1007,70 @@ public class JitAllocationModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next free local index without reserving it
|
||||
* Temporarily allocate the next {@code count} indices of local variables
|
||||
*
|
||||
* <p>
|
||||
* This should be used by operator code generators <em>after</em> all the
|
||||
* {@link JitBytesPcodeExecutorState state} bypassing local variables have been allocated. The
|
||||
* variables should be scoped to that operator only, so that the ids used are freed for the next
|
||||
* operator.
|
||||
* These indices are reserved only within the scope of the {@code try-with-resources} block
|
||||
* creating the allocation. If the {@code primitiveType} is a {@code long} or {@code double},
|
||||
* then the number of actual indices allocated is multiplied by 2, such that the total number of
|
||||
* variables is given by {@code count}.
|
||||
* <p>
|
||||
* This should be used by operator code generators <em>after</em> all the local variables,
|
||||
* including those used to bypass {@link JitBytesPcodeExecutorState state}, have been allocated,
|
||||
* or else this may generate colliding indices. These variables ought to be released before the
|
||||
* next operator's code generator is invoked.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This will automatically invoke
|
||||
* {@link MethodVisitor#visitLocalVariable(String, String, String, Label, Label, int)} and place
|
||||
* the appropriate labels for you.
|
||||
*
|
||||
* @return the next id
|
||||
* @param mv the method visitor
|
||||
* @param prefix the name of the local variable, or its prefix if count > 1
|
||||
* @param primitiveType the type of each variable. NOTE: If heterogeneous allocations are
|
||||
* needed, invoke this method more than once in the {@code try-with-resources}
|
||||
* assignment.
|
||||
* @param count the number of variables to allocate
|
||||
* @return the handle to the allocation.
|
||||
*/
|
||||
public int nextFreeLocal() {
|
||||
return nextLocal;
|
||||
public JvmTempAlloc allocateTemp(MethodVisitor mv, String prefix, Class<?> primitiveType,
|
||||
int count) {
|
||||
if (count == 0) {
|
||||
return null;
|
||||
}
|
||||
int startIndex = nextLocal;
|
||||
int step = primitiveType == long.class || primitiveType == double.class ? 2 : 1;
|
||||
int countIndices = count * step;
|
||||
nextLocal += countIndices;
|
||||
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
mv.visitLabel(start);
|
||||
JvmTempAlloc temp =
|
||||
new JvmTempAlloc(mv, prefix, primitiveType, count, startIndex, step, start, end);
|
||||
tempAllocs.push(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily allocate the next {@code count} indices of local {@code int} variables
|
||||
*
|
||||
* @param mv the method visitor
|
||||
* @param prefix the name of the local variable, or its prefix if count > 1
|
||||
* @param count the number of variables to allocate
|
||||
* @return the handle to the allocation.
|
||||
* @see #allocateTemp(MethodVisitor, String, Class, int)
|
||||
*/
|
||||
public JvmTempAlloc allocateTemp(MethodVisitor mv, String prefix, int count) {
|
||||
return allocateTemp(mv, prefix, int.class, count);
|
||||
}
|
||||
|
||||
private void releaseTemp(JvmTempAlloc alloc) {
|
||||
JvmTempAlloc popped = tempAllocs.pop();
|
||||
if (popped != alloc) {
|
||||
throw new AssertionError("Temp allocations must obey stack semantics");
|
||||
}
|
||||
alloc.visitLocals();
|
||||
nextLocal = alloc.startIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,12 +1081,10 @@ public class JitAllocationModel {
|
||||
* @param desc the (whole) variable's descriptor
|
||||
* @return the allocated JVM locals from most to least significant
|
||||
*/
|
||||
private List<JvmLocal> genFreeLocals(String name, List<SimpleJitType> types,
|
||||
private List<JvmLocal> genFreeLocals(String name, List<? extends SimpleJitType> types,
|
||||
VarDesc desc) {
|
||||
JvmLocal[] result = new JvmLocal[types.size()];
|
||||
Iterable<SimpleJitType> it = language.isBigEndian()
|
||||
? types
|
||||
: () -> new ReverseListIterator<SimpleJitType>(types);
|
||||
Iterable<? extends SimpleJitType> it = language.isBigEndian() ? types : types.reversed();
|
||||
long offset = desc.offset;
|
||||
int i = 0;
|
||||
for (SimpleJitType t : it) {
|
||||
@@ -1022,20 +1167,55 @@ public class JitAllocationModel {
|
||||
* locals.
|
||||
*/
|
||||
private VarHandler createComplicatedHandler(Varnode vn) {
|
||||
Entry<Address, JvmLocal> leftEntry = locals.floorEntry(vn.getAddress());
|
||||
assert overlapsLeft(leftEntry.getValue().vn, vn);
|
||||
Address min = leftEntry.getKey();
|
||||
NavigableMap<Address, JvmLocal> sub = locals.subMap(min, true, maxAddr(vn), true);
|
||||
|
||||
List<MultiLocalPart> parts = new ArrayList<>();
|
||||
for (JvmLocal local : sub.values()) {
|
||||
int offset = (int) switch (endian) {
|
||||
case BIG -> maxAddr(leftEntry.getValue().vn).subtract(maxAddr(vn));
|
||||
case LITTLE -> vn.getAddress().subtract(leftEntry.getKey());
|
||||
};
|
||||
parts.add(new MultiLocalPart(local, offset));
|
||||
JitType type = JitTypeBehavior.INTEGER.type(vn.getSize());
|
||||
NavigableMap<Varnode, MultiLocalPart> legs =
|
||||
new TreeMap<>(Comparator.comparing(Varnode::getAddress));
|
||||
switch (endian) {
|
||||
case BIG -> {
|
||||
Address address = vn.getAddress();
|
||||
for (SimpleJitType legType : type.legTypes()) {
|
||||
Varnode legVn = new Varnode(address, legType.size());
|
||||
legs.put(legVn, new MultiLocalPart(new ArrayList<>(), legType));
|
||||
address = address.add(legType.size());
|
||||
}
|
||||
}
|
||||
case LITTLE -> {
|
||||
Address address = maxAddr(vn);
|
||||
for (SimpleJitType legType : type.legTypes()) {
|
||||
address = address.subtract(legType.size() - 1);
|
||||
Varnode legVn = new Varnode(address, legType.size());
|
||||
legs.put(legVn, new MultiLocalPart(new ArrayList<>(), legType));
|
||||
address = address.subtractWrap(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new MultiLocalVarHandler(parts, JitTypeBehavior.INTEGER.type(vn.getSize()));
|
||||
|
||||
Entry<Address, JvmLocal> firstEntry = locals.floorEntry(vn.getAddress());
|
||||
assert overlapsLeft(firstEntry.getValue().vn, vn);
|
||||
Address min = firstEntry.getKey();
|
||||
NavigableMap<Address, JvmLocal> sub = locals.subMap(min, true, maxAddr(vn), true);
|
||||
for (JvmLocal local : sub.values()) {
|
||||
Varnode startVn = legs.floorKey(local.vn);
|
||||
if (startVn == null || !startVn.intersects(local.vn)) {
|
||||
startVn = local.vn;
|
||||
}
|
||||
for (Entry<Varnode, MultiLocalPart> ent : legs.tailMap(startVn).entrySet()) {
|
||||
Varnode legVn = ent.getKey();
|
||||
if (!legVn.intersects(local.vn)) {
|
||||
break;
|
||||
}
|
||||
int offset = (int) switch (endian) {
|
||||
case BIG -> maxAddr(local.vn).subtract(maxAddr(legVn));
|
||||
case LITTLE -> legVn.getAddress().subtract(local.vn.getAddress());
|
||||
};
|
||||
ent.getValue().subs.add(new MultiLocalSub(local, offset));
|
||||
}
|
||||
}
|
||||
List<MultiLocalPart> parts = List.copyOf(legs.values());
|
||||
return new MultiLocalVarHandler(switch (endian) {
|
||||
case BIG -> parts;
|
||||
case LITTLE -> parts.reversed();
|
||||
}, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-2
@@ -79,6 +79,10 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
return endian;
|
||||
}
|
||||
|
||||
public Varnode truncVnFromRight(Varnode vn, int amt) {
|
||||
return new Varnode(vn.getAddress(), vn.getSize() - amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove {@code amt} bytes from the right of the <em>varnode</em>.
|
||||
*
|
||||
@@ -94,10 +98,14 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
* @return the resulting value
|
||||
*/
|
||||
public JitVal truncFromRight(Varnode in1Vn, int amt, JitVal in1) {
|
||||
Varnode outVn = new Varnode(in1Vn.getAddress(), in1Vn.getSize() - amt);
|
||||
Varnode outVn = truncVnFromRight(in1Vn, amt);
|
||||
return subpiece(outVn, endian.isBigEndian() ? amt : 0, in1);
|
||||
}
|
||||
|
||||
public Varnode truncVnFromLeft(Varnode vn, int amt) {
|
||||
return new Varnode(vn.getAddress().add(amt), vn.getSize() - amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove {@code amt} bytes from the left of the <em>varnode</em>.
|
||||
*
|
||||
@@ -113,7 +121,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||
* @return the resulting value
|
||||
*/
|
||||
public JitVal truncFromLeft(Varnode in1Vn, int amt, JitVal in1) {
|
||||
Varnode outVn = new Varnode(in1Vn.getAddress().add(amt), in1Vn.getSize() - amt);
|
||||
Varnode outVn = truncVnFromLeft(in1Vn, amt);
|
||||
return subpiece(outVn, endian.isBigEndian() ? 0 : amt, in1);
|
||||
}
|
||||
|
||||
|
||||
+17
-7
@@ -224,30 +224,40 @@ public class JitDataFlowState implements PcodeExecutorState<JitVal> {
|
||||
*/
|
||||
protected List<JitVal> doGetDefinitions(NavigableMap<Long, JitVal> map, AddressSpace space,
|
||||
long offset, int size) {
|
||||
long end = offset + size;
|
||||
List<JitVal> result = new ArrayList<>();
|
||||
Entry<Long, JitVal> preEntry = map.lowerEntry(offset);
|
||||
long cursor = offset;
|
||||
if (preEntry != null) {
|
||||
if (endOf(preEntry) > offset) {
|
||||
if (endOf(preEntry) > offset) { // Do I intersect the lower entry?
|
||||
JitVal preVal = preEntry.getValue();
|
||||
Varnode preVn = new Varnode(space.getAddress(preEntry.getKey()), preVal.size());
|
||||
int shave = (int) (offset - preEntry.getKey());
|
||||
JitVal truncVal = arithmetic.truncFromLeft(preVn, shave, preVal);
|
||||
cursor = endOf(preEntry);
|
||||
result.add(truncVal);
|
||||
int shaveLeft = (int) (offset - preEntry.getKey());
|
||||
JitVal truncVal = arithmetic.truncFromLeft(preVn, shaveLeft, preVal);
|
||||
if (endOf(preEntry) > end) { // Am I contained in the lower entry?
|
||||
Varnode truncVn = arithmetic.truncVnFromLeft(preVn, shaveLeft);
|
||||
int shaveRight = (int) (endOf(preEntry) - end);
|
||||
truncVal = arithmetic.truncFromRight(truncVn, shaveRight, truncVal);
|
||||
cursor = end;
|
||||
result.add(truncVal);
|
||||
}
|
||||
else {
|
||||
cursor = endOf(preEntry);
|
||||
result.add(truncVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
long end = offset + size;
|
||||
for (Entry<Long, JitVal> entry : map.subMap(offset, end).entrySet()) {
|
||||
if (entry.getKey() > cursor) {
|
||||
result.add(new JitMissingVar(
|
||||
new Varnode(space.getAddress(cursor), (int) (entry.getKey() - cursor))));
|
||||
}
|
||||
if (endOf(entry) > end) {
|
||||
if (endOf(entry) > end) { // Do I have off the end?
|
||||
JitVal postVal = entry.getValue();
|
||||
Varnode postVn = new Varnode(space.getAddress(entry.getKey()), postVal.size());
|
||||
int shave = (int) (endOf(entry) - end);
|
||||
JitVal truncVal = arithmetic.truncFromRight(postVn, shave, postVal);
|
||||
// NOTE: No need to check for contained here. Would have been caught above.
|
||||
cursor = end;
|
||||
result.add(truncVal);
|
||||
break;
|
||||
|
||||
+10
-9
@@ -152,17 +152,13 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||
* Get the type behavior from the userop's Java method
|
||||
*
|
||||
* <p>
|
||||
* If the userop is not backed by a Java method, or its return type is not supported, this
|
||||
* If the userop is not backed by a Java method, or its output type is not supported, this
|
||||
* return {@link JitTypeBehavior#ANY}.
|
||||
*
|
||||
* @return the type behavior
|
||||
*/
|
||||
private JitTypeBehavior getReturnType() {
|
||||
Method method = decOp.getJavaMethod();
|
||||
if (method == null) {
|
||||
return JitTypeBehavior.ANY;
|
||||
}
|
||||
return JitTypeBehavior.forJavaType(method.getReturnType());
|
||||
private JitTypeBehavior getOutputTypeBehavior() {
|
||||
return JitTypeBehavior.forJavaType(getOutputType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,8 +206,8 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||
}
|
||||
else {
|
||||
JitOutVar out = dfm.generateOutVar(outVn);
|
||||
dfm.notifyOp(new JitCallOtherDefOp(op, out, getReturnType(), decOp, inVals, inTypes,
|
||||
state.captureState()));
|
||||
dfm.notifyOp(new JitCallOtherDefOp(op, out, getOutputTypeBehavior(), decOp, inVals,
|
||||
inTypes, state.captureState()));
|
||||
state.setVar(outVn, out);
|
||||
}
|
||||
}
|
||||
@@ -236,6 +232,11 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||
return decOp.canInlinePcode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return decOp.getOutputType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getJavaMethod() {
|
||||
return decOp.getJavaMethod();
|
||||
|
||||
+50
-12
@@ -21,6 +21,8 @@ import java.util.*;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
|
||||
/**
|
||||
* The p-code type of an operand.
|
||||
*
|
||||
@@ -124,6 +126,9 @@ public interface JitType {
|
||||
* @return this type as an int
|
||||
*/
|
||||
SimpleJitType asInt();
|
||||
|
||||
@Override
|
||||
SimpleJitType ext();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +206,11 @@ public interface JitType {
|
||||
public IntJitType asInt() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntJitType> legTypes() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,6 +288,11 @@ public interface JitType {
|
||||
public LongJitType asInt() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LongJitType> legTypes() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,6 +341,11 @@ public interface JitType {
|
||||
public IntJitType asInt() {
|
||||
return IntJitType.I4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatJitType> legTypes() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,10 +394,20 @@ public interface JitType {
|
||||
public LongJitType asInt() {
|
||||
return LongJitType.I8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DoubleJitType> legTypes() {
|
||||
return List.of(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>WIP</b>: The p-code types for integers of size 9 and greater.
|
||||
* The p-code types for integers of size 9 and greater.
|
||||
*
|
||||
* <p>
|
||||
* We take the strategy of inlined manipulation of int locals, composed to form the full
|
||||
* variable. When stored on the stack, the least-significant portion is always toward the top,
|
||||
* no matter the language endianness.
|
||||
*
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
@@ -432,22 +462,14 @@ public interface JitType {
|
||||
return size % Integer.BYTES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public List<SimpleJitType> legTypes() {
|
||||
@Override
|
||||
public List<IntJitType> legTypes() {
|
||||
IntJitType[] types = new IntJitType[legsAlloc()];
|
||||
int i = 0;
|
||||
if (partialSize() != 0) {
|
||||
types[i++] = IntJitType.forSize(partialSize());
|
||||
}
|
||||
for (; i < legsWhole(); i++) {
|
||||
for (; i < types.length; i++) {
|
||||
types[i] = IntJitType.I4;
|
||||
}
|
||||
return Arrays.asList(types);
|
||||
@@ -492,6 +514,11 @@ public interface JitType {
|
||||
public MpFloatJitType ext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SimpleJitType> legTypes() {
|
||||
return Unfinished.TODO("MpFloat");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,4 +553,15 @@ public interface JitType {
|
||||
* @return the extended type
|
||||
*/
|
||||
JitType ext();
|
||||
|
||||
/**
|
||||
* 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 big-endian order.
|
||||
*/
|
||||
List<? extends SimpleJitType> legTypes();
|
||||
}
|
||||
|
||||
+3
@@ -159,6 +159,9 @@ public enum JitTypeBehavior {
|
||||
if (cls == long.class) {
|
||||
return INTEGER;
|
||||
}
|
||||
if (cls == int[].class) {
|
||||
return INTEGER;
|
||||
}
|
||||
if (cls == float.class) {
|
||||
return FLOAT;
|
||||
}
|
||||
|
||||
+1
@@ -222,6 +222,7 @@ public class JitTypeModel {
|
||||
* @param c the number of votes cast
|
||||
*/
|
||||
private void vote(JitTypeBehavior candidate, int c) {
|
||||
Objects.requireNonNull(candidate);
|
||||
if (candidate == JitTypeBehavior.ANY || candidate == JitTypeBehavior.COPY) {
|
||||
return;
|
||||
}
|
||||
|
||||
+5
@@ -128,6 +128,11 @@ public class DecoderUseropLibrary extends AnnotatedPcodeUseropLibrary<Object> {
|
||||
return rtOp.canInlinePcode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return rtOp.getOutputType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getJavaMethod() {
|
||||
return rtOp.getJavaMethod();
|
||||
|
||||
@@ -91,7 +91,7 @@ public interface GenConsts {
|
||||
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_UNSIGNED =
|
||||
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);
|
||||
@@ -135,6 +135,9 @@ public interface GenConsts {
|
||||
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 =
|
||||
@@ -147,6 +150,9 @@ public interface GenConsts {
|
||||
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);
|
||||
@@ -182,10 +188,23 @@ public interface GenConsts {
|
||||
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 =
|
||||
|
||||
+30
-21
@@ -39,6 +39,7 @@ 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.tgt.JitCompiledPassageClass;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.var.ValGen;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||
@@ -216,9 +217,6 @@ public class JitCodeGenerator {
|
||||
private final MethodVisitor initMv;
|
||||
private final MethodVisitor runMv;
|
||||
|
||||
private final Label startLocals = new Label();
|
||||
private final Label endLocals = new Label();
|
||||
|
||||
/**
|
||||
* Construct a code generator for the given passage's target classfile
|
||||
*
|
||||
@@ -594,10 +592,11 @@ public class JitCodeGenerator {
|
||||
*
|
||||
* @param v the value to read
|
||||
* @param typeReq the required type of the value
|
||||
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||
* @return the actual type of the value on the stack
|
||||
*/
|
||||
public JitType generateValReadCode(JitVal v, JitTypeBehavior typeReq) {
|
||||
return ValGen.lookup(v).generateValReadCode(this, v, typeReq, runMv);
|
||||
public JitType generateValReadCode(JitVal v, JitTypeBehavior typeReq, Ext ext) {
|
||||
return ValGen.lookup(v).generateValReadCode(this, v, typeReq, ext, runMv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,9 +610,10 @@ public class JitCodeGenerator {
|
||||
*
|
||||
* @param v the variable to write
|
||||
* @param type the actual type of the value on the stack
|
||||
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||
*/
|
||||
public void generateVarWriteCode(JitVar v, JitType type) {
|
||||
VarGen.lookup(v).generateVarWriteCode(this, v, type, runMv);
|
||||
public void generateVarWriteCode(JitVar v, JitType type, Ext ext) {
|
||||
VarGen.lookup(v).generateVarWriteCode(this, v, type, ext, runMv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -858,20 +858,9 @@ public class JitCodeGenerator {
|
||||
*/
|
||||
protected void generateRunCode() {
|
||||
runMv.visitCode();
|
||||
final Label startLocals = new Label();
|
||||
runMv.visitLabel(startLocals);
|
||||
|
||||
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||
fixed.generateDeclCode(runMv, nameThis, startLocals, endLocals);
|
||||
}
|
||||
|
||||
for (JvmLocal local : am.allLocals()) {
|
||||
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||
}
|
||||
// TODO: This for loop doesn't actually do anything....
|
||||
for (JitVal v : dfm.allValuesSorted()) {
|
||||
VarHandler handler = am.getHandler(v);
|
||||
handler.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||
}
|
||||
/**
|
||||
* NB. opIdx starts at 1, because JVM will ignore "Line number 0"
|
||||
*/
|
||||
@@ -886,6 +875,10 @@ public class JitCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||
fixed.generateInitCode(runMv, nameThis);
|
||||
}
|
||||
|
||||
// []
|
||||
RunFixedLocal.BLOCK_ID.generateLoadCode(runMv);
|
||||
// [blockId]
|
||||
@@ -929,6 +922,22 @@ public class JitCodeGenerator {
|
||||
for (ExceptionHandler handler : excHandlers.values()) {
|
||||
handler.generateRunCode(this, runMv);
|
||||
}
|
||||
|
||||
final Label endLocals = new Label();
|
||||
runMv.visitLabel(endLocals);
|
||||
|
||||
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||
fixed.generateDeclCode(runMv, nameThis, startLocals, endLocals);
|
||||
}
|
||||
|
||||
for (JvmLocal local : am.allLocals()) {
|
||||
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||
}
|
||||
// TODO: This for loop doesn't actually do anything....
|
||||
for (JitVal v : dfm.allValuesSorted()) {
|
||||
VarHandler handler = am.getHandler(v);
|
||||
handler.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -953,8 +962,9 @@ public class JitCodeGenerator {
|
||||
dest.getParentFile().mkdirs();
|
||||
try (OutputStream os = new FileOutputStream(dest)) {
|
||||
os.write(bytes);
|
||||
new ProcessBuilder("javap", "-c", "-l", dest.getPath()).inheritIO().start().waitFor();
|
||||
}
|
||||
catch (IOException e) {
|
||||
catch (IOException | InterruptedException e) {
|
||||
Msg.warn(this, "Could not dump class file: " + nameThis + " (" + e + ")");
|
||||
}
|
||||
return bytes;
|
||||
@@ -989,7 +999,6 @@ public class JitCodeGenerator {
|
||||
initMv.visitMaxs(20, 20);
|
||||
initMv.visitEnd();
|
||||
|
||||
runMv.visitLabel(endLocals);
|
||||
try {
|
||||
runMv.visitMaxs(20, 20);
|
||||
}
|
||||
|
||||
+139
-3
@@ -15,11 +15,19 @@
|
||||
*/
|
||||
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.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
|
||||
/**
|
||||
@@ -29,6 +37,134 @@ import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
*/
|
||||
public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||
|
||||
/**
|
||||
* A choice of static method parameter to take as operator output
|
||||
*/
|
||||
enum TakeOut {
|
||||
/**
|
||||
* The out (first) parameter
|
||||
*/
|
||||
OUT,
|
||||
/**
|
||||
* The left (second) parameter
|
||||
*/
|
||||
LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode that implements an mp-int binary operator via delegation to a static method on
|
||||
* {@link JitCompiledPassage}. The method must have the signature:
|
||||
*
|
||||
* <pre>
|
||||
* void method(int[] out, int[] inL, int[] inR);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This method presumes that the left operand's legs are at the top of the stack,
|
||||
* least-significant leg on top, followed by the right operand legs, also least-significant leg
|
||||
* on top. This will allocate the output array, move the operands into their respective input
|
||||
* arrays, invoke the method, and then place the result legs on the stack, least-significant leg
|
||||
* on top.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @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 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.
|
||||
*/
|
||||
static void generateMpDelegationToStaticMethod(JitCodeGenerator gen, MpIntJitType type,
|
||||
String methodName, MethodVisitor mv, int overProvisionLeft, TakeOut takeOut) {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this operator is signed
|
||||
* <p>
|
||||
* In many cases, the operator itself is not affected by the signedness of the operands;
|
||||
* however, if size adjustments to the operands are needed, this can determine how those
|
||||
* operands are extended.
|
||||
*
|
||||
* @return true for signed, false if not
|
||||
*/
|
||||
boolean isSigned();
|
||||
|
||||
/**
|
||||
* When loading and storing variables, the kind of extension to apply
|
||||
*
|
||||
* @return the extension kind
|
||||
*/
|
||||
default Ext ext() {
|
||||
return Ext.forSigned(isSigned());
|
||||
}
|
||||
|
||||
/**
|
||||
* When loading the right operand, the kind of extension to apply
|
||||
*
|
||||
* @return the extension kind
|
||||
*/
|
||||
default Ext rExt() {
|
||||
return ext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code between reading the left and right operands
|
||||
*
|
||||
@@ -79,12 +215,12 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||
*/
|
||||
@Override
|
||||
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
|
||||
JitType lType = gen.generateValReadCode(op.l(), op.lType());
|
||||
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());
|
||||
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);
|
||||
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-30
@@ -17,14 +17,16 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -32,7 +34,11 @@ import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
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.
|
||||
@@ -49,7 +55,7 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
int longOpcode();
|
||||
|
||||
/**
|
||||
* <b>WIP</b>: The implementation for multi-precision ints.
|
||||
* The implementation for multi-precision ints.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param type the type of each operand, including the reuslt
|
||||
@@ -66,37 +72,25 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
mv.visitLabel(start);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitLocalVariable("result" + i, Type.getDescriptor(int.class), null, start, end,
|
||||
firstIndex + i);
|
||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
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);
|
||||
}
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
// [lleg1,...,llegN:INT]
|
||||
mv.visitVarInsn(ILOAD, firstIndex + i);
|
||||
// [lleg1,...,llegN:INT,rlegN:INT]
|
||||
mv.visitInsn(intOpcode());
|
||||
// [lleg1,...,olegN:INT]
|
||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
// [lleg1,...]
|
||||
}
|
||||
|
||||
// Push it all back, in reverse order
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ILOAD, firstIndex + legCount - i - 1);
|
||||
}
|
||||
mv.visitLabel(end);
|
||||
}
|
||||
|
||||
@Override
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +104,7 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(intOpcode());
|
||||
case LongJitType t -> rv.visitInsn(longOpcode());
|
||||
@@ -118,6 +112,6 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
|
||||
* @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 OpBehaviorBoolAnd}. Thus, this is identical to {@link IntAndOpGen}.
|
||||
* @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 BoolAndOpGen implements BitwiseBinOpGen<JitBoolAndOp> {
|
||||
/** The generator singleton */
|
||||
|
||||
+10
-2
@@ -17,7 +17,6 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
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.*;
|
||||
@@ -36,6 +35,11 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitBoolNegateOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
@@ -48,7 +52,11 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||
rv.visitLdcInsn(1L);
|
||||
rv.visitInsn(LXOR);
|
||||
}
|
||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
||||
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;
|
||||
|
||||
+3
-2
@@ -25,6 +25,7 @@ import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
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.op.JitBranchIndOp;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
@@ -55,9 +56,9 @@ public enum BranchIndOpGen implements OpGen<JitBranchIndOp> {
|
||||
JitBlock block, MethodVisitor rv) {
|
||||
gen.generatePassageExit(block, () -> {
|
||||
// [...]
|
||||
JitType targetType = gen.generateValReadCode(op.target(), op.targetType());
|
||||
JitType targetType = gen.generateValReadCode(op.target(), op.targetType(), Ext.ZERO);
|
||||
// [...,target:?]
|
||||
TypeConversions.generateToLong(targetType, LongJitType.I8, rv);
|
||||
TypeConversions.generateToLong(targetType, LongJitType.I8, Ext.ZERO, rv);
|
||||
// [...,target:LONG]
|
||||
}, ctx, rv);
|
||||
|
||||
|
||||
+2
-1
@@ -27,6 +27,7 @@ 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.var.VarGen;
|
||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
||||
@@ -146,7 +147,7 @@ public enum CBranchOpGen implements OpGen<JitCBranchOp> {
|
||||
return;
|
||||
}
|
||||
|
||||
JitType cType = gen.generateValReadCode(op.cond(), op.condType());
|
||||
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);
|
||||
|
||||
+130
-18
@@ -19,23 +19,31 @@ 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.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.gen.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
|
||||
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.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.PcodeUseropDefinition;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
@@ -153,6 +161,31 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
transition.generateInv(rv);
|
||||
}
|
||||
|
||||
static Parameter findOutputParameter(Parameter[] parameters, Method method) {
|
||||
List<Parameter> found =
|
||||
Stream.of(parameters).filter(p -> p.getAnnotation(OpOutput.class) != null).toList();
|
||||
return switch (found.size()) {
|
||||
case 0 -> null;
|
||||
case 1 -> {
|
||||
Parameter p = found.get(0);
|
||||
if (p.getType() == int[].class) {
|
||||
yield p;
|
||||
}
|
||||
throw new IllegalArgumentException("""
|
||||
@%s requires parameter to have type int[] when functional=true. \
|
||||
Got %s (method %s)""".formatted(
|
||||
OpOutput.class.getSimpleName(), p, method.getName()));
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException("""
|
||||
@%s can only be applied to one parameter of method %s. \
|
||||
It is applied to: %s""".formatted(
|
||||
OpOutput.class.getSimpleName(), method.getName(),
|
||||
found.stream().map(Parameter::toString).collect(Collectors.joining(", "))));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code to implement the Direct strategy (see the class documentation)
|
||||
*
|
||||
@@ -175,6 +208,8 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
rv.visitTryCatchBlock(tryStart, tryEnd,
|
||||
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).label(), NAME_THROWABLE);
|
||||
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
|
||||
// []
|
||||
useropField.generateLoadCode(gen, rv);
|
||||
// [userop]
|
||||
@@ -186,29 +221,106 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
rv.visitTypeInsn(CHECKCAST, owningLibName);
|
||||
// [library:OWNING_TYPE]
|
||||
Parameter[] parameters = method.getParameters();
|
||||
for (int i = 0; i < op.args().size(); i++) {
|
||||
JitVal arg = op.args().get(i);
|
||||
Parameter p = parameters[i];
|
||||
|
||||
JitType type = gen.generateValReadCode(arg, JitTypeBehavior.ANY);
|
||||
if (p.getType() == boolean.class) {
|
||||
TypeConversions.generateIntToBool(type, rv);
|
||||
Parameter outputParameter = findOutputParameter(parameters, method);
|
||||
if (outputParameter != null && method.getReturnType() != void.class) {
|
||||
throw new IllegalArgumentException("""
|
||||
@%s cannot be applied to any parameter of a method returning non-void. \
|
||||
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));
|
||||
}
|
||||
else {
|
||||
TypeConversions.generate(gen, type, JitType.forJavaType(p.getType()), rv);
|
||||
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);
|
||||
}
|
||||
}
|
||||
// [library,params...]
|
||||
rv.visitLabel(tryStart);
|
||||
rv.visitMethodInsn(INVOKEVIRTUAL, owningLibName, method.getName(),
|
||||
Type.getMethodDescriptor(method), false);
|
||||
// [return?]
|
||||
rv.visitLabel(tryEnd);
|
||||
if (op instanceof JitCallOtherDefOp defOp) {
|
||||
gen.generateVarWriteCode(defOp.out(), JitType.forJavaType(method.getReturnType()));
|
||||
}
|
||||
|
||||
static class ResourceGroup implements AutoCloseable {
|
||||
private final List<AutoCloseable> resources = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
for (AutoCloseable r : resources) {
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
else if (method.getReturnType() != void.class) {
|
||||
TypeConversions.generatePop(JitType.forJavaType(method.getReturnType()), rv);
|
||||
|
||||
public <T extends AutoCloseable> T add(T resource) {
|
||||
resources.add(resource);
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import ghidra.pcode.emu.jit.op.JitFloatTestOp;
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends BinOpGen<T> {
|
||||
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends FloatBinOpGen<T> {
|
||||
|
||||
/**
|
||||
* The JVM opcode to perform the comparison with float operands on the stack.
|
||||
|
||||
+41
-10
@@ -20,7 +20,7 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
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.*;
|
||||
@@ -34,11 +34,10 @@ import ghidra.pcode.emu.jit.op.JitIntTestOp;
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T> {
|
||||
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends IntBinOpGen<T> {
|
||||
|
||||
/**
|
||||
* Whether the comparison of p-code integers is signed
|
||||
*
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* If the comparison is unsigned, we will emit invocations of
|
||||
* {@link Integer#compareUnsigned(int, int)} or {@link Long#compareUnsigned(long, long)},
|
||||
@@ -49,6 +48,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
*
|
||||
* @return true if signed, false if not
|
||||
*/
|
||||
@Override
|
||||
boolean isSigned();
|
||||
|
||||
/**
|
||||
@@ -58,6 +58,11 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -69,8 +74,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
rv.visitJumpInsn(icmpOpcode(), lblTrue);
|
||||
}
|
||||
else {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "compareUnsigned",
|
||||
MDESC_INTEGER__COMPARE_UNSIGNED, false);
|
||||
generateIntCmp("compareUnsigned", rv);
|
||||
rv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||
}
|
||||
}
|
||||
@@ -94,7 +98,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -104,7 +108,34 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
@Override
|
||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformZExt(lType, rType, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,11 +154,11 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||
Label lblTrue = new Label();
|
||||
Label lblDone = new Label();
|
||||
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
||||
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 -> Unfinished.TODO("MpInt");
|
||||
case MpIntJitType t -> generateMpIntCmp(gen, t, lblTrue, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
|
||||
@@ -34,6 +34,11 @@ public enum CopyOpGen implements UnOpGen<JitCopyOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitCopyOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ 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 UnOpGen<JitFloatAbsOp> {
|
||||
public enum FloatAbsOpGen implements FloatUnOpGen<JitFloatAbsOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatAddOp;
|
||||
* This uses the binary operator generator and simply emits {@link #FADD} or {@link #DADD} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum FloatAddOpGen implements BinOpGen<JitFloatAddOp> {
|
||||
public enum FloatAddOpGen implements FloatBinOpGen<JitFloatAddOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/* ###
|
||||
* 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.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 FloatBinOpGen<T extends JitFloatBinOp> extends BinOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -34,7 +34,7 @@ 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 UnOpGen<JitFloatCeilOp> {
|
||||
public enum FloatCeilOpGen implements FloatUnOpGen<JitFloatCeilOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatDivOp;
|
||||
* This uses the binary operator generator and simply emits {@link #FDIV} or {@link #DDIV} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum FloatDivOpGen implements BinOpGen<JitFloatDivOp> {
|
||||
public enum FloatDivOpGen implements FloatBinOpGen<JitFloatDivOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import ghidra.pcode.emu.jit.op.JitFloatFloat2FloatOp;
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #F2D} or {@link #D2F}.
|
||||
*/
|
||||
public enum FloatFloat2FloatOpGen implements UnOpGen<JitFloatFloat2FloatOp> {
|
||||
public enum FloatFloat2FloatOpGen implements FloatUnOpGen<JitFloatFloat2FloatOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ 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 UnOpGen<JitFloatFloorOp> {
|
||||
public enum FloatFloorOpGen implements FloatUnOpGen<JitFloatFloorOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+5
@@ -36,6 +36,11 @@ public enum FloatInt2FloatOpGen implements UnOpGen<JitFloatInt2FloatOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
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;
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatMultOp;
|
||||
* This uses the binary operator generator and simply emits {@link #FMUL} or {@link #DMUL} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum FloatMultOpGen implements BinOpGen<JitFloatMultOp> {
|
||||
public enum FloatMultOpGen implements FloatBinOpGen<JitFloatMultOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ 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 UnOpGen<JitFloatNaNOp> {
|
||||
public enum FloatNaNOpGen implements FloatUnOpGen<JitFloatNaNOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import ghidra.pcode.emu.jit.op.JitFloatNegOp;
|
||||
* <p>
|
||||
* This uses the unary operator generator and emits {@link #FNEG} or {@link #DNEG}.
|
||||
*/
|
||||
public enum FloatNegOpGen implements UnOpGen<JitFloatNegOp> {
|
||||
public enum FloatNegOpGen implements FloatUnOpGen<JitFloatNegOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ 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 UnOpGen<JitFloatRoundOp> {
|
||||
public enum FloatRoundOpGen implements FloatUnOpGen<JitFloatRoundOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ 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 UnOpGen<JitFloatSqrtOp> {
|
||||
public enum FloatSqrtOpGen implements FloatUnOpGen<JitFloatSqrtOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatSubOp;
|
||||
* This uses the binary operator generator and simply emits {@link #FSUB} or {@link #DSUB} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum FloatSubOpGen implements BinOpGen<JitFloatSubOp> {
|
||||
public enum FloatSubOpGen implements FloatBinOpGen<JitFloatSubOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatTruncOp;
|
||||
* This uses the unary operator generator and emits {@link #F2I}, {@link #F2L}, {@link #D2I}, or
|
||||
* {@link #D2L}.
|
||||
*/
|
||||
public enum FloatTruncOpGen implements UnOpGen<JitFloatTruncOp> {
|
||||
public enum FloatTruncOpGen implements FloatUnOpGen<JitFloatTruncOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/* ###
|
||||
* 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.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 FloatUnOpGen<T extends JitFloatUnOp> extends UnOpGen<T> {
|
||||
@Override
|
||||
default boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+59
-3
@@ -15,13 +15,17 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
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.op.JitInt2CompOp;
|
||||
|
||||
/**
|
||||
@@ -31,17 +35,69 @@ import ghidra.pcode.emu.jit.op.JitInt2CompOp;
|
||||
* This uses the unary operator generator and emits {@link #INEG} or {@link #LNEG}, depending on
|
||||
* type.
|
||||
*/
|
||||
public enum Int2CompOpGen implements UnOpGen<JitInt2CompOp> {
|
||||
public enum Int2CompOpGen implements IntUnOpGen<JitInt2CompOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
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 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 -> Unfinished.TODO("MpInt");
|
||||
case MpIntJitType t -> generateMpInt2Comp(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
|
||||
+41
-41
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
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.op.JitIntAddOp;
|
||||
|
||||
/**
|
||||
@@ -36,12 +36,12 @@ import ghidra.pcode.emu.jit.op.JitIntAddOp;
|
||||
* <p>
|
||||
* NOTE: The multi-precision integer parts of this are a work in progress.
|
||||
*/
|
||||
public enum IntAddOpGen implements BinOpGen<JitIntAddOp> {
|
||||
public enum IntAddOpGen implements IntBinOpGen<JitIntAddOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
static void generateMpIntLegAdd(JitCodeGenerator gen, int idx, boolean takesCarry,
|
||||
boolean givesCarry, MethodVisitor mv) {
|
||||
boolean givesCarry, boolean storesResult, MethodVisitor mv) {
|
||||
if (takesCarry) {
|
||||
// [...,llegN:INT,olegN+1:LONG]
|
||||
mv.visitLdcInsn(32);
|
||||
@@ -50,31 +50,38 @@ public enum IntAddOpGen implements BinOpGen<JitIntAddOp> {
|
||||
mv.visitInsn(DUP2_X1);
|
||||
mv.visitInsn(POP2);
|
||||
// [...,carryinN:LONG,llegN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, mv);
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,carryinN:LONG,llegN:LONG]
|
||||
mv.visitInsn(LADD);
|
||||
// [...,sumpartN:LONG]
|
||||
}
|
||||
else {
|
||||
// [...,legN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, mv);
|
||||
// [...,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, mv);
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,sumpartN:LONG,rlegN:LONG]
|
||||
mv.visitInsn(LADD);
|
||||
// [...,olegN:LONG]
|
||||
if (givesCarry) {
|
||||
mv.visitInsn(DUP2);
|
||||
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);
|
||||
}
|
||||
}
|
||||
// [...,(olegN:LONG),olegN:LONG]
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, mv);
|
||||
// [...,(olegN:LONG),olegN:INT]
|
||||
/** NB. The store will perform the masking */
|
||||
mv.visitVarInsn(ISTORE, idx);
|
||||
// [...,(olegN:LONG)]
|
||||
}
|
||||
|
||||
private void generateMpIntAdd(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
@@ -88,47 +95,40 @@ public enum IntAddOpGen implements BinOpGen<JitIntAddOp> {
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
mv.visitLabel(start);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitLocalVariable("result" + i, Type.getDescriptor(int.class), null, start, end,
|
||||
firstIndex + i);
|
||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
}
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean isLast = i == legCount - 1;
|
||||
boolean takesCarry = i != 0; // not first
|
||||
generateMpIntLegAdd(gen, firstIndex + i, takesCarry, !isLast, mv);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Push it all back, in reverse order
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ILOAD, firstIndex + legCount - i - 1);
|
||||
}
|
||||
mv.visitLabel(end);
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntAddOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntAddOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, 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 -> TODO("MpInt of differing sizes");
|
||||
case MpIntJitType t -> throw new AssertionError("forceUniform didn't work?");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/* ###
|
||||
* 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.op.JitBinOp;
|
||||
|
||||
/**
|
||||
* An extension for integer binary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||
// Intentionally empty
|
||||
}
|
||||
+38
-34
@@ -18,13 +18,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.*;
|
||||
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.op.JitIntCarryOp;
|
||||
|
||||
/**
|
||||
@@ -48,7 +50,7 @@ import ghidra.pcode.emu.jit.op.JitIntCarryOp;
|
||||
* <p>
|
||||
* NOTE: The multi-precision integer parts of this are a work in progress.
|
||||
*/
|
||||
public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
public enum IntCarryOpGen implements IntBinOpGen<JitIntCarryOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@@ -59,35 +61,36 @@ public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc();
|
||||
int remSize = type.partialSize();
|
||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
mv.visitLabel(start);
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitLocalVariable("temp" + i, Type.getDescriptor(int.class), null, start, end,
|
||||
firstIndex + i);
|
||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
|
||||
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);
|
||||
}
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean takesCarry = i != 0; // not first
|
||||
IntAddOpGen.generateMpIntLegAdd(gen, firstIndex + i, takesCarry, true, 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, mv);
|
||||
mv.visitLdcInsn(1);
|
||||
mv.visitInsn(IAND);
|
||||
mv.visitLabel(end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,7 +105,7 @@ public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
* 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.forceUniformZExt(lType, rType, rv);
|
||||
lType = TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
switch (lType) {
|
||||
case IntJitType(int size) when size == Integer.BYTES -> rv.visitInsn(DUP);
|
||||
case IntJitType lt -> {
|
||||
@@ -110,7 +113,8 @@ public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
case LongJitType(int size) when size == Long.BYTES -> rv.visitInsn(DUP2);
|
||||
case LongJitType lt -> {
|
||||
}
|
||||
case MpIntJitType lt -> TODO("MpInt");
|
||||
case MpIntJitType lt -> {
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return lType;
|
||||
@@ -119,7 +123,7 @@ public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntCarryOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType(int size) when size == Integer.BYTES -> {
|
||||
// [l,l,r]
|
||||
@@ -128,7 +132,7 @@ public enum IntCarryOpGen implements BinOpGen<JitIntCarryOp> {
|
||||
rv.visitInsn(SWAP); // spare an LDC,XOR
|
||||
// [sum,l]
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "compareUnsigned",
|
||||
MDESC_INTEGER__COMPARE_UNSIGNED, false);
|
||||
MDESC_INTEGER__COMPARE, false);
|
||||
// [cmpU(sum,l)] sum < l iff sign bit is 1
|
||||
rv.visitLdcInsn(31);
|
||||
rv.visitInsn(IUSHR);
|
||||
|
||||
+17
-7
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
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;
|
||||
@@ -36,29 +35,40 @@ import ghidra.pcode.emu.jit.op.JitIntDivOp;
|
||||
* {@link Integer#divideUnsigned(int, int)} or {@link Long#divideUnsigned(long, long)} depending on
|
||||
* the type.
|
||||
*/
|
||||
public enum IntDivOpGen implements BinOpGen<JitIntDivOp> {
|
||||
public enum IntDivOpGen implements IntBinOpGen<JitIntDivOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntDiv(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntDivide", mv, 1, TakeOut.OUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntDivOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntDivOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, 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 -> TODO("MpInt");
|
||||
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();
|
||||
}
|
||||
// TODO: For MpInt case, we should use the outvar's size to cull operations.
|
||||
return lType;
|
||||
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public enum IntLeftOpGen implements ShiftIntBinOpGen<JitIntLeftOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String methodName() {
|
||||
return "intLeft";
|
||||
|
||||
+43
-8
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
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;
|
||||
@@ -24,6 +22,7 @@ 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.op.JitIntMultOp;
|
||||
|
||||
/**
|
||||
@@ -33,27 +32,63 @@ import ghidra.pcode.emu.jit.op.JitIntMultOp;
|
||||
* This uses the binary operator generator and simply emits {@link #IMUL} or {@link #LMUL} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum IntMultOpGen implements BinOpGen<JitIntMultOp> {
|
||||
public enum IntMultOpGen implements IntBinOpGen<JitIntMultOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the mp-int multiply code.
|
||||
* <p>
|
||||
* <b>NOTE:</b> I'd really like to know how many legs of the input operands are actually
|
||||
* relevant. Very often, the following idiom is used:
|
||||
*
|
||||
* <pre>
|
||||
* temp: 16 = zext(r1) * zext(r2);
|
||||
* r0 = temp(0);
|
||||
* </pre>
|
||||
* <p>
|
||||
* 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.
|
||||
* <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
|
||||
* used by a later cross-build. Maybe with the right API calls, I could derive that without
|
||||
* additional Sleigh compiler support. If used, I should not cull any computations, so that the
|
||||
* retired value is the full value.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param type the (uniform) type of the inputs and output operands
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
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.forceUniformZExt(lType, rType, 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.forceUniformZExt(rType, lType, 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 -> TODO("MpInt");
|
||||
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();
|
||||
}
|
||||
// TODO: For MpInt case, we should use the outvar's size to cull operations.
|
||||
return lType;
|
||||
// FIXME: For MpInt case, we should use the operands' (relevant) sizes to cull operations.
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-5
@@ -17,7 +17,7 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
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.*;
|
||||
@@ -32,23 +32,45 @@ import ghidra.pcode.emu.jit.op.JitIntNegateOp;
|
||||
* 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 UnOpGen<JitIntNegateOp> {
|
||||
public enum IntNegateOpGen implements IntUnOpGen<JitIntNegateOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
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 JitType generateUnOpRunCode(JitCodeGenerator gen, JitIntNegateOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case IntJitType t -> {
|
||||
rv.visitInsn(ICONST_M1);
|
||||
rv.visitLdcInsn(-1 >>> (Integer.SIZE - t.size() * Byte.SIZE));
|
||||
rv.visitInsn(IXOR);
|
||||
}
|
||||
case LongJitType t -> {
|
||||
rv.visitLdcInsn(-1L);
|
||||
rv.visitLdcInsn(-1L >>> (Long.SIZE - t.size() * Byte.SIZE));
|
||||
rv.visitInsn(LXOR);
|
||||
}
|
||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
||||
case MpIntJitType t -> generateMpIntNegate(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return uType;
|
||||
|
||||
+16
-6
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
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;
|
||||
@@ -35,29 +34,40 @@ import ghidra.pcode.emu.jit.op.JitIntRemOp;
|
||||
* {@link Integer#remainderUnsigned(int, int)} or {@link Long#remainderUnsigned(long, long)}
|
||||
* depending on the type.
|
||||
*/
|
||||
public enum IntRemOpGen implements BinOpGen<JitIntRemOp> {
|
||||
public enum IntRemOpGen implements IntBinOpGen<JitIntRemOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntRem(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntDivide", mv, 1, TakeOut.LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntRemOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntRemOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, rExt(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "remainderUnsigned",
|
||||
MDESC_$INT_BINOP, false);
|
||||
case LongJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "remainderUnsigned",
|
||||
MDESC_$LONG_BINOP, false);
|
||||
case MpIntJitType t -> TODO("MpInt");
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntRem(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();
|
||||
}
|
||||
// TODO: For MpInt case, we should use the outvar's size to cull operations.
|
||||
return lType;
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -29,6 +29,11 @@ public enum IntRightOpGen implements ShiftIntBinOpGen<JitIntRightOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String methodName() {
|
||||
return "intRight";
|
||||
|
||||
+10
-5
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
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;
|
||||
@@ -26,6 +25,7 @@ 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.JitIntSBorrowOp;
|
||||
|
||||
/**
|
||||
@@ -37,20 +37,25 @@ import ghidra.pcode.emu.jit.op.JitIntSBorrowOp;
|
||||
* {@link JitCompiledPassage#sBorrowLongRaw(long, long)} depending on the type. We must then emit a
|
||||
* shift and mask to extract the correct bit.
|
||||
*/
|
||||
public enum IntSBorrowOpGen implements BinOpGen<JitIntSBorrowOp> {
|
||||
public enum IntSBorrowOpGen implements IntBinOpGen<JitIntSBorrowOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntSBorrowOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformSExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, Ext.SIGN, rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntSBorrowOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformSExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.SIGN, rv);
|
||||
switch (rType) {
|
||||
case IntJitType(int size) -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "sBorrowIntRaw",
|
||||
@@ -74,7 +79,7 @@ public enum IntSBorrowOpGen implements BinOpGen<JitIntSBorrowOp> {
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case MpIntJitType t -> {
|
||||
return TODO("MpInt");
|
||||
return IntSCarryOpGen.generateMpIntSCarry(gen, t, "sBorrowMpInt", rv);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
|
||||
+27
-7
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
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;
|
||||
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.*;
|
||||
@@ -37,20 +38,39 @@ import ghidra.pcode.emu.jit.op.JitIntSCarryOp;
|
||||
* {@link JitCompiledPassage#sCarryLongRaw(long, long)} depending on the type. We must then emit a
|
||||
* shift and mask to extract the correct bit.
|
||||
*/
|
||||
public enum IntSCarryOpGen implements BinOpGen<JitIntSCarryOp> {
|
||||
public enum IntSCarryOpGen implements IntBinOpGen<JitIntSCarryOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntSCarryOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformSExt(lType, rType, rv);
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static IntJitType generateMpIntSCarry(JitCodeGenerator gen, MpIntJitType type,
|
||||
String methodName, MethodVisitor mv) {
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
int legCount = type.legsAlloc();
|
||||
try (
|
||||
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", legCount);
|
||||
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", legCount)) {
|
||||
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||
|
||||
OpGen.generateMpLegsIntoArray(tmpL, legCount, legCount, mv);
|
||||
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||
mv.visitLdcInsn((type.size() % Integer.BYTES) * Byte.SIZE - 1);
|
||||
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName,
|
||||
MDESC_JIT_COMPILED_PASSAGE__S_CARRY_MP_INT, true);
|
||||
}
|
||||
return IntJitType.I4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntSCarryOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformSExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType(int size) -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "sCarryIntRaw",
|
||||
@@ -74,7 +94,7 @@ public enum IntSCarryOpGen implements BinOpGen<JitIntSCarryOp> {
|
||||
return IntJitType.I1;
|
||||
}
|
||||
case MpIntJitType t -> {
|
||||
return TODO("MpInt");
|
||||
return generateMpIntSCarry(gen, t, "sCarryMpInt", rv);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user