mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
GP-5214: Add support for multi-precision integer operations in the JIT-accelerated emulator.
This commit is contained in:
parent
13ffa3a4a8
commit
df90de2367
87 changed files with 5278 additions and 956 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -33,24 +31,36 @@ import ghidra.pcode.emu.jit.op.JitIntSDivOp;
|
|||
* This uses the binary operator generator and simply emits {@link #IDIV} or {@link #LDIV} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum IntSDivOpGen implements BinOpGen<JitIntSDivOp> {
|
||||
public enum IntSDivOpGen implements IntBinOpGen<JitIntSDivOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void generateMpIntSDiv(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntSignedDivide", mv, 1,
|
||||
TakeOut.OUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntSDivOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformSExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntSDivOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformSExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, rExt(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(IDIV);
|
||||
case LongJitType t -> rv.visitInsn(LDIV);
|
||||
case MpIntJitType t -> TODO("MpInt");
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntSDiv(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.
|
||||
|
|
|
@ -17,12 +17,11 @@ 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.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.op.JitIntSExtOp;
|
||||
|
||||
/**
|
||||
|
@ -35,43 +34,18 @@ import ghidra.pcode.emu.jit.op.JitIntSExtOp;
|
|||
* {@link IntJitType#I4 int4} to {@link LongJitType#I8 int8} is implemented with by emitting only
|
||||
* {@link #I2L}.
|
||||
*/
|
||||
public enum IntSExtOpGen implements UnOpGen<JitIntSExtOp> {
|
||||
public enum IntSExtOpGen implements IntUnOpGen<JitIntSExtOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitIntSExtOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||
|
||||
if (uType == IntJitType.I4 && outType == LongJitType.I8) {
|
||||
rv.visitInsn(I2L);
|
||||
return outType;
|
||||
}
|
||||
|
||||
TypeConversions.generate(gen, uType, outType, rv);
|
||||
switch (outType) {
|
||||
case IntJitType t -> {
|
||||
int shamt = Integer.SIZE - op.u().size() * Byte.SIZE;
|
||||
if (shamt != 0) {
|
||||
rv.visitLdcInsn(shamt);
|
||||
rv.visitInsn(ISHL);
|
||||
rv.visitLdcInsn(shamt);
|
||||
rv.visitInsn(ISHR);
|
||||
}
|
||||
}
|
||||
case LongJitType t -> {
|
||||
int shamt = Long.SIZE - op.u().size() * Byte.SIZE;
|
||||
if (shamt != 0) {
|
||||
rv.visitLdcInsn(shamt);
|
||||
rv.visitInsn(LSHL);
|
||||
rv.visitLdcInsn(shamt);
|
||||
rv.visitInsn(LSHR);
|
||||
}
|
||||
}
|
||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return outType;
|
||||
return uType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.*;
|
||||
|
@ -32,24 +31,36 @@ import ghidra.pcode.emu.jit.op.JitIntSRemOp;
|
|||
* This uses the binary operator generator and simply emits {@link #IREM} or {@link #LREM} depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum IntSRemOpGen implements BinOpGen<JitIntSRemOp> {
|
||||
public enum IntSRemOpGen implements IntBinOpGen<JitIntSRemOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void generateMpIntSRem(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
BinOpGen.generateMpDelegationToStaticMethod(gen, type, "mpIntSignedDivide", mv, 1,
|
||||
TakeOut.LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntSRemOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
return TypeConversions.forceUniformSExt(lType, rType, rv);
|
||||
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntSRemOp op, JitBlock block,
|
||||
JitType lType, JitType rType, MethodVisitor rv) {
|
||||
rType = TypeConversions.forceUniformSExt(rType, lType, rv);
|
||||
rType = TypeConversions.forceUniform(gen, rType, lType, rExt(), rv);
|
||||
switch (rType) {
|
||||
case IntJitType t -> rv.visitInsn(IREM);
|
||||
case LongJitType t -> rv.visitInsn(LREM);
|
||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
||||
case MpIntJitType t when t.size() == lType.size() -> generateMpIntSRem(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.
|
||||
|
|
|
@ -15,12 +15,7 @@
|
|||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.op.JitIntSRightOp;
|
||||
|
||||
/**
|
||||
|
@ -35,14 +30,12 @@ public enum IntSRightOpGen implements ShiftIntBinOpGen<JitIntSRightOp> {
|
|||
GEN;
|
||||
|
||||
@Override
|
||||
public String methodName() {
|
||||
return "intSRight";
|
||||
public boolean isSigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType afterLeft(JitCodeGenerator gen, JitIntSRightOp op, JitType lType, JitType rType,
|
||||
MethodVisitor rv) {
|
||||
TypeConversions.generateSExt(lType, rv);
|
||||
return lType;
|
||||
public String methodName() {
|
||||
return "intSRight";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,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.JitIntSubOp;
|
||||
|
||||
/**
|
||||
|
@ -36,10 +38,15 @@ import ghidra.pcode.emu.jit.op.JitIntSubOp;
|
|||
* <p>
|
||||
* NOTE: The multi-precision integer parts of this are a work in progress.
|
||||
*/
|
||||
public enum IntSubOpGen implements BinOpGen<JitIntSubOp> {
|
||||
public enum IntSubOpGen implements IntBinOpGen<JitIntSubOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntLegSub(JitCodeGenerator gen, int idx, boolean takesBorrow,
|
||||
boolean givesBorrow, MethodVisitor mv) {
|
||||
if (takesBorrow) {
|
||||
|
@ -50,19 +57,19 @@ public enum IntSubOpGen implements BinOpGen<JitIntSubOp> {
|
|||
mv.visitInsn(DUP2_X1);
|
||||
mv.visitInsn(POP2);
|
||||
// [...,borrowinN:LONG,llegN:INT]
|
||||
mv.visitInsn(I2L); // yes, signed
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,borrowinN:LONG,llegN:LONG]
|
||||
mv.visitInsn(LADD); // Yes, add, because borrow is 0 or -1
|
||||
// [...,diffpartN:LONG]
|
||||
}
|
||||
else {
|
||||
// [...,legN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, mv);
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,diffpartN:LONG] (legN + 0)
|
||||
}
|
||||
mv.visitVarInsn(ILOAD, idx);
|
||||
// [...,diffpartN:LONG,rlegN:INT]
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, mv);
|
||||
TypeConversions.generateIntToLong(IntJitType.I4, LongJitType.I8, Ext.ZERO, mv);
|
||||
// [...,diffpartN:LONG,rlegN:LONG]
|
||||
mv.visitInsn(LSUB);
|
||||
// [...,olegN:LONG]
|
||||
|
@ -70,7 +77,7 @@ public enum IntSubOpGen implements BinOpGen<JitIntSubOp> {
|
|||
mv.visitInsn(DUP2);
|
||||
}
|
||||
// [...,(olegN:LONG),olegN:LONG]
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, mv);
|
||||
TypeConversions.generateLongToInt(LongJitType.I8, IntJitType.I4, Ext.ZERO, mv);
|
||||
// [...,(olegN:LONG),olegN:INT]
|
||||
/** NB. The store will perform the masking */
|
||||
mv.visitVarInsn(ISTORE, idx);
|
||||
|
@ -82,46 +89,34 @@ public enum IntSubOpGen implements BinOpGen<JitIntSubOp> {
|
|||
* The strategy is to allocate a temp local for each leg of the result. First, we'll pop the
|
||||
* right operand into the temp. Then, as we work with each leg of the left operand, we'll
|
||||
* execute the algorithm. Convert both right and left legs to a long and add them (along
|
||||
* with a possible carry in). Store the result back into the temp locals. Shift the leg
|
||||
* with a possible borrow in). Store the result back into the temp locals. Shift the leg
|
||||
* right 32 to get the carry out, then continue to the next leg up. The final carry out can
|
||||
* be dropped (overflow). The result legs are then pushed back to the stack.
|
||||
*/
|
||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||
int legCount = type.legsAlloc(); // include partial
|
||||
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);
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean isLast = i == legCount - 1;
|
||||
boolean takesCarry = i != 0; // not first
|
||||
generateMpIntLegSub(gen, result.idx(i), takesCarry, !isLast, mv);
|
||||
}
|
||||
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||
}
|
||||
// [lleg1,...,llegN:INT]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
boolean isLast = i == legCount - 1;
|
||||
boolean takesCarry = i != 0; // not first
|
||||
generateMpIntLegSub(gen, firstIndex + i, takesCarry, !isLast, 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 JitType afterLeft(JitCodeGenerator gen, JitIntSubOp 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, JitIntSubOp 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(ISUB);
|
||||
case LongJitType t -> rv.visitInsn(LSUB);
|
||||
|
@ -129,6 +124,6 @@ public enum IntSubOpGen implements BinOpGen<JitIntSubOp> {
|
|||
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
||||
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.JitIntUnOp;
|
||||
|
||||
/**
|
||||
* An extension for integer unary operators
|
||||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface IntUnOpGen<T extends JitIntUnOp> extends UnOpGen<T> {
|
||||
// Intentionally empty
|
||||
}
|
|
@ -35,10 +35,22 @@ import ghidra.pcode.emu.jit.op.JitIntZExtOp;
|
|||
* Note that this implementation is equivalent to {@link CopyOpGen}, except that differences in
|
||||
* operand sizes are expected.
|
||||
*/
|
||||
public enum IntZExtOpGen implements UnOpGen<JitIntZExtOp> {
|
||||
public enum IntZExtOpGen implements IntUnOpGen<JitIntZExtOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @implNote No need for explicit zero-extended type conversion (vice {@link IntSExtOpGen}),
|
||||
* because conversion will happen as a manner of writing the output. Thus, this is
|
||||
* identical in operation to {@link CopyOpGen}.
|
||||
*/
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitIntZExtOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
|
|
|
@ -26,6 +26,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.*;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitLoadOp;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
|
@ -184,9 +185,9 @@ public enum LoadOpGen implements OpGen<JitLoadOp> {
|
|||
// [...]
|
||||
gen.requestFieldForSpaceIndirect(op.space()).generateLoadCode(gen, rv);
|
||||
// [...,space]
|
||||
JitType offsetType = gen.generateValReadCode(op.offset(), op.offsetType());
|
||||
JitType offsetType = gen.generateValReadCode(op.offset(), op.offsetType(), Ext.ZERO);
|
||||
// [...,space,offset:?INT/LONG]
|
||||
TypeConversions.generateToLong(offsetType, LongJitType.I8, rv);
|
||||
TypeConversions.generateToLong(offsetType, LongJitType.I8, Ext.ZERO, rv);
|
||||
// [...,space,offset:LONG]
|
||||
rv.visitLdcInsn(op.out().size());
|
||||
// [...,space,offset,size]
|
||||
|
@ -204,7 +205,7 @@ public enum LoadOpGen implements OpGen<JitLoadOp> {
|
|||
default -> throw new AssertionError();
|
||||
}
|
||||
// [...,value]
|
||||
gen.generateVarWriteCode(op.out(), outType);
|
||||
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
|
||||
// [...]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.bouncycastle.util.Bytes;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
|
@ -34,19 +35,77 @@ import ghidra.pcode.emu.jit.op.JitLzCountOp;
|
|||
* {@link Integer#numberOfLeadingZeros(int)} or {@link Long#numberOfLeadingZeros(long)}, depending
|
||||
* on the type.
|
||||
*/
|
||||
public enum LzCountOpGen implements UnOpGen<JitLzCountOp> {
|
||||
public enum LzCountOpGen implements IntUnOpGen<JitLzCountOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
/**
|
||||
* We use zero extension and then, when there is slack, we subtract off the zero bits that
|
||||
* came from the extension.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntLzCount(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
// [leg1:INT,...,legN:INT]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "numberOfLeadingZeros",
|
||||
MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS, false);
|
||||
// [lzc1:INT,leg2:INT,...,legN:INT]
|
||||
for (int i = 1; i < type.legsAlloc(); i++) {
|
||||
mv.visitInsn(SWAP);
|
||||
// [leg2:INT,lzc1:INT,...,legN:INT]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "numberOfLeadingZeros",
|
||||
MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS, false);
|
||||
// [lzc2:INT,lzc1:INT,...,legN:INT]
|
||||
|
||||
Label lblAdd = new Label();
|
||||
Label lblNext = new Label();
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn(Integer.SIZE);
|
||||
mv.visitJumpInsn(IF_ICMPEQ, lblAdd);
|
||||
// [lzc2:INT,lzc1:INT,...,legN:INT]
|
||||
mv.visitInsn(SWAP);
|
||||
mv.visitInsn(POP);
|
||||
// [lzc2:INT,...,legN:INT]
|
||||
mv.visitJumpInsn(GOTO, lblNext);
|
||||
mv.visitLabel(lblAdd);
|
||||
// [lzc2:INT,lzc1:INT,...,legN:INT]
|
||||
mv.visitInsn(IADD);
|
||||
// [lzc2+lzc1:INT,...,legN:INT]
|
||||
mv.visitLabel(lblNext);
|
||||
// [lzcT:INT,...,legN:INT]
|
||||
}
|
||||
|
||||
SimpleJitType mslType = type.legTypes().get(0);
|
||||
if (mslType.size() < Integer.BYTES) {
|
||||
mv.visitLdcInsn(Integer.SIZE - mslType.size() * Byte.SIZE);
|
||||
mv.visitInsn(ISUB);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitLzCountOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
switch (uType) {
|
||||
case IntJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER,
|
||||
"numberOfLeadingZeros", MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS, false);
|
||||
case LongJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_LONG,
|
||||
"numberOfLeadingZeros", MDESC_LONG__NUMBER_OF_LEADING_ZEROS, false);
|
||||
case MpIntJitType t -> TODO("MpInt");
|
||||
case IntJitType t -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "numberOfLeadingZeros",
|
||||
MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS, false);
|
||||
if (t.size() < Integer.BYTES) {
|
||||
rv.visitLdcInsn(Integer.SIZE - t.size() * Byte.SIZE);
|
||||
rv.visitInsn(ISUB);
|
||||
}
|
||||
}
|
||||
case LongJitType t -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "numberOfLeadingZeros",
|
||||
MDESC_LONG__NUMBER_OF_LEADING_ZEROS, false);
|
||||
if (t.size() < Long.BYTES) {
|
||||
rv.visitLdcInsn(Long.SIZE - t.size() * Bytes.SIZE);
|
||||
rv.visitInsn(ISUB);
|
||||
}
|
||||
}
|
||||
case MpIntJitType t -> generateMpIntLzCount(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return IntJitType.I4;
|
||||
|
|
|
@ -15,14 +15,19 @@
|
|||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import java.io.PrintStream;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
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.op.*;
|
||||
import ghidra.pcode.emu.jit.var.*;
|
||||
|
@ -562,6 +567,148 @@ public interface OpGen<T extends JitOp> extends Opcodes {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to move all legs from the stack into a temporary allocation
|
||||
* <p>
|
||||
* This consumes {@code legCount} legs from the stack. Nothing else is pushed to the stack. The
|
||||
* legs are placed in ascending indices as popped from the stack, i.e., in little-endian order.
|
||||
*
|
||||
* @param temp the allocation of temporary legs
|
||||
* @param legCount the number of legs to move
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void generateMpLegsIntoTemp(JvmTempAlloc temp, int legCount, MethodVisitor mv) {
|
||||
// [leg1,...,legN]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ISTORE, temp.idx(i));
|
||||
}
|
||||
// []
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to move all legs from a temporary allocation onto the stack
|
||||
* <p>
|
||||
* This consumes nothing. It places {@code legCount} legs onto the stack, pushed in descending
|
||||
* order, i.e., such that they would be popped in little-endian order.
|
||||
*
|
||||
* @param temp the allocation of temporary legs
|
||||
* @param legCount the number of lets to move
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void generateMpLegsFromTemp(JvmTempAlloc temp, int legCount, MethodVisitor mv) {
|
||||
// []
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitVarInsn(ILOAD, temp.idx(legCount - i - 1));
|
||||
}
|
||||
// [leg1,...,legN]
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to copy all legs from a temporary allocation into an array
|
||||
* <p>
|
||||
* This does not consume anything from the stack. Upon return, the new array is pushed onto the
|
||||
* stack. The legs are positioned in the array in the same order as in the locals. When used
|
||||
* with {@link #generateMpLegsIntoTemp(JvmTempAlloc, int, MethodVisitor)}, this is little-endian
|
||||
* order.
|
||||
*
|
||||
* @param temp the allocation of temporary legs
|
||||
* @param arrSize the size of the array, possibly over-provisioned
|
||||
* @param legCount the number of legs to move
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void generateMpLegsIntoArray(JvmTempAlloc temp, int arrSize, int legCount,
|
||||
MethodVisitor mv) {
|
||||
assert arrSize >= legCount;
|
||||
// []
|
||||
mv.visitLdcInsn(arrSize);
|
||||
// [count:INT]
|
||||
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||
// [arr:INT[count]]
|
||||
for (int i = 0; i < legCount; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
// [arr,arr:INT[count]]
|
||||
mv.visitLdcInsn(i);
|
||||
// [idx:INT,arr,arr:INT[count]]
|
||||
mv.visitVarInsn(ILOAD, temp.idx(i));
|
||||
// [leg:INT,idx:INT,arr,arr:INT[count]]
|
||||
mv.visitInsn(IASTORE);
|
||||
// [arr:INT[count]]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to push all legs from an array onto the stack
|
||||
* <p>
|
||||
* This consumes the array at the top of the stack, and pushes its legs onto the stack in the
|
||||
* reverse order as they are positioned in the array. If the legs are in little-endian order, as
|
||||
* is convention, this method will push the legs to the stack with the least-significant leg on
|
||||
* top.
|
||||
*
|
||||
* @param legCount the number of legs in the array
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void generateMpLegsFromArray(int legCount, MethodVisitor mv) {
|
||||
// [out:INT[count]]
|
||||
for (int i = 0; i < legCount - 1; i++) {
|
||||
// [out]
|
||||
mv.visitInsn(DUP);
|
||||
// [out,out]
|
||||
mv.visitLdcInsn(legCount - 1 - i);
|
||||
// [idx,out,out]
|
||||
mv.visitInsn(IALOAD);
|
||||
// [legN:INT,out]
|
||||
mv.visitInsn(SWAP);
|
||||
// [out,legN:INT]
|
||||
}
|
||||
mv.visitLdcInsn(0);
|
||||
// [idx,out,...]
|
||||
mv.visitInsn(IALOAD);
|
||||
// [leg1,...]
|
||||
}
|
||||
|
||||
static void generateSyserrInts(JitCodeGenerator gen, int count, MethodVisitor mv) {
|
||||
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", count)) {
|
||||
// [leg1,...,legN]
|
||||
generateMpLegsIntoTemp(temp, count, mv);
|
||||
// []
|
||||
mv.visitFieldInsn(GETSTATIC, Type.getInternalName(System.class), "err",
|
||||
Type.getDescriptor(PrintStream.class));
|
||||
// [System.err]
|
||||
String fmt =
|
||||
IntStream.range(0, count).mapToObj(i -> "%08x").collect(Collectors.joining(":"));
|
||||
mv.visitLdcInsn(fmt);
|
||||
// [fmt:String,System.err]
|
||||
|
||||
mv.visitLdcInsn(count);
|
||||
// [count,fmt,System.err]
|
||||
mv.visitTypeInsn(ANEWARRAY, Type.getInternalName(Object.class));
|
||||
// [blegs:Object[count],fmt,System.err]
|
||||
for (int i = 0; i < count; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
// [blegs,blegs,fmt,System.err]
|
||||
mv.visitLdcInsn(i);
|
||||
// [idx:INT,blegs,blegs,fmt,System.err]
|
||||
mv.visitVarInsn(ILOAD, temp.idx(count - i - 1));
|
||||
// [val:INT,idx,blegs,blegs,fmt,System.err]
|
||||
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf",
|
||||
Type.getMethodDescriptor(Type.getType(Integer.class), Type.INT_TYPE), false);
|
||||
// [val:Integer,idx,blegs,blegs,fmt,System.err]
|
||||
mv.visitInsn(AASTORE);
|
||||
// [blegs,fmt,System.err]
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(String.class), "formatted",
|
||||
Type.getMethodDescriptor(Type.getType(String.class), Type.getType(Object[].class)),
|
||||
false);
|
||||
// [msg:String,System.err]
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(PrintStream.class), "println",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false);
|
||||
// []
|
||||
generateMpLegsFromTemp(temp, count, mv);
|
||||
// [leg1,...,legN]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode into the class constructor.
|
||||
*
|
||||
|
@ -579,9 +726,9 @@ public interface OpGen<T extends JitOp> extends Opcodes {
|
|||
* This method must emit the code needed to load any input operands, convert them to the
|
||||
* appropriate type, perform the actual operation, and then if applicable, store the output
|
||||
* operand. The implementations should delegate to
|
||||
* {@link JitCodeGenerator#generateValReadCode(JitVal, JitTypeBehavior)},
|
||||
* {@link JitCodeGenerator#generateVarWriteCode(JitVar, JitType)}, and {@link TypeConversions}
|
||||
* appropriately.
|
||||
* {@link JitCodeGenerator#generateValReadCode(JitVal, JitTypeBehavior, Ext)},
|
||||
* {@link JitCodeGenerator#generateVarWriteCode(JitVar, JitType, Ext)}, and
|
||||
* {@link TypeConversions} appropriately.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param op the p-code op (use-def node) to translate
|
||||
|
|
|
@ -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;
|
||||
|
@ -33,10 +32,30 @@ import ghidra.pcode.emu.jit.op.JitPopCountOp;
|
|||
* This uses the unary operator generator and emits an invocation of {@link Integer#bitCount(int)}
|
||||
* or {@link Long#bitCount(long)}, depending on the type.
|
||||
*/
|
||||
public enum PopCountOpGen implements UnOpGen<JitPopCountOp> {
|
||||
public enum PopCountOpGen implements IntUnOpGen<JitPopCountOp> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateMpIntPopCount(JitCodeGenerator gen, MpIntJitType type, MethodVisitor mv) {
|
||||
// [leg1:INT,...,legN:INT]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "bitCount", MDESC_INTEGER__BIT_COUNT, false);
|
||||
// [pop1:INT,leg2:INT...,legN:INT]
|
||||
for (int i = 1; i < type.legsAlloc(); i++) {
|
||||
mv.visitInsn(SWAP);
|
||||
// [leg2:INT,pop1:INT,...,legN:INT]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "bitCount", MDESC_INTEGER__BIT_COUNT,
|
||||
false);
|
||||
// [pop2:INT,pop1:INT,...,legN:INT]
|
||||
mv.visitInsn(IADD);
|
||||
// [popT:INT,...,legN:INT]
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitPopCountOp op, JitBlock block,
|
||||
JitType uType, MethodVisitor rv) {
|
||||
|
@ -45,7 +64,7 @@ public enum PopCountOpGen implements UnOpGen<JitPopCountOp> {
|
|||
MDESC_INTEGER__BIT_COUNT, false);
|
||||
case LongJitType t -> rv.visitMethodInsn(INVOKESTATIC, NAME_LONG, "bitCount",
|
||||
MDESC_LONG__BIT_COUNT, false);
|
||||
case MpIntJitType t -> TODO("MpInt");
|
||||
case MpIntJitType t -> generateMpIntPopCount(gen, t, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
return IntJitType.I4;
|
||||
|
|
|
@ -19,12 +19,14 @@ 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.IntJitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||
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.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitIntBinOp;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +39,17 @@ import ghidra.pcode.emu.jit.op.JitIntBinOp;
|
|||
*
|
||||
* @param <T> the class of p-code op node in the use-def graph
|
||||
*/
|
||||
public interface ShiftIntBinOpGen<T extends JitIntBinOp> extends BinOpGen<T> {
|
||||
public interface ShiftIntBinOpGen<T extends JitIntBinOp> extends IntBinOpGen<T> {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The shift amount is always treated unsigned.
|
||||
*/
|
||||
@Override
|
||||
default Ext rExt() {
|
||||
return Ext.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the static method in {@link JitCompiledPassage} to invoke
|
||||
*
|
||||
|
@ -45,6 +57,89 @@ public interface ShiftIntBinOpGen<T extends JitIntBinOp> extends BinOpGen<T> {
|
|||
*/
|
||||
String methodName();
|
||||
|
||||
default MpIntJitType generateShiftMpPrimitive(JitAllocationModel am, int legCount,
|
||||
SimpleJitType rType, MpIntJitType outType, String mdesc, MethodVisitor mv) {
|
||||
try (
|
||||
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", legCount);
|
||||
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", rType.javaType(), 1)) {
|
||||
// [amt:INT, lleg1:INT,...,llegN:INT]
|
||||
mv.visitVarInsn(rType.opcodeStore(), tmpR.idx(0));
|
||||
// [lleg1,...,llegN]
|
||||
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||
// []
|
||||
/**
|
||||
* FIXME: We could avoid this array allocation by shifting in place, but then we'd still
|
||||
* need to communicate the actual out size. Things are easy if the out size is smaller
|
||||
* than the left-in size, but not so easy if larger. Or, maybe over-provision if
|
||||
* larger....
|
||||
*/
|
||||
mv.visitLdcInsn(outType.legsAlloc());
|
||||
// [outLegCount:INT]
|
||||
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||
// [out:ARR]
|
||||
mv.visitInsn(DUP);
|
||||
// [out,out]
|
||||
mv.visitLdcInsn(outType.size());
|
||||
// [outBytes:INT,out,out]
|
||||
OpGen.generateMpLegsIntoArray(tmpL, legCount, legCount, mv);
|
||||
// [inL:ARR,outBytes:INT,out,out]
|
||||
mv.visitVarInsn(rType.opcodeLoad(), tmpR.idx(0));
|
||||
// [inR:SIMPLE,inL:ARR,outBytes,out,out]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(), mdesc, true);
|
||||
// [out]
|
||||
OpGen.generateMpLegsFromArray(outType.legsAlloc(), mv);
|
||||
// [oleg1,...,olegN]
|
||||
}
|
||||
return outType.ext();
|
||||
}
|
||||
|
||||
default SimpleJitType generateShiftPrimitiveMp(JitAllocationModel am, SimpleJitType lType,
|
||||
int legCount, String mdesc, MethodVisitor mv) {
|
||||
try (JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", legCount)) {
|
||||
// [rleg1:INT,...,rlegN:INT,val:INT]
|
||||
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||
// [val:INT]
|
||||
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||
// [inR:ARR,val:INT]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(), mdesc, true);
|
||||
// [out:INT]
|
||||
}
|
||||
return lType.ext();
|
||||
}
|
||||
|
||||
default MpIntJitType generateShiftMpMp(JitAllocationModel am, int leftLegCount,
|
||||
int rightLegCount, MpIntJitType outType, MethodVisitor mv) {
|
||||
try (
|
||||
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", leftLegCount);
|
||||
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", rightLegCount)) {
|
||||
// [rleg1:INT,...,rlegN:INT,lleg1:INT,...,llegN:INT]
|
||||
OpGen.generateMpLegsIntoTemp(tmpR, rightLegCount, mv);
|
||||
// [lleg1,...,llegN]
|
||||
OpGen.generateMpLegsIntoTemp(tmpL, leftLegCount, mv);
|
||||
// []
|
||||
// FIXME: Same as in shiftPrimitiveMp
|
||||
int outLegCount = outType.legsAlloc();
|
||||
mv.visitLdcInsn(outLegCount);
|
||||
// [outLegCount:INT]
|
||||
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||
// [out:ARR]
|
||||
mv.visitInsn(DUP);
|
||||
// [out,out]
|
||||
mv.visitLdcInsn(outType.size());
|
||||
// [outBytes:INT,out,out]
|
||||
OpGen.generateMpLegsIntoArray(tmpL, leftLegCount, leftLegCount, mv);
|
||||
// [inL:ARR,outBytes,out,out]
|
||||
OpGen.generateMpLegsIntoArray(tmpR, rightLegCount, rightLegCount, mv);
|
||||
// [inR,inL,outBytes,out,out]
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(),
|
||||
MDESC_$SHIFT_AA, true);
|
||||
// [out]
|
||||
OpGen.generateMpLegsFromArray(outLegCount, mv);
|
||||
// [oleg1,...,olegN]
|
||||
}
|
||||
return outType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -55,20 +150,49 @@ public interface ShiftIntBinOpGen<T extends JitIntBinOp> extends BinOpGen<T> {
|
|||
@Override
|
||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||
JitType rType, MethodVisitor rv) {
|
||||
String mdesc = switch (lType) {
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
return switch (lType) {
|
||||
case IntJitType lt -> switch (rType) {
|
||||
case IntJitType rt -> MDESC_$SHIFT_II;
|
||||
case LongJitType rt -> MDESC_$SHIFT_IJ;
|
||||
case IntJitType rt -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(),
|
||||
MDESC_$SHIFT_II, true);
|
||||
yield lType.ext();
|
||||
}
|
||||
case LongJitType rt -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(),
|
||||
MDESC_$SHIFT_IJ, true);
|
||||
yield lType.ext();
|
||||
}
|
||||
case MpIntJitType rt -> generateShiftPrimitiveMp(am, lt, rt.legsAlloc(),
|
||||
MDESC_$SHIFT_IA, rv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
|
||||
case LongJitType lt -> switch (rType) {
|
||||
case IntJitType rt -> MDESC_$SHIFT_JI;
|
||||
case LongJitType rt -> MDESC_$SHIFT_JJ;
|
||||
case IntJitType rt -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(),
|
||||
MDESC_$SHIFT_JI, true);
|
||||
yield lType.ext();
|
||||
}
|
||||
case LongJitType rt -> {
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(),
|
||||
MDESC_$SHIFT_JJ, true);
|
||||
yield lType.ext();
|
||||
}
|
||||
case MpIntJitType rt -> generateShiftPrimitiveMp(am, lt, rt.legsAlloc(),
|
||||
MDESC_$SHIFT_JA, rv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpIntJitType lt -> switch (rType) {
|
||||
case IntJitType rt -> generateShiftMpPrimitive(am, lt.legsAlloc(), rt,
|
||||
MpIntJitType.forSize(op.out().size()), MDESC_$SHIFT_AI, rv);
|
||||
case LongJitType rt -> generateShiftMpPrimitive(am, lt.legsAlloc(), rt,
|
||||
MpIntJitType.forSize(op.out().size()), MDESC_$SHIFT_AJ, rv);
|
||||
case MpIntJitType rt -> generateShiftMpMp(am, lt.legsAlloc(), rt.legsAlloc(),
|
||||
MpIntJitType.forSize(op.out().size()), rv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName(), mdesc, true);
|
||||
return lType.ext();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,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.*;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitStoreOp;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
|
@ -184,11 +185,11 @@ public enum StoreOpGen implements OpGen<JitStoreOp> {
|
|||
// [...]
|
||||
gen.requestFieldForSpaceIndirect(op.space()).generateLoadCode(gen, rv);
|
||||
// [...,space]
|
||||
JitType offsetType = gen.generateValReadCode(op.offset(), op.offsetType());
|
||||
JitType offsetType = gen.generateValReadCode(op.offset(), op.offsetType(), Ext.ZERO);
|
||||
// [...,space,offset:?]
|
||||
TypeConversions.generateToLong(offsetType, LongJitType.I8, rv);
|
||||
TypeConversions.generateToLong(offsetType, LongJitType.I8, Ext.ZERO, rv);
|
||||
// [...,space,offset:LONG]
|
||||
JitType valueType = gen.generateValReadCode(op.value(), op.valueType());
|
||||
JitType valueType = gen.generateValReadCode(op.value(), op.valueType(), Ext.ZERO);
|
||||
// [...,space,offset,value]
|
||||
rv.visitLdcInsn(op.value().size());
|
||||
// [...,space,offset,value,size]
|
||||
|
|
|
@ -15,12 +15,15 @@
|
|||
*/
|
||||
package ghidra.pcode.emu.jit.gen.op;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
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.*;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitSubPieceOp;
|
||||
|
||||
/**
|
||||
|
@ -42,18 +45,23 @@ public enum SubPieceOpGen implements OpGen<JitSubPieceOp> {
|
|||
GEN;
|
||||
|
||||
/**
|
||||
* <b>WIP</b>: Assumes the previous (next more significant) leg is on the stack and the current
|
||||
* (unshifted) leg is in the given variable. Computes the resulting output leg and puts in into
|
||||
* the given local variable, but leaves a copy of the current unshifted leg on the stack.
|
||||
* <b>Assumes the next-more-significant leg (i.e., the one from the previous iteration) is on
|
||||
* the stack and the current (unshifted) leg is in the given variable. Computes the resulting
|
||||
* output leg and puts in into the given local variable, but leaves a copy of the current
|
||||
* unshifted leg on the stack.
|
||||
*
|
||||
* @param rv the method visitor
|
||||
* @param bitShift the number of <em>bits</em> to shift
|
||||
* @param index the index of the local variable for the current leg
|
||||
* @implNote This <em>cannot</em> yet be factored with the shifting operators, because those
|
||||
* take a variable for the shift amount. The subpiece offset is always constant.
|
||||
* If/when we optimize shift operators with constant shift amounts, then we can
|
||||
* consider factoring the common parts with this.
|
||||
*/
|
||||
private void generateShiftWithPrevLeg(MethodVisitor rv, int bitShift, int index) {
|
||||
private static void generateShiftWithPrevLeg(MethodVisitor rv, int bitShift, int index) {
|
||||
// [...,prevLegIn]
|
||||
rv.visitLdcInsn(Integer.SIZE - bitShift);
|
||||
rv.visitInsn(ISHR);
|
||||
rv.visitInsn(ISHL);
|
||||
// [...,prevLegIn:SLACK]
|
||||
rv.visitVarInsn(ILOAD, index);
|
||||
// [...,prevLegIn:SLACK,legIn]
|
||||
|
@ -68,86 +76,85 @@ public enum SubPieceOpGen implements OpGen<JitSubPieceOp> {
|
|||
// [...,legIn]
|
||||
}
|
||||
|
||||
private static MpIntJitType generateMpIntSubPiece(JitCodeGenerator gen, JitSubPieceOp op,
|
||||
MpIntJitType type, MethodVisitor mv) {
|
||||
MpIntJitType outMpType = MpIntJitType.forSize(op.out().size());
|
||||
int outLegCount = outMpType.legsAlloc();
|
||||
int legsLeft = type.legsAlloc();
|
||||
int popCount = op.offset() / Integer.BYTES;
|
||||
int byteShift = op.offset() % Integer.BYTES;
|
||||
for (int i = 0; i < popCount; i++) {
|
||||
mv.visitInsn(POP);
|
||||
legsLeft--;
|
||||
}
|
||||
|
||||
JitAllocationModel am = gen.getAllocationModel();
|
||||
try (JvmTempAlloc subpieces = am.allocateTemp(mv, "subpiece", outLegCount)) {
|
||||
for (int i = 0; i < outLegCount; i++) {
|
||||
mv.visitVarInsn(ISTORE, subpieces.idx(i));
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
legsLeft--;
|
||||
}
|
||||
|
||||
if (byteShift > 0) {
|
||||
int curLeg = outLegCount - 1;
|
||||
if (legsLeft > 0) {
|
||||
// [...,prevLegIn]
|
||||
generateShiftWithPrevLeg(mv, byteShift * Byte.SIZE, subpieces.idx(curLeg));
|
||||
// [...,legIn]
|
||||
legsLeft--;
|
||||
curLeg--;
|
||||
}
|
||||
else {
|
||||
// [...]
|
||||
mv.visitVarInsn(ILOAD, subpieces.idx(curLeg));
|
||||
// [...,legIn]
|
||||
mv.visitInsn(DUP);
|
||||
// [...,legIn,legIn]
|
||||
mv.visitLdcInsn(byteShift * Byte.SIZE);
|
||||
mv.visitInsn(IUSHR);
|
||||
// [...,legIn,legOut]
|
||||
mv.visitVarInsn(ISTORE, subpieces.idx(curLeg));
|
||||
// [...,legIn]
|
||||
curLeg--;
|
||||
}
|
||||
while (curLeg >= 0) {
|
||||
generateShiftWithPrevLeg(mv, byteShift * Byte.SIZE, subpieces.idx(curLeg));
|
||||
legsLeft--;
|
||||
curLeg--;
|
||||
}
|
||||
}
|
||||
while (legsLeft > 0) {
|
||||
mv.visitInsn(POP);
|
||||
legsLeft--;
|
||||
}
|
||||
// NOTE: More significant legs have higher indices
|
||||
for (int i = outLegCount - 1; i >= 0; i--) {
|
||||
mv.visitVarInsn(ILOAD, subpieces.idx(i));
|
||||
}
|
||||
}
|
||||
return outMpType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRunCode(JitCodeGenerator gen, JitSubPieceOp op, JitBlock block,
|
||||
MethodVisitor rv) {
|
||||
JitType vType = gen.generateValReadCode(op.u(), op.uType());
|
||||
JitType outType;
|
||||
switch (vType) {
|
||||
JitType vType = gen.generateValReadCode(op.u(), op.uType(), Ext.ZERO);
|
||||
JitType outType = switch (vType) {
|
||||
case IntJitType vIType -> {
|
||||
rv.visitLdcInsn(op.offset() * Byte.SIZE);
|
||||
rv.visitInsn(IUSHR);
|
||||
outType = vIType;
|
||||
yield vIType;
|
||||
}
|
||||
case LongJitType vLType -> {
|
||||
rv.visitLdcInsn(op.offset() * Byte.SIZE);
|
||||
rv.visitInsn(LUSHR);
|
||||
outType = vLType;
|
||||
}
|
||||
case MpIntJitType vMpType -> {
|
||||
// WIP
|
||||
MpIntJitType outMpType = MpIntJitType.forSize(op.out().size());
|
||||
int outLegCount = outMpType.legsAlloc();
|
||||
int legsLeft = vMpType.legsAlloc();
|
||||
int popCount = op.offset() / Integer.BYTES;
|
||||
int byteShift = op.offset() % Integer.BYTES;
|
||||
for (int i = 0; i < popCount; i++) {
|
||||
rv.visitInsn(POP);
|
||||
}
|
||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
rv.visitLabel(start);
|
||||
for (int i = 0; i < outLegCount; i++) {
|
||||
rv.visitLocalVariable("subpiece" + i, Type.getDescriptor(int.class), null,
|
||||
start, end, firstIndex + i);
|
||||
rv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
||||
legsLeft--;
|
||||
}
|
||||
|
||||
if (byteShift > 0) {
|
||||
int curLeg = outLegCount - 1;
|
||||
if (legsLeft > 0) {
|
||||
// [...,prevLegIn]
|
||||
generateShiftWithPrevLeg(rv, byteShift * Byte.SIZE, firstIndex + curLeg);
|
||||
// [...,legIn]
|
||||
legsLeft--;
|
||||
curLeg--;
|
||||
}
|
||||
else {
|
||||
// [...]
|
||||
rv.visitVarInsn(ILOAD, firstIndex + curLeg);
|
||||
// [...,legIn]
|
||||
rv.visitInsn(DUP);
|
||||
// [...,legIn,legIn]
|
||||
rv.visitLdcInsn(byteShift * Byte.SIZE);
|
||||
rv.visitInsn(IUSHR);
|
||||
// [...,legIn,legOut]
|
||||
rv.visitVarInsn(ISTORE, firstIndex + curLeg);
|
||||
// [...,legIn]
|
||||
curLeg--;
|
||||
}
|
||||
while (curLeg >= 0) {
|
||||
generateShiftWithPrevLeg(rv, byteShift * Byte.SIZE, firstIndex + curLeg);
|
||||
legsLeft--;
|
||||
curLeg--;
|
||||
}
|
||||
}
|
||||
while (legsLeft > 0) {
|
||||
rv.visitInsn(POP);
|
||||
legsLeft--;
|
||||
}
|
||||
// NOTE: More significant legs have higher indices
|
||||
for (int i = outLegCount - 1; i >= 0; i--) {
|
||||
rv.visitVarInsn(ILOAD, firstIndex + i);
|
||||
}
|
||||
rv.visitLabel(end);
|
||||
outType = outMpType;
|
||||
yield vLType;
|
||||
}
|
||||
case MpIntJitType vMpType -> generateMpIntSubPiece(gen, op, vMpType, rv);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
gen.generateVarWriteCode(op.out(), outType);
|
||||
};
|
||||
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitUnOp;
|
||||
|
||||
/**
|
||||
|
@ -29,6 +30,26 @@ import ghidra.pcode.emu.jit.op.JitUnOp;
|
|||
*/
|
||||
public interface UnOpGen<T extends JitUnOp> extends OpGen<T> {
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code for the unary operator
|
||||
*
|
||||
|
@ -56,8 +77,8 @@ public interface UnOpGen<T extends JitUnOp> extends OpGen<T> {
|
|||
*/
|
||||
@Override
|
||||
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
|
||||
JitType uType = gen.generateValReadCode(op.u(), op.uType());
|
||||
JitType uType = gen.generateValReadCode(op.u(), op.uType(), ext());
|
||||
JitType outType = generateUnOpRunCode(gen, op, block, uType, rv);
|
||||
gen.generateVarWriteCode(op.out(), outType);
|
||||
gen.generateVarWriteCode(op.out(), outType, ext());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ package ghidra.pcode.emu.jit.gen.tgt;
|
|||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
|
@ -1058,6 +1060,35 @@ public interface JitCompiledPassage {
|
|||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_SCARRY int_sborrow} on multi-precision ints.
|
||||
*
|
||||
* @param a the first operand as in {@code a - b}
|
||||
* @param b the second operand as in {@code a - b}
|
||||
* @param shift one less than the number of bits in each most-significant leg, i.e., the number
|
||||
* of bits to shift right such that the most-significant bit of the most-significant
|
||||
* leg becomes the least-significant bit of the <em>most</em>-significant leg.
|
||||
* @return the one carry bit
|
||||
*/
|
||||
static int sBorrowMpInt(int[] a, int[] b, int shift) {
|
||||
assert a.length == b.length;
|
||||
long carry = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
carry >>= Integer.SIZE;
|
||||
carry += (a[i] & MASK_I2UL) - (b[i] & MASK_I2UL);
|
||||
}
|
||||
int msr = (int) carry;
|
||||
int msa = a[a.length - 1];
|
||||
int msb = b[b.length - 1];
|
||||
|
||||
msa ^= msr;
|
||||
msr ^= msb;
|
||||
msr ^= -1;
|
||||
msa &= msr;
|
||||
|
||||
return (msa >> shift) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_SCARRY int_scarry} on JVM ints.
|
||||
*
|
||||
|
@ -1098,6 +1129,199 @@ public interface JitCompiledPassage {
|
|||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_SCARRY int_scarry} on multi-precision ints.
|
||||
*
|
||||
* @param a the first operand as in {@code a + b}
|
||||
* @param b the second operand as in {@code a + b}
|
||||
* @param shift one less than the number of bits in each most-significant leg, i.e., the number
|
||||
* of bits to shift right such that the most-significant bit of the most-significant
|
||||
* leg becomes the least-significant bit of the <em>most</em>-significant leg.
|
||||
* @return the one carry bit
|
||||
*/
|
||||
static int sCarryMpInt(int[] a, int[] b, int shift) {
|
||||
assert a.length == b.length;
|
||||
long carry = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
carry >>>= Integer.SIZE;
|
||||
carry += (a[i] & MASK_I2UL) + (b[i] & MASK_I2UL);
|
||||
}
|
||||
int msr = (int) carry;
|
||||
int msa = a[a.length - 1];
|
||||
int msb = b[b.length - 1];
|
||||
|
||||
msr ^= msa;
|
||||
msa ^= msb;
|
||||
msa ^= -1;
|
||||
msr &= msa;
|
||||
|
||||
return (msr >> shift) & 1;
|
||||
}
|
||||
|
||||
enum MpShiftPrivate {
|
||||
;
|
||||
static void shl(int[] out, int[] val, int amt) {
|
||||
int legs = amt >>> 5;
|
||||
int bits = amt & 0x1f;
|
||||
/*for (int i = 0; i < out.length && i < legs; i++) {
|
||||
out[i] = 0;
|
||||
}*/
|
||||
if (bits == 0) {
|
||||
for (int i = 0; i < val.length - legs & i < out.length - legs; i++) {
|
||||
out[i + legs] = val[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
int prev = 0;
|
||||
for (int i = 0; i < val.length - legs & i < out.length - legs; i++) {
|
||||
out[i + legs] = (val[i] << bits) | (prev >>> (Integer.SIZE - bits));
|
||||
prev = val[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void ushr(int[] out, int[] val, int amt) {
|
||||
int legs = amt >>> 5;
|
||||
int bits = amt & 0x1f;
|
||||
/*for (int i = 0; i < out.length && i < legs; i++) {
|
||||
out[i + legs] = 0;
|
||||
}*/
|
||||
if (bits == 0) {
|
||||
for (int i = 0; i < val.length - legs & i < out.length; i++) {
|
||||
out[i] = val[i + legs];
|
||||
}
|
||||
return;
|
||||
}
|
||||
int prev = 0;
|
||||
for (int i = Math.min(val.length - legs, out.length) - 1; i >= 0; i--) {
|
||||
out[i] = (val[i + legs] >>> bits) | (prev << (Integer.SIZE - bits));
|
||||
prev = val[i + legs];
|
||||
}
|
||||
}
|
||||
|
||||
static void sshr(int[] out, int[] val, int amt, int sign) {
|
||||
int legs = amt >>> 5;
|
||||
int bits = amt & 0x1f;
|
||||
if (bits == 0) {
|
||||
for (int i = 0; i < val.length - legs & i < out.length; i++) {
|
||||
out[i] = val[i + legs];
|
||||
}
|
||||
if (sign != 0) {
|
||||
for (int i = val.length - legs; i < out.length; i++) {
|
||||
out[i] = sign;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
int prev = 0;
|
||||
// Only apply signed shift to most-significant leg of val
|
||||
if (val.length - legs - 1 >= 0) {
|
||||
out[val.length - legs - 1] = (val[val.length - 1] >> bits);
|
||||
prev = val[val.length - 1];
|
||||
}
|
||||
for (int i = Math.min(val.length - legs, out.length) - 2; i >= 0; i--) {
|
||||
out[i] = (val[i + legs] >>> bits) | (prev << (Integer.SIZE - bits));
|
||||
prev = val[i + legs];
|
||||
}
|
||||
if (sign != 0) {
|
||||
for (int i = val.length - legs; i < out.length; i++) {
|
||||
out[i] = sign;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on multi-precision ints.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHL ishl}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val << amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val << amt}, in little-endian order
|
||||
*/
|
||||
static void intLeft(int[] out, int outBytes, int[] val, int[] amt) {
|
||||
if (Integer.compareUnsigned(amt[0], outBytes * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MpShiftPrivate.shl(out, val, amt[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on an mp-int with a JVM long shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHL ishl}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val << amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val << amt}
|
||||
*/
|
||||
static void intLeft(int[] out, int outBytes, int[] val, long amt) {
|
||||
if (Long.compareUnsigned(amt, (outBytes & MASK_I2UL) * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
MpShiftPrivate.shl(out, val, (int) amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on an mp-int with a JVM int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHL ishl}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val << amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val << amt}
|
||||
*/
|
||||
static void intLeft(int[] out, int outBytes, int[] val, int amt) {
|
||||
if (Integer.compareUnsigned(amt, outBytes * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
MpShiftPrivate.shl(out, val, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on a JVM long with an mp-int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHL ishl}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param val the value as in {@code val << amt}
|
||||
* @param amt the amt as in {@code val << amt}, in little-endian order
|
||||
* @return the value
|
||||
*/
|
||||
static long intLeft(long val, int[] amt) {
|
||||
if (Long.compareUnsigned(Integer.toUnsignedLong(amt[0]), Long.SIZE) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return val << amt[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on JVM longs.
|
||||
*
|
||||
|
@ -1134,6 +1358,30 @@ public interface JitCompiledPassage {
|
|||
return val << amt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on a JVM int with an mp-int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHL ishl}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param val the value as in {@code val << amt}
|
||||
* @param amt the amt as in {@code val << amt}, in little-endian order
|
||||
* @return the value
|
||||
*/
|
||||
static long intLeft(int val, int[] amt) {
|
||||
if (Integer.compareUnsigned(amt[0], Integer.SIZE) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return val << amt[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_LEFT int_left} on JVM int with long amt.
|
||||
*
|
||||
|
@ -1170,6 +1418,98 @@ public interface JitCompiledPassage {
|
|||
return val << amt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on multi-precision ints.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#IUSHR iushr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val >> amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val >> amt}, in little-endian order
|
||||
*/
|
||||
static void intRight(int[] out, int outBytes, int[] val, int[] amt) {
|
||||
if (Integer.compareUnsigned(amt[0], outBytes * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MpShiftPrivate.ushr(out, val, amt[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on an mp-int with a JVM long shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#IUSHR iushr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val >> amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val >> amt}
|
||||
*/
|
||||
static void intRight(int[] out, int outBytes, int[] val, long amt) {
|
||||
if (Long.compareUnsigned(amt, (outBytes & MASK_I2UL) * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
MpShiftPrivate.ushr(out, val, (int) amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on an mp-int with a JVM int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#IUSHR iushr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val >> amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val >> amt}
|
||||
*/
|
||||
static void intRight(int[] out, int outBytes, int[] val, int amt) {
|
||||
if (Integer.compareUnsigned(amt, outBytes * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, 0);
|
||||
return;
|
||||
}
|
||||
MpShiftPrivate.ushr(out, val, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on a JVM long with an mp-int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#IUSHR iushr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param val the value as in {@code val >> amt}
|
||||
* @param amt the amt as in {@code val >> amt}, in little-endian order
|
||||
* @return the value
|
||||
*/
|
||||
static long intRight(long val, int[] amt) {
|
||||
if (Long.compareUnsigned(Integer.toUnsignedLong(amt[0]), Long.SIZE) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return val >>> amt[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on JVM longs.
|
||||
*
|
||||
|
@ -1206,6 +1546,30 @@ public interface JitCompiledPassage {
|
|||
return val >>> amt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on a JVM int with an mp-int shift
|
||||
* amount.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#IUSHR iushr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size clear the register.
|
||||
*
|
||||
* @param val the value as in {@code val >> amt}
|
||||
* @param amt the amt as in {@code val >> amt}, in little-endian order
|
||||
* @return the value
|
||||
*/
|
||||
static long intRight(int val, int[] amt) {
|
||||
if (Integer.compareUnsigned(amt[0], Integer.SIZE) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return val >>> amt[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_right} on JVM int with long amt.
|
||||
*
|
||||
|
@ -1242,6 +1606,34 @@ public interface JitCompiledPassage {
|
|||
return val >>> amt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_RIGHT int_sright} on multi-precision ints.
|
||||
*
|
||||
* <p>
|
||||
* The semantics here are subtly different than the JVM's {@link Opcodes#ISHR ishr}: 1) The
|
||||
* amount must be treated as unsigned. 2) Shifts in excess of val's size fill the register with
|
||||
* the sign bit.
|
||||
*
|
||||
* @param out the array to receive the output, in little-endian order
|
||||
* @param outBytes the actual size in bytes of the output operand
|
||||
* @param val the value as in {@code val s>> amt}, in little-endian order
|
||||
* @param amt the amt as in {@code val s>> amt}, in little-endian order
|
||||
*/
|
||||
static void intSRight(int[] out, int outBytes, int[] val, int[] amt) {
|
||||
int sign = val[val.length - 1] < 0 ? -1 : 0;
|
||||
if (Integer.compareUnsigned(amt[0], outBytes * Byte.SIZE) >= 0) {
|
||||
Arrays.fill(out, sign);
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < amt.length; i++) {
|
||||
if (amt[i] != 0) {
|
||||
Arrays.fill(out, sign);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MpShiftPrivate.sshr(out, val, amt[0], sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_SRIGHT int_sright} on JVM longs.
|
||||
*
|
||||
|
@ -1318,6 +1710,323 @@ public interface JitCompiledPassage {
|
|||
return val >> amt;
|
||||
}
|
||||
|
||||
static final long MASK_I2UL = 0x0000_0000_ffff_ffffL;
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_MULT} on mp-ints.
|
||||
* <p>
|
||||
* All arrays are in little-endian order
|
||||
*
|
||||
* @param out the array allocated to receive the output
|
||||
* @param inL the array of left input legs
|
||||
* @param inR the array of right input legs
|
||||
*/
|
||||
static void mpIntMultiply(int[] out, int[] inL, int[] inR) {
|
||||
long carry = 0;
|
||||
long rp = inR[0] & MASK_I2UL;
|
||||
for (int li = 0; li < inL.length && li < out.length; li++) {
|
||||
long lp = inL[li] & MASK_I2UL;
|
||||
carry += lp * rp;
|
||||
out[li] = (int) carry;
|
||||
carry >>>= Integer.SIZE;
|
||||
}
|
||||
|
||||
for (int ri = 1; ri < inR.length && ri < out.length; ri++) {
|
||||
carry = 0;
|
||||
rp = inR[ri] & MASK_I2UL;
|
||||
for (int li = 0; li < inL.length && ri + li < out.length; li++) {
|
||||
long lp = inL[li] & MASK_I2UL;
|
||||
long op = out[li + ri] & MASK_I2UL;
|
||||
carry += op + lp * rp;
|
||||
out[li + ri] = (int) carry;
|
||||
carry >>>= Integer.SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String mpToString(int[] legs) {
|
||||
if (legs == null) {
|
||||
return "null";
|
||||
}
|
||||
List<String> list = IntStream.of(legs).mapToObj(i -> "%08x".formatted(i)).toList();
|
||||
return list.reversed().stream().collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
enum MpDivPrivate {
|
||||
;
|
||||
|
||||
/**
|
||||
* Count the number of leading 0 bits in the first non-zero leg, and identify that leg's
|
||||
* index
|
||||
*
|
||||
* @param legs
|
||||
*/
|
||||
static int lz(int[] legs) {
|
||||
// Least-significant leg is first
|
||||
int count = 0;
|
||||
for (int i = legs.length - 1; i >= 0; i--) {
|
||||
int llz = Integer.numberOfLeadingZeros(legs[i]);
|
||||
count += llz;
|
||||
if (llz != Integer.SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int size(int[] legs) {
|
||||
// Least-significant leg is first
|
||||
for (int i = legs.length - 1; i >= 0; i--) {
|
||||
if (legs[i] != 0) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift the given mp-int legs left the given number of bits
|
||||
*
|
||||
* @param legs the legs
|
||||
* @param shift the number of bits to shift left
|
||||
*/
|
||||
static void shl(int[] legs, int shift) {
|
||||
if (shift == 0) {
|
||||
return; // The extra leading leg is already 0
|
||||
}
|
||||
assert shift >= 0 && shift < Integer.SIZE;
|
||||
|
||||
long carry = 0;
|
||||
for (int i = 0; i < legs.length; i++) {
|
||||
carry |= (legs[i] & MASK_I2UL) << shift;
|
||||
legs[i] = (int) carry;
|
||||
carry >>>= Integer.SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void shr(int[] legs, int shift) {
|
||||
if (shift == 0) {
|
||||
return;
|
||||
}
|
||||
assert shift >= 0 && shift < Integer.SIZE;
|
||||
|
||||
long carry = 0;
|
||||
for (int i = legs.length - 1; i >= 0; i--) {
|
||||
carry |= (legs[i] & MASK_I2UL) << (Integer.SIZE - shift);
|
||||
legs[i] = (int) (carry >> Integer.SIZE);
|
||||
carry <<= Integer.SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform unsigned division for a multi-precision dividend and single-precision divisor
|
||||
*
|
||||
* @param out the output for the quotient
|
||||
* @param inL the dividend, and the output for the remainder
|
||||
* @param sizeL the number of legs in the dividend
|
||||
* @param inR the divisor
|
||||
*/
|
||||
static void divideMpSp(int[] out, int[] inL, int sizeL, int inR) {
|
||||
long r = 0;
|
||||
for (int j = sizeL - 1; j >= 0; j--) {
|
||||
r <<= Integer.SIZE;
|
||||
r += inL[j] & MASK_I2UL;
|
||||
out[j] = (int) (r / inR);
|
||||
r %= inR;
|
||||
inL[j] = 0; // So that the mp-int inL is truly the remainder
|
||||
}
|
||||
inL[0] = (int) r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform unsigned division (or division of magnitudes)
|
||||
*
|
||||
* @param out the output for the quotient
|
||||
* @param inL the dividend, and the output for the remainder
|
||||
* @param inR the divisor
|
||||
* @implNote this is just Algorithm D from Knuth's TAOCP Volume 2 without any sophisticated
|
||||
* optimizations. We don't really need to optimize for the "big" case, we just
|
||||
* need to support the bigger-than-a-machine-word case.
|
||||
*/
|
||||
static void divide(int[] out, int[] inL, int[] inR) {
|
||||
/**
|
||||
* Before we mutate anything, compute lengths for D2. We'll compute sizeR from the
|
||||
* leading-zeroes computation in D1.
|
||||
*/
|
||||
int sizeL = size(inL);
|
||||
|
||||
/**
|
||||
* D1 [Normalize]
|
||||
*
|
||||
* My understanding of this step is to assure that the divisor (inR) has a 1 in the
|
||||
* most-significant bit of its most-significant leg ("digit" in the text's terminology).
|
||||
*/
|
||||
int shiftBits = lz(inR);
|
||||
|
||||
int truncR = shiftBits / Integer.SIZE;
|
||||
int sizeR = inR.length - truncR;
|
||||
|
||||
if (sizeR == 1) {
|
||||
/**
|
||||
* Never mind all this. Just do the simple algorithm (Exercise 16, in TAOCP Vol. 2,
|
||||
* Section 4.3.1). We actually can't use the full multi-precision algorithm, because
|
||||
* the adjustment of qHat in step D3 assumes the size of the divisor is >= 2 legs.
|
||||
*/
|
||||
divideMpSp(out, inL, sizeL, inR[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
int shift = shiftBits % Integer.SIZE;
|
||||
|
||||
shl(inL, shift);
|
||||
shl(inR, shift);
|
||||
|
||||
/**
|
||||
* D2 [Initialize j]
|
||||
*
|
||||
* What should be an <em>easy</em> step here is complicated by the fact that every
|
||||
* operand has to (conventionally) have equal size is Sleigh. Thus, we need to seek out
|
||||
* the most-significant leg with a non-zero value for each, and then compute j. Probably
|
||||
* need to avoid an off-by-one error here, too.
|
||||
*
|
||||
* dividend has size m + n "sizeL" (text calls dividend u_{m+n-1}...u_0)
|
||||
*
|
||||
* divisor has size n "sizeR" (text calls divisor v_{n-1}...v_0})
|
||||
*
|
||||
* Thus m = sizeL - sizeR
|
||||
*/
|
||||
for (int j = sizeL - sizeR; j >= 0; j--) { // step and test are D7
|
||||
/**
|
||||
* D3 [Calculate q\^]
|
||||
*/
|
||||
// NOTE That inL is over-provisioned by 1, so we're good to index m+n
|
||||
long qHat = (inL[sizeR + j] & MASK_I2UL) << Integer.SIZE;
|
||||
qHat |= inL[sizeR + j - 1] & MASK_I2UL;
|
||||
long rHat = qHat;
|
||||
long vNm1 = inR[sizeR - 1] & MASK_I2UL; // v_{n-1}
|
||||
qHat /= vNm1;
|
||||
rHat %= vNm1;
|
||||
|
||||
do {
|
||||
if (qHat == 1L << Integer.SIZE || Long.compareUnsigned(
|
||||
qHat * (inR[sizeR - 2] & MASK_I2UL),
|
||||
(rHat << Integer.SIZE) + (inL[sizeR + j - 2] & MASK_I2UL)) > 0) {
|
||||
qHat--;
|
||||
rHat += vNm1;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (Long.compareUnsigned(rHat, 1L << Integer.SIZE) < 0);
|
||||
|
||||
/**
|
||||
* D4 [Multiply and subtract]
|
||||
*
|
||||
* NOTE: borrow will become -1 if a borrow is needed, so add it to each subsequent
|
||||
* leg and use signed shift.
|
||||
*/
|
||||
long borrow = 0;
|
||||
for (int i = 0; i < sizeR - 1; i++) {
|
||||
borrow = (inL[j + i] & MASK_I2UL) - qHat * (inR[i] & MASK_I2UL) + borrow;
|
||||
inL[j + i] = (int) borrow;
|
||||
borrow >>= Integer.SIZE;
|
||||
}
|
||||
borrow = (inL[j + sizeR] & MASK_I2UL) + borrow;
|
||||
inL[j + sizeR] = (int) borrow;
|
||||
borrow >>= Integer.SIZE;
|
||||
|
||||
/**
|
||||
* D5 [Test remainder]
|
||||
*/
|
||||
if (borrow != 0) {
|
||||
assert borrow == -1;
|
||||
/**
|
||||
* D6 [Add back]
|
||||
*/
|
||||
qHat--;
|
||||
|
||||
long carry = 0;
|
||||
for (int i = 0; i < sizeR; i++) {
|
||||
carry += (inL[j + i] & MASK_I2UL) + inR[i];
|
||||
inL[j + i] = (int) carry;
|
||||
carry >>>= Integer.SIZE;
|
||||
}
|
||||
}
|
||||
out[j] = (int) qHat; // Completion of D5
|
||||
|
||||
/**
|
||||
* D7 [Loop on j]
|
||||
*
|
||||
* The step and test of the for loop that ends here implements D7
|
||||
*/
|
||||
}
|
||||
/**
|
||||
* D8 [Unnormalize]
|
||||
*/
|
||||
shr(inL, shift);
|
||||
}
|
||||
|
||||
static void neg(int[] legs, int count) {
|
||||
long carry = 1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
carry += (~legs[i] & MASK_I2UL);
|
||||
legs[i] = (int) carry;
|
||||
carry >>>= Integer.SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdivide(int[] out, int[] inL, int[] inR) {
|
||||
// NOTE: inL is over-provisioned by 1
|
||||
boolean signL = inL[inL.length - 2] < 0;
|
||||
boolean signR = inR[inR.length - 1] < 0;
|
||||
if (signL) {
|
||||
neg(inL, inL.length - 1);
|
||||
}
|
||||
if (signR) {
|
||||
neg(inR, inR.length);
|
||||
}
|
||||
divide(out, inL, inR);
|
||||
if (signL != signR) {
|
||||
neg(out, out.length);
|
||||
}
|
||||
if (signL) {
|
||||
neg(inL, inL.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_DIV} on mp-ints.
|
||||
* <p>
|
||||
* All arrays are in little-endian order. While this directly implements
|
||||
* {@link PcodeOp#INT_DIV}, it is also used for {@link PcodeOp#INT_REM},
|
||||
* {@link PcodeOp#INT_SDIV}, and {@link PcodeOp#INT_SREM}.
|
||||
*
|
||||
* @param out the array allocated to receive the quotient
|
||||
* @param inL the array of dividend input legs, over-provisioned by 1, which will also receive
|
||||
* the remainder
|
||||
* @param inR the array of divisor input legs
|
||||
*/
|
||||
static void mpIntDivide(int[] out, int[] inL, int[] inR) {
|
||||
MpDivPrivate.divide(out, inL, inR);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link PcodeOp#INT_SDIV} on mp-ints.
|
||||
* <p>
|
||||
* All arrays are in little-endian order. While this directly implements
|
||||
* {@link PcodeOp#INT_SDIV}, it is also used for {@link PcodeOp#INT_SREM}.
|
||||
*
|
||||
* @param out the array allocated to receive the quotient
|
||||
* @param inL the array of dividend input legs, over-provisioned by 1, which will also receive
|
||||
* the remainder
|
||||
* @param inR the array of divisor input legs
|
||||
*/
|
||||
static void mpIntSignedDivide(int[] out, int[] inL, int[] inR) {
|
||||
MpDivPrivate.sdivide(out, inL, inR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language for the given string language ID
|
||||
*
|
||||
|
|
|
@ -17,16 +17,17 @@ package ghidra.pcode.emu.jit.gen.type;
|
|||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||
import ghidra.pcode.emu.jit.analysis.*;
|
||||
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.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.op.BinOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.IntSExtOpGen;
|
||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
|
@ -61,6 +62,21 @@ import ghidra.program.model.pcode.PcodeOp;
|
|||
* </ul>
|
||||
*/
|
||||
public interface TypeConversions extends Opcodes {
|
||||
|
||||
/**
|
||||
* Kinds of extension
|
||||
*/
|
||||
enum Ext {
|
||||
/** Zero extension */
|
||||
ZERO,
|
||||
/** Sign extension */
|
||||
SIGN;
|
||||
|
||||
public static Ext forSigned(boolean signed) {
|
||||
return signed ? SIGN : ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an {@link Opcodes#IAND} to reduce the number of bits to those permitted in an int of the
|
||||
* given size.
|
||||
|
@ -72,12 +88,24 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void checkGenIntMask(JitType from, IntJitType to, MethodVisitor mv) {
|
||||
static void checkGenIntExt(JitType from, IntJitType to, Ext ext, MethodVisitor mv) {
|
||||
if (to.size() < from.size() && to.size() < Integer.BYTES) {
|
||||
mv.visitLdcInsn(-1 >>> (Integer.SIZE - to.size() * Byte.SIZE));
|
||||
mv.visitInsn(IAND);
|
||||
int shamt = Integer.SIZE - to.size() * Byte.SIZE;
|
||||
switch (ext) {
|
||||
case ZERO -> {
|
||||
mv.visitLdcInsn(-1 >>> shamt);
|
||||
mv.visitInsn(IAND);
|
||||
}
|
||||
case SIGN -> {
|
||||
mv.visitLdcInsn(shamt);
|
||||
mv.visitInsn(ISHL);
|
||||
mv.visitLdcInsn(shamt);
|
||||
mv.visitInsn(ISHR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,11 +114,12 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static IntJitType generateIntToInt(IntJitType from, IntJitType to, MethodVisitor mv) {
|
||||
checkGenIntMask(from, to, mv);
|
||||
static IntJitType generateIntToInt(IntJitType from, IntJitType to, Ext ext, MethodVisitor mv) {
|
||||
checkGenIntExt(from, to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -99,12 +128,14 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static IntJitType generateLongToInt(LongJitType from, IntJitType to, MethodVisitor mv) {
|
||||
static IntJitType generateLongToInt(LongJitType from, IntJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
mv.visitInsn(L2I);
|
||||
checkGenIntMask(from, to, mv);
|
||||
checkGenIntExt(from, to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -130,10 +161,12 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static IntJitType generateMpIntToInt(MpIntJitType from, IntJitType to, MethodVisitor mv) {
|
||||
static IntJitType generateMpIntToInt(MpIntJitType from, IntJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
if (to.size() == from.size()) {
|
||||
// We're done. The one leg on the stack becomes the int
|
||||
return to;
|
||||
|
@ -146,7 +179,7 @@ public interface TypeConversions extends Opcodes {
|
|||
mv.visitInsn(POP);
|
||||
// [...,legN]
|
||||
}
|
||||
checkGenIntMask(from, to, mv);
|
||||
checkGenIntExt(from, to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -158,16 +191,17 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static IntJitType generateToInt(JitType from, IntJitType to, MethodVisitor mv) {
|
||||
static IntJitType generateToInt(JitType from, IntJitType to, Ext ext, MethodVisitor mv) {
|
||||
return switch (from) {
|
||||
case IntJitType iFrom -> generateIntToInt(iFrom, to, mv); // in case of mask
|
||||
case LongJitType lFrom -> generateLongToInt(lFrom, to, mv);
|
||||
case IntJitType iFrom -> generateIntToInt(iFrom, to, ext, mv); // in case of ext
|
||||
case LongJitType lFrom -> generateLongToInt(lFrom, to, ext, mv);
|
||||
case FloatJitType fFrom -> generateFloatToInt(fFrom, to, mv);
|
||||
case DoubleJitType dFrom -> throw new AssertionError("Size mismatch");
|
||||
case MpIntJitType mpFrom -> generateMpIntToInt(mpFrom, to, mv);
|
||||
case MpIntJitType mpFrom -> generateMpIntToInt(mpFrom, to, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
@ -183,12 +217,24 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
*/
|
||||
static void checkGenLongMask(JitType from, LongJitType to, MethodVisitor mv) {
|
||||
static void checkGenLongExt(JitType from, LongJitType to, Ext ext, MethodVisitor mv) {
|
||||
if (to.size() < from.size()) {
|
||||
mv.visitLdcInsn(-1L >>> (Long.SIZE - to.size() * Byte.SIZE));
|
||||
mv.visitInsn(LAND);
|
||||
int shamt = Long.SIZE - to.size() * Byte.SIZE;
|
||||
switch (ext) {
|
||||
case ZERO -> {
|
||||
mv.visitLdcInsn(-1L >>> shamt);
|
||||
mv.visitInsn(LAND);
|
||||
}
|
||||
case SIGN -> {
|
||||
mv.visitLdcInsn(shamt);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitInsn(LSHL);
|
||||
mv.visitInsn(LSHR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,27 +246,37 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static LongJitType generateIntToLong(IntJitType from, LongJitType to, MethodVisitor mv) {
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "toUnsignedLong",
|
||||
MDESC_INTEGER__TO_UNSIGNED_LONG, false);
|
||||
static LongJitType generateIntToLong(IntJitType from, LongJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
switch (ext) {
|
||||
case ZERO -> mv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "toUnsignedLong",
|
||||
MDESC_INTEGER__TO_UNSIGNED_LONG, false);
|
||||
case SIGN -> {
|
||||
generateSExt(from, mv);
|
||||
mv.visitInsn(I2L);
|
||||
}
|
||||
}
|
||||
// In theory, never necessary, unless long is used temporarily with size 1-4.
|
||||
checkGenLongMask(from, to, mv);
|
||||
checkGenLongExt(from, to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to convert one p-code in (in a JVM long) to another
|
||||
* Emit bytecode to convert one p-code int (in a JVM long) to another
|
||||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static LongJitType generateLongToLong(LongJitType from, LongJitType to, MethodVisitor mv) {
|
||||
checkGenLongMask(from, to, mv);
|
||||
static LongJitType generateLongToLong(LongJitType from, LongJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
checkGenLongExt(from, to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -246,12 +302,14 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static LongJitType generateMpIntToLong(MpIntJitType from, LongJitType to, MethodVisitor mv) {
|
||||
static LongJitType generateMpIntToLong(MpIntJitType from, LongJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
if (from.legsAlloc() == 1) {
|
||||
generateIntToLong(IntJitType.forSize(from.size()), to, mv);
|
||||
generateIntToLong(IntJitType.forSize(from.size()), to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
// Remove all but the 2 least-significant legs
|
||||
|
@ -265,7 +323,7 @@ public interface TypeConversions extends Opcodes {
|
|||
// [...,legN-1,legN]
|
||||
}
|
||||
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "conv2IntToLong",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG, false);
|
||||
MDESC_JIT_COMPILED_PASSAGE__CONV_OFFSET2_TO_LONG, true);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -277,16 +335,17 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static LongJitType generateToLong(JitType from, LongJitType to, MethodVisitor mv) {
|
||||
static LongJitType generateToLong(JitType from, LongJitType to, Ext ext, MethodVisitor mv) {
|
||||
return switch (from) {
|
||||
case IntJitType iFrom -> generateIntToLong(iFrom, to, mv);
|
||||
case LongJitType lFrom -> generateLongToLong(lFrom, to, mv); // in case of mask
|
||||
case IntJitType iFrom -> generateIntToLong(iFrom, to, ext, mv);
|
||||
case LongJitType lFrom -> generateLongToLong(lFrom, to, ext, mv); // in case of mask
|
||||
case FloatJitType fFrom -> throw new AssertionError("Size mismatch");
|
||||
case DoubleJitType dFrom -> generateDoubleToLong(dFrom, to, mv);
|
||||
case MpIntJitType mpFrom -> generateMpIntToLong(mpFrom, to, mv);
|
||||
case MpIntJitType mpFrom -> generateMpIntToLong(mpFrom, to, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
@ -369,39 +428,63 @@ public interface TypeConversions extends Opcodes {
|
|||
*
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static MpIntJitType generateIntToMpInt(IntJitType from, MpIntJitType to, MethodVisitor mv) {
|
||||
static MpIntJitType generateIntToMpInt(IntJitType from, MpIntJitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
if (to.legsAlloc() == 1) {
|
||||
checkGenIntMask(from, IntJitType.forSize(to.size()), mv);
|
||||
checkGenIntExt(from, IntJitType.forSize(to.size()), ext, mv);
|
||||
return to;
|
||||
}
|
||||
// Insert as many more significant legs as needed
|
||||
for (int i = 1; i < to.legsAlloc(); i++) {
|
||||
mv.visitLdcInsn(0);
|
||||
mv.visitInsn(SWAP);
|
||||
// First, figure out what those additional legs should be
|
||||
// [lsl:INT]
|
||||
switch (ext) {
|
||||
case ZERO -> mv.visitLdcInsn(0); // [0:I,lsl:I]
|
||||
case SIGN -> {
|
||||
mv.visitInsn(DUP);
|
||||
// [lsl:INT,lsl:INT]
|
||||
mv.visitLdcInsn(Integer.SIZE - 1);
|
||||
// [31:INT,lsl:INT,lsl:INT]
|
||||
mv.visitInsn(ISHR);
|
||||
// [sign:INT,lsl:INT]
|
||||
}
|
||||
}
|
||||
// NB. Because "from" is the least-significant leg, I can just do this repeatedly.
|
||||
// Do two! less:
|
||||
// Start at 1, because the lsl is already present.
|
||||
// End 1 before total, because last op will be SWAP instead.
|
||||
for (int i = 1; i < to.legsAlloc() - 1; i++) {
|
||||
mv.visitInsn(DUP_X1);
|
||||
// [sign:INT,lsl:INT,sign:INT,...]
|
||||
}
|
||||
mv.visitInsn(SWAP);
|
||||
// [lsl:INT,sign:INT,sign:INT,...]
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit bytecode to convert a p-code int that its int a JVM long to multi-precision int.
|
||||
* Emit bytecode to convert a p-code int that is in a JVM long to multi-precision int.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static MpIntJitType generateLongToMpInt(LongJitType from, MpIntJitType to, MethodVisitor mv) {
|
||||
static MpIntJitType generateLongToMpInt(JitCodeGenerator gen, LongJitType from, MpIntJitType to,
|
||||
Ext ext, MethodVisitor mv) {
|
||||
if (to.legsAlloc() == 1) {
|
||||
mv.visitInsn(L2I);
|
||||
checkGenIntMask(from, IntJitType.forSize(to.size()), mv);
|
||||
checkGenIntExt(from, IntJitType.forSize(to.size()), ext, mv);
|
||||
return to;
|
||||
}
|
||||
if (from.size() <= Integer.BYTES) {
|
||||
mv.visitInsn(L2I);
|
||||
generateIntToMpInt(IntJitType.forSize(from.size()), to, mv);
|
||||
generateIntToMpInt(IntJitType.forSize(from.size()), to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
// Convert, then insert as many more significant legs as needed
|
||||
|
@ -413,29 +496,53 @@ public interface TypeConversions extends Opcodes {
|
|||
mv.visitLdcInsn(Integer.SIZE);
|
||||
mv.visitInsn(LUSHR);
|
||||
mv.visitInsn(L2I);
|
||||
/** This is the upper leg, which may need masking */
|
||||
checkGenIntMask(IntJitType.forSize(from.size() - Integer.BYTES),
|
||||
IntJitType.forSize(to.partialSize()), mv);
|
||||
// [val:LONG,msl:INT]
|
||||
mv.visitInsn(DUP_X2);
|
||||
// [msl:INT,val:LONG,msl:INT]
|
||||
mv.visitInsn(POP);
|
||||
// [msl:INT,val:LONG]
|
||||
mv.visitInsn(L2I);
|
||||
// [msl:INT,lsl:INT]
|
||||
|
||||
// Now add legs
|
||||
if (to.legsAlloc() > 2) {
|
||||
mv.visitLdcInsn(0);
|
||||
// [msl:INT,lsl:INT,0]
|
||||
for (int i = 2; i < to.legsAlloc(); i++) {
|
||||
// [msl:INT,lsl:INT,0]
|
||||
mv.visitInsn(DUP_X2);
|
||||
// [0,msl:INT,lsl:INT,0]
|
||||
/** This is the upper leg, which may need extending */
|
||||
if (to.size() < Long.BYTES) {
|
||||
checkGenIntExt(IntJitType.forSize(from.size() - Integer.BYTES),
|
||||
IntJitType.forSize(to.size() - Integer.BYTES), ext, mv);
|
||||
}
|
||||
|
||||
int tempCount = switch (ext) {
|
||||
case ZERO -> 0;
|
||||
case SIGN -> 1;
|
||||
};
|
||||
try (JvmTempAlloc sign = gen.getAllocationModel().allocateTemp(mv, "sign", tempCount)) {
|
||||
switch (ext) {
|
||||
case ZERO -> {
|
||||
}
|
||||
case SIGN -> {
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn(Integer.SIZE - 1);
|
||||
mv.visitInsn(ISHR);
|
||||
mv.visitVarInsn(ISTORE, sign.idx(0));
|
||||
}
|
||||
}
|
||||
// [...,0,msl:INT,lsl:INT,0]
|
||||
|
||||
// [val:LONG,msl:INT]
|
||||
mv.visitInsn(DUP_X2);
|
||||
// [msl:INT,val:LONG,msl:INT]
|
||||
mv.visitInsn(POP);
|
||||
// [...,0,msl:INT,lsl:INT]
|
||||
// [msl:INT,val:LONG]
|
||||
mv.visitInsn(L2I);
|
||||
// [msl:INT,lsl:INT]
|
||||
|
||||
// Now add legs
|
||||
if (to.legsAlloc() > 2) {
|
||||
switch (ext) {
|
||||
case ZERO -> mv.visitLdcInsn(0);
|
||||
case SIGN -> mv.visitVarInsn(ILOAD, sign.idx(0));
|
||||
}
|
||||
// [msl:INT,lsl:INT,sign:INT]
|
||||
for (int i = 2; i < to.legsAlloc(); i++) {
|
||||
// [msl:INT,lsl:INT,sign:INT]
|
||||
mv.visitInsn(DUP_X2);
|
||||
// [sign:INT,msl:INT,lsl:INT,sign:INT]
|
||||
}
|
||||
// [...,sign:INT,msl:INT,lsl:INT,sign:INT]
|
||||
mv.visitInsn(POP);
|
||||
// [...,sign:INT,msl:INT,lsl:INT]
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
@ -446,22 +553,23 @@ public interface TypeConversions extends Opcodes {
|
|||
* @param gen the code generator
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static MpIntJitType generateMpIntToMpInt(JitCodeGenerator gen, MpIntJitType from,
|
||||
MpIntJitType to, MethodVisitor mv) {
|
||||
MpIntJitType to, Ext ext, MethodVisitor mv) {
|
||||
if (to.size() == from.size()) {
|
||||
// Nothing to convert
|
||||
return to;
|
||||
}
|
||||
// Some special cases to avoid use of local variables:
|
||||
if (to.legsAlloc() == 1) {
|
||||
generateMpIntToInt(from, IntJitType.forSize(to.size()), mv);
|
||||
generateMpIntToInt(from, IntJitType.forSize(to.size()), ext, mv);
|
||||
return to;
|
||||
}
|
||||
if (from.legsAlloc() == 1) {
|
||||
generateIntToMpInt(IntJitType.forSize(from.size()), to, mv);
|
||||
generateIntToMpInt(IntJitType.forSize(from.size()), to, ext, mv);
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -469,42 +577,44 @@ public interface TypeConversions extends Opcodes {
|
|||
int legsIn = from.legsAlloc();
|
||||
int legsOut = to.legsAlloc();
|
||||
int localsCount = Integer.min(legsIn, legsOut);
|
||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
||||
Label localsStart = new Label();
|
||||
Label localsEnd = new Label();
|
||||
mv.visitLabel(localsStart);
|
||||
for (int i = 0; i < localsCount; i++) {
|
||||
mv.visitLocalVariable("temp" + i, Type.getDescriptor(int.class), null, localsStart,
|
||||
localsEnd, firstIndex + i);
|
||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
||||
}
|
||||
|
||||
// Add or remove legs
|
||||
int toAdd = legsOut - legsIn;
|
||||
for (int i = 0; i < toAdd; i++) {
|
||||
mv.visitLdcInsn(0);
|
||||
}
|
||||
int toRemove = legsIn - legsOut;
|
||||
for (int i = 0; i < toRemove; i++) {
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
try (JvmTempAlloc temp = gen.getAllocationModel().allocateTemp(mv, "temp", localsCount)) {
|
||||
for (int i = 0; i < localsCount; i++) {
|
||||
mv.visitVarInsn(ISTORE, temp.idx(i));
|
||||
}
|
||||
|
||||
// Start pushing them back, but the most significant may need masking
|
||||
int idx = firstIndex + localsCount;
|
||||
idx--;
|
||||
mv.visitVarInsn(ILOAD, idx);
|
||||
if (to.size() < from.size()) {
|
||||
checkGenIntMask(
|
||||
from, // already checked size, so anything greater
|
||||
IntJitType.forSize(to.partialSize()), mv);
|
||||
}
|
||||
// push the rest back
|
||||
for (int i = 0; i < localsCount; i++) {
|
||||
idx--;
|
||||
mv.visitVarInsn(ILOAD, idx);
|
||||
}
|
||||
// Add or remove legs
|
||||
int toAdd = legsOut - legsIn;
|
||||
if (toAdd >= 1) {
|
||||
switch (ext) {
|
||||
case ZERO -> mv.visitLdcInsn(0);
|
||||
case SIGN -> {
|
||||
mv.visitVarInsn(ILOAD, temp.idx(localsCount - 1));
|
||||
mv.visitLdcInsn(Integer.SIZE - 1);
|
||||
mv.visitInsn(ISHR);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < toAdd; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
}
|
||||
int toRemove = -toAdd;
|
||||
for (int i = 0; i < toRemove; i++) {
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
|
||||
mv.visitLabel(localsEnd);
|
||||
// Start pushing them back, but the most significant may need extending
|
||||
mv.visitVarInsn(ILOAD, temp.idx(localsCount - 1));
|
||||
if (to.size() < from.size()) {
|
||||
checkGenIntExt(
|
||||
from, // already checked size, so anything greater
|
||||
IntJitType.forSize(to.partialSize()), ext, mv);
|
||||
}
|
||||
// push the rest back
|
||||
for (int i = 1; i < localsCount; i++) {
|
||||
mv.visitVarInsn(ILOAD, temp.idx(localsCount - i - 1));
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -518,17 +628,18 @@ public interface TypeConversions extends Opcodes {
|
|||
* @param gen the code generator
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the destination type
|
||||
*/
|
||||
static MpIntJitType generateToMpInt(JitCodeGenerator gen, JitType from, MpIntJitType to,
|
||||
MethodVisitor mv) {
|
||||
Ext ext, MethodVisitor mv) {
|
||||
return switch (from) {
|
||||
case IntJitType iFrom -> generateIntToMpInt(iFrom, to, mv);
|
||||
case LongJitType lFrom -> generateLongToMpInt(lFrom, to, mv);
|
||||
case IntJitType iFrom -> generateIntToMpInt(iFrom, to, ext, mv);
|
||||
case LongJitType lFrom -> generateLongToMpInt(gen, lFrom, to, ext, mv);
|
||||
case FloatJitType fFrom -> throw new AssertionError("Size mismatch");
|
||||
case DoubleJitType dFrom -> throw new AssertionError("Size mismatch");
|
||||
case MpIntJitType mpFrom -> generateMpIntToMpInt(gen, mpFrom, to, mv);
|
||||
case MpIntJitType mpFrom -> generateMpIntToMpInt(gen, mpFrom, to, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
@ -543,16 +654,18 @@ public interface TypeConversions extends Opcodes {
|
|||
* @param gen the code generator
|
||||
* @param from the source type
|
||||
* @param to the destination type
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the resulting (destination) type
|
||||
*/
|
||||
static JitType generate(JitCodeGenerator gen, JitType from, JitType to, MethodVisitor mv) {
|
||||
static JitType generate(JitCodeGenerator gen, JitType from, JitType to, Ext ext,
|
||||
MethodVisitor mv) {
|
||||
return switch (to) {
|
||||
case IntJitType iTo -> generateToInt(from, iTo, mv);
|
||||
case LongJitType lTo -> generateToLong(from, lTo, mv);
|
||||
case IntJitType iTo -> generateToInt(from, iTo, ext, mv);
|
||||
case LongJitType lTo -> generateToLong(from, lTo, ext, mv);
|
||||
case FloatJitType fTo -> generateToFloat(from, fTo, mv);
|
||||
case DoubleJitType dTo -> generateToDouble(from, dTo, mv);
|
||||
case MpIntJitType mpTo -> generateToMpInt(gen, from, mpTo, mv);
|
||||
case MpIntJitType mpTo -> generateToMpInt(gen, from, mpTo, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
@ -695,9 +808,6 @@ public interface TypeConversions extends Opcodes {
|
|||
/**
|
||||
* Emit code to extend a signed value of the given type to fill its host JVM type.
|
||||
*
|
||||
* <p>
|
||||
* This is implemented in the same manner as {@link IntSExtOpGen int_sext}.
|
||||
*
|
||||
* @param type the p-code type
|
||||
* @param mv the method visitor
|
||||
* @return the p-code type that exactly fits the host JVM type, i.e., the resulting p-code type.
|
||||
|
@ -727,21 +837,6 @@ public interface TypeConversions extends Opcodes {
|
|||
return type.ext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a signed {@link IntJitType#I4 int4} to {@link LongJitType#I8 int8}.
|
||||
*
|
||||
* <p>
|
||||
* Note that if conversion from a smaller int type is needed, the generator must first call
|
||||
* {@link #generateSExt(JitType, MethodVisitor)}.
|
||||
*
|
||||
* @param mv the method visitor
|
||||
* @return the resulting type ({@link LongJitType#I8 int8})
|
||||
*/
|
||||
static LongJitType generateSExtIntToLong(MethodVisitor mv) {
|
||||
mv.visitInsn(I2L);
|
||||
return LongJitType.I8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the larger of two types and emit code to convert an unsigned value of the first type
|
||||
* to the host JVM type of the selected type.
|
||||
|
@ -769,59 +864,38 @@ public interface TypeConversions extends Opcodes {
|
|||
* generateBinOpRunCode} if we're using it. The two resulting types should now be equal, and we
|
||||
* can examine them and emit the correct bytecodes.
|
||||
*
|
||||
* @param gen the code generator
|
||||
* @param myType the type of an operand, probably in a binary operator
|
||||
* @param otherType the type of the other operand of a binary operator
|
||||
* @param ext whether the extension is signed or not
|
||||
* @param mv the method visitor
|
||||
* @return the new type of the operand
|
||||
*/
|
||||
static JitType forceUniformZExt(JitType myType, JitType otherType, MethodVisitor mv) {
|
||||
return switch (myType.ext()) {
|
||||
case IntJitType mt -> switch (otherType.ext()) {
|
||||
static JitType forceUniform(JitCodeGenerator gen, JitType myType, JitType otherType,
|
||||
Ext ext, MethodVisitor mv) {
|
||||
// TODO: Why was .ext() being used here (inconsistently, too)
|
||||
return switch (myType) {
|
||||
case IntJitType mt -> switch (otherType) {
|
||||
case IntJitType ot -> mt;
|
||||
case LongJitType ot -> generateIntToLong(mt, ot, mv);
|
||||
case MpIntJitType ot -> generateIntToMpInt(mt, MpIntJitType.forSize(mt.size()),
|
||||
mv);
|
||||
case LongJitType ot -> generateIntToLong(mt, ot, ext, mv);
|
||||
// FIXME: Would be nice to allow non-uniform mp-int sizes
|
||||
case MpIntJitType ot -> generateIntToMpInt(mt, ot, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case LongJitType mt -> switch (otherType) {
|
||||
case IntJitType ot -> mt; // Other operand needs up-conversion
|
||||
case LongJitType ot -> mt;
|
||||
case MpIntJitType ot -> generateLongToMpInt(mt, MpIntJitType.forSize(mt.size()),
|
||||
mv);
|
||||
// FIXME: Would be nice to allow non-uniform mp-int sizes
|
||||
case MpIntJitType ot -> generateLongToMpInt(gen, mt, ot, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpIntJitType mt -> mt; // Other may need up-conversion
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the same as {@link #forceUniformZExt(JitType, JitType, MethodVisitor)}, but with signed
|
||||
* values.
|
||||
*
|
||||
* @param myType the type of an operand, probably in a binary operator
|
||||
* @param otherType the type of the other operand of a binary operator
|
||||
* @param mv the method visitor
|
||||
* @return the new type of the operand
|
||||
*/
|
||||
static JitType forceUniformSExt(JitType myType, JitType otherType, MethodVisitor mv) {
|
||||
JitType myExtType = generateSExt(myType, mv);
|
||||
return switch (myExtType) {
|
||||
case IntJitType mt -> switch (otherType.ext()) { // Don't extend other, yet
|
||||
case IntJitType ot -> mt;
|
||||
case LongJitType ot -> generateSExtIntToLong(mv);
|
||||
case MpIntJitType ot -> generateIntToMpInt(mt, MpIntJitType.forSize(mt.size()),
|
||||
mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case LongJitType mt -> switch (otherType.ext()) {
|
||||
// FIXME: Would be nice to allow non-uniform mp-int sizes
|
||||
case MpIntJitType mt -> switch (otherType) {
|
||||
case IntJitType ot -> mt; // Other operand needs up-conversion
|
||||
case LongJitType ot -> mt;
|
||||
case MpIntJitType ot -> generateLongToMpInt(mt, MpIntJitType.forSize(mt.size()),
|
||||
mv);
|
||||
case LongJitType ot -> mt; // Other operand needs up-conversion
|
||||
case MpIntJitType ot -> generateMpIntToMpInt(gen, mt, ot, ext, mv);
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
case MpIntJitType mt -> mt; // Other may need up-conversion
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import ghidra.pcode.emu.jit.analysis.JitType;
|
|||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitConstVal;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +42,7 @@ public enum ConstValGen implements ValGen<JitConstVal> {
|
|||
|
||||
@Override
|
||||
public JitType generateValReadCode(JitCodeGenerator gen, JitConstVal v, JitTypeBehavior typeReq,
|
||||
MethodVisitor rv) {
|
||||
Ext ext, MethodVisitor rv) {
|
||||
JitType type = typeReq.resolve(gen.getTypeModel().typeOf(v));
|
||||
switch (type) {
|
||||
case IntJitType t -> rv.visitLdcInsn(v.value().intValue());
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitDirectMemoryVar;
|
||||
|
||||
/**
|
||||
|
@ -33,7 +34,7 @@ public enum DirectMemoryVarGen implements MemoryVarGen<JitDirectMemoryVar> {
|
|||
|
||||
@Override
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitDirectMemoryVar v, JitType type,
|
||||
MethodVisitor rv) {
|
||||
Ext ext, MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitFailVal;
|
||||
|
||||
/**
|
||||
|
@ -34,8 +35,8 @@ public enum FailValGen implements ValGen<JitFailVal> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JitType generateValReadCode(JitCodeGenerator gen, JitFailVal v,
|
||||
JitTypeBehavior typeReq, MethodVisitor rv) {
|
||||
public JitType generateValReadCode(JitCodeGenerator gen, JitFailVal v, JitTypeBehavior typeReq,
|
||||
Ext ext, MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitInputVar;
|
||||
|
||||
/**
|
||||
|
@ -32,7 +33,7 @@ public enum InputVarGen implements LocalVarGen<JitInputVar> {
|
|||
GEN;
|
||||
|
||||
@Override
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitInputVar v, JitType type,
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitInputVar v, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.VarHandler;
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitLocalOutVar;
|
||||
|
||||
/**
|
||||
|
@ -31,8 +32,8 @@ public enum LocalOutVarGen implements LocalVarGen<JitLocalOutVar> {
|
|||
|
||||
@Override
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitLocalOutVar v, JitType type,
|
||||
MethodVisitor rv) {
|
||||
Ext ext, MethodVisitor rv) {
|
||||
VarHandler handler = gen.getAllocationModel().getHandler(v);
|
||||
handler.generateStoreCode(gen, type, rv);
|
||||
handler.generateStoreCode(gen, type, ext, rv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import ghidra.pcode.emu.jit.analysis.JitAllocationModel.VarHandler;
|
|||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitVarnodeVar;
|
||||
|
||||
/**
|
||||
|
@ -41,11 +42,11 @@ public interface LocalVarGen<V extends JitVarnodeVar> extends VarGen<V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
default JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq,
|
||||
default JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
VarHandler handler = gen.getAllocationModel().getHandler(v);
|
||||
JitType type = typeReq.resolve(gen.getTypeModel().typeOf(v));
|
||||
handler.generateLoadCode(gen, type, rv);
|
||||
handler.generateLoadCode(gen, type, ext, rv);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.objectweb.asm.MethodVisitor;
|
|||
|
||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.var.JitMemoryOutVar;
|
||||
|
||||
/**
|
||||
|
@ -29,7 +30,7 @@ public enum MemoryOutVarGen implements MemoryVarGen<JitMemoryOutVar> {
|
|||
GEN;
|
||||
|
||||
@Override
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitMemoryOutVar v, JitType type,
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitMemoryOutVar v, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
VarGen.generateValWriteCodeDirect(gen, v, type, rv);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
|||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypedAccessGen;
|
||||
import ghidra.pcode.emu.jit.var.JitVarnodeVar;
|
||||
|
||||
|
@ -41,7 +42,7 @@ public interface MemoryVarGen<V extends JitVarnodeVar> extends VarGen<V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
default JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq,
|
||||
default JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
return VarGen.generateValReadCodeDirect(gen, v, typeReq, rv);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.objectweb.asm.Opcodes;
|
|||
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.JitPhiOp;
|
||||
import ghidra.pcode.emu.jit.var.JitMissingVar;
|
||||
|
||||
|
@ -61,7 +62,7 @@ public enum MissingVarGen implements VarGen<JitMissingVar> {
|
|||
|
||||
@Override
|
||||
public JitType generateValReadCode(JitCodeGenerator gen, JitMissingVar v,
|
||||
JitTypeBehavior typeReq, MethodVisitor rv) {
|
||||
JitTypeBehavior typeReq, Ext ext, MethodVisitor rv) {
|
||||
// [...]
|
||||
rv.visitTypeInsn(NEW, NAME_ASSERTION_ERROR);
|
||||
// [...,error:NEW]
|
||||
|
@ -79,7 +80,7 @@ public enum MissingVarGen implements VarGen<JitMissingVar> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitMissingVar v, JitType type,
|
||||
public void generateVarWriteCode(JitCodeGenerator gen, JitMissingVar v, JitType type, Ext ext,
|
||||
MethodVisitor rv) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import ghidra.pcode.emu.jit.analysis.JitType.SimpleJitType;
|
|||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.op.*;
|
||||
import ghidra.pcode.emu.jit.var.*;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
@ -143,9 +144,10 @@ public interface ValGen<V extends JitVal> {
|
|||
* @param gen the code generator
|
||||
* @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
|
||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||
* @return the actual p-code type (which determines the JVM type) of the value on the stack
|
||||
*/
|
||||
JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq,
|
||||
JitType generateValReadCode(JitCodeGenerator gen, V v, JitTypeBehavior typeReq, Ext ext,
|
||||
MethodVisitor rv);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
|||
import ghidra.pcode.emu.jit.gen.op.CBranchOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.op.CallOtherOpGen;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||
import ghidra.pcode.emu.jit.gen.type.TypedAccessGen;
|
||||
import ghidra.pcode.emu.jit.var.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -307,7 +308,8 @@ public interface VarGen<V extends JitVar> extends ValGen<V> {
|
|||
* @param v the variable to write
|
||||
* @param type the p-code type (which also determines the expected JVM type) of the value on the
|
||||
* stack
|
||||
* @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 generateVarWriteCode(JitCodeGenerator gen, V v, JitType type, MethodVisitor rv);
|
||||
void generateVarWriteCode(JitCodeGenerator gen, V v, JitType type, Ext ext, MethodVisitor rv);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.pcode.emu.jit.op;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowState.MiniDFState;
|
||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||
|
@ -39,6 +40,10 @@ public record JitCallOtherDefOp(PcodeOp op, JitOutVar out, JitTypeBehavior type,
|
|||
PcodeUseropDefinition<Object> userop, List<JitVal> args, List<JitTypeBehavior> inputTypes,
|
||||
MiniDFState dfState) implements JitCallOtherOpIf, JitDefOp {
|
||||
|
||||
public JitCallOtherDefOp {
|
||||
Objects.requireNonNull(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeRemoved() {
|
||||
return JitCallOtherOpIf.super.canBeRemoved() && JitDefOp.super.canBeRemoved();
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.lang.invoke.MethodHandle;
|
|||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -81,7 +82,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
opdef.posLib = pos;
|
||||
}
|
||||
},
|
||||
OUTPUT(OpOutput.class, Varnode.class) {
|
||||
OUTPUT(OpOutput.class, Varnode.class, int[].class) {
|
||||
@Override
|
||||
int getPos(AnnotatedPcodeUseropDefinition<?> opdef) {
|
||||
return opdef.posOut;
|
||||
|
@ -115,11 +116,11 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
}
|
||||
|
||||
private final Class<? extends Annotation> annotCls;
|
||||
private final Class<?> paramCls;
|
||||
private final List<Class<?>> allowedClsList;
|
||||
|
||||
private ParamAnnotProc(Class<? extends Annotation> annotCls, Class<?> paramCls) {
|
||||
private ParamAnnotProc(Class<? extends Annotation> annotCls, Class<?>... paramCls) {
|
||||
this.annotCls = annotCls;
|
||||
this.paramCls = paramCls;
|
||||
this.allowedClsList = List.of(paramCls);
|
||||
}
|
||||
|
||||
abstract int getPos(AnnotatedPcodeUseropDefinition<?> opdef);
|
||||
|
@ -130,7 +131,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
return p.getAnnotation(annotCls) != null;
|
||||
}
|
||||
|
||||
Type getArgumentType(Type opType) {
|
||||
static Type parameterize(Class<?> paramCls, Type opType) {
|
||||
TypeVariable<?>[] typeParams = paramCls.getTypeParameters();
|
||||
if (typeParams.length == 0) {
|
||||
return paramCls;
|
||||
|
@ -141,6 +142,25 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
throw new AssertionError();
|
||||
}
|
||||
|
||||
String nameAllowedArgumentTypes(Type opType) {
|
||||
return allowedClsList.stream()
|
||||
.map(cls -> parameterize(cls, opType).toString())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
record MatchedClassWithArgs(Class<?> paramCls, Map<TypeVariable<?>, Type> typeArgs) {
|
||||
static MatchedClassWithArgs find(Type paramType, List<Class<?>> allowed) {
|
||||
for (Class<?> cls : allowed) {
|
||||
Map<TypeVariable<?>, Type> typeArgs =
|
||||
TypeUtils.getTypeArguments(paramType, cls);
|
||||
if (typeArgs != null) {
|
||||
return new MatchedClassWithArgs(cls, typeArgs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void processParameterPerAnnot(AnnotatedPcodeUseropDefinition<?> opdef, Type declClsOpType,
|
||||
int i, Parameter p) {
|
||||
if (getPos(opdef) != -1) {
|
||||
|
@ -148,20 +168,21 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
"Can only have one parameter with @" + annotCls.getSimpleName());
|
||||
}
|
||||
Type pType = p.getParameterizedType();
|
||||
Map<TypeVariable<?>, Type> typeArgs = TypeUtils.getTypeArguments(pType, paramCls);
|
||||
if (typeArgs == null) {
|
||||
MatchedClassWithArgs match = MatchedClassWithArgs.find(pType, allowedClsList);
|
||||
if (match == null) {
|
||||
throw new IllegalArgumentException("Parameter " + p.getName() + " with @" +
|
||||
annotCls.getSimpleName() + " must acccept " + getArgumentType(declClsOpType));
|
||||
annotCls.getSimpleName() + " must acccept " +
|
||||
nameAllowedArgumentTypes(declClsOpType));
|
||||
}
|
||||
if (typeArgs.isEmpty()) {
|
||||
if (match.typeArgs.isEmpty()) {
|
||||
// Nothing
|
||||
}
|
||||
else if (typeArgs.size() == 1) {
|
||||
Type declMthOpType = typeArgs.get(paramCls.getTypeParameters()[0]);
|
||||
else if (match.typeArgs.size() == 1) {
|
||||
Type declMthOpType = match.typeArgs.get(match.paramCls.getTypeParameters()[0]);
|
||||
if (!Objects.equals(declClsOpType, declMthOpType)) {
|
||||
throw new IllegalArgumentException("Parameter " + p.getName() + " with @" +
|
||||
annotCls.getSimpleName() + " must acccept " +
|
||||
getArgumentType(declClsOpType));
|
||||
nameAllowedArgumentTypes(declClsOpType));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -324,6 +345,14 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
return canInline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
if (posOut == -1) {
|
||||
return method.getReturnType();
|
||||
}
|
||||
return method.getParameterTypes()[posOut];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getJavaMethod() {
|
||||
return method;
|
||||
|
@ -421,6 +450,23 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
}
|
||||
}
|
||||
|
||||
record IntArrayUseropInputParam(int position) implements UseropInputParam {
|
||||
@Override
|
||||
public <T> Object convert(Varnode vn, PcodeExecutor<T> executor) {
|
||||
PcodeExecutorStatePiece<T, T> state = executor.getState();
|
||||
PcodeArithmetic<T> arithmetic = executor.getArithmetic();
|
||||
BigInteger value =
|
||||
arithmetic.toBigInteger(state.getVar(vn, executor.getReason()), Purpose.OTHER);
|
||||
int[] result = new int[(vn.getSize() + 3) / 4];
|
||||
// This is terribly slow
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = value.intValue();
|
||||
value = value.shiftRight(Integer.SIZE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
record FloatUseropInputParam(int position) implements UseropInputParam {
|
||||
@Override
|
||||
public <T> Object convert(Varnode vn, PcodeExecutor<T> executor) {
|
||||
|
@ -482,6 +528,9 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
else if (pType == long.class) {
|
||||
paramsIn.add(new LongUseropInputParam(i));
|
||||
}
|
||||
else if (pType == int[].class) {
|
||||
paramsIn.add(new IntArrayUseropInputParam(i));
|
||||
}
|
||||
else if (pType == float.class) {
|
||||
paramsIn.add(new FloatUseropInputParam(i));
|
||||
}
|
||||
|
@ -735,7 +784,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||
* An annotation to receive the output varnode into a parameter
|
||||
*
|
||||
* <p>
|
||||
* The annotated parameter must have type {@link Varnode}.
|
||||
* The annotated parameter must have type {@link Varnode} (or {@code int[]} for direct
|
||||
* invocation when the output is a multi-precision integer}).
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
|||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.OpOutput;
|
||||
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.PcodeUserop;
|
||||
import ghidra.pcodeCPort.slghsymbol.UserOpSymbol;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
@ -219,6 +220,17 @@ public interface PcodeUseropLibrary<T> {
|
|||
*/
|
||||
boolean canInlinePcode();
|
||||
|
||||
/**
|
||||
* If this userop is defined as a java callback, get the type of the output
|
||||
*
|
||||
* <p>
|
||||
* If the method has a {@code @}{@link OpOutput} annotation, this is the type of the output
|
||||
* parameter. Otherwise, this is the method's return type.
|
||||
*
|
||||
* @return the output type
|
||||
*/
|
||||
Class<?> getOutputType();
|
||||
|
||||
/**
|
||||
* If this userop is defined as a java callback, get the method
|
||||
*
|
||||
|
|
|
@ -221,6 +221,11 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return void.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getJavaMethod() {
|
||||
return null;
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -27,38 +27,12 @@ public class OpBehaviorPopcount extends UnaryOpBehavior {
|
|||
|
||||
@Override
|
||||
public long evaluateUnary(int sizeout, int sizein, long val) {
|
||||
val = (val & 0x5555555555555555L) + ((val >>> 1) & 0x5555555555555555L);
|
||||
val = (val & 0x3333333333333333L) + ((val >>> 2) & 0x3333333333333333L);
|
||||
val = (val & 0x0f0f0f0f0f0f0f0fL) + ((val >>> 4) & 0x0f0f0f0f0f0f0f0fL);
|
||||
val = (val & 0x00ff00ff00ff00ffL) + ((val >>> 8) & 0x00ff00ff00ff00ffL);
|
||||
val = (val & 0x0000ffff0000ffffL) + ((val >>> 16) & 0x0000ffff0000ffffL);
|
||||
int res = (int) (val & 0xff);
|
||||
res += (int) ((val >> 32) & 0xff);
|
||||
return res;
|
||||
return Long.bitCount(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger evaluateUnary(int sizeout, int sizein, BigInteger unsignedIn1) {
|
||||
int bitcount = 0;
|
||||
while (sizein >= 8) {
|
||||
bitcount += evaluateUnary(1, 8, unsignedIn1.longValue());
|
||||
sizein -= 8;
|
||||
if (sizein == 0) {
|
||||
break;
|
||||
}
|
||||
unsignedIn1 = unsignedIn1.shiftRight(64);
|
||||
}
|
||||
if (sizein > 0) {
|
||||
long mask = sizein * 8 - 1;
|
||||
bitcount += evaluateUnary(1, 8, unsignedIn1.longValue() & mask);
|
||||
}
|
||||
if (sizeout == 1) {
|
||||
bitcount &= 0xff;
|
||||
}
|
||||
else if (sizeout == 2) {
|
||||
bitcount &= 0xffff;
|
||||
}
|
||||
return BigInteger.valueOf(bitcount);
|
||||
return BigInteger.valueOf(unsignedIn1.bitCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.GhidraTestApplicationLayout;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
|
||||
@Ignore("For developer workstation")
|
||||
public class JitMpIntPerformanceExperiment {
|
||||
public static final int N = 100_000_000;
|
||||
public static final BigInteger MASK_16_BYTES =
|
||||
BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE);
|
||||
private static SleighLanguage toy;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
Application.initializeApplication(
|
||||
new GhidraTestApplicationLayout(new File(AbstractGenericTest.getTestDirectoryPath())),
|
||||
new ApplicationConfiguration());
|
||||
|
||||
toy = (SleighLanguage) DefaultLanguageService.getLanguageService()
|
||||
.getLanguage(new LanguageID("Toy:BE:64:default"));
|
||||
Assemblers.getAssembler(toy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedBigInteger() {
|
||||
BigInteger previous = BigInteger.ZERO;
|
||||
BigInteger current = BigInteger.ONE;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
BigInteger next = previous.add(current);
|
||||
next = next.and(MASK_16_BYTES);
|
||||
previous = current;
|
||||
current = next;
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %s".formatted(N, current.toString(16)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedIntArray() {
|
||||
int[] previous = new int[] { 0, 0, 0, 0 };
|
||||
int[] current = new int[] { 1, 0, 0, 0 };
|
||||
int[] next = new int[4];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
doArrAdd(next, previous, current);
|
||||
int[] temp = previous;
|
||||
previous = current;
|
||||
current = next;
|
||||
next = temp;
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
current[3], current[2], current[1], current[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedIntArrayFinalLoop() {
|
||||
final int[] previous = new int[] { 0, 0, 0, 0 };
|
||||
final int[] current = new int[] { 1, 0, 0, 0 };
|
||||
final int[] next = new int[4];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
doArrAdd(next, previous, current);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
previous[j] = current[j];
|
||||
current[j] = next[j];
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
current[3], current[2], current[1], current[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedIntArrayFinalArrayCopy() {
|
||||
final int[] previous = new int[] { 0, 0, 0, 0 };
|
||||
final int[] current = new int[] { 1, 0, 0, 0 };
|
||||
final int[] next = new int[4];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
doArrAdd(next, previous, current);
|
||||
System.arraycopy(current, 0, previous, 0, 4);
|
||||
System.arraycopy(next, 0, current, 0, 4);
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
current[3], current[2], current[1], current[0]));
|
||||
}
|
||||
|
||||
private static void doArrAdd(final int[] result, final int[] a, final int[] b) {
|
||||
long t = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
t += Integer.toUnsignedLong(a[i]);
|
||||
t += Integer.toUnsignedLong(b[i]);
|
||||
result[i] = (int) t;
|
||||
t >>>= 32;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedIntArrayInlined() {
|
||||
int[] previous = new int[] { 0, 0, 0, 0 };
|
||||
int[] current = new int[] { 1, 0, 0, 0 };
|
||||
int[] next = new int[4];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
long t = 0;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
t += Integer.toUnsignedLong(previous[j]);
|
||||
t += Integer.toUnsignedLong(current[j]);
|
||||
next[j] = (int) t;
|
||||
t >>>= 32;
|
||||
}
|
||||
|
||||
int[] temp = previous;
|
||||
previous = current;
|
||||
current = next;
|
||||
next = temp;
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
current[3], current[2], current[1], current[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedScalarized() {
|
||||
int prev0 = 0, prev1 = 0, prev2 = 0, prev3 = 0;
|
||||
int curr0 = 1, curr1 = 0, curr2 = 0, curr3 = 0;
|
||||
int next0, next1, next2, next3;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
long t = 0;
|
||||
t += Integer.toUnsignedLong(prev0);
|
||||
t += Integer.toUnsignedLong(curr0);
|
||||
next0 = (int) t;
|
||||
t >>>= 32;
|
||||
t += Integer.toUnsignedLong(prev1);
|
||||
t += Integer.toUnsignedLong(curr1);
|
||||
next1 = (int) t;
|
||||
t >>>= 32;
|
||||
t += Integer.toUnsignedLong(prev2);
|
||||
t += Integer.toUnsignedLong(curr2);
|
||||
next2 = (int) t;
|
||||
t >>>= 32;
|
||||
t += Integer.toUnsignedLong(prev3);
|
||||
t += Integer.toUnsignedLong(curr3);
|
||||
next3 = (int) t;
|
||||
|
||||
prev0 = curr0;
|
||||
prev1 = curr1;
|
||||
prev2 = curr2;
|
||||
prev3 = curr3;
|
||||
|
||||
curr0 = next0;
|
||||
curr1 = next1;
|
||||
curr2 = next2;
|
||||
curr3 = next3;
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
curr3, curr2, curr1, curr0));
|
||||
}
|
||||
|
||||
static final long LONG_MASK = 0xffffffffL;
|
||||
|
||||
@Test
|
||||
public void testSpeedScalarizedAnd() {
|
||||
int prev0 = 0, prev1 = 0, prev2 = 0, prev3 = 0;
|
||||
int curr0 = 1, curr1 = 0, curr2 = 0, curr3 = 0;
|
||||
int next0, next1, next2, next3;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
long t = 0;
|
||||
t += prev0 & LONG_MASK;
|
||||
t += curr0 & LONG_MASK;
|
||||
next0 = (int) t;
|
||||
t >>>= 32;
|
||||
t += prev1 & LONG_MASK;
|
||||
t += curr1 & LONG_MASK;
|
||||
next1 = (int) t;
|
||||
t >>>= 32;
|
||||
t += prev2 & LONG_MASK;
|
||||
t += curr2 & LONG_MASK;
|
||||
next2 = (int) t;
|
||||
t >>>= 32;
|
||||
t += prev3 & LONG_MASK;
|
||||
t += curr3 & LONG_MASK;
|
||||
next3 = (int) t;
|
||||
|
||||
prev0 = curr0;
|
||||
prev1 = curr1;
|
||||
prev2 = curr2;
|
||||
prev3 = curr3;
|
||||
|
||||
curr0 = next0;
|
||||
curr1 = next1;
|
||||
curr2 = next2;
|
||||
curr3 = next3;
|
||||
}
|
||||
|
||||
System.out.println("fib(%d) = %08x%08x%08x%08x".formatted(N,
|
||||
curr3, curr2, curr1, curr0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedPlainEmu() {
|
||||
String sleigh = """
|
||||
counter:4 = 0;
|
||||
prev:16 = 0;
|
||||
curr:16 = 1;
|
||||
<loop>
|
||||
next:16 = prev + curr;
|
||||
prev = curr;
|
||||
curr = next;
|
||||
counter = counter + 1;
|
||||
if (counter < 0x%08x) goto <loop>;
|
||||
r0 = curr(0);
|
||||
r1 = curr(8);
|
||||
goto 0xdeadbeef;
|
||||
""".formatted(N);
|
||||
|
||||
PcodeEmulator emu = new PcodeEmulator(toy);
|
||||
PcodeThread<byte[]> thread = emu.newThread();
|
||||
|
||||
Address address = toy.getDefaultSpace().getAddress(0x00400000);
|
||||
thread.inject(address, sleigh);
|
||||
thread.overrideCounter(address);
|
||||
thread.reInitialize();
|
||||
|
||||
try {
|
||||
thread.run();
|
||||
}
|
||||
catch (DecodePcodeExecutionException e) {
|
||||
if (e.getProgramCounter().getOffset() != 0xdeadbeef) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
System.out.println("fib(%d) = %016x%016x".formatted(N,
|
||||
thread.getArithmetic()
|
||||
.toLong(thread.getState().getVar(toy.getRegister("r1"), Reason.INSPECT),
|
||||
Purpose.INSPECT),
|
||||
thread.getArithmetic()
|
||||
.toLong(thread.getState().getVar(toy.getRegister("r0"), Reason.INSPECT),
|
||||
Purpose.INSPECT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpeedJitEmu() {
|
||||
String sleigh = """
|
||||
counter:4 = 0;
|
||||
prev:16 = 0;
|
||||
curr:16 = 1;
|
||||
<loop>
|
||||
next:16 = prev + curr;
|
||||
prev = curr;
|
||||
curr = next;
|
||||
counter = counter + 1;
|
||||
if (counter < 0x%08x) goto <loop>;
|
||||
r0 = curr(0);
|
||||
r1 = curr(8);
|
||||
goto 0xdeadbeef;
|
||||
""".formatted(N);
|
||||
|
||||
JitPcodeEmulator emu =
|
||||
new JitPcodeEmulator(toy, new JitConfiguration(), MethodHandles.lookup());
|
||||
JitPcodeThread thread = emu.newThread();
|
||||
|
||||
Address address = toy.getDefaultSpace().getAddress(0x00400000);
|
||||
thread.inject(address, sleigh);
|
||||
thread.overrideCounter(address);
|
||||
thread.reInitialize();
|
||||
|
||||
try {
|
||||
thread.run();
|
||||
}
|
||||
catch (DecodePcodeExecutionException e) {
|
||||
if (e.getProgramCounter().getOffset() != 0xdeadbeef) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
System.out.println("fib(%d) = %016x%016x".formatted(N,
|
||||
thread.getArithmetic()
|
||||
.toLong(thread.getState().getVar(toy.getRegister("r1"), Reason.INSPECT),
|
||||
Purpose.INSPECT),
|
||||
thread.getArithmetic()
|
||||
.toLong(thread.getState().getVar(toy.getRegister("r0"), Reason.INSPECT),
|
||||
Purpose.INSPECT)));
|
||||
}
|
||||
}
|
|
@ -164,7 +164,7 @@ public class Varnode {
|
|||
if (spaceID != varnode.spaceID) {
|
||||
return false;
|
||||
}
|
||||
if (isConstant() || isUnique() || isHash()) {
|
||||
if (isConstant() || isHash()) {
|
||||
// this is not really a valid use case
|
||||
return offset == varnode.getOffset();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,122 @@
|
|||
/* ###
|
||||
* 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.tgt;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.MpDivPrivate;
|
||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.MpShiftPrivate;
|
||||
|
||||
public class JitRuntimeLibraryTest {
|
||||
|
||||
int[] intsLE(int... legs) {
|
||||
ArrayUtils.reverse(legs);
|
||||
return legs;
|
||||
}
|
||||
|
||||
String mpToString(int[] legs) {
|
||||
List<String> strs = IntStream.of(legs).mapToObj(i -> "%08x".formatted(i)).toList();
|
||||
return strs.reversed().stream().collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
void assertMpEquals(int[] expected, int[] actual) {
|
||||
assertEquals(mpToString(expected), mpToString(actual));
|
||||
}
|
||||
|
||||
int[] out(int size, Consumer<int[]> func) {
|
||||
int[] out = new int[size];
|
||||
func.accept(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpShiftPrivateShl() {
|
||||
assertMpEquals(intsLE(0x89abcdef, 0xfedcba98, 0x76543210, 0x00000000),
|
||||
out(4, o -> MpShiftPrivate.shl(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 32)));
|
||||
assertMpEquals(intsLE(0x9abcdeff, 0xedcba987, 0x65432100, 0x00000000),
|
||||
out(4, o -> MpShiftPrivate.shl(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
assertMpEquals(intsLE(0xedcba987, 0x65432100, 0x00000000),
|
||||
out(3, o -> MpShiftPrivate.shl(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
assertMpEquals(intsLE(0x00000000, 0x9abcdeff, 0xedcba987, 0x65432100, 0x00000000),
|
||||
out(5, o -> MpShiftPrivate.shl(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpShiftPrivateUshr() {
|
||||
assertMpEquals(intsLE(0x00000000, 0x01234567, 0x89abcdef, 0xfedcba98),
|
||||
out(4, o -> MpShiftPrivate.ushr(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 32)));
|
||||
assertMpEquals(intsLE(0x00000000, 0x00123456, 0x789abcde, 0xffedcba9),
|
||||
out(4, o -> MpShiftPrivate.ushr(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
assertMpEquals(intsLE(0x00123456, 0x789abcde, 0xffedcba9),
|
||||
out(3, o -> MpShiftPrivate.ushr(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
assertMpEquals(intsLE(0x00000000, 0x00000000, 0x00123456, 0x789abcde, 0xffedcba9),
|
||||
out(5, o -> MpShiftPrivate.ushr(o,
|
||||
intsLE(0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210), 36)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpDivPrivateLz() {
|
||||
assertEquals(0, MpDivPrivate.lz(intsLE(-1)));
|
||||
assertEquals(1, MpDivPrivate.lz(intsLE(Integer.MAX_VALUE)));
|
||||
assertEquals(64, MpDivPrivate.lz(intsLE(0, 0, -1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpDivPrivateShl() {
|
||||
int[] ints = intsLE(0xffffffff, 0x12345678);
|
||||
MpDivPrivate.shl(ints, 4);
|
||||
assertMpEquals(intsLE(0xfffffff1, 0x23456780), ints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpDivPrivateShr() {
|
||||
int[] ints = intsLE(0x12345678, 0xffffffff);
|
||||
MpDivPrivate.shr(ints, 4);
|
||||
assertMpEquals(intsLE(0x01234567, 0x8fffffff), ints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpDivPrivateNeg() {
|
||||
int[] ints;
|
||||
|
||||
ints = intsLE(0x00000000, 0x00000000);
|
||||
MpDivPrivate.neg(ints, 2);
|
||||
assertMpEquals(intsLE(0x00000000, 0x00000000), ints);
|
||||
|
||||
ints = intsLE(0x80000000, 0x00000000);
|
||||
MpDivPrivate.neg(ints, 2);
|
||||
assertMpEquals(intsLE(0x80000000, 0x00000000), ints);
|
||||
|
||||
ints = intsLE(0xffffffff, 0xffff0000);
|
||||
MpDivPrivate.neg(ints, 2);
|
||||
assertMpEquals(intsLE(0x00000000, 0x00010000), ints);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue