From 3f7aab1320ceb5da28b2815344a5e6832e4b1eb1 Mon Sep 17 00:00:00 2001 From: Federico Berti Date: Wed, 22 Nov 2023 16:06:05 +0000 Subject: [PATCH] 32x: drc refactor --- src/main/java/s32x/sh2/Sh2Impl.java | 4 +- .../s32x/sh2/drc/Ow2Sh2BlockRecompiler.java | 111 ++------------- .../java/s32x/sh2/drc/Ow2Sh2Bytecode.java | 4 +- src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java | 2 +- src/main/java/s32x/sh2/drc/Sh2Block.java | 2 +- .../java/s32x/sh2/drc/Sh2BlockRecompiler.java | 126 ++++++++++++++++++ 6 files changed, 143 insertions(+), 106 deletions(-) create mode 100644 src/main/java/s32x/sh2/drc/Sh2BlockRecompiler.java diff --git a/src/main/java/s32x/sh2/Sh2Impl.java b/src/main/java/s32x/sh2/Sh2Impl.java index 518e9af8..0c32c5b3 100644 --- a/src/main/java/s32x/sh2/Sh2Impl.java +++ b/src/main/java/s32x/sh2/Sh2Impl.java @@ -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; @@ -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())); } } diff --git a/src/main/java/s32x/sh2/drc/Ow2Sh2BlockRecompiler.java b/src/main/java/s32x/sh2/drc/Ow2Sh2BlockRecompiler.java index c27d0212..ff76d34b 100644 --- a/src/main/java/s32x/sh2/drc/Ow2Sh2BlockRecompiler.java +++ b/src/main/java/s32x/sh2/drc/Ow2Sh2BlockRecompiler.java @@ -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; @@ -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 + *

+ * uses objectWeb ASM to create Sh2 blocks as Java Classes. + *

* Federico Berti *

* 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 = ""; 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)}); @@ -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); } @@ -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()); diff --git a/src/main/java/s32x/sh2/drc/Ow2Sh2Bytecode.java b/src/main/java/s32x/sh2/drc/Ow2Sh2Bytecode.java index d4a8966b..e1e514ab 100644 --- a/src/main/java/s32x/sh2/drc/Ow2Sh2Bytecode.java +++ b/src/main/java/s32x/sh2/drc/Ow2Sh2Bytecode.java @@ -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))); } diff --git a/src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java b/src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java index 6883f72a..81b80383 100644 --- a/src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java +++ b/src/main/java/s32x/sh2/drc/Ow2Sh2Helper.java @@ -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) { diff --git a/src/main/java/s32x/sh2/drc/Sh2Block.java b/src/main/java/s32x/sh2/drc/Sh2Block.java index 36bbeeeb..60ae779c 100644 --- a/src/main/java/s32x/sh2/drc/Sh2Block.java +++ b/src/main/java/s32x/sh2/drc/Sh2Block.java @@ -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); } } diff --git a/src/main/java/s32x/sh2/drc/Sh2BlockRecompiler.java b/src/main/java/s32x/sh2/drc/Sh2BlockRecompiler.java new file mode 100644 index 00000000..b802a73f --- /dev/null +++ b/src/main/java/s32x/sh2/drc/Sh2BlockRecompiler.java @@ -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 + *

+ * 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); + } +} \ No newline at end of file