Moved rest of codegen to translator class.
This commit is contained in:
@@ -12,7 +12,6 @@ import li.cil.circuity.api.vm.device.memory.PhysicalMemory;
|
||||
import li.cil.circuity.api.vm.device.memory.Sizes;
|
||||
import li.cil.circuity.api.vm.device.rtc.RealTimeCounter;
|
||||
import li.cil.circuity.vm.BitUtils;
|
||||
import li.cil.circuity.vm.UnsafeGetter;
|
||||
import li.cil.circuity.vm.device.memory.exception.*;
|
||||
import li.cil.circuity.vm.riscv.dbt.Trace;
|
||||
import li.cil.circuity.vm.riscv.dbt.Translator;
|
||||
@@ -22,8 +21,6 @@ import li.cil.circuity.vm.riscv.exception.R5Exception;
|
||||
import li.cil.circuity.vm.riscv.exception.R5IllegalInstructionException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.*;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -51,7 +48,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
public class R5CPU implements Steppable, RealTimeCounter, InterruptController {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Unsafe UNSAFE = UnsafeGetter.get();
|
||||
|
||||
public static final int PC_INIT = 0x1000; // Initial position of program counter.
|
||||
|
||||
@@ -149,72 +145,6 @@ public class R5CPU implements Steppable, RealTimeCounter, InterruptController {
|
||||
private final HotTrace[] hotTraces = new HotTrace[HOT_TRACE_COUNT];
|
||||
private final Int2ObjectMap<Trace> traces = new Int2ObjectOpenHashMap<>(EXPECTED_MAX_TRACE_COUNT);
|
||||
|
||||
// Bytecode generation.
|
||||
private static final String TRACE_EXECUTE_NAME = "execute";
|
||||
private static final String TRACE_EXECUTE_DESC = "(Lli/cil/circuity/vm/riscv/R5CPU;)V";
|
||||
private static final Type OBJECT_TYPE = Type.getType(Object.class);
|
||||
private static final Type TRACE_TYPE = Type.getType(Trace.class);
|
||||
private static final org.objectweb.asm.commons.Method INIT_VOID = org.objectweb.asm.commons.Method.getMethod("void <init> ()");
|
||||
|
||||
private Trace translateTrace(final int pc) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
cw.visit(Opcodes.V1_8,
|
||||
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL,
|
||||
TRACE_TYPE.getInternalName() + "$" + Integer.toHexString(pc),
|
||||
null,
|
||||
OBJECT_TYPE.getInternalName(),
|
||||
new String[]{TRACE_TYPE.getInternalName()});
|
||||
|
||||
generateDefaultConstructor(cw);
|
||||
generateExecuteMethod(cw, pc);
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
return instantiateTrace(defineClass(cw.toByteArray()));
|
||||
}
|
||||
|
||||
private static Class<Trace> defineClass(final byte[] data) {
|
||||
@SuppressWarnings("unchecked") final Class<Trace> traceClass = (Class<Trace>) UNSAFE.defineAnonymousClass(R5CPU.class, data, null);
|
||||
UNSAFE.ensureClassInitialized(traceClass);
|
||||
return traceClass;
|
||||
}
|
||||
|
||||
private static Trace instantiateTrace(final Class<Trace> traceClass) {
|
||||
try {
|
||||
return (Trace) UNSAFE.allocateInstance(traceClass);
|
||||
} catch (final InstantiationException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDefaultConstructor(final ClassVisitor cv) {
|
||||
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, OBJECT_TYPE.getInternalName(), INIT_VOID.getName(), INIT_VOID.getDescriptor(), false);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void generateExecuteMethod(final ClassVisitor cv, final int pc) {
|
||||
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, TRACE_EXECUTE_NAME, TRACE_EXECUTE_DESC, null, new String[]{
|
||||
Type.getInternalName(R5Exception.class),
|
||||
Type.getInternalName(MemoryAccessException.class)
|
||||
});
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
new Translator(mv, R5CPU.this::fetchPage).translateTrace(pc);
|
||||
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// Real time counter -- at least in RISC-V Linux 5.1 the mtime CSR is needed in add_device_randomness
|
||||
// where it doesn't use the SBI. Not implementing it would cause an illegal instruction exception
|
||||
@@ -354,7 +284,7 @@ public class R5CPU implements Steppable, RealTimeCounter, InterruptController {
|
||||
} else if (++hotTrace.count >= TRACE_COUNT_THRESHOLD) {
|
||||
hotTrace.pc = -1;
|
||||
|
||||
final Trace trace = translateTrace(pc);
|
||||
final Trace trace = Translator.translateTrace(this::fetchPage, pc);
|
||||
traces.put(pc, trace);
|
||||
invokeTrace(trace);
|
||||
|
||||
|
||||
@@ -3,26 +3,34 @@ package li.cil.circuity.vm.riscv.dbt;
|
||||
import li.cil.circuity.api.vm.device.memory.MemoryAccessException;
|
||||
import li.cil.circuity.api.vm.device.memory.Sizes;
|
||||
import li.cil.circuity.vm.BitUtils;
|
||||
import li.cil.circuity.vm.UnsafeGetter;
|
||||
import li.cil.circuity.vm.riscv.R5;
|
||||
import li.cil.circuity.vm.riscv.R5CPU;
|
||||
import li.cil.circuity.vm.riscv.exception.R5BreakpointException;
|
||||
import li.cil.circuity.vm.riscv.exception.R5ECallException;
|
||||
import li.cil.circuity.vm.riscv.exception.R5Exception;
|
||||
import li.cil.circuity.vm.riscv.exception.R5IllegalInstructionException;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.*;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Translator {
|
||||
private static final Unsafe UNSAFE = UnsafeGetter.get();
|
||||
|
||||
private static final String TRACE_EXECUTE_NAME = "execute";
|
||||
private static final String TRACE_EXECUTE_DESC = "(Lli/cil/circuity/vm/riscv/R5CPU;)V";
|
||||
|
||||
private static final Type OBJECT_TYPE = Type.getType(Object.class);
|
||||
private static final Type CPU_TYPE = Type.getType(R5CPU.class);
|
||||
private static final Type TRACE_TYPE = Type.getType(Trace.class);
|
||||
private static final Type ECALL_EXCEPTION_TYPE = Type.getType(R5ECallException.class);
|
||||
private static final Type BREAKPOINT_EXCEPTION_TYPE = Type.getType(R5BreakpointException.class);
|
||||
private static final Type ILLEGAL_INSTRUCTION_EXCEPTION_TYPE = Type.getType(R5IllegalInstructionException.class);
|
||||
|
||||
private static final org.objectweb.asm.commons.Method INIT_VOID = org.objectweb.asm.commons.Method.getMethod("void <init> ()");
|
||||
private static final org.objectweb.asm.commons.Method INIT_INT = org.objectweb.asm.commons.Method.getMethod("void <init> (int)");
|
||||
|
||||
@@ -39,17 +47,76 @@ public final class Translator {
|
||||
// Cached opcode implementations by name for faster lookup in generation.
|
||||
private static final Map<String, OpcodeMethod> OPCODE_METHODS = new HashMap<>();
|
||||
|
||||
public final MethodVisitor mv;
|
||||
private final MethodVisitor mv;
|
||||
private final InstructionAccess fetch;
|
||||
private int instOffset;
|
||||
private int toPC;
|
||||
|
||||
public Translator(final MethodVisitor mv, final InstructionAccess fetch) {
|
||||
private Translator(final MethodVisitor mv, final InstructionAccess fetch) {
|
||||
this.mv = mv;
|
||||
this.fetch = fetch;
|
||||
}
|
||||
|
||||
public void translateTrace(final int startPC) {
|
||||
public static Trace translateTrace(final InstructionAccess access, final int pc) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
cw.visit(Opcodes.V1_8,
|
||||
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL,
|
||||
TRACE_TYPE.getInternalName() + "$" + Integer.toHexString(pc),
|
||||
null,
|
||||
OBJECT_TYPE.getInternalName(),
|
||||
new String[]{TRACE_TYPE.getInternalName()});
|
||||
|
||||
generateDefaultConstructor(cw);
|
||||
generateExecuteMethod(cw, access, pc);
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
return instantiateTrace(defineClass(cw.toByteArray()));
|
||||
}
|
||||
|
||||
private static Class<Trace> defineClass(final byte[] data) {
|
||||
@SuppressWarnings("unchecked") final Class<Trace> traceClass = (Class<Trace>) UNSAFE.defineAnonymousClass(R5CPU.class, data, null);
|
||||
UNSAFE.ensureClassInitialized(traceClass);
|
||||
return traceClass;
|
||||
}
|
||||
|
||||
private static Trace instantiateTrace(final Class<Trace> traceClass) {
|
||||
try {
|
||||
return (Trace) UNSAFE.allocateInstance(traceClass);
|
||||
} catch (final InstantiationException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateDefaultConstructor(final ClassVisitor cv) {
|
||||
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, OBJECT_TYPE.getInternalName(), INIT_VOID.getName(), INIT_VOID.getDescriptor(), false);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static void generateExecuteMethod(final ClassVisitor cv, final InstructionAccess access, final int pc) {
|
||||
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, TRACE_EXECUTE_NAME, TRACE_EXECUTE_DESC, null, new String[]{
|
||||
Type.getInternalName(R5Exception.class),
|
||||
Type.getInternalName(MemoryAccessException.class)
|
||||
});
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
new Translator(mv, access).translateTrace(pc);
|
||||
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void translateTrace(final int startPC) {
|
||||
final Label startLabel = new Label();
|
||||
final Label returnLabel = new Label();
|
||||
final Label catchLabel = new Label();
|
||||
@@ -662,12 +729,12 @@ public final class Translator {
|
||||
}
|
||||
|
||||
private static final class OpcodeMethod {
|
||||
public final Class<?>[] argTypes;
|
||||
public final Class<?> returnType;
|
||||
public final boolean throwsExceptions;
|
||||
public final String descriptor;
|
||||
final Class<?>[] argTypes;
|
||||
final Class<?> returnType;
|
||||
final boolean throwsExceptions;
|
||||
final String descriptor;
|
||||
|
||||
public OpcodeMethod(final java.lang.reflect.Method method) {
|
||||
OpcodeMethod(final java.lang.reflect.Method method) {
|
||||
argTypes = method.getParameterTypes();
|
||||
returnType = method.getReturnType();
|
||||
throwsExceptions = method.getExceptionTypes().length > 0;
|
||||
|
||||
Reference in New Issue
Block a user