Skip to content

Commit

Permalink
32x: drc refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Berti committed Nov 22, 2023
1 parent b933fa5 commit 3f7aab1
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 106 deletions.
4 changes: 2 additions & 2 deletions src/main/java/s32x/sh2/Sh2Impl.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import s32x.event.PollSysEventManager;
import s32x.sh2.Sh2Helper.Sh2Config;
import s32x.sh2.device.IntControl;
import s32x.sh2.drc.Ow2Sh2BlockRecompiler;
import s32x.sh2.drc.Sh2Block;
import s32x.sh2.drc.Sh2BlockRecompiler;
import s32x.sh2.drc.Sh2DrcBlockOptimizer;
import s32x.util.Md32xRuntimeData;
import s32x.util.S32xUtil;
Expand Down Expand Up @@ -60,7 +60,7 @@ public Sh2Impl(Sh2Bus memory) {
this.sh2Config = Sh2Config.get();
this.tasReadOffset = sh2Config.tasQuirk ? S32xDict.SH2_CACHE_THROUGH_OFFSET : 0;
if (sh2Config.drcEn) {
Ow2Sh2BlockRecompiler.newInstance(String.valueOf(System.currentTimeMillis()));
Sh2BlockRecompiler.newInstance(String.valueOf(System.currentTimeMillis()));
}
}

Expand Down
111 changes: 11 additions & 100 deletions src/main/java/s32x/sh2/drc/Ow2Sh2BlockRecompiler.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package s32x.sh2.drc;

import omegadrive.util.FileUtil;
import omegadrive.util.LogHelper;
import omegadrive.util.Util;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.LocalVariablesSorter;
import org.objectweb.asm.util.ASMifier;
Expand All @@ -11,101 +9,41 @@
import org.slf4j.Logger;
import s32x.Sh2MMREG;
import s32x.bus.Sh2Bus;
import s32x.bus.Sh2BusImpl;
import s32x.sh2.Sh2Context;
import s32x.sh2.device.Sh2DeviceHelper;
import s32x.sh2.prefetch.Sh2Prefetch.BytecodeContext;
import s32x.sh2.prefetch.Sh2Prefetch.Sh2DrcContext;
import s32x.sh2.prefetch.Sh2Prefetcher.Sh2BlockUnit;

import java.io.FileWriter;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

import static omegadrive.util.Util.th;
import static org.objectweb.asm.Opcodes.*;
import static s32x.sh2.drc.Ow2Sh2Helper.DRC_CLASS_FIELD.*;
import static s32x.sh2.drc.Ow2Sh2Helper.SH2CTX_CLASS_FIELD.devices;
import static s32x.sh2.drc.Ow2Sh2Helper.SH2_DRC_CTX_CLASS_FIELD.sh2Ctx;

/**
* Ow2Sh2BlockRecompiler
* <p>
* uses objectWeb ASM to create Sh2 blocks as Java Classes.
* <p>
* Federico Berti
* <p>
* Copyright 2022
*/
public class Ow2Sh2BlockRecompiler {
public class Ow2Sh2BlockRecompiler implements Sh2BlockRecompiler.InternalSh2BlockRecompiler {

private final static Logger LOG = LogHelper.getLogger(Ow2Sh2BlockRecompiler.class.getSimpleName());
private static final Path drcFolder = Paths.get("./res/drc_" + System.currentTimeMillis());
private final static boolean writeClass = false;

private final OwnClassLoader cl = new OwnClassLoader();

public static final String drcPackage = Ow2Sh2BlockRecompiler.class.getPackageName();
public static final String intArrayDesc = Type.getDescriptor(int[].class);
public static final String intDesc = Type.getDescriptor(int.class);
public static final String noArgsNoRetDesc = "()V";
public static final String classConstructor = "<init>";
public static final String runMethodName = "run";

//detect that memory is Sh2BusImpl vs Sh2Bus and use a Sh2BusImpl field for the class
//should be faster
public static Class<?> memoryClass;

private static Ow2Sh2BlockRecompiler current = null;
private String token;

/**
* There is no easy way of releasing/removing a classLoader,
* GC should take care of it.
*/
public static Ow2Sh2BlockRecompiler newInstance(String token) {
boolean firstOne = current == null;
boolean newOne = firstOne || Objects.equals(current.token, token);
if (newOne) {
Ow2Sh2BlockRecompiler recompiler = new Ow2Sh2BlockRecompiler();
recompiler.token = token;
current = recompiler;
LOG.info("New recompiler with token: {}", token);
}
return current;
}

public static Ow2Sh2BlockRecompiler getInstance() {
assert current != null;
return current;
}

private static class OwnClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}

public Runnable createDrcClass(Sh2Block block, Sh2DrcContext drcCtx) {
String blockClass = drcPackage + "." + drcCtx.sh2Ctx.sh2TypeCode + "_" + th(block.prefetchPc)
+ "_" + th(block.hashCodeWords) + "_" + System.nanoTime();
Runnable r;
try {
byte[] binc = createClassBinary(block, drcCtx, blockClass);
writeClassMaybe(blockClass, binc);
Class<?> clazz = cl.defineClass(blockClass, binc);
Object b = clazz.getDeclaredConstructor(int[].class, int[].class, Sh2DrcContext.class).
newInstance(drcCtx.sh2Ctx.registers, block.prefetchWords, drcCtx);
assert b instanceof Runnable;
r = (Runnable) b;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Fatal! ," + blockClass);
}
return r;
}

private static byte[] createClassBinary(Sh2Block block, Sh2DrcContext drcCtx, String blockClass) {
@Override
public byte[] createClassBinary(Sh2Block block, Sh2DrcContext drcCtx, String blockClass, Class<?> memoryClass) {
String blockClassDesc = blockClass.replace('.', '/');
memoryClass = drcCtx.memory instanceof Sh2BusImpl ? Sh2BusImpl.class : Sh2Bus.class;
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(V11, ACC_PUBLIC | ACC_FINAL, blockClassDesc, null, Type.getInternalName(Object.class),
new String[]{Type.getInternalName(Runnable.class)});
Expand Down Expand Up @@ -176,9 +114,9 @@ private static byte[] createClassBinary(Sh2Block block, Sh2DrcContext drcCtx, St
ctx.mv = dsCtx.mv = lvs;
int totCycles = 0;
for (int i = 0; i < block.prefetchLenWords; i++) {
setDrcContext(ctx, block.inst[i], false);
Sh2BlockRecompiler.setDrcContext(ctx, block.inst[i], false);
if (ctx.sh2Inst.isBranchDelaySlot) {
setDrcContext(dsCtx, block.inst[i + 1], true);
Sh2BlockRecompiler.setDrcContext(dsCtx, block.inst[i + 1], true);
ctx.delaySlotCtx = dsCtx;
//LOG.info("Block at PC {}, setting delaySlot ctx\nctx {}\nds {}", th(block.prefetchPc), ctx, dsCtx);
}
Expand Down Expand Up @@ -210,35 +148,8 @@ private static void setClassField(MethodVisitor mv, String classDesc, int varInd
mv.visitFieldInsn(PUTFIELD, classDesc, field.name(), typeDesc);
}

private static void setDrcContext(BytecodeContext ctx, Sh2BlockUnit sbu, boolean delaySlot) {
ctx.opcode = sbu.opcode;
ctx.pc = sbu.pc;
ctx.sh2Inst = sbu.inst;
ctx.delaySlot = delaySlot;
ctx.delaySlotCtx = null;
}

private static void writeClassMaybe(String blockClass, byte[] binc) {
if (!writeClass) {
return;
}
boolean res = drcFolder.toFile().mkdirs();
if (!res && !drcFolder.toFile().exists()) {
LOG.error("Unable to log files to: {}", drcFolder.toFile());
return;
}
Path p = Paths.get(drcFolder.toAbsolutePath().toString(), (blockClass + ".class"));
Path bc = Paths.get(drcFolder.toAbsolutePath().toString(), (blockClass + ".bytecode"));
Util.executorService.submit(() -> {
printSource(bc, binc);
LOG.info("Bytecode Class written: {}", bc.toAbsolutePath());
FileUtil.writeFileSafe(p, binc);
LOG.info("Drc Class written: {}", p.toAbsolutePath());
});
LOG.info("{} job submitted", blockClass);
}

private static void printSource(Path file, byte[] code) {
@Override
public void printSource(Path file, byte[] code) {
ClassReader reader;
try (
FileWriter fileWriter = new FileWriter(file.toFile());
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/s32x/sh2/drc/Ow2Sh2Bytecode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2257,14 +2257,14 @@ public static void movGBRShiftToReg0(BytecodeContext ctx, Size size) {

public static void writeMem(BytecodeContext ctx, Size size) {
ctx.mv.visitFieldInsn(GETSTATIC, Type.getInternalName(Size.class), size.name(), Type.getDescriptor(Size.class));
int invoke = Ow2Sh2BlockRecompiler.memoryClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL;
int invoke = Sh2BlockRecompiler.memoryClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL;
ctx.mv.visitMethodInsn(invoke, Type.getInternalName(Sh2BusImpl.class), SH2MEMORY_METHOD.write.name(),
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(Size.class)));
}

public static void readMem(BytecodeContext ctx, Size size) {
ctx.mv.visitFieldInsn(GETSTATIC, Type.getInternalName(Size.class), size.name(), Type.getDescriptor(Size.class));
int invoke = Ow2Sh2BlockRecompiler.memoryClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL;
int invoke = Sh2BlockRecompiler.memoryClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL;
ctx.mv.visitMethodInsn(invoke, Type.getInternalName(Sh2BusImpl.class), SH2MEMORY_METHOD.read.name(),
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.getType(Size.class)));
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public static void pushSh2Context(Sh2Prefetch.BytecodeContext ctx) {
public static void pushMemory(Sh2Prefetch.BytecodeContext ctx) {
ctx.mv.visitVarInsn(ALOAD, 0); //this
ctx.mv.visitFieldInsn(GETFIELD, ctx.classDesc, DRC_CLASS_FIELD.memory.name(),
Type.getDescriptor(Ow2Sh2BlockRecompiler.memoryClass));
Type.getDescriptor(Sh2BlockRecompiler.memoryClass));
}

public static void pushSh2ContextIntField(Sh2Prefetch.BytecodeContext ctx, String name) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/s32x/sh2/drc/Sh2Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public void stage1(Sh2Prefetcher.Sh2BlockUnit[] ic) {
public void stage2() {
if (Sh2Config.get().drcEn) {
assert drcContext != null;
stage2Drc = Ow2Sh2BlockRecompiler.getInstance().createDrcClass(this, drcContext);
stage2Drc = Sh2BlockRecompiler.getInstance().createDrcClass(this, drcContext);
}
}

Expand Down
126 changes: 126 additions & 0 deletions src/main/java/s32x/sh2/drc/Sh2BlockRecompiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package s32x.sh2.drc;

import omegadrive.util.FileUtil;
import omegadrive.util.LogHelper;
import omegadrive.util.Util;
import org.slf4j.Logger;
import s32x.bus.Sh2Bus;
import s32x.bus.Sh2BusImpl;
import s32x.sh2.prefetch.Sh2Prefetch.BytecodeContext;
import s32x.sh2.prefetch.Sh2Prefetch.Sh2DrcContext;
import s32x.sh2.prefetch.Sh2Prefetcher.Sh2BlockUnit;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

import static omegadrive.util.Util.th;

/**
* Federico Berti
* <p>
* Copyright 2022
*/
public class Sh2BlockRecompiler {

private final static Logger LOG = LogHelper.getLogger(Sh2BlockRecompiler.class.getSimpleName());
private static final Path drcFolder = Paths.get("./res/drc_" + System.currentTimeMillis());
private final static boolean writeClass = false;

private final OwnClassLoader cl = new OwnClassLoader();

public static final String drcPackage = Sh2BlockRecompiler.class.getPackageName();

//detect that memory is Sh2BusImpl vs Sh2Bus and use a Sh2BusImpl field for the class
//should be faster
public static Class<?> memoryClass;

private static Sh2BlockRecompiler current = null;
private String token;
private InternalSh2BlockRecompiler recompiler;

interface InternalSh2BlockRecompiler {
byte[] createClassBinary(Sh2Block block, Sh2DrcContext drcCtx, String blockClass, Class<?> memoryClass);

void printSource(Path file, byte[] code);
}

public static class OwnClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}

/**
* There is no easy way of releasing/removing a classLoader,
* GC should take care of it.
*/
public static Sh2BlockRecompiler newInstance(String token) {
boolean firstOne = current == null;
boolean newOne = firstOne || Objects.equals(current.token, token);
if (newOne) {
Sh2BlockRecompiler newrec = new Sh2BlockRecompiler();
newrec.token = token;
current = newrec;
LOG.info("New recompiler: {}, with token: {}", newrec.recompiler.getClass().getName(), token);
}
return current;
}

private Sh2BlockRecompiler() {
recompiler = new Ow2Sh2BlockRecompiler();
}

public static Sh2BlockRecompiler getInstance() {
assert current != null;
return current;
}

public Runnable createDrcClass(Sh2Block block, Sh2DrcContext drcCtx) {
String blockClass = drcPackage + "." + drcCtx.sh2Ctx.sh2TypeCode + "_" + th(block.prefetchPc)
+ "_" + th(block.hashCodeWords) + "_" + System.nanoTime();
memoryClass = drcCtx.memory instanceof Sh2BusImpl ? Sh2BusImpl.class : Sh2Bus.class;
Runnable r;
try {
byte[] binc = recompiler.createClassBinary(block, drcCtx, blockClass, memoryClass);
writeClassMaybe(blockClass, binc);
Class<?> clazz = cl.defineClass(blockClass, binc);
Object b = clazz.getDeclaredConstructor(int[].class, int[].class, Sh2DrcContext.class).
newInstance(drcCtx.sh2Ctx.registers, block.prefetchWords, drcCtx);
assert b instanceof Runnable;
r = (Runnable) b;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Fatal! ," + blockClass);
}
return r;
}

public static void setDrcContext(BytecodeContext ctx, Sh2BlockUnit sbu, boolean delaySlot) {
ctx.opcode = sbu.opcode;
ctx.pc = sbu.pc;
ctx.sh2Inst = sbu.inst;
ctx.delaySlot = delaySlot;
ctx.delaySlotCtx = null;
}

private void writeClassMaybe(String blockClass, byte[] binc) {
if (!writeClass) {
return;
}
boolean res = drcFolder.toFile().mkdirs();
if (!res && !drcFolder.toFile().exists()) {
LOG.error("Unable to log files to: {}", drcFolder.toFile());
return;
}
Path p = Paths.get(drcFolder.toAbsolutePath().toString(), (blockClass + ".class"));
Path bc = Paths.get(drcFolder.toAbsolutePath().toString(), (blockClass + ".bytecode"));
Util.executorService.submit(() -> {
recompiler.printSource(bc, binc);
LOG.info("Bytecode Class written: {}", bc.toAbsolutePath());
FileUtil.writeFileSafe(p, binc);
LOG.info("Drc Class written: {}", p.toAbsolutePath());
});
LOG.info("{} job submitted", blockClass);
}
}

0 comments on commit 3f7aab1

Please sign in to comment.