/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.Allegrex.compiler;
import static java.lang.Math.min;
import static jpcsp.Allegrex.Common._a0;
import static jpcsp.Allegrex.Common._a1;
import static jpcsp.Allegrex.Common._a2;
import static jpcsp.Allegrex.Common._a3;
import static jpcsp.Allegrex.Common._f0;
import static jpcsp.Allegrex.Common._ra;
import static jpcsp.Allegrex.Common._sp;
import static jpcsp.Allegrex.Common._t0;
import static jpcsp.Allegrex.Common._t1;
import static jpcsp.Allegrex.Common._t2;
import static jpcsp.Allegrex.Common._t3;
import static jpcsp.Allegrex.Common._t4;
import static jpcsp.Allegrex.Common._t5;
import static jpcsp.Allegrex.Common._t6;
import static jpcsp.Allegrex.Common._t7;
import static jpcsp.Allegrex.Common._t8;
import static jpcsp.Allegrex.Common._t9;
import static jpcsp.Allegrex.Common._v0;
import static jpcsp.Allegrex.Common._v1;
import static jpcsp.Allegrex.Common._zr;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.NIDMapper;
import jpcsp.Processor;
import jpcsp.State;
import jpcsp.Allegrex.Common;
import jpcsp.Allegrex.CpuState;
import jpcsp.Allegrex.GprState;
import jpcsp.Allegrex.Instructions;
import jpcsp.Allegrex.VfpuState;
import jpcsp.Allegrex.Common.Instruction;
import jpcsp.Allegrex.FpuState.Fcr31;
import jpcsp.Allegrex.VfpuState.Vcr;
import jpcsp.Allegrex.VfpuState.Vcr.PfxDst;
import jpcsp.Allegrex.VfpuState.Vcr.PfxSrc;
import jpcsp.Allegrex.compiler.nativeCode.NativeCodeInstruction;
import jpcsp.Allegrex.compiler.nativeCode.NativeCodeManager;
import jpcsp.Allegrex.compiler.nativeCode.NativeCodeSequence;
import jpcsp.Allegrex.compiler.nativeCode.Nop;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.DebugMemory;
import jpcsp.HLE.HLEModuleFunction;
import jpcsp.HLE.HLEModuleManager;
import jpcsp.HLE.HLEUidClass;
import jpcsp.HLE.HLEUidObjectMapping;
import jpcsp.HLE.Modules;
import jpcsp.HLE.PspString;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.StringInfo;
import jpcsp.HLE.SyscallHandler;
import jpcsp.HLE.TErrorPointer32;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer16;
import jpcsp.HLE.TPointer32;
import jpcsp.HLE.TPointer64;
import jpcsp.HLE.TPointer8;
import jpcsp.HLE.kernel.Managers;
import jpcsp.HLE.kernel.managers.IntrManager;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceKernelThreadInfo;
import jpcsp.HLE.kernel.types.SceModule;
import jpcsp.HLE.kernel.types.pspAbstractMemoryMappedStructure;
import jpcsp.HLE.modules.ThreadManForUser;
import jpcsp.hardware.Interrupts;
import jpcsp.memory.FastMemory;
import jpcsp.memory.SafeFastMemory;
import jpcsp.util.ClassAnalyzer;
import jpcsp.util.DurationStatistics;
import jpcsp.util.ClassAnalyzer.ParameterInfo;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* @author gid15
*
*/
public class CompilerContext implements ICompilerContext {
protected static Logger log = Compiler.log;
private CompilerClassLoader classLoader;
private CodeBlock codeBlock;
private int numberInstructionsToBeSkipped;
private boolean skipDelaySlot;
private MethodVisitor mv;
private CodeInstruction codeInstruction;
private static final boolean storeCpuLocal = true;
private static final boolean storeMemoryIntLocal = false;
private static final int LOCAL_CPU = 0;
private static final int LOCAL_INSTRUCTION_COUNT = 1;
private static final int LOCAL_MEMORY_INT = 2;
private static final int LOCAL_TMP1 = 3;
private static final int LOCAL_TMP2 = 4;
private static final int LOCAL_TMP3 = 5;
private static final int LOCAL_TMP4 = 6;
private static final int LOCAL_TMP_VD0 = 7;
private static final int LOCAL_TMP_VD1 = 8;
private static final int LOCAL_TMP_VD2 = 9;
private static final int LOCAL_MAX = 10;
private static final int LOCAL_FIRST_SAVED_PARAMETER = LOCAL_MAX;
private static final int LOCAL_NUMBER_SAVED_PARAMETERS = 8;
private static final int LOCAL_MAX_WITH_SAVED_PARAMETERS = LOCAL_FIRST_SAVED_PARAMETER + LOCAL_NUMBER_SAVED_PARAMETERS;
private static final int DEFAULT_MAX_STACK_SIZE = 11;
private static final int SYSCALL_MAX_STACK_SIZE = 100;
private static final int LOCAL_ERROR_POINTER = LOCAL_TMP3;
private boolean enableIntructionCounting = false;
public Set<Integer> analysedAddresses = new HashSet<Integer>();
public Stack<Integer> blocksToBeAnalysed = new Stack<Integer>();
private int currentInstructionCount;
private int preparedRegisterForStore = -1;
private boolean memWritePrepared = false;
private boolean hiloPrepared = false;
private int methodMaxInstructions;
private NativeCodeManager nativeCodeManager;
private final VfpuPfxSrcState vfpuPfxsState = new VfpuPfxSrcState();
private final VfpuPfxSrcState vfpuPfxtState = new VfpuPfxSrcState();
private final VfpuPfxDstState vfpuPfxdState = new VfpuPfxDstState();
private Label interpretPfxLabel = null;
private boolean pfxVdOverlap = false;
private static final String runtimeContextInternalName = Type.getInternalName(RuntimeContext.class);
private static final String processorDescriptor = Type.getDescriptor(Processor.class);
private static final String cpuDescriptor = Type.getDescriptor(CpuState.class);
private static final String cpuInternalName = Type.getInternalName(CpuState.class);
private static final String instructionsInternalName = Type.getInternalName(Instructions.class);
private static final String instructionInternalName = Type.getInternalName(Instruction.class);
private static final String instructionDescriptor = Type.getDescriptor(Instruction.class);
private static final String sceKernalThreadInfoInternalName = Type.getInternalName(SceKernelThreadInfo.class);
private static final String sceKernalThreadInfoDescriptor = Type.getDescriptor(SceKernelThreadInfo.class);
private static final String stringDescriptor = Type.getDescriptor(String.class);
private static final String memoryDescriptor = Type.getDescriptor(Memory.class);
private static final String memoryInternalName = Type.getInternalName(Memory.class);
private static final String profilerInternalName = Type.getInternalName(Profiler.class);
public static final String executableDescriptor = Type.getDescriptor(IExecutable.class);
public static final String executableInternalName = Type.getInternalName(IExecutable.class);
private static final String arraycopyDescriptor = "(" + Type.getDescriptor(Object.class) + "I" + Type.getDescriptor(Object.class) + "II)V";
private static Set<Integer> fastSyscalls;
private int instanceIndex;
private NativeCodeSequence preparedCallNativeCodeBlock = null;
private int maxStackSize = DEFAULT_MAX_STACK_SIZE;
private int maxLocalSize = LOCAL_MAX;
private boolean parametersSavedToLocals;
private CompilerTypeManager compilerTypeManager;
public CompilerContext(CompilerClassLoader classLoader, int instanceIndex) {
Compiler compiler = Compiler.getInstance();
this.classLoader = classLoader;
this.instanceIndex = instanceIndex;
nativeCodeManager = compiler.getNativeCodeManager();
methodMaxInstructions = compiler.getDefaultMethodMaxInstructions();
compilerTypeManager = compiler.getCompilerTypeManager();
// Count instructions only when the profile is enabled or
// when the statistics are enabled
if (Profiler.isProfilerEnabled() || DurationStatistics.collectStatistics) {
enableIntructionCounting = true;
}
if (fastSyscalls == null) {
fastSyscalls = new TreeSet<Integer>();
addFastSyscall(0x3AD58B8C); // sceKernelSuspendDispatchThread
addFastSyscall(0x110DEC9A); // sceKernelUSec2SysClock
addFastSyscall(0xC8CD158C); // sceKernelUSec2SysClockWide
addFastSyscall(0xBA6B92E2); // sceKernelSysClock2USec
addFastSyscall(0xE1619D7C); // sceKernelSysClock2USecWide
addFastSyscall(0xDB738F35); // sceKernelGetSystemTime
addFastSyscall(0x82BC5777); // sceKernelGetSystemTimeWide
addFastSyscall(0x369ED59D); // sceKernelGetSystemTimeLow
addFastSyscall(0xB5F6DC87); // sceMpegRingbufferAvailableSize
addFastSyscall(0xE0D68148); // sceGeListUpdateStallAddr
addFastSyscall(0x34B9FA9E); // sceKernelDcacheWritebackInvalidateRangeFunction
addFastSyscall(0xE47E40E4); // sceGeEdramGetAddrFunction
addFastSyscall(0x1F6752AD); // sceGeEdramGetSizeFunction
addFastSyscall(0x74AE582A); // __sceSasGetEnvelopeHeight
addFastSyscall(0x68A46B95); // __sceSasGetEndFlag
}
}
private void addFastSyscall(int nid) {
int syscallCode = NIDMapper.getInstance().getSyscallByNid(nid);
if (syscallCode >= 0) {
fastSyscalls.add(syscallCode);
}
}
public CompilerClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(CompilerClassLoader classLoader) {
this.classLoader = classLoader;
}
public CodeBlock getCodeBlock() {
return codeBlock;
}
public void setCodeBlock(CodeBlock codeBlock) {
this.codeBlock = codeBlock;
}
public NativeCodeManager getNativeCodeManager() {
return nativeCodeManager;
}
private void loadCpu() {
if (storeCpuLocal) {
mv.visitVarInsn(Opcodes.ALOAD, LOCAL_CPU);
} else {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "cpu", cpuDescriptor);
}
}
private void loadProcessor() {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "processor", processorDescriptor);
}
private void loadMemory() {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "memory", memoryDescriptor);
}
private void loadModule(String moduleName) {
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(Modules.class), moduleName + "Module", "Ljpcsp/HLE/modules/" + moduleName + ";");
}
private void loadFpr() {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "fpr", "[F");
}
private void loadVprFloat() {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "vprFloat", "[F");
}
private void loadVprInt() {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "vprInt", "[I");
}
@Override
public void loadRegister(int reg) {
if (reg == _zr) {
loadImm(0);
} else {
loadCpu();
mv.visitFieldInsn(Opcodes.GETFIELD, cpuInternalName, getGprFieldName(reg), "I");
}
}
@Override
public void loadFRegister(int reg) {
loadFpr();
loadImm(reg);
mv.visitInsn(Opcodes.FALOAD);
}
private Float getPfxSrcCstValue(VfpuPfxSrcState pfxSrcState, int n) {
if (pfxSrcState == null ||
pfxSrcState.isUnknown() ||
!pfxSrcState.pfxSrc.enabled ||
!pfxSrcState.pfxSrc.cst[n]) {
return null;
}
float value = 0.0f;
switch (pfxSrcState.pfxSrc.swz[n]) {
case 0:
value = pfxSrcState.pfxSrc.abs[n] ? 3.0f : 0.0f;
break;
case 1:
value = pfxSrcState.pfxSrc.abs[n] ? (1.0f / 3.0f) : 1.0f;
break;
case 2:
value = pfxSrcState.pfxSrc.abs[n] ? (1.0f / 4.0f) : 2.0f;
break;
case 3:
value = pfxSrcState.pfxSrc.abs[n] ? (1.0f / 6.0f) : 0.5f;
break;
}
if (pfxSrcState.pfxSrc.neg[n]) {
value = 0.0f - value;
}
if (log.isTraceEnabled() && pfxSrcState.isKnown() && pfxSrcState.pfxSrc.enabled) {
log.trace(String.format("PFX %08X - getPfxSrcCstValue %d -> %f", getCodeInstruction().getAddress(), n, value));
}
return new Float(value);
}
private void convertVFloatToInt() {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class), "floatToRawIntBits", "(F)I");
}
private void convertVIntToFloat() {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class), "intBitsToFloat", "(I)F");
}
private void applyPfxSrcPostfix(VfpuPfxSrcState pfxSrcState, int n, boolean isFloat) {
if (pfxSrcState == null ||
pfxSrcState.isUnknown() ||
!pfxSrcState.pfxSrc.enabled) {
return;
}
if (pfxSrcState.pfxSrc.abs[n]) {
if (log.isTraceEnabled() && pfxSrcState.isKnown() && pfxSrcState.pfxSrc.enabled) {
log.trace(String.format("PFX %08X - applyPfxSrcPostfix abs(%d)", getCodeInstruction().getAddress(), n));
}
if (isFloat) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), "abs", "(F)F");
} else {
loadImm(0x7FFFFFFF);
mv.visitInsn(Opcodes.IAND);
}
}
if (pfxSrcState.pfxSrc.neg[n]) {
if (log.isTraceEnabled() && pfxSrcState.isKnown() && pfxSrcState.pfxSrc.enabled) {
log.trace(String.format("PFX %08X - applyPfxSrcPostfix neg(%d)", getCodeInstruction().getAddress(), n));
}
if (isFloat) {
mv.visitInsn(Opcodes.FNEG);
} else {
loadImm(0x80000000);
mv.visitInsn(Opcodes.IXOR);
}
}
}
private int getPfxSrcIndex(VfpuPfxSrcState pfxSrcState, int n) {
if (pfxSrcState == null ||
pfxSrcState.isUnknown() ||
!pfxSrcState.pfxSrc.enabled ||
pfxSrcState.pfxSrc.cst[n]) {
return n;
}
if (log.isTraceEnabled() && pfxSrcState.isKnown() && pfxSrcState.pfxSrc.enabled) {
log.trace(String.format("PFX %08X - getPfxSrcIndex %d -> %d", getCodeInstruction().getAddress(), n, pfxSrcState.pfxSrc.swz[n]));
}
return pfxSrcState.pfxSrc.swz[n];
}
private void loadVRegister(int m, int c, int r, boolean isFloat) {
int index = VfpuState.getVprIndex(m, c, r);
if (isFloat) {
loadVprFloat();
loadImm(index);
mv.visitInsn(Opcodes.FALOAD);
} else {
loadVprInt();
loadImm(index);
mv.visitInsn(Opcodes.IALOAD);
}
}
private void loadCstValue(Float cstValue, boolean isFloat) {
if (isFloat) {
mv.visitLdcInsn(cstValue.floatValue());
} else {
loadImm(Float.floatToRawIntBits(cstValue.floatValue()));
}
}
private void loadVRegister(int vsize, int reg, int n, VfpuPfxSrcState pfxSrcState, boolean isFloat) {
if (log.isTraceEnabled() && pfxSrcState != null && pfxSrcState.isKnown() && pfxSrcState.pfxSrc.enabled) {
log.trace(String.format("PFX %08X - loadVRegister %d, %d, %d", getCodeInstruction().getAddress(), vsize, reg, n));
}
int m = (reg >> 2) & 7;
int i = (reg >> 0) & 3;
int s;
switch (vsize) {
case 1: {
s = (reg >> 5) & 3;
Float cstValue = getPfxSrcCstValue(pfxSrcState, n);
if (cstValue != null) {
loadCstValue(cstValue, isFloat);
} else {
loadVRegister(m, i, s, isFloat);
applyPfxSrcPostfix(pfxSrcState, n, isFloat);
}
break;
}
case 2: {
s = (reg & 64) >> 5;
Float cstValue = getPfxSrcCstValue(pfxSrcState, n);
if (cstValue != null) {
loadCstValue(cstValue, isFloat);
} else {
int index = getPfxSrcIndex(pfxSrcState, n);
if ((reg & 32) != 0) {
loadVRegister(m, s + index, i, isFloat);
} else {
loadVRegister(m, i, s + index, isFloat);
}
applyPfxSrcPostfix(pfxSrcState, n, isFloat);
}
break;
}
case 3: {
s = (reg & 64) >> 6;
Float cstValue = getPfxSrcCstValue(pfxSrcState, n);
if (cstValue != null) {
loadCstValue(cstValue, isFloat);
} else {
int index = getPfxSrcIndex(pfxSrcState, n);
if ((reg & 32) != 0) {
loadVRegister(m, s + index, i, isFloat);
} else {
loadVRegister(m, i, s + index, isFloat);
}
applyPfxSrcPostfix(pfxSrcState, n, isFloat);
}
break;
}
case 4: {
s = (reg & 64) >> 5;
Float cstValue = getPfxSrcCstValue(pfxSrcState, n);
if (cstValue != null) {
loadCstValue(cstValue, isFloat);
} else {
int index = getPfxSrcIndex(pfxSrcState, (n + s) & 3);
if ((reg & 32) != 0) {
loadVRegister(m, index, i, isFloat);
} else {
loadVRegister(m, i, index, isFloat);
}
applyPfxSrcPostfix(pfxSrcState, n, isFloat);
}
break;
}
}
}
public void prepareRegisterForStore(int reg) {
if (preparedRegisterForStore < 0) {
loadCpu();
preparedRegisterForStore = reg;
}
}
private String getGprFieldName(int reg) {
return Common.gprNames[reg].replace('$', '_');
}
public void storeRegister(int reg) {
if (preparedRegisterForStore == reg) {
mv.visitFieldInsn(Opcodes.PUTFIELD, cpuInternalName, getGprFieldName(reg), "I");
preparedRegisterForStore = -1;
} else {
loadCpu();
mv.visitInsn(Opcodes.SWAP);
mv.visitFieldInsn(Opcodes.PUTFIELD, cpuInternalName, getGprFieldName(reg), "I");
}
}
@Override
public void storeRegister(int reg, int constantValue) {
if (preparedRegisterForStore == reg) {
preparedRegisterForStore = -1;
} else {
loadCpu();
}
loadImm(constantValue);
mv.visitFieldInsn(Opcodes.PUTFIELD, cpuInternalName, getGprFieldName(reg), "I");
}
public void prepareFRegisterForStore(int reg) {
if (preparedRegisterForStore < 0) {
loadFpr();
loadImm(reg);
preparedRegisterForStore = reg;
}
}
public void storeFRegister(int reg) {
if (preparedRegisterForStore == reg) {
mv.visitInsn(Opcodes.FASTORE);
preparedRegisterForStore = -1;
} else {
loadFpr();
mv.visitInsn(Opcodes.SWAP);
loadImm(reg);
mv.visitInsn(Opcodes.SWAP);
mv.visitInsn(Opcodes.FASTORE);
}
}
private boolean isPfxDstMasked(VfpuPfxDstState pfxDstState, int n) {
if (pfxDstState == null ||
pfxDstState.isUnknown() ||
!pfxDstState.pfxDst.enabled) {
return false;
}
return pfxDstState.pfxDst.msk[n];
}
private void applyPfxDstPostfix(VfpuPfxDstState pfxDstState, int n, boolean isFloat) {
if (pfxDstState == null ||
pfxDstState.isUnknown() ||
!pfxDstState.pfxDst.enabled) {
return;
}
switch (pfxDstState.pfxDst.sat[n]) {
case 1:
if (log.isTraceEnabled() && pfxDstState != null && pfxDstState.isKnown() && pfxDstState.pfxDst.enabled) {
log.trace(String.format("PFX %08X - applyPfxDstPostfix %d [0:1]", getCodeInstruction().getAddress(), n));
}
if (!isFloat) {
convertVIntToFloat();
}
mv.visitLdcInsn(1.0f);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), "min", "(FF)F");
mv.visitLdcInsn(0.0f);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), "max", "(FF)F");
if (!isFloat) {
convertVFloatToInt();
}
break;
case 3:
if (log.isTraceEnabled() && pfxDstState != null && pfxDstState.isKnown() && pfxDstState.pfxDst.enabled) {
log.trace(String.format("PFX %08X - applyPfxDstPostfix %d [-1:1]", getCodeInstruction().getAddress(), n));
}
if (!isFloat) {
convertVIntToFloat();
}
mv.visitLdcInsn(1.0f);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), "min", "(FF)F");
mv.visitLdcInsn(-1.0f);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), "max", "(FF)F");
if (!isFloat) {
convertVFloatToInt();
}
break;
}
}
private void prepareVRegisterForStore(int m, int c, int r, boolean isFloat) {
int index = VfpuState.getVprIndex(m, c, r);
if (isFloat) {
// Prepare the array and index for the int value
loadVprInt();
loadImm(index);
// Prepare the array and index for the float value
loadVprFloat();
loadImm(index);
} else {
// Prepare the array and index for the float value
loadVprFloat();
loadImm(index);
// Prepare the array and index for the int value
loadVprInt();
loadImm(index);
}
}
public void prepareVRegisterForStore(int vsize, int reg, int n, VfpuPfxDstState pfxDstState, boolean isFloat) {
if (preparedRegisterForStore < 0) {
if (!isPfxDstMasked(pfxDstState, n)) {
int m = (reg >> 2) & 7;
int i = (reg >> 0) & 3;
int s;
switch (vsize) {
case 1: {
s = (reg >> 5) & 3;
prepareVRegisterForStore(m, i, s, isFloat);
break;
}
case 2: {
s = (reg & 64) >> 5;
if ((reg & 32) != 0) {
prepareVRegisterForStore(m, s + n, i, isFloat);
} else {
prepareVRegisterForStore(m, i, s + n, isFloat);
}
break;
}
case 3: {
s = (reg & 64) >> 6;
if ((reg & 32) != 0) {
prepareVRegisterForStore(m, s + n, i, isFloat);
} else {
prepareVRegisterForStore(m, i, s + n, isFloat);
}
break;
}
case 4: {
s = (reg & 64) >> 5;
if ((reg & 32) != 0) {
prepareVRegisterForStore(m, (n + s) & 3, i, isFloat);
} else {
prepareVRegisterForStore(m, i, (n + s) & 3, isFloat);
}
break;
}
}
}
preparedRegisterForStore = reg;
}
}
private void storeVRegister(int vsize, int reg, int n, VfpuPfxDstState pfxDstState, boolean isFloat) {
if (log.isTraceEnabled() && pfxDstState != null && pfxDstState.isKnown() && pfxDstState.pfxDst.enabled) {
log.trace(String.format("PFX %08X - storeVRegister %d, %d, %d", getCodeInstruction().getAddress(), vsize, reg, n));
}
if (preparedRegisterForStore == reg) {
if (isPfxDstMasked(pfxDstState, n)) {
if (log.isTraceEnabled() && pfxDstState != null && pfxDstState.isKnown() && pfxDstState.pfxDst.enabled) {
log.trace(String.format("PFX %08X - storeVRegister %d masked", getCodeInstruction().getAddress(), n));
}
mv.visitInsn(Opcodes.POP);
} else {
applyPfxDstPostfix(pfxDstState, n, isFloat);
if (isFloat) {
// Keep a copy of the value for the int value
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn(Opcodes.FASTORE); // First store the float value
convertVFloatToInt();
mv.visitInsn(Opcodes.IASTORE); // Second store the int value
} else {
// Keep a copy of the value for the float value
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn(Opcodes.IASTORE); // First store the int value
convertVIntToFloat();
mv.visitInsn(Opcodes.FASTORE); // Second store the float value
}
}
preparedRegisterForStore = -1;
} else {
log.error("storeVRegister with non-prepared register is not supported");
}
}
public void loadFcr31() {
loadCpu();
mv.visitFieldInsn(Opcodes.GETFIELD, cpuInternalName, "fcr31", Type.getDescriptor(Fcr31.class));
}
public void loadVcr() {
loadCpu();
mv.visitFieldInsn(Opcodes.GETFIELD, cpuInternalName, "vcr", Type.getDescriptor(Vcr.class));
}
@Override
public void loadHilo() {
loadCpu();
mv.visitFieldInsn(Opcodes.GETFIELD, cpuInternalName, "hilo", Type.getDescriptor(long.class));
}
@Override
public void prepareHiloForStore() {
loadCpu();
hiloPrepared = true;
}
@Override
public void storeHilo() {
if (!hiloPrepared) {
loadCpu();
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn(Opcodes.POP);
}
mv.visitFieldInsn(Opcodes.PUTFIELD, cpuInternalName, "hilo", Type.getDescriptor(long.class));
hiloPrepared = false;
}
@Override
public void loadFcr31c() {
loadFcr31();
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(Fcr31.class), "c", "Z");
}
@Override
public void prepareFcr31cForStore() {
loadFcr31();
}
@Override
public void storeFcr31c() {
mv.visitFieldInsn(Opcodes.PUTFIELD, Type.getInternalName(Fcr31.class), "c", "Z");
}
public void loadVcrCc() {
loadVcrCc((codeInstruction.getOpcode() >> 18) & 7);
}
@Override
public void loadVcrCc(int cc) {
loadVcr();
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(Vcr.class), "cc", "[Z");
loadImm(cc);
mv.visitInsn(Opcodes.BALOAD);
}
@Override
public void loadLocalVar(int localVar) {
mv.visitVarInsn(Opcodes.ILOAD, localVar);
}
private void storeLocalVar(int localVar) {
mv.visitVarInsn(Opcodes.ISTORE, localVar);
}
private void loadInstruction(Instruction insn) {
String classInternalName = instructionsInternalName;
if (insn == Common.UNK) {
// UNK instruction is in Common class, not Instructions
classInternalName = Type.getInternalName(Common.class);
}
mv.visitFieldInsn(Opcodes.GETSTATIC, classInternalName, insn.name().replace('.', '_').replace(' ', '_'), instructionDescriptor);
}
@Override
public void storePc() {
loadCpu();
loadImm(codeInstruction.getAddress());
mv.visitFieldInsn(Opcodes.PUTFIELD, cpuInternalName, "pc", "I");
}
private void visitContinueToAddress(int returnAddress, boolean returnOnUnknownAddress) {
// if (x != returnAddress) {
// RuntimeContext.jump(x, returnAddress);
// }
Label continueLabel = new Label();
Label isReturnAddress = new Label();
mv.visitInsn(Opcodes.DUP);
loadImm(returnAddress);
visitJump(Opcodes.IF_ICMPEQ, isReturnAddress);
if (returnOnUnknownAddress) {
visitJump();
} else {
loadImm(returnAddress);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "jump", "(II)V");
mv.visitJumpInsn(Opcodes.GOTO, continueLabel);
}
mv.visitLabel(isReturnAddress);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(continueLabel);
}
public void visitJump() {
flushInstructionCount(true, false);
checkSync();
endMethod();
mv.visitInsn(Opcodes.IRETURN);
}
public void prepareCall(int address, int returnAddress, int returnRegister) {
preparedCallNativeCodeBlock = null;
// Do not call native block directly if we are profiling,
// this would loose profiler information
if (!Profiler.isProfilerEnabled()) {
// Is a native equivalent for this CodeBlock available?
preparedCallNativeCodeBlock = nativeCodeManager.getCompiledNativeCodeBlock(address);
}
if (preparedCallNativeCodeBlock == null) {
if (returnRegister != _zr) {
// Load the return register ($ra) with the return address
// before the delay slot is executed. The delay slot might overwrite it.
// For example:
// addiu $sp, $sp, -16
// sw $ra, 0($sp)
// jal 0x0XXXXXXX
// lw $ra, 0($sp)
// jr $ra
// addiu $sp, $sp, 16
prepareRegisterForStore(returnRegister);
loadImm(returnAddress);
storeRegister(returnRegister);
}
}
}
public void visitCall(int address, int returnAddress, int returnRegister, boolean returnRegisterModified, boolean returnOnUnknownAddress) {
flushInstructionCount(false, false);
if (preparedCallNativeCodeBlock != null) {
if (preparedCallNativeCodeBlock.getNativeCodeSequenceClass().equals(Nop.class)) {
// NativeCodeSequence Nop means nothing to do!
} else {
// Call NativeCodeSequence
if (log.isDebugEnabled()) {
log.debug(String.format("Inlining call at 0x%08X to %s", getCodeInstruction().getAddress(), preparedCallNativeCodeBlock));
}
visitNativeCodeSequence(preparedCallNativeCodeBlock, address, null);
}
} else {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, getClassName(address, instanceIndex), getStaticExecMethodName(), getStaticExecMethodDesc());
visitContinueToAddress(returnAddress, returnOnUnknownAddress);
}
preparedCallNativeCodeBlock = null;
}
public void visitCall(int returnAddress, int returnRegister) {
flushInstructionCount(false, false);
if (returnRegister != _zr) {
storeRegister(returnRegister, returnAddress);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "call", "(I)I");
visitContinueToAddress(returnAddress, false);
}
public void visitCall(int address, String methodName) {
flushInstructionCount(false, false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, getClassName(address, instanceIndex), methodName, "()V");
}
public void visitIntepreterCall(int opcode, Instruction insn) {
loadInstruction(insn);
loadProcessor();
loadImm(opcode);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, instructionInternalName, "interpret", "(" + processorDescriptor + "I)V");
}
private boolean isFastSyscall(int code) {
return fastSyscalls.contains(code);
}
/**
* Generate the required Java code to load one parameter for
* the syscall function from the CPU registers.
*
* The following code is generated based on the parameter type:
* Processor: parameterValue = RuntimeContext.processor
* int: parameterValue = cpu.gpr[paramIndex++]
* float: parameterValue = cpu.fpr[paramFloatIndex++]
* long: parameterValue = (cpu.gpr[paramIndex++] & 0xFFFFFFFFL) + ((long) cpu.gpr[paramIndex++]) << 32)
* boolean: parameterValue = cpu.gpr[paramIndex++]
* TPointer,
* TPointer8,
* TPointer16,
* TPointer32,
* TPointer64,
* TErrorPointer32:
* if (checkMemoryAccess()) {
* if (canBeNullParam && address == 0) {
* goto addressGood;
* }
* if (RuntimeContext.checkMemoryPointer(address)) {
* goto addressGood;
* }
* cpu.gpr[_v0] = SceKernelErrors.ERROR_INVALID_POINTER;
* pop all the parameters already prepared on the stack;
* goto afterSyscall;
* addressGood:
* }
* <parameterType> pointer = new <parameterType>(address);
* if (parameterType == TErrorPointer32.class) {
* parameterReader.setHasErrorPointer(true);
* localVar[LOCAL_ERROR_POINTER] = pointer;
* }
* parameterValue = pointer
* HLEUidClass defined in annotation:
* <parameterType> uidObject = HLEUidObjectMapping.getObject("<parameterType>", uid);
* if (uidObject == null) {
* cpu.gpr[_v0] = errorValueOnNotFound;
* pop all the parameters already prepared on the stack;
* goto afterSyscall;
* }
* parameterValue = uidObject
*
* And then common for all the types:
* try {
* parameterValue = <module>.<methodToCheck>(parameterValue);
* } catch (SceKernelErrorException e) {
* goto catchSceKernelErrorException;
* }
* push parameterValue on stack
*
* @param parameterReader the current parameter state
* @param func the syscall function
* @param parameterType the type of the parameter
* @param afterSyscallLabel the Label pointing after the call to the syscall function
* @param catchSceKernelErrorException the Label pointing to the SceKernelErrorException catch handler
*/
private void loadParameter(CompilerParameterReader parameterReader, HLEModuleFunction func, Class<?> parameterType, Annotation[] parameterAnnotations, Label afterSyscallLabel, Label catchSceKernelErrorException) {
if (parameterType == Processor.class) {
loadProcessor();
parameterReader.incrementCurrentStackSize();
} else if (parameterType == CpuState.class) {
loadCpu();
parameterReader.incrementCurrentStackSize();
} else if (parameterType == int.class) {
parameterReader.loadNextInt();
parameterReader.incrementCurrentStackSize();
} else if (parameterType == float.class) {
parameterReader.loadNextFloat();
parameterReader.incrementCurrentStackSize();
} else if (parameterType == long.class) {
parameterReader.loadNextLong();
parameterReader.incrementCurrentStackSize(2);
} else if (parameterType == boolean.class) {
parameterReader.loadNextInt();
parameterReader.incrementCurrentStackSize();
} else if (parameterType == String.class) {
parameterReader.loadNextInt();
int maxLength = 16 * 1024;
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation instanceof StringInfo) {
StringInfo stringInfo = ((StringInfo)parameterAnnotation);
maxLength = stringInfo.maxLength();
break;
}
}
loadImm(maxLength);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
runtimeContextInternalName,
"readStringNZ", "(II)" + Type.getDescriptor(String.class)
);
parameterReader.incrementCurrentStackSize();
} else if (parameterType == PspString.class) {
parameterReader.loadNextInt();
int maxLength = 16 * 1024;
boolean canBeNull = false;
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation instanceof StringInfo) {
StringInfo stringInfo = ((StringInfo)parameterAnnotation);
maxLength = stringInfo.maxLength();
}
if (parameterAnnotation instanceof CanBeNull) {
canBeNull = true;
}
}
loadImm(maxLength);
loadImm(canBeNull);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
runtimeContextInternalName,
"readPspStringNZ", "(IIZ)" + Type.getDescriptor(PspString.class)
);
parameterReader.incrementCurrentStackSize();
} else if (parameterType == TPointer.class || parameterType == TPointer8.class || parameterType == TPointer16.class || parameterType == TPointer32.class || parameterType == TPointer64.class || parameterType == TErrorPointer32.class) {
// if (checkMemoryAccess()) {
// if (canBeNullParam && address == 0) {
// goto addressGood;
// }
// if (RuntimeContext.checkMemoryPointer(address)) {
// goto addressGood;
// }
// cpu.gpr[_v0] = SceKernelErrors.ERROR_INVALID_POINTER;
// pop all the parameters already prepared on the stack;
// goto afterSyscall;
// addressGood:
// }
// <parameterType> pointer = new <parameterType>(address);
// if (parameterType == TErrorPointer32.class) {
// parameterReader.setHasErrorPointer(true);
// localVar[LOCAL_ERROR_POINTER] = pointer;
// }
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(parameterType));
mv.visitInsn(Opcodes.DUP);
loadMemory();
parameterReader.loadNextInt();
boolean canBeNull = false;
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation instanceof CanBeNull) {
canBeNull = true;
break;
}
}
if (checkMemoryAccess() && afterSyscallLabel != null) {
Label addressGood = new Label();
if (canBeNull) {
mv.visitInsn(Opcodes.DUP);
mv.visitJumpInsn(Opcodes.IFEQ, addressGood);
}
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryPointer", "(I)Z");
mv.visitJumpInsn(Opcodes.IFNE, addressGood);
storeRegister(_v0, SceKernelErrors.ERROR_INVALID_POINTER);
parameterReader.popAllStack(4);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
mv.visitLabel(addressGood);
}
if (parameterType == TPointer8.class || parameterType == TPointer16.class || parameterType == TPointer32.class || parameterType == TPointer64.class) {
loadImm(canBeNull);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parameterType), "<init>", "(" + memoryDescriptor + "IZ)V");
} else {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parameterType), "<init>", "(" + memoryDescriptor + "I)V");
}
if (parameterType == TErrorPointer32.class) {
parameterReader.setHasErrorPointer(true);
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ASTORE, LOCAL_ERROR_POINTER);
}
parameterReader.incrementCurrentStackSize();
} else if (pspAbstractMemoryMappedStructure.class.isAssignableFrom(parameterType)) {
parameterReader.loadNextInt();
boolean canBeNull = false;
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation instanceof CanBeNull) {
canBeNull = true;
break;
}
}
if (checkMemoryAccess() && afterSyscallLabel != null) {
Label addressGood = new Label();
if (canBeNull) {
mv.visitInsn(Opcodes.DUP);
mv.visitJumpInsn(Opcodes.IFEQ, addressGood);
}
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryPointer", "(I)Z");
mv.visitJumpInsn(Opcodes.IFNE, addressGood);
storeRegister(_v0, SceKernelErrors.ERROR_INVALID_POINTER);
parameterReader.popAllStack(1);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
mv.visitLabel(addressGood);
}
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(parameterType));
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parameterType), "<init>", "()V");
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
loadMemory();
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(parameterType), "read", "(" + memoryDescriptor + "I)V");
parameterReader.incrementCurrentStackSize();
} else {
HLEUidClass hleUidClass = parameterType.getAnnotation(HLEUidClass.class);
if (hleUidClass != null) {
int errorValueOnNotFound = hleUidClass.errorValueOnNotFound();
// <parameterType> uidObject = HLEUidObjectMapping.getObject("<parameterType>", uid);
// if (uidObject == null) {
// cpu.gpr[_v0] = errorValueOnNotFound;
// pop all the parameters already prepared on the stack;
// goto afterSyscall;
// }
mv.visitLdcInsn(parameterType.getName());
// Load the UID
parameterReader.loadNextInt();
// Load the UID Object
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(HLEUidObjectMapping.class), "getObject", "(" + Type.getDescriptor(String.class) + "I)" + Type.getDescriptor(Object.class));
if (afterSyscallLabel != null) {
Label foundUid = new Label();
mv.visitInsn(Opcodes.DUP);
mv.visitJumpInsn(Opcodes.IFNONNULL, foundUid);
storeRegister(_v0, errorValueOnNotFound);
parameterReader.popAllStack(1);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
mv.visitLabel(foundUid);
}
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(parameterType));
parameterReader.incrementCurrentStackSize();
} else {
log.error(String.format("Unsupported sycall parameter type '%s'", parameterType.getName()));
Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_UNIMPLEMENTED);
}
}
Method methodToCheck = null;
if (afterSyscallLabel != null) {
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation instanceof CheckArgument) {
CheckArgument checkArgument = (CheckArgument) parameterAnnotation;
try {
methodToCheck = func.getHLEModule().getClass().getMethod(checkArgument.value(), parameterType);
} catch (Exception e) {
log.error(String.format("CheckArgument method '%s' not found in %s", checkArgument.value(), func.getModuleName()), e);
}
break;
}
}
}
if (methodToCheck != null) {
// try {
// parameterValue = <module>.<methodToCheck>(parameterValue);
// } catch (SceKernelErrorException e) {
// goto catchSceKernelErrorException;
// }
loadModule(func.getModuleName());
mv.visitInsn(Opcodes.SWAP);
Label tryStart = new Label();
Label tryEnd = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchSceKernelErrorException, Type.getInternalName(SceKernelErrorException.class));
mv.visitLabel(tryStart);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(methodToCheck.getDeclaringClass()), methodToCheck.getName(), "(" + Type.getDescriptor(parameterType) + ")" + Type.getDescriptor(parameterType));
mv.visitLabel(tryEnd);
}
parameterReader.incrementCurrentParameterIndex();
}
/**
* Generate the required Java code to store the return value of
* the syscall function into the CPU registers.
*
* The following code is generated depending on the return type:
* void: -
* int: cpu.gpr[_v0] = intValue
* boolean: cpu.gpr[_v0] = booleanValue
* long: cpu.gpr[_v0] = (int) (longValue & 0xFFFFFFFFL)
* cpu.gpr[_v1] = (int) (longValue >>> 32)
* float: cpu.fpr[_f0] = floatValue
* HLEUidClass: if (moduleMethodUidGenerator == "") {
* cpu.gpr[_v0] = HLEUidObjectMapping.createUidForObject("<return type>", returnValue);
* } else {
* int uid = <module>.<moduleMethodUidGenerator>();
* cpu.gpr[_v0] = HLEUidObjectMapping.addObjectMap("<return type>", uid, returnValue);
* }
*
* @param func the syscall function
* @param returnType the type of the return value
*/
private void storeReturnValue(HLEModuleFunction func, Class<?> returnType) {
if (returnType == void.class) {
// Nothing to do
} else if (returnType == int.class) {
// cpu.gpr[_v0] = intValue
storeRegister(_v0);
} else if (returnType == boolean.class) {
// cpu.gpr[_v0] = booleanValue
storeRegister(_v0);
} else if (returnType == long.class) {
// cpu.gpr[_v0] = (int) (longValue & 0xFFFFFFFFL)
// cpu.gpr[_v1] = (int) (longValue >>> 32)
mv.visitInsn(Opcodes.DUP2);
mv.visitLdcInsn(0xFFFFFFFFL);
mv.visitInsn(Opcodes.LAND);
mv.visitInsn(Opcodes.L2I);
storeRegister(_v0);
loadImm(32);
mv.visitInsn(Opcodes.LSHR);
mv.visitInsn(Opcodes.L2I);
storeRegister(_v1);
} else if (returnType == float.class) {
// cpu.fpr[_f0] = floatValue
storeFRegister(_f0);
} else {
HLEUidClass hleUidClass = returnType.getAnnotation(HLEUidClass.class);
if (hleUidClass != null) {
// if (moduleMethodUidGenerator == "") {
// cpu.gpr[_v0] = HLEUidObjectMapping.createUidForObject("<return type>", returnValue);
// } else {
// int uid = <module>.<moduleMethodUidGenerator>();
// cpu.gpr[_v0] = HLEUidObjectMapping.addObjectMap("<return type>", uid, returnValue);
// }
if (hleUidClass.moduleMethodUidGenerator().length() <= 0) {
// No UID generator method, use the default one
mv.visitLdcInsn(returnType.getName());
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(HLEUidObjectMapping.class), "createUidForObject", "(" + Type.getDescriptor(String.class) + Type.getDescriptor(Object.class) + ")I");
storeRegister(_v0);
} else {
mv.visitLdcInsn(returnType.getName());
mv.visitInsn(Opcodes.SWAP);
loadModule(func.getModuleName());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(func.getHLEModuleMethod().getDeclaringClass()), hleUidClass.moduleMethodUidGenerator(), "()I");
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(HLEUidObjectMapping.class), "addObjectMap", "(" + Type.getDescriptor(String.class) + "I" + Type.getDescriptor(Object.class) + ")I");
storeRegister(_v0);
}
} else {
log.error(String.format("Unsupported sycall return value type '%s'", returnType.getName()));
}
}
}
private void loadModuleLoggger(HLEModuleFunction func) {
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(func.getHLEModuleMethod().getDeclaringClass()), "log", Type.getDescriptor(Logger.class));
}
private void logSyscall(HLEModuleFunction func, String logPrefix, String logCheckFunction, String logFunction) {
// Modules.getLogger(func.getModuleName()).warn("Unimplemented...");
loadModuleLoggger(func);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), logCheckFunction, "()Z");
Label loggingDisabled = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, loggingDisabled);
loadModuleLoggger(func);
StringBuilder formatString = new StringBuilder();
if (logPrefix != null) {
formatString.append(logPrefix);
}
formatString.append(func.getFunctionName());
ParameterInfo[] parameters = new ClassAnalyzer().getParameters(func.getFunctionName(), func.getHLEModuleMethod().getDeclaringClass());
if (parameters != null) {
// Log message:
// String.format(
// "Unimplemented <function name>
// <parameterIntegerName>=0x%X,
// <parameterBooleanName>=%b,
// <parameterLongName>=0x%X,
// <parameterFloatName>=%f,
// <parameterOtherTypeName>=%s",
// new Object[] {
// new Integer(parameterValueInteger),
// new Boolean(parameterValueBoolean),
// new Long(parameterValueLong),
// new Float(parameterValueFloat),
// parameterValueOtherTypes
// })
loadImm(parameters.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
CompilerParameterReader parameterReader = new CompilerParameterReader(this);
Annotation[][] paramsAnotations = func.getHLEModuleMethod().getParameterAnnotations();
int objectArrayIndex = 0;
for (int paramIndex = 0; paramIndex < parameters.length; paramIndex++) {
ParameterInfo parameter = parameters[paramIndex];
Class<?> parameterType = parameter.type;
CompilerTypeInformation typeInformation = compilerTypeManager.getCompilerTypeInformation(parameterType);
mv.visitInsn(Opcodes.DUP);
loadImm(objectArrayIndex);
formatString.append(paramIndex > 0 ? ", " : " ");
formatString.append(parameter.name);
formatString.append("=");
formatString.append(typeInformation.formatString);
if (typeInformation.boxingTypeInternalName != null) {
mv.visitTypeInsn(Opcodes.NEW, typeInformation.boxingTypeInternalName);
mv.visitInsn(Opcodes.DUP);
}
loadParameter(parameterReader, func, parameterType, paramsAnotations[paramIndex], null, null);
if (typeInformation.boxingTypeInternalName != null) {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, typeInformation.boxingTypeInternalName, "<init>", typeInformation.boxingMethodDescriptor);
}
mv.visitInsn(Opcodes.AASTORE);
objectArrayIndex++;
}
mv.visitLdcInsn(formatString.toString());
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), "format", "(" + Type.getDescriptor(String.class) + "[" + Type.getDescriptor(Object.class) + ")" + Type.getDescriptor(String.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), logFunction, "(" + Type.getDescriptor(Object.class) + ")V");
parameterReader = new CompilerParameterReader(this);
for (int paramIndex = 0; paramIndex < parameters.length; paramIndex++) {
ParameterInfo parameter = parameters[paramIndex];
Class<?> parameterType = parameter.type;
LengthInfo lengthInfo = BufferInfo.defaultLengthInfo;
int length = BufferInfo.defaultLength;
Usage usage = BufferInfo.defaultUsage;
for (Annotation parameterAnnotation : paramsAnotations[paramIndex]) {
if (parameterAnnotation instanceof BufferInfo) {
BufferInfo bufferInfo = (BufferInfo) parameterAnnotation;
lengthInfo = bufferInfo.lengthInfo();
length = bufferInfo.length();
usage = bufferInfo.usage();
}
}
boolean parameterRead = false;
if ((usage == Usage.in || usage == Usage.inout) && (lengthInfo != LengthInfo.unknown || parameterType == TPointer16.class || parameterType == TPointer32.class || parameterType == TPointer64.class)) {
loadModuleLoggger(func);
loadImm(1);
mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
mv.visitInsn(Opcodes.DUP);
loadImm(0);
Label done = new Label();
Label addressNull = new Label();
parameterReader.loadNextInt();
parameterRead = true;
mv.visitInsn(Opcodes.DUP);
mv.visitJumpInsn(Opcodes.IFEQ, addressNull);
String format = String.format("%s[%s]:%%s", parameter.name, usage);
boolean useMemoryDump = true;
switch (lengthInfo) {
case fixedLength:
loadImm(length);
break;
case nextNextParameter:
parameterReader.skipNextInt();
paramIndex++;
parameterReader.loadNextInt();
paramIndex++;
break;
case nextParameter:
parameterReader.loadNextInt();
paramIndex++;
break;
case previousParameter:
// Go back to the address parameter
parameterReader.rewindPreviousInt();
// Go back to the previous parameter
parameterReader.rewindPreviousInt();
// Load the length from the previous parameter
parameterReader.loadNextInt();
// Skip again the address parameter
// to come back to the above situation
parameterReader.skipNextInt();
break;
case variableLength:
mv.visitInsn(Opcodes.DUP);
loadMemory();
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
break;
case unknown:
useMemoryDump = false;
format = String.format("%s[%s]: 0x%%X", parameter.name, usage);
loadMemory();
mv.visitInsn(Opcodes.SWAP);
if (parameterType == TPointer64.class) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read64", "(I)J");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Long.class));
mv.visitInsn(Opcodes.DUP);
mv.visitInsn(Opcodes.DUP2_X2);
mv.visitInsn(Opcodes.POP2);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Long.class), "<init>", "(J)V");
} else if (parameterType == TPointer16.class) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read16", "(I)I");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Integer.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Integer.class), "<init>", "(I)V");
} else {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Integer.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Integer.class), "<init>", "(I)V");
}
break;
default:
log.error(String.format("Unimplemented lengthInfo=%s", lengthInfo));
break;
}
if (useMemoryDump) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Utilities.class), "getMemoryDump", "(II)" + Type.getDescriptor(String.class));
}
mv.visitInsn(Opcodes.AASTORE);
mv.visitLdcInsn(format);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), "format", "(" + Type.getDescriptor(String.class) + "[" + Type.getDescriptor(Object.class) + ")" + Type.getDescriptor(String.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), logFunction, "(" + Type.getDescriptor(Object.class) + ")V");
mv.visitJumpInsn(Opcodes.GOTO, done);
mv.visitLabel(addressNull);
mv.visitInsn(Opcodes.POP);
mv.visitInsn(Opcodes.POP2);
mv.visitInsn(Opcodes.POP2);
mv.visitLabel(done);
}
if (!parameterRead) {
if (parameterType == long.class) {
parameterReader.skipNextLong();
} else if (parameterType == float.class) {
parameterReader.skipNextFloat();
} else {
parameterReader.skipNextInt();
}
}
}
} else {
mv.visitLdcInsn(formatString.toString());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), logFunction, "(" + Type.getDescriptor(Object.class) + ")V");
}
mv.visitLabel(loggingDisabled);
}
private String getLogCheckFunction(String loggingLevel) {
String logCheckFunction = "isInfoEnabled";
if ("trace".equals(loggingLevel)) {
logCheckFunction = "isTraceEnabled";
} else if ("debug".equals(loggingLevel)) {
logCheckFunction = "isDebugEnabled";
}
return logCheckFunction;
}
private String getLoggingLevel(HLEModuleFunction func) {
String loggingLevel = func.getLoggingLevel();
if (loggingLevel != null) {
if (func.isUnimplemented() && codeBlock.isHLEFunction()) {
// Do not log at the WARN level HLE methods that are
// unimplemented but have been overwritten by real PSP modules
if ("warn".equals(loggingLevel)) {
loggingLevel = "debug";
}
}
}
return loggingLevel;
}
private void logSyscallStart(HLEModuleFunction func) {
String loggingLevel = getLoggingLevel(func);
if (loggingLevel != null) {
String prefix = null;
if (func.isUnimplemented() && !codeBlock.isHLEFunction()) {
prefix = "Unimplemented ";
}
logSyscall(func, prefix, getLogCheckFunction(loggingLevel), loggingLevel);
}
}
private void logSyscallEnd(HLEModuleFunction func, boolean isErrorCode) {
String loggingLevel = getLoggingLevel(func);
if (loggingLevel == null) {
return;
}
String logCheckFunction = getLogCheckFunction(loggingLevel);
// if (Modules.getLogger(func.getModuleName()).isDebugEnabled()) {
// Modules.getLogger(func.getModuleName()).debug(String.format("<function name> returning 0x%X", new Object[1] { new Integer(returnValue) }));
// }
loadModuleLoggger(func);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), logCheckFunction, "()Z");
Label notDebug = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, notDebug);
boolean isReturningVoid = func.getHLEModuleMethod().getReturnType() == void.class;
mv.visitInsn(Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Integer.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Integer.class), "<init>", "(I)V");
loadImm(1);
mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
loadImm(0);
mv.visitInsn(Opcodes.SWAP);
mv.visitInsn(Opcodes.AASTORE);
String prefix = func.isUnimplemented() && !codeBlock.isHLEFunction() ? "Unimplemented " : "";
mv.visitLdcInsn(String.format("%s%s returning %s%s", prefix, func.getFunctionName(), isErrorCode ? "errorCode " : "", isReturningVoid ? "void" : "0x%X"));
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), "format", "(" + Type.getDescriptor(String.class) + "[" + Type.getDescriptor(Object.class) + ")" + Type.getDescriptor(String.class));
loadModuleLoggger(func);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), loggingLevel, "(" + Type.getDescriptor(Object.class) + ")V");
if (!isErrorCode) {
ParameterInfo[] parameters = new ClassAnalyzer().getParameters(func.getFunctionName(), func.getHLEModuleMethod().getDeclaringClass());
if (parameters != null) {
CompilerParameterReader parameterReader;
if (parametersSavedToLocals) {
parameterReader = new CompilerLocalVarParameterReader(this, LOCAL_FIRST_SAVED_PARAMETER);
} else {
parameterReader = new CompilerParameterReader(this);
}
Annotation[][] paramsAnotations = func.getHLEModuleMethod().getParameterAnnotations();
for (int paramIndex = 0; paramIndex < parameters.length; paramIndex++) {
ParameterInfo parameter = parameters[paramIndex];
Class<?> parameterType = parameter.type;
LengthInfo lengthInfo = BufferInfo.defaultLengthInfo;
int length = BufferInfo.defaultLength;
Usage usage = BufferInfo.defaultUsage;
boolean debugMemory = false;
for (Annotation parameterAnnotation : paramsAnotations[paramIndex]) {
if (parameterAnnotation instanceof BufferInfo) {
BufferInfo bufferInfo = (BufferInfo) parameterAnnotation;
lengthInfo = bufferInfo.lengthInfo();
length = bufferInfo.length();
usage = bufferInfo.usage();
} else if (parameterAnnotation instanceof DebugMemory) {
debugMemory = true;
}
}
boolean parameterRead = false;
if ((usage == Usage.out || usage == Usage.inout) && (lengthInfo != LengthInfo.unknown || parameterType == TPointer16.class || parameterType == TPointer32.class || parameterType == TPointer64.class)) {
loadModuleLoggger(func);
loadImm(1);
mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
mv.visitInsn(Opcodes.DUP);
loadImm(0);
Label done = new Label();
Label addressNull = new Label();
parameterReader.loadNextInt();
parameterRead = true;
mv.visitInsn(Opcodes.DUP);
mv.visitJumpInsn(Opcodes.IFEQ, addressNull);
String format = String.format("%s[%s]:%%s", parameter.name, usage);
boolean useMemoryDump = true;
switch (lengthInfo) {
case fixedLength:
loadImm(length);
break;
case nextNextParameter:
parameterReader.skipNextInt();
paramIndex++;
parameterReader.loadNextInt();
paramIndex++;
break;
case nextParameter:
parameterReader.loadNextInt();
paramIndex++;
break;
case previousParameter:
// Go back to the address parameter
parameterReader.rewindPreviousInt();
// Go back to the previous parameter
parameterReader.rewindPreviousInt();
// Load the length from the previous parameter
parameterReader.loadNextInt();
// Skip again the address parameter
// to come back to the above situation
parameterReader.skipNextInt();
break;
case variableLength:
mv.visitInsn(Opcodes.DUP);
loadMemory();
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
break;
case returnValue:
loadRegister(_v0);
break;
case unknown:
useMemoryDump = false;
format = String.format("%s[%s]: 0x%%X", parameter.name, usage);
loadMemory();
mv.visitInsn(Opcodes.SWAP);
if (parameterType == TPointer64.class) {
if (debugMemory) {
mv.visitInsn(Opcodes.DUP);
loadImm(8);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemory", "(II)V");
}
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read64", "(I)J");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Long.class));
mv.visitInsn(Opcodes.DUP);
mv.visitInsn(Opcodes.DUP2_X2);
mv.visitInsn(Opcodes.POP2);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Long.class), "<init>", "(J)V");
} else if (parameterType == TPointer16.class) {
if (debugMemory) {
mv.visitInsn(Opcodes.DUP);
loadImm(2);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemory", "(II)V");
}
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read16", "(I)I");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Integer.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Integer.class), "<init>", "(I)V");
} else {
if (debugMemory) {
mv.visitInsn(Opcodes.DUP);
loadImm(4);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemory", "(II)V");
}
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Integer.class));
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Integer.class), "<init>", "(I)V");
}
break;
default:
log.error(String.format("Unimplemented lengthInfo=%s", lengthInfo));
break;
}
if (useMemoryDump) {
if (debugMemory) {
mv.visitInsn(Opcodes.DUP2);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemory", "(II)V");
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Utilities.class), "getMemoryDump", "(II)" + Type.getDescriptor(String.class));
}
mv.visitInsn(Opcodes.AASTORE);
mv.visitLdcInsn(format);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), "format", "(" + Type.getDescriptor(String.class) + "[" + Type.getDescriptor(Object.class) + ")" + Type.getDescriptor(String.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), loggingLevel, "(" + Type.getDescriptor(Object.class) + ")V");
mv.visitJumpInsn(Opcodes.GOTO, done);
mv.visitLabel(addressNull);
mv.visitInsn(Opcodes.POP);
mv.visitInsn(Opcodes.POP2);
mv.visitInsn(Opcodes.POP2);
mv.visitLabel(done);
}
if (!parameterRead) {
if (parameterType == long.class) {
parameterReader.skipNextLong();
} else if (parameterType == float.class) {
parameterReader.skipNextFloat();
} else {
parameterReader.skipNextInt();
}
}
}
}
}
mv.visitLabel(notDebug);
}
/**
* Generate the required Java code to call a syscall function.
* The code generated must match the Java behavior implemented in
* jpcsp.HLE.modules.HLEModuleFunctionReflection
*
* The following code is generated:
* if (func.getFirmwareVersion() <= RuntimeContext.firmwareVersion) {
* if (!fastSyscall) {
* RuntimeContext.preSyscall();
* }
* if (func.checkInsideInterrupt()) {
* if (IntrManager.getInstance.isInsideInterrupt()) {
* cpu.gpr[_v0] = SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT;
* goto afterSyscall;
* }
* }
* if (func.checkDispatchThreadEnabled()) {
* if (!Modules.ThreadManForUserModule.isDispatchThreadEnabled()) {
* cpu.gpr[_v0] = SceKernelErrors.ERROR_KERNEL_WAIT_CAN_NOT_WAIT;
* goto afterSyscall;
* }
* }
* if (func.isUnimplemented()) {
* Modules.getLogger(func.getModuleName()).warn("Unimplemented <function name> parameterName1=parameterValue1, parameterName2=parameterValue2, ...");
* }
* foreach parameter {
* loadParameter(parameter);
* }
* try {
* returnValue = <module name>.<function name>(...parameters...);
* storeReturnValue();
* if (parameterReader.hasErrorPointer()) {
* errorPointer.setValue(0);
* }
* } catch (SceKernelErrorException e) {
* errorCode = e.errorCode;
* if (Modules.getLogger(func.getModuleName()).isDebugEnabled()) {
* Modules.getLogger(func.getModuleName()).debug(String.format("<function name> return errorCode 0x%08X", errorCode));
* }
* if (parameterReader.hasErrorPointer()) {
* errorPointer.setValue(errorCode);
* cpu.gpr[_v0] = 0;
* } else {
* cpu.gpr[_v0] = errorCode;
* }
* reload cpu.gpr[_ra]; // an exception is always clearing the whole stack
* }
* afterSyscall:
* if (fastSyscall) {
* RuntimeContext.postSyscallFast();
* } else {
* RuntimeContext.postSyscall();
* }
* } else {
* Modules.getLogger(func.getModuleName()).warn("<function name> is not supported in firmware version <firmwareVersion>, it requires at least firmware version <function firmwareVersion>");
* cpu.gpr[_v0] = -1;
* }
*
* @param func the syscall function
* @param fastSyscall true if this is a fast syscall (i.e. without context switching)
* false if not (i.e. a syscall where context switching could happen)
*/
private void visitSyscall(HLEModuleFunction func, boolean fastSyscall) {
// The compilation of a syscall requires more stack size than usual
maxStackSize = SYSCALL_MAX_STACK_SIZE;
boolean needFirmwareVersionCheck = true;
if (func.getFirmwareVersion() >= 999) {
// Dummy version number meaning valid for all versions
needFirmwareVersionCheck = false;
} else if (codeInstruction.getAddress() < MemoryMap.START_USERSPACE) {
// When compiling code in the kernel memory space, do not perform any version check.
// This is used by overwritten HLE functions.
needFirmwareVersionCheck = false;
} else {
// When compiling code loaded from flash0, do not perform any version check.
// This is used by overwritten HLE functions.
SceModule module = Managers.modules.getModuleByAddress(codeInstruction.getAddress());
if (module != null && module.pspfilename != null && module.pspfilename.startsWith("flash0:")) {
if (log.isDebugEnabled()) {
log.debug(String.format("syscall from a flash0 module(%s, '%s'), no firmware version check", module, module.pspfilename));
}
needFirmwareVersionCheck = false;
}
}
Label unsupportedVersionLabel = null;
if (needFirmwareVersionCheck) {
unsupportedVersionLabel = new Label();
loadImm(func.getFirmwareVersion());
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "firmwareVersion", "I");
mv.visitJumpInsn(Opcodes.IF_ICMPGT, unsupportedVersionLabel);
}
// Save the syscall parameter to locals for debugging
if (!fastSyscall) {
saveParametersToLocals();
}
if (!fastSyscall) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "preSyscall", "()V");
}
Label afterSyscallLabel = new Label();
if (func.checkInsideInterrupt()) {
// if (IntrManager.getInstance().isInsideInterrupt()) {
// if (Modules.getLogger(func.getModuleName()).isDebugEnabled()) {
// Modules.getLogger(func.getModuleName()).debug("<function name> return errorCode 0x80020064 (ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT)");
// }
// cpu.gpr[_v0] = SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT;
// goto afterSyscall
// }
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(IntrManager.class), "getInstance", "()" + Type.getDescriptor(IntrManager.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(IntrManager.class), "isInsideInterrupt", "()Z");
Label notInsideInterrupt = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, notInsideInterrupt);
loadModuleLoggger(func);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), "isDebugEnabled", "()Z");
Label notDebug = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, notDebug);
loadModuleLoggger(func);
mv.visitLdcInsn(String.format("%s returning errorCode 0x%08X (ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT)", func.getFunctionName(), SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), "debug", "(" + Type.getDescriptor(Object.class) + ")V");
mv.visitLabel(notDebug);
storeRegister(_v0, SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
mv.visitLabel(notInsideInterrupt);
}
if (func.checkDispatchThreadEnabled()) {
// if (!Modules.ThreadManForUserModule.isDispatchThreadEnabled() || !Interrupts.isInterruptsEnabled()) {
// if (Modules.getLogger(func.getModuleName()).isDebugEnabled()) {
// Modules.getLogger(func.getModuleName()).debug("<function name> return errorCode 0x800201A7 (ERROR_KERNEL_WAIT_CAN_NOT_WAIT)");
// }
// cpu.gpr[_v0] = SceKernelErrors.ERROR_KERNEL_WAIT_CAN_NOT_WAIT;
// goto afterSyscall
// }
loadModule("ThreadManForUser");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(ThreadManForUser.class), "isDispatchThreadEnabled", "()Z");
Label returnError = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, returnError);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Interrupts.class), "isInterruptsEnabled", "()Z");
Label noError = new Label();
mv.visitJumpInsn(Opcodes.IFNE, noError);
mv.visitLabel(returnError);
loadModuleLoggger(func);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), "isDebugEnabled", "()Z");
Label notDebug = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, notDebug);
loadModuleLoggger(func);
mv.visitLdcInsn(String.format("%s returning errorCode 0x%08X (ERROR_KERNEL_WAIT_CAN_NOT_WAIT)", func.getFunctionName(), SceKernelErrors.ERROR_KERNEL_WAIT_CAN_NOT_WAIT));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), "debug", "(" + Type.getDescriptor(Object.class) + ")V");
mv.visitLabel(notDebug);
storeRegister(_v0, SceKernelErrors.ERROR_KERNEL_WAIT_CAN_NOT_WAIT);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
mv.visitLabel(noError);
}
logSyscallStart(func);
if (func.hasStackUsage()) {
loadMemory();
loadRegister(_sp);
loadImm(func.getStackUsage());
mv.visitInsn(Opcodes.ISUB);
loadImm(0);
loadImm(func.getStackUsage());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "memset", "(IBI)V");
}
// Collecting the parameters and calling the module function...
CompilerParameterReader parameterReader = new CompilerParameterReader(this);
loadModule(func.getModuleName());
parameterReader.incrementCurrentStackSize();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchSceKernelErrorException = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchSceKernelErrorException, Type.getInternalName(SceKernelErrorException.class));
Class<?>[] parameterTypes = func.getHLEModuleMethod().getParameterTypes();
Class<?> returnType = func.getHLEModuleMethod().getReturnType();
StringBuilder methodDescriptor = new StringBuilder();
methodDescriptor.append("(");
Annotation[][] paramsAnotations = func.getHLEModuleMethod().getParameterAnnotations();
int paramIndex = 0;
for (Class<?> parameterType : parameterTypes) {
methodDescriptor.append(Type.getDescriptor(parameterType));
loadParameter(parameterReader, func, parameterType, paramsAnotations[paramIndex], afterSyscallLabel, catchSceKernelErrorException);
paramIndex++;
}
methodDescriptor.append(")");
methodDescriptor.append(Type.getDescriptor(returnType));
mv.visitLabel(tryStart);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(func.getHLEModuleMethod().getDeclaringClass()), func.getFunctionName(), methodDescriptor.toString());
storeReturnValue(func, returnType);
if (parameterReader.hasErrorPointer()) {
// errorPointer.setValue(0);
mv.visitVarInsn(Opcodes.ALOAD, LOCAL_ERROR_POINTER);
loadImm(0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(TErrorPointer32.class), "setValue", "(I)V");
}
loadRegister(_v0);
logSyscallEnd(func, false);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, afterSyscallLabel);
// catch (SceKernelErrorException e) {
// errorCode = e.errorCode;
// if (Modules.log.isDebugEnabled()) {
// Modules.log.debug(String.format("<function name> return errorCode 0x%08X", errorCode));
// }
// if (hasErrorPointer()) {
// errorPointer.setValue(errorCode);
// cpu.gpr[_v0] = 0;
// } else {
// cpu.gpr[_v0] = errorCode;
// }
// }
mv.visitLabel(catchSceKernelErrorException);
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(SceKernelErrorException.class), "errorCode", "I");
logSyscallEnd(func, true);
if (parameterReader.hasErrorPointer()) {
// errorPointer.setValue(errorCode);
// cpu.gpr[_v0] = 0;
mv.visitVarInsn(Opcodes.ALOAD, LOCAL_ERROR_POINTER);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(TErrorPointer32.class), "setValue", "(I)V");
storeRegister(_v0, 0);
} else {
// cpu.gpr[_v0] = errorCode;
storeRegister(_v0);
}
// Reload the $ra register, the stack is lost after an exception
CodeInstruction previousInstruction = codeBlock.getCodeInstruction(codeInstruction.getAddress() - 4);
if (previousInstruction != null && previousInstruction.getInsn() == Instructions.JR) {
int jumpRegister = (previousInstruction.getOpcode() >> 21) & 0x1F;
loadRegister(jumpRegister);
}
mv.visitLabel(afterSyscallLabel);
if (fastSyscall) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "postSyscallFast", "()V");
} else {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "postSyscall", "()V");
}
if (needFirmwareVersionCheck) {
Label afterVersionCheckLabel = new Label();
mv.visitJumpInsn(Opcodes.GOTO, afterVersionCheckLabel);
mv.visitLabel(unsupportedVersionLabel);
loadModuleLoggger(func);
mv.visitLdcInsn(String.format("%s is not supported in firmware version %d, it requires at least firmware version %d", func.getFunctionName(), RuntimeContext.firmwareVersion, func.getFirmwareVersion()));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Logger.class), "warn", "(" + Type.getDescriptor(Object.class) + ")V");
storeRegister(_v0, -1);
mv.visitLabel(afterVersionCheckLabel);
}
}
/**
* Generate the required Java code to perform a syscall.
*
* When the syscall function is an HLEModuleFunctionReflection,
* generate the code for calling the module function directly, as
* HLEModuleFunctionReflection.execute() would.
*
* Otherwise, generate the code for calling
* RuntimeContext.syscall()
* or
* RuntimeContext.syscallFast()
*
* @param opcode opcode of the instruction
*/
public void visitSyscall(int opcode) {
flushInstructionCount(false, false);
int code = (opcode >> 6) & 0x000FFFFF;
int syscallAddr = NIDMapper.getInstance().getAddressBySyscall(code);
// Call the HLE method only when it has not been overwritten
if (syscallAddr != 0) {
if (log.isDebugEnabled()) {
log.debug(String.format("Calling overwritten HLE method '%s' instead of syscall", NIDMapper.getInstance().getNameBySyscall(code)));
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, getClassName(syscallAddr, instanceIndex), getStaticExecMethodName(), getStaticExecMethodDesc());
} else {
if (code == SyscallHandler.syscallUnmappedImport) {
storePc();
}
HLEModuleFunction func = HLEModuleManager.getInstance().getFunctionFromSyscallCode(code);
boolean fastSyscall = isFastSyscall(code);
if (func == null) {
loadImm(code);
if (fastSyscall) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "syscallFast", "(I)V");
} else {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "syscall", "(I)V");
}
} else {
visitSyscall(func, fastSyscall);
}
// The following registers are always set to 0xDEADBEEF after a syscall
int deadbeef = 0xDEADBEEF;
storeRegister(_a0, deadbeef);
storeRegister(_a1, deadbeef);
storeRegister(_a2, deadbeef);
storeRegister(_a3, deadbeef);
storeRegister(_t0, deadbeef);
storeRegister(_t1, deadbeef);
storeRegister(_t2, deadbeef);
storeRegister(_t3, deadbeef);
storeRegister(_t4, deadbeef);
storeRegister(_t5, deadbeef);
storeRegister(_t6, deadbeef);
storeRegister(_t7, deadbeef);
storeRegister(_t8, deadbeef);
storeRegister(_t9, deadbeef);
prepareHiloForStore();
mv.visitLdcInsn(new Long(0xDEADBEEFDEADBEEFL));
storeHilo();
}
}
public void startClass(ClassVisitor cv) {
if (RuntimeContext.enableLineNumbers) {
cv.visitSource(getCodeBlock().getClassName() + ".java", null);
}
}
public void startSequenceMethod() {
if (storeCpuLocal) {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "cpu", cpuDescriptor);
mv.visitVarInsn(Opcodes.ASTORE, LOCAL_CPU);
}
if (storeMemoryIntLocal) {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "memoryInt", "[I");
mv.visitVarInsn(Opcodes.ASTORE, LOCAL_MEMORY_INT);
}
if (enableIntructionCounting) {
currentInstructionCount = 0;
mv.visitInsn(Opcodes.ICONST_0);
storeLocalVar(LOCAL_INSTRUCTION_COUNT);
}
startNonBranchingCodeSequence();
}
public void endSequenceMethod() {
flushInstructionCount(false, true);
mv.visitInsn(Opcodes.RETURN);
}
public void checkSync() {
if (RuntimeContext.enableDaemonThreadSync) {
Label doNotWantSync = new Label();
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "wantSync", "Z");
mv.visitJumpInsn(Opcodes.IFEQ, doNotWantSync);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.syncName, "()V");
mv.visitLabel(doNotWantSync);
}
}
private void saveParametersToLocals() {
// Store all register parameters ($a0..$a3, $t0..$t3) in local variables.
// These values will be used at the end of the HLE method for debugging buffers.
for (int i = 0; i < LOCAL_NUMBER_SAVED_PARAMETERS; i++) {
loadRegister(_a0 + i);
storeLocalVar(LOCAL_FIRST_SAVED_PARAMETER + i);
}
maxLocalSize = LOCAL_MAX_WITH_SAVED_PARAMETERS;
parametersSavedToLocals = true;
}
private void startHLEMethod() {
HLEModuleFunction func = HLEModuleManager.getInstance().getFunctionFromAddress(codeBlock.getStartAddress());
codeBlock.setHLEFunction(func);
if (codeBlock.isHLEFunction()) {
saveParametersToLocals();
logSyscallStart(codeBlock.getHLEFunction());
}
}
private void endHLEMethod() {
if (codeBlock.isHLEFunction()) {
loadRegister(_v0);
logSyscallEnd(codeBlock.getHLEFunction(), false);
mv.visitInsn(Opcodes.POP);
}
}
private void startInternalMethod() {
// if (e != null)
Label notReplacedLabel = new Label();
mv.visitFieldInsn(Opcodes.GETSTATIC, codeBlock.getClassName(), getReplaceFieldName(), executableDescriptor);
mv.visitJumpInsn(Opcodes.IFNULL, notReplacedLabel);
{
// return e.exec(returnAddress, alternativeReturnAddress, isJump);
mv.visitFieldInsn(Opcodes.GETSTATIC, codeBlock.getClassName(), getReplaceFieldName(), executableDescriptor);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, executableInternalName, getExecMethodName(), getExecMethodDesc());
mv.visitInsn(Opcodes.IRETURN);
}
mv.visitLabel(notReplacedLabel);
if (Profiler.isProfilerEnabled()) {
loadImm(getCodeBlock().getStartAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, profilerInternalName, "addCall", "(I)V");
}
if (RuntimeContext.debugCodeBlockCalls) {
loadImm(getCodeBlock().getStartAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.debugCodeBlockStart, "(I)V");
}
}
public void startMethod() {
startInternalMethod();
startSequenceMethod();
startHLEMethod();
}
private void flushInstructionCount(boolean local, boolean last) {
if (enableIntructionCounting) {
if (local) {
if (currentInstructionCount > 0) {
mv.visitIincInsn(LOCAL_INSTRUCTION_COUNT, currentInstructionCount);
}
} else {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "currentThread", sceKernalThreadInfoDescriptor);
mv.visitInsn(Opcodes.DUP);
mv.visitFieldInsn(Opcodes.GETFIELD, sceKernalThreadInfoInternalName, "runClocks", "J");
loadLocalVar(LOCAL_INSTRUCTION_COUNT);
if (currentInstructionCount > 0) {
loadImm(currentInstructionCount);
mv.visitInsn(Opcodes.IADD);
}
if (Profiler.isProfilerEnabled()) {
mv.visitInsn(Opcodes.DUP);
loadImm(getCodeBlock().getStartAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, profilerInternalName, "addInstructionCount", "(II)V");
}
mv.visitInsn(Opcodes.I2L);
mv.visitInsn(Opcodes.LADD);
mv.visitFieldInsn(Opcodes.PUTFIELD, sceKernalThreadInfoInternalName, "runClocks", "J");
if (!last) {
mv.visitInsn(Opcodes.ICONST_0);
storeLocalVar(LOCAL_INSTRUCTION_COUNT);
}
}
currentInstructionCount = 0;
}
}
private void endInternalMethod() {
if (RuntimeContext.debugCodeBlockCalls) {
mv.visitInsn(Opcodes.DUP);
loadImm(getCodeBlock().getStartAddress());
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.debugCodeBlockEnd, "(II)V");
}
}
public void endMethod() {
endInternalMethod();
endHLEMethod();
flushInstructionCount(false, true);
}
public void beforeInstruction(CodeInstruction codeInstruction) {
if (enableIntructionCounting) {
if (codeInstruction.isBranchTarget()) {
flushInstructionCount(true, false);
}
currentInstructionCount++;
}
if (RuntimeContext.enableLineNumbers) {
// Force the instruction to emit a label
codeInstruction.getLabel(false);
}
}
private void startNonBranchingCodeSequence() {
vfpuPfxsState.reset();
vfpuPfxtState.reset();
vfpuPfxdState.reset();
}
private boolean isNonBranchingCodeSequence(CodeInstruction codeInstruction) {
return !codeInstruction.isBranchTarget() && !codeInstruction.isBranching();
}
public void startInstruction(CodeInstruction codeInstruction) {
if (RuntimeContext.enableLineNumbers) {
int lineNumber = codeInstruction.getAddress() - getCodeBlock().getLowestAddress();
// Java line number is unsigned 16bits
if (lineNumber >= 0 && lineNumber <= 0xFFFF) {
mv.visitLineNumber(lineNumber, codeInstruction.getLabel());
}
}
if (RuntimeContext.debugCodeInstruction) {
loadImm(codeInstruction.getAddress());
loadImm(codeInstruction.getOpcode());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.debugCodeInstructionName, "(II)V");
}
if (RuntimeContext.enableInstructionTypeCounting) {
if (codeInstruction.getInsn() != null) {
loadInstruction(codeInstruction.getInsn());
loadImm(codeInstruction.getOpcode());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.instructionTypeCount, "(" + instructionDescriptor + "I)V");
}
}
if (RuntimeContext.enableDebugger) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.debuggerName, "(I)V");
}
if (RuntimeContext.checkCodeModification && !(codeInstruction instanceof NativeCodeInstruction)) {
// Generate the following sequence:
//
// if (memory.read32(pc) != opcode) {
// RuntimeContext.onCodeModification(pc, opcode);
// }
//
loadMemory();
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
loadImm(codeInstruction.getOpcode());
Label codeUnchanged = new Label();
mv.visitJumpInsn(Opcodes.IF_ICMPEQ, codeUnchanged);
loadImm(codeInstruction.getAddress());
loadImm(codeInstruction.getOpcode());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "onCodeModification", "(II)V");
mv.visitLabel(codeUnchanged);
}
if (!isNonBranchingCodeSequence(codeInstruction)) {
startNonBranchingCodeSequence();
}
// This instructions consumes the PFXT prefix but does not use it.
if (codeInstruction.hasFlags(Instruction.FLAG_CONSUMES_VFPU_PFXT)) {
disablePfxSrc(vfpuPfxtState);
}
}
private void disablePfxSrc(VfpuPfxSrcState pfxSrcState) {
pfxSrcState.pfxSrc.enabled = false;
pfxSrcState.setKnown(true);
}
private void disablePfxDst(VfpuPfxDstState pfxDstState) {
pfxDstState.pfxDst.enabled = false;
pfxDstState.setKnown(true);
}
public void endInstruction() {
if (codeInstruction != null) {
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXS)) {
disablePfxSrc(vfpuPfxsState);
}
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXT)) {
disablePfxSrc(vfpuPfxtState);
}
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXD)) {
disablePfxDst(vfpuPfxdState);
}
}
}
public void visitJump(int opcode, CodeInstruction target) {
// Back branch? i.e probably a loop
if (target.getAddress() <= getCodeInstruction().getAddress()) {
checkSync();
if (Profiler.isProfilerEnabled()) {
loadImm(getCodeInstruction().getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, profilerInternalName, "addBackBranch", "(I)V");
}
}
visitJump(opcode, target.getLabel());
}
public void visitJump(int opcode, Label label) {
flushInstructionCount(true, false);
mv.visitJumpInsn(opcode, label);
}
public void visitJump(int opcode, int address) {
flushInstructionCount(true, false);
if (opcode == Opcodes.GOTO) {
loadImm(address);
visitJump();
} else {
Label jumpTarget = new Label();
Label notJumpTarget = new Label();
mv.visitJumpInsn(opcode, jumpTarget);
mv.visitJumpInsn(Opcodes.GOTO, notJumpTarget);
mv.visitLabel(jumpTarget);
loadImm(address);
visitJump();
mv.visitLabel(notJumpTarget);
}
}
public static String getClassName(int address, int instanceIndex) {
return "_S1_" + instanceIndex + "_" + Integer.toHexString(address).toUpperCase();
}
public static int getClassAddress(String name) {
String hexAddress = name.substring(name.lastIndexOf("_") + 1);
return Integer.parseInt(hexAddress, 16);
}
public static int getClassInstanceIndex(String name) {
int startIndex = name.indexOf("_", 1);
int endIndex = name.lastIndexOf("_");
String instanceIndex = name.substring(startIndex + 1, endIndex);
return Integer.parseInt(instanceIndex);
}
public String getExecMethodName() {
return "exec";
}
public String getExecMethodDesc() {
return "()I";
}
public String getReplaceFieldName() {
return "e";
}
public String getReplaceMethodName() {
return "setExecutable";
}
public String getReplaceMethodDesc() {
return "(" + executableDescriptor + ")V";
}
public String getGetMethodName() {
return "getExecutable";
}
public String getGetMethodDesc() {
return "()" + executableDescriptor;
}
public String getStaticExecMethodName() {
return "s";
}
public String getStaticExecMethodDesc() {
return "()I";
}
public boolean isAutomaticMaxLocals() {
return false;
}
public int getMaxLocals() {
return maxLocalSize;
}
public boolean isAutomaticMaxStack() {
return false;
}
public int getMaxStack() {
return maxStackSize;
}
public void visitPauseEmuWithStatus(MethodVisitor mv, int status) {
loadImm(status);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.pauseEmuWithStatus, "(I)V");
}
public void visitLogInfo(MethodVisitor mv, String message) {
mv.visitLdcInsn(message);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, RuntimeContext.logInfo, "(" + stringDescriptor + ")V");
}
@Override
public MethodVisitor getMethodVisitor() {
return mv;
}
public void setMethodVisitor(MethodVisitor mv) {
this.mv = mv;
}
@Override
public CodeInstruction getCodeInstruction() {
return codeInstruction;
}
@Override
public CodeInstruction getCodeInstruction(int address) {
return getCodeBlock().getCodeInstruction(address);
}
public void setCodeInstruction(CodeInstruction codeInstruction) {
this.codeInstruction = codeInstruction;
}
@Override
public int getSaValue() {
return codeInstruction.getSaValue();
}
@Override
public int getRsRegisterIndex() {
return codeInstruction.getRsRegisterIndex();
}
@Override
public int getRtRegisterIndex() {
return codeInstruction.getRtRegisterIndex();
}
@Override
public int getRdRegisterIndex() {
return codeInstruction.getRdRegisterIndex();
}
@Override
public void loadRs() {
loadRegister(getRsRegisterIndex());
}
@Override
public void loadRt() {
loadRegister(getRtRegisterIndex());
}
@Override
public void loadRd() {
loadRegister(getRdRegisterIndex());
}
@Override
public void loadSaValue() {
loadImm(getSaValue());
}
public void loadRegisterIndex(int registerIndex) {
loadImm(registerIndex);
}
public void loadRsIndex() {
loadRegisterIndex(getRsRegisterIndex());
}
public void loadRtIndex() {
loadRegisterIndex(getRtRegisterIndex());
}
public void loadRdIndex() {
loadRegisterIndex(getRdRegisterIndex());
}
public void loadFdIndex() {
loadRegisterIndex(getFdRegisterIndex());
}
public void loadFsIndex() {
loadRegisterIndex(getFsRegisterIndex());
}
public void loadFtIndex() {
loadRegisterIndex(getFtRegisterIndex());
}
@Override
public int getImm16(boolean signedImm) {
return codeInstruction.getImm16(signedImm);
}
@Override
public int getImm14(boolean signedImm) {
return codeInstruction.getImm14(signedImm);
}
@Override
public void loadImm16(boolean signedImm) {
loadImm(getImm16(signedImm));
}
@Override
public void loadImm(int imm) {
switch (imm) {
case -1: mv.visitInsn(Opcodes.ICONST_M1); break;
case 0: mv.visitInsn(Opcodes.ICONST_0); break;
case 1: mv.visitInsn(Opcodes.ICONST_1); break;
case 2: mv.visitInsn(Opcodes.ICONST_2); break;
case 3: mv.visitInsn(Opcodes.ICONST_3); break;
case 4: mv.visitInsn(Opcodes.ICONST_4); break;
case 5: mv.visitInsn(Opcodes.ICONST_5); break;
default:
if (Byte.MIN_VALUE <= imm && imm < Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, imm);
} else if (Short.MIN_VALUE <= imm && imm < Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, imm);
} else {
mv.visitLdcInsn(new Integer(imm));
}
break;
}
}
public void loadImm(boolean imm) {
mv.visitInsn(imm ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
}
public void loadPspNaNInt() {
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(VfpuState.class), "pspNaNint", "I");
}
@Override
public void compileInterpreterInstruction() {
visitIntepreterCall(codeInstruction.getOpcode(), codeInstruction.getInsn());
}
@Override
public void compileRTRSIMM(String method, boolean signedImm) {
loadCpu();
loadRtIndex();
loadRsIndex();
loadImm16(signedImm);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cpuInternalName, method, "(III)V");
}
@Override
public void compileRDRT(String method) {
loadCpu();
loadRdIndex();
loadRtIndex();
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cpuInternalName, method, "(II)V");
}
@Override
public void compileFDFSFT(String method) {
loadCpu();
loadFdIndex();
loadFsIndex();
loadFtIndex();
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, cpuInternalName, method, "(III)V");
}
@Override
public void storeRd() {
storeRegister(getRdRegisterIndex());
}
@Override
public void storeRd(int constantValue) {
storeRegister(getRdRegisterIndex(), constantValue);
}
@Override
public void storeRt() {
storeRegister(getRtRegisterIndex());
}
@Override
public void storeRt(int constantValue) {
storeRegister(getRtRegisterIndex(), constantValue);
}
@Override
public boolean isRdRegister0() {
return getRdRegisterIndex() == _zr;
}
@Override
public boolean isRtRegister0() {
return getRtRegisterIndex() == _zr;
}
@Override
public boolean isRsRegister0() {
return getRsRegisterIndex() == _zr;
}
@Override
public void prepareRdForStore() {
prepareRegisterForStore(getRdRegisterIndex());
}
@Override
public void prepareRtForStore() {
prepareRegisterForStore(getRtRegisterIndex());
}
private void loadMemoryInt() {
if (storeMemoryIntLocal) {
mv.visitVarInsn(Opcodes.ALOAD, LOCAL_MEMORY_INT);
} else {
mv.visitFieldInsn(Opcodes.GETSTATIC, runtimeContextInternalName, "memoryInt", "[I");
}
}
@Override
public void memRead32(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
prepareMemIndex(registerIndex, offset, true, 32);
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read32", "(I)I");
} else {
mv.visitInsn(Opcodes.IALOAD);
}
}
@Override
public void memRead16(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.debugMemoryRead) {
mv.visitInsn(Opcodes.DUP);
loadImm(0);
loadImm(codeInstruction.getAddress());
loadImm(1);
loadImm(16);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemoryReadWrite", "(IIIZI)V");
}
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read16", "(I)I");
} else {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryRead16", "(II)I");
loadImm(1);
mv.visitInsn(Opcodes.IUSHR);
} else {
// memoryInt[(address & 0x3FFFFFFF) / 4] == memoryInt[(address << 2) >>> 4]
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(3);
mv.visitInsn(Opcodes.IUSHR);
}
mv.visitInsn(Opcodes.DUP);
loadImm(1);
mv.visitInsn(Opcodes.IAND);
loadImm(4);
mv.visitInsn(Opcodes.ISHL);
storeTmp1();
loadImm(1);
mv.visitInsn(Opcodes.IUSHR);
mv.visitInsn(Opcodes.IALOAD);
loadTmp1();
mv.visitInsn(Opcodes.IUSHR);
loadImm(0xFFFF);
mv.visitInsn(Opcodes.IAND);
}
}
@Override
public void memRead8(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.debugMemoryRead) {
mv.visitInsn(Opcodes.DUP);
loadImm(0);
loadImm(codeInstruction.getAddress());
loadImm(1);
loadImm(8);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemoryReadWrite", "(IIIZI)V");
}
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "read8", "(I)I");
} else {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryRead8", "(II)I");
} else {
// memoryInt[(address & 0x3FFFFFFF) / 4] == memoryInt[(address << 2) >>> 4]
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
}
mv.visitInsn(Opcodes.DUP);
loadImm(3);
mv.visitInsn(Opcodes.IAND);
loadImm(3);
mv.visitInsn(Opcodes.ISHL);
storeTmp1();
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
mv.visitInsn(Opcodes.IALOAD);
loadTmp1();
mv.visitInsn(Opcodes.IUSHR);
loadImm(0xFF);
mv.visitInsn(Opcodes.IAND);
}
}
private void prepareMemIndex(int registerIndex, int offset, boolean isRead, int width) {
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.debugMemoryRead && isRead) {
if (!RuntimeContext.debugMemoryReadWriteNoSP || registerIndex != _sp) {
mv.visitInsn(Opcodes.DUP);
loadImm(0);
loadImm(codeInstruction.getAddress());
loadImm(isRead);
loadImm(width);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemoryReadWrite", "(IIIZI)V");
}
}
if (RuntimeContext.hasMemoryInt()) {
if (registerIndex == _sp) {
// No need to check for a valid memory access when referencing the $sp register
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
} else if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
String checkMethodName = String.format("checkMemory%s%d", isRead ? "Read" : "Write", width);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, checkMethodName, "(II)I");
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
} else {
// memoryInt[(address & 0x3FFFFFFF) / 4] == memoryInt[(address << 2) >>> 4]
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
}
}
@Override
public void prepareMemWrite32(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
prepareMemIndex(registerIndex, offset, false, 32);
memWritePrepared = true;
}
@Override
public void memWrite32(int registerIndex, int offset) {
if (!memWritePrepared) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
mv.visitInsn(Opcodes.SWAP);
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite32", "(II)I");
}
mv.visitInsn(Opcodes.SWAP);
}
if (RuntimeContext.debugMemoryWrite) {
if (!RuntimeContext.debugMemoryReadWriteNoSP || registerIndex != _sp) {
mv.visitInsn(Opcodes.DUP2);
mv.visitInsn(Opcodes.SWAP);
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
mv.visitInsn(Opcodes.SWAP);
loadImm(codeInstruction.getAddress());
loadImm(0);
loadImm(32);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "debugMemoryReadWrite", "(IIIZI)V");
}
}
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "write32", "(II)V");
} else {
mv.visitInsn(Opcodes.IASTORE);
}
memWritePrepared = false;
}
@Override
public void prepareMemWrite16(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.hasMemoryInt()) {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite16", "(II)I");
}
}
memWritePrepared = true;
}
@Override
public void memWrite16(int registerIndex, int offset) {
if (!memWritePrepared) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
mv.visitInsn(Opcodes.SWAP);
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.hasMemoryInt()) {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite16", "(II)I");
}
}
mv.visitInsn(Opcodes.SWAP);
}
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "write16", "(IS)V");
} else {
// tmp2 = value & 0xFFFF;
// tmp1 = (address & 2) << 3;
// memoryInt[address >> 2] = (memoryInt[address >> 2] & ((0xFFFF << tmp1) ^ 0xFFFFFFFF)) | (tmp2 << tmp1);
loadImm(0xFFFF);
mv.visitInsn(Opcodes.IAND);
storeTmp2();
mv.visitInsn(Opcodes.DUP);
loadImm(2);
mv.visitInsn(Opcodes.IAND);
loadImm(3);
mv.visitInsn(Opcodes.ISHL);
storeTmp1();
if (checkMemoryAccess()) {
loadImm(2);
mv.visitInsn(Opcodes.ISHR);
} else {
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
mv.visitInsn(Opcodes.DUP2);
mv.visitInsn(Opcodes.IALOAD);
loadImm(0xFFFF);
loadTmp1();
mv.visitInsn(Opcodes.ISHL);
loadImm(-1);
mv.visitInsn(Opcodes.IXOR);
mv.visitInsn(Opcodes.IAND);
loadTmp2();
loadTmp1();
mv.visitInsn(Opcodes.ISHL);
mv.visitInsn(Opcodes.IOR);
mv.visitInsn(Opcodes.IASTORE);
}
memWritePrepared = false;
}
@Override
public void prepareMemWrite8(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.hasMemoryInt()) {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite8", "(II)I");
}
}
memWritePrepared = true;
}
@Override
public void memWrite8(int registerIndex, int offset) {
if (!memWritePrepared) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
mv.visitInsn(Opcodes.SWAP);
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.hasMemoryInt()) {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite8", "(II)I");
}
}
mv.visitInsn(Opcodes.SWAP);
}
if (!RuntimeContext.hasMemoryInt()) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "write8", "(IB)V");
} else {
// tmp2 = value & 0xFF;
// tmp1 = (address & 3) << 3;
// memoryInt[address >> 2] = (memoryInt[address >> 2] & ((0xFF << tmp1) ^ 0xFFFFFFFF)) | (tmp2 << tmp1);
loadImm(0xFF);
mv.visitInsn(Opcodes.IAND);
storeTmp2();
mv.visitInsn(Opcodes.DUP);
loadImm(3);
mv.visitInsn(Opcodes.IAND);
loadImm(3);
mv.visitInsn(Opcodes.ISHL);
storeTmp1();
if (checkMemoryAccess()) {
loadImm(2);
mv.visitInsn(Opcodes.ISHR);
} else {
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
mv.visitInsn(Opcodes.DUP2);
mv.visitInsn(Opcodes.IALOAD);
loadImm(0xFF);
loadTmp1();
mv.visitInsn(Opcodes.ISHL);
loadImm(-1);
mv.visitInsn(Opcodes.IXOR);
mv.visitInsn(Opcodes.IAND);
loadTmp2();
loadTmp1();
mv.visitInsn(Opcodes.ISHL);
mv.visitInsn(Opcodes.IOR);
mv.visitInsn(Opcodes.IASTORE);
}
memWritePrepared = false;
}
@Override
public void memWriteZero8(int registerIndex, int offset) {
if (!RuntimeContext.hasMemoryInt()) {
loadMemory();
} else {
loadMemoryInt();
}
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (RuntimeContext.hasMemoryInt()) {
if (checkMemoryAccess()) {
loadImm(codeInstruction.getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "checkMemoryWrite8", "(II)I");
}
}
if (!RuntimeContext.hasMemoryInt()) {
loadImm(0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, memoryInternalName, "write8", "(IB)V");
} else {
// tmp1 = (address & 3) << 3;
// memoryInt[address >> 2] = (memoryInt[address >> 2] & ((0xFF << tmp1) ^ 0xFFFFFFFF));
mv.visitInsn(Opcodes.DUP);
loadImm(3);
mv.visitInsn(Opcodes.IAND);
loadImm(3);
mv.visitInsn(Opcodes.ISHL);
storeTmp1();
if (checkMemoryAccess()) {
loadImm(2);
mv.visitInsn(Opcodes.ISHR);
} else {
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
mv.visitInsn(Opcodes.DUP2);
mv.visitInsn(Opcodes.IALOAD);
loadImm(0xFF);
loadTmp1();
mv.visitInsn(Opcodes.ISHL);
loadImm(-1);
mv.visitInsn(Opcodes.IXOR);
mv.visitInsn(Opcodes.IAND);
mv.visitInsn(Opcodes.IASTORE);
}
}
@Override
public void compileSyscall() {
visitSyscall(codeInstruction.getOpcode());
}
@Override
public void convertUnsignedIntToLong() {
mv.visitInsn(Opcodes.I2L);
mv.visitLdcInsn(0xFFFFFFFFL);
mv.visitInsn(Opcodes.LAND);
}
public int getMethodMaxInstructions() {
return methodMaxInstructions;
}
public void setMethodMaxInstructions(int methodMaxInstructions) {
this.methodMaxInstructions = methodMaxInstructions;
}
private boolean checkMemoryAccess() {
if (!RuntimeContext.hasMemoryInt()) {
return false;
}
if (RuntimeContext.memory instanceof SafeFastMemory) {
return true;
}
return false;
}
public void compileDelaySlotAsBranchTarget(CodeInstruction codeInstruction) {
if (codeInstruction.getInsn() == Instructions.NOP) {
// NOP nothing to do
return;
}
boolean skipDelaySlotInstruction = true;
CodeInstruction previousInstruction = getCodeBlock().getCodeInstruction(codeInstruction.getAddress() - 4);
if (previousInstruction != null) {
if (Compiler.isEndBlockInsn(previousInstruction.getAddress(), previousInstruction.getOpcode(), previousInstruction.getInsn())) {
// The previous instruction was a J, JR or unconditional branch
// instruction, we do not need to skip the delay slot instruction
skipDelaySlotInstruction = false;
}
}
Label afterDelaySlot = null;
if (skipDelaySlotInstruction) {
afterDelaySlot = new Label();
mv.visitJumpInsn(Opcodes.GOTO, afterDelaySlot);
}
codeInstruction.compile(this, mv);
if (skipDelaySlotInstruction) {
mv.visitLabel(afterDelaySlot);
}
}
public void compileExecuteInterpreter(int startAddress) {
loadImm(startAddress);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeContextInternalName, "executeInterpreter", "(I)I");
endMethod();
mv.visitInsn(Opcodes.IRETURN);
}
private void visitNativeCodeSequence(NativeCodeSequence nativeCodeSequence, int address, NativeCodeInstruction nativeCodeInstruction) {
StringBuilder methodSignature = new StringBuilder("(");
int numberParameters = nativeCodeSequence.getNumberParameters();
for (int i = 0; i < numberParameters; i++) {
loadImm(nativeCodeSequence.getParameterValue(i, address));
methodSignature.append("I");
}
if (nativeCodeSequence.isMethodReturning()) {
methodSignature.append(")I");
} else {
methodSignature.append(")V");
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(nativeCodeSequence.getNativeCodeSequenceClass()), nativeCodeSequence.getMethodName(), methodSignature.toString());
if (nativeCodeInstruction != null && nativeCodeInstruction.isBranching()) {
CodeInstruction targetInstruction = getCodeBlock().getCodeInstruction(nativeCodeInstruction.getBranchingTo());
if (targetInstruction != null) {
visitJump(Opcodes.GOTO, targetInstruction);
} else {
visitJump(Opcodes.GOTO, nativeCodeInstruction.getBranchingTo());
}
}
}
public void compileNativeCodeSequence(NativeCodeSequence nativeCodeSequence, NativeCodeInstruction nativeCodeInstruction) {
visitNativeCodeSequence(nativeCodeSequence, nativeCodeInstruction.getAddress(), nativeCodeInstruction);
if (nativeCodeSequence.isReturning()) {
loadRegister(_ra);
endInternalMethod();
mv.visitInsn(Opcodes.IRETURN);
} else if (nativeCodeSequence.isMethodReturning()) {
endInternalMethod();
mv.visitInsn(Opcodes.IRETURN);
}
// Replacing the whole CodeBlock?
if (getCodeBlock().getLength() == nativeCodeSequence.getNumOpcodes() && !nativeCodeSequence.hasBranchInstruction()) {
nativeCodeManager.setCompiledNativeCodeBlock(getCodeBlock().getStartAddress(), nativeCodeSequence);
// Be more verbose when Debug enabled.
// Only log "Nop" native code sequence in debug.
if (log.isDebugEnabled() || nativeCodeSequence.getNativeCodeSequenceClass().equals(Nop.class)) {
if (log.isDebugEnabled()) {
log.debug(String.format("Replacing CodeBlock at 0x%08X (%08X-0x%08X, length %d) by %s", getCodeBlock().getStartAddress(), getCodeBlock().getLowestAddress(), codeBlock.getHighestAddress(), codeBlock.getLength(), nativeCodeSequence));
}
} else if (log.isInfoEnabled()) {
log.info(String.format("Replacing CodeBlock at 0x%08X by Native Code '%s'", getCodeBlock().getStartAddress(), nativeCodeSequence.getName()));
}
} else {
// Be more verbose when Debug enabled
int endAddress = getCodeInstruction().getAddress() + (nativeCodeSequence.getNumOpcodes() - 1) * 4;
if (log.isDebugEnabled()) {
log.debug(String.format("Replacing CodeSequence at 0x%08X-0x%08X by Native Code %s", getCodeInstruction().getAddress(), endAddress, nativeCodeSequence));
} else if (log.isInfoEnabled()) {
log.info(String.format("Replacing CodeSequence at 0x%08X-0x%08X by Native Code '%s'", getCodeInstruction().getAddress(), endAddress, nativeCodeSequence.getName()));
}
}
}
public int getNumberInstructionsToBeSkipped() {
return numberInstructionsToBeSkipped;
}
public boolean isSkipDelaySlot() {
return skipDelaySlot;
}
@Override
public void skipInstructions(int numberInstructionsToBeSkipped, boolean skipDelaySlot) {
this.numberInstructionsToBeSkipped = numberInstructionsToBeSkipped;
this.skipDelaySlot = skipDelaySlot;
}
@Override
public int getFdRegisterIndex() {
return codeInstruction.getFdRegisterIndex();
}
@Override
public int getFsRegisterIndex() {
return codeInstruction.getFsRegisterIndex();
}
@Override
public int getFtRegisterIndex() {
return codeInstruction.getFtRegisterIndex();
}
@Override
public void loadFd() {
loadFRegister(getFdRegisterIndex());
}
@Override
public void loadFs() {
loadFRegister(getFsRegisterIndex());
}
@Override
public void loadFt() {
loadFRegister(getFtRegisterIndex());
}
@Override
public void prepareFdForStore() {
prepareFRegisterForStore(getFdRegisterIndex());
}
@Override
public void prepareFtForStore() {
prepareFRegisterForStore(getFtRegisterIndex());
}
@Override
public void storeFd() {
storeFRegister(getFdRegisterIndex());
}
@Override
public void storeFt() {
storeFRegister(getFtRegisterIndex());
}
@Override
public void loadFCr() {
loadFRegister(getCrValue());
}
@Override
public void prepareFCrForStore() {
prepareFRegisterForStore(getCrValue());
}
@Override
public void prepareVcrCcForStore(int cc) {
if (preparedRegisterForStore < 0) {
loadVcr();
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(Vcr.class), "cc", "[Z");
loadImm(cc);
preparedRegisterForStore = cc;
}
}
@Override
public void storeVcrCc(int cc) {
if (preparedRegisterForStore == cc) {
mv.visitInsn(Opcodes.BASTORE);
preparedRegisterForStore = -1;
} else {
loadVcr();
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(Vcr.class), "cc", "[Z");
mv.visitInsn(Opcodes.SWAP);
loadImm(cc);
mv.visitInsn(Opcodes.SWAP);
mv.visitInsn(Opcodes.BASTORE);
}
}
@Override
public int getCrValue() {
return codeInstruction.getCrValue();
}
@Override
public void storeFCr() {
storeFRegister(getCrValue());
}
@Override
public int getVdRegisterIndex() {
return codeInstruction.getVdRegisterIndex();
}
@Override
public int getVsRegisterIndex() {
return codeInstruction.getVsRegisterIndex();
}
@Override
public int getVtRegisterIndex() {
return codeInstruction.getVtRegisterIndex();
}
@Override
public int getVsize() {
return codeInstruction.getVsize();
}
@Override
public void loadVs(int n) {
loadVRegister(getVsize(), getVsRegisterIndex(), n, vfpuPfxsState, true);
}
@Override
public void loadVsInt(int n) {
loadVRegister(getVsize(), getVsRegisterIndex(), n, vfpuPfxsState, false);
}
@Override
public void loadVt(int n) {
loadVRegister(getVsize(), getVtRegisterIndex(), n, vfpuPfxtState, true);
}
@Override
public void loadVtInt(int n) {
loadVRegister(getVsize(), getVtRegisterIndex(), n, vfpuPfxtState, false);
}
@Override
public void loadVt(int vsize, int n) {
loadVRegister(vsize, getVtRegisterIndex(), n, vfpuPfxtState, true);
}
@Override
public void loadVtInt(int vsize, int n) {
loadVRegister(vsize, getVtRegisterIndex(), n, vfpuPfxtState, false);
}
@Override
public void loadVt(int vsize, int vt, int n) {
loadVRegister(vsize, vt, n, vfpuPfxtState, true);
}
@Override
public void loadVtInt(int vsize, int vt, int n) {
loadVRegister(vsize, vt, n, vfpuPfxtState, false);
}
@Override
public void loadVd(int n) {
loadVRegister(getVsize(), getVdRegisterIndex(), n, null, true);
}
@Override
public void loadVdInt(int n) {
loadVRegister(getVsize(), getVdRegisterIndex(), n, null, false);
}
@Override
public void loadVd(int vsize, int n) {
loadVRegister(vsize, getVdRegisterIndex(), n, null, true);
}
@Override
public void loadVdInt(int vsize, int n) {
loadVRegister(vsize, getVdRegisterIndex(), n, null, false);
}
@Override
public void loadVd(int vsize, int vd, int n) {
loadVRegister(vsize, vd, n, null, true);
}
@Override
public void loadVdInt(int vsize, int vd, int n) {
loadVRegister(vsize, vd, n, null, false);
}
@Override
public void prepareVdForStore(int n) {
prepareVdForStore(getVsize(), n);
}
@Override
public void prepareVdForStore(int vsize, int n) {
prepareVdForStore(vsize, getVdRegisterIndex(), n);
}
@Override
public void prepareVdForStore(int vsize, int vd, int n) {
if (pfxVdOverlap && n < vsize - 1) {
// Do nothing, value will be store in tmp local variable
} else {
prepareVRegisterForStore(vsize, vd, n, vfpuPfxdState, true);
}
}
@Override
public void prepareVdForStoreInt(int n) {
prepareVdForStoreInt(getVsize(), n);
}
@Override
public void prepareVdForStoreInt(int vsize, int n) {
prepareVdForStoreInt(vsize, getVdRegisterIndex(), n);
}
@Override
public void prepareVdForStoreInt(int vsize, int vd, int n) {
if (pfxVdOverlap && n < vsize - 1) {
// Do nothing, value will be store in tmp local variable
} else {
prepareVRegisterForStore(vsize, vd, n, vfpuPfxdState, false);
}
}
@Override
public void prepareVtForStore(int n) {
prepareVRegisterForStore(getVsize(), getVtRegisterIndex(), n, null, true);
}
@Override
public void prepareVtForStore(int vsize, int n) {
prepareVRegisterForStore(vsize, getVtRegisterIndex(), n, null, true);
}
@Override
public void prepareVtForStoreInt(int n) {
prepareVRegisterForStore(getVsize(), getVtRegisterIndex(), n, null, false);
}
@Override
public void prepareVtForStoreInt(int vsize, int n) {
prepareVRegisterForStore(vsize, getVtRegisterIndex(), n, null, false);
}
@Override
public void storeVd(int n) {
storeVd(getVsize(), n);
}
@Override
public void storeVdInt(int n) {
storeVdInt(getVsize(), n);
}
@Override
public void storeVd(int vsize, int n) {
storeVd(vsize, getVdRegisterIndex(), n);
}
@Override
public void storeVdInt(int vsize, int n) {
storeVdInt(vsize, getVdRegisterIndex(), n);
}
@Override
public void storeVd(int vsize, int vd, int n) {
if (pfxVdOverlap && n < vsize - 1) {
storeFTmpVd(n, true);
} else {
storeVRegister(vsize, vd, n, vfpuPfxdState, true);
}
}
@Override
public void storeVdInt(int vsize, int vd, int n) {
if (pfxVdOverlap && n < vsize - 1) {
storeFTmpVd(n, false);
} else {
storeVRegister(vsize, vd, n, vfpuPfxdState, false);
}
}
@Override
public void storeVt(int n) {
storeVRegister(getVsize(), getVtRegisterIndex(), n, null, true);
}
@Override
public void storeVtInt(int n) {
storeVRegister(getVsize(), getVtRegisterIndex(), n, null, false);
}
@Override
public void storeVt(int vsize, int n) {
storeVRegister(vsize, getVtRegisterIndex(), n, null, true);
}
@Override
public void storeVtInt(int vsize, int n) {
storeVRegister(vsize, getVtRegisterIndex(), n, null, false);
}
@Override
public void storeVt(int vsize, int vt, int n) {
storeVRegister(vsize, vt, n, null, true);
}
@Override
public void storeVtInt(int vsize, int vt, int n) {
storeVRegister(vsize, vt, n, null, false);
}
@Override
public void prepareVtForStore(int vsize, int vt, int n) {
prepareVRegisterForStore(vsize, vt, n, null, true);
}
@Override
public void prepareVtForStoreInt(int vsize, int vt, int n) {
prepareVRegisterForStore(vsize, vt, n, null, false);
}
@Override
public int getImm7() {
return codeInstruction.getImm7();
}
@Override
public int getImm5() {
return codeInstruction.getImm5();
}
@Override
public int getImm4() {
return codeInstruction.getImm4();
}
@Override
public int getImm3() {
return codeInstruction.getImm3();
}
@Override
public void loadVs(int vsize, int n) {
loadVRegister(vsize, getVsRegisterIndex(), n, vfpuPfxsState, true);
}
@Override
public void loadVsInt(int vsize, int n) {
loadVRegister(vsize, getVsRegisterIndex(), n, vfpuPfxsState, false);
}
@Override
public void loadVs(int vsize, int vs, int n) {
loadVRegister(vsize, vs, n, vfpuPfxsState, true);
}
@Override
public void loadVsInt(int vsize, int vs, int n) {
loadVRegister(vsize, vs, n, vfpuPfxsState, false);
}
@Override
public void loadTmp1() {
loadLocalVar(LOCAL_TMP1);
}
@Override
public void loadTmp2() {
loadLocalVar(LOCAL_TMP2);
}
@Override
public void loadLTmp1() {
mv.visitVarInsn(Opcodes.LLOAD, LOCAL_TMP1);
}
@Override
public void loadFTmp1() {
mv.visitVarInsn(Opcodes.FLOAD, LOCAL_TMP1);
}
@Override
public void loadFTmp2() {
mv.visitVarInsn(Opcodes.FLOAD, LOCAL_TMP2);
}
@Override
public void loadFTmp3() {
mv.visitVarInsn(Opcodes.FLOAD, LOCAL_TMP3);
}
@Override
public void loadFTmp4() {
mv.visitVarInsn(Opcodes.FLOAD, LOCAL_TMP4);
}
private void loadFTmpVd(int n, boolean isFloat) {
int opcode = isFloat ? Opcodes.FLOAD : Opcodes.ILOAD;
if (n == 0) {
mv.visitVarInsn(opcode, LOCAL_TMP_VD0);
} else if (n == 1) {
mv.visitVarInsn(opcode, LOCAL_TMP_VD1);
} else {
mv.visitVarInsn(opcode, LOCAL_TMP_VD2);
}
}
@Override
public void storeTmp1() {
storeLocalVar(LOCAL_TMP1);
}
@Override
public void storeTmp2() {
storeLocalVar(LOCAL_TMP2);
}
@Override
public void storeLTmp1() {
mv.visitVarInsn(Opcodes.LSTORE, LOCAL_TMP1);
}
@Override
public void storeFTmp1() {
mv.visitVarInsn(Opcodes.FSTORE, LOCAL_TMP1);
}
@Override
public void storeFTmp2() {
mv.visitVarInsn(Opcodes.FSTORE, LOCAL_TMP2);
}
@Override
public void storeFTmp3() {
mv.visitVarInsn(Opcodes.FSTORE, LOCAL_TMP3);
}
@Override
public void storeFTmp4() {
mv.visitVarInsn(Opcodes.FSTORE, LOCAL_TMP4);
}
private void storeFTmpVd(int n, boolean isFloat) {
int opcode = isFloat ? Opcodes.FSTORE : Opcodes.ISTORE;
if (n == 0) {
mv.visitVarInsn(opcode, LOCAL_TMP_VD0);
} else if (n == 1) {
mv.visitVarInsn(opcode, LOCAL_TMP_VD1);
} else {
mv.visitVarInsn(opcode, LOCAL_TMP_VD2);
}
}
@Override
public VfpuPfxDstState getPfxdState() {
return vfpuPfxdState;
}
@Override
public VfpuPfxSrcState getPfxsState() {
return vfpuPfxsState;
}
@Override
public VfpuPfxSrcState getPfxtState() {
return vfpuPfxtState;
}
private void startPfxCompiled(VfpuPfxState vfpuPfxState, String name, String descriptor, String internalName, boolean isFloat) {
if (vfpuPfxState.isUnknown()) {
if (interpretPfxLabel == null) {
interpretPfxLabel = new Label();
}
loadVcr();
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(Vcr.class), name, descriptor);
mv.visitFieldInsn(Opcodes.GETFIELD, internalName, "enabled", "Z");
mv.visitJumpInsn(Opcodes.IFNE, interpretPfxLabel);
}
}
@Override
public void startPfxCompiled() {
startPfxCompiled(true);
}
@Override
public void startPfxCompiled(boolean isFloat) {
interpretPfxLabel = null;
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXS)) {
startPfxCompiled(vfpuPfxsState, "pfxs", Type.getDescriptor(PfxSrc.class), Type.getInternalName(PfxSrc.class), isFloat);
}
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXT)) {
startPfxCompiled(vfpuPfxtState, "pfxt", Type.getDescriptor(PfxSrc.class), Type.getInternalName(PfxSrc.class), isFloat);
}
if (codeInstruction.hasFlags(Instruction.FLAG_USE_VFPU_PFXD)) {
startPfxCompiled(vfpuPfxdState, "pfxd", Type.getDescriptor(PfxDst.class), Type.getInternalName(PfxDst.class), isFloat);
}
pfxVdOverlap = false;
if (getCodeInstruction().hasFlags(Instruction.FLAG_USE_VFPU_PFXS | Instruction.FLAG_USE_VFPU_PFXD)) {
pfxVdOverlap |= isVsVdOverlap();
}
if (getCodeInstruction().hasFlags(Instruction.FLAG_USE_VFPU_PFXT | Instruction.FLAG_USE_VFPU_PFXD)) {
pfxVdOverlap |= isVtVdOverlap();
}
}
@Override
public void endPfxCompiled() {
endPfxCompiled(true);
}
@Override
public void endPfxCompiled(boolean isFloat) {
endPfxCompiled(getVsize(), isFloat);
}
@Override
public void endPfxCompiled(int vsize) {
endPfxCompiled(vsize, true);
}
@Override
public void endPfxCompiled(int vsize, boolean isFloat) {
endPfxCompiled(vsize, isFloat, true);
}
@Override
public void endPfxCompiled(int vsize, boolean isFloat, boolean doFlush) {
if (doFlush) {
flushPfxCompiled(vsize, getVdRegisterIndex(), isFloat);
}
if (interpretPfxLabel != null) {
Label continueLabel = new Label();
mv.visitJumpInsn(Opcodes.GOTO, continueLabel);
mv.visitLabel(interpretPfxLabel);
compileInterpreterInstruction();
mv.visitLabel(continueLabel);
interpretPfxLabel = null;
}
pfxVdOverlap = false;
}
@Override
public void flushPfxCompiled(int vsize, int vd, boolean isFloat) {
if (pfxVdOverlap) {
// Write back the temporary overlap variables
pfxVdOverlap = false;
for (int n = 0; n < vsize - 1; n++) {
if (isFloat) {
prepareVdForStore(vsize, vd, n);
} else {
prepareVdForStoreInt(vsize, vd, n);
}
loadFTmpVd(n, isFloat);
if (isFloat) {
storeVd(vsize, vd, n);
} else {
storeVdInt(vsize, vd, n);
}
}
pfxVdOverlap = true;
}
}
@Override
public boolean isPfxConsumed(int flag) {
if (log.isTraceEnabled()) {
log.trace(String.format("PFX -> %08X: %s", getCodeInstruction().getAddress(), getCodeInstruction().getInsn().disasm(getCodeInstruction().getAddress(), getCodeInstruction().getOpcode())));
}
int address = getCodeInstruction().getAddress();
while (true) {
address += 4;
CodeInstruction codeInstruction = getCodeBlock().getCodeInstruction(address);
if (log.isTraceEnabled()) {
log.trace(String.format("PFX %08X: %s", codeInstruction.getAddress(), codeInstruction.getInsn().disasm(codeInstruction.getAddress(), codeInstruction.getOpcode())));
}
if (codeInstruction == null || !isNonBranchingCodeSequence(codeInstruction)) {
return false;
}
if (codeInstruction.hasFlags(flag)) {
return codeInstruction.hasFlags(Instruction.FLAG_COMPILED_PFX);
}
}
}
private boolean isVxVdOverlap(VfpuPfxSrcState pfxSrcState, int registerIndex) {
if (!pfxSrcState.isKnown()) {
return false;
}
int vsize = getVsize();
int vd = getVdRegisterIndex();
// Check if registers are overlapping
if (registerIndex != vd) {
if (vsize != 3) {
// Different register numbers, no overlap possible
return false;
}
// For vsize==3, a possible overlap exist. E.g.
// C000.t and C001.t
// are partially overlapping.
if ((registerIndex & 63) != (vd & 63)) {
return false;
}
}
if (!pfxSrcState.pfxSrc.enabled) {
return true;
}
for (int n = 0; n < vsize; n++) {
if (!pfxSrcState.pfxSrc.cst[n] && pfxSrcState.pfxSrc.swz[n] != n) {
return true;
}
}
return false;
}
@Override
public boolean isVsVdOverlap() {
return isVxVdOverlap(vfpuPfxsState, getVsRegisterIndex());
}
@Override
public boolean isVtVdOverlap() {
return isVxVdOverlap(vfpuPfxtState, getVtRegisterIndex());
}
private boolean canUseVFPUInt(int vsize) {
if (vfpuPfxsState.isKnown() && vfpuPfxsState.pfxSrc.enabled) {
for (int i = 0; i < vsize; i++) {
// abs, neg and cst source prefixes can be handled as int
if (vfpuPfxsState.pfxSrc.swz[i] != i) {
return false;
}
}
}
if (vfpuPfxdState.isKnown() && vfpuPfxdState.pfxDst.enabled) {
return false;
}
return true;
}
@Override
public void compileVFPUInstr(Object cstBefore, int opcode, String mathFunction) {
int vsize = getVsize();
boolean useVt = getCodeInstruction().hasFlags(Instruction.FLAG_USE_VFPU_PFXT);
if (mathFunction == null &&
opcode == Opcodes.NOP &&
!useVt &&
cstBefore == null &&
canUseVFPUInt(vsize)) {
// VMOV should use int instead of float
startPfxCompiled(false);
for (int n = 0; n < vsize; n++) {
prepareVdForStoreInt(n);
loadVsInt(n);
storeVdInt(n);
}
endPfxCompiled(vsize, false);
} else {
startPfxCompiled(true);
for (int n = 0; n < vsize; n++) {
prepareVdForStore(n);
if (cstBefore != null) {
mv.visitLdcInsn(cstBefore);
}
loadVs(n);
if (useVt) {
loadVt(n);
}
if (mathFunction != null) {
if ("abs".equals(mathFunction)) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), mathFunction, "(F)F");
} else if ("max".equals(mathFunction) || "min".equals(mathFunction)) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), mathFunction, "(FF)F");
} else {
mv.visitInsn(Opcodes.F2D);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Math.class), mathFunction, "(D)D");
mv.visitInsn(Opcodes.D2F);
}
}
Label doneStore = null;
if (opcode != Opcodes.NOP) {
Label doneOpcode = null;
if (opcode == Opcodes.FDIV && cstBefore == null) {
// if (value1 == 0f && value2 == 0f) {
// result = PSP-NaN | (sign(value1) ^ sign(value2));
// } else {
// result = value1 / value2;
// }
doneOpcode = new Label();
doneStore = new Label();
Label notZeroByZero = new Label();
Label notZeroByZeroPop = new Label();
mv.visitInsn(Opcodes.DUP2);
mv.visitInsn(Opcodes.FCONST_0);
mv.visitInsn(Opcodes.FCMPG);
mv.visitJumpInsn(Opcodes.IFNE, notZeroByZeroPop);
mv.visitInsn(Opcodes.FCONST_0);
mv.visitInsn(Opcodes.FCMPG);
mv.visitJumpInsn(Opcodes.IFNE, notZeroByZero);
convertVFloatToInt();
loadImm(0x80000000);
mv.visitInsn(Opcodes.IAND);
mv.visitInsn(Opcodes.SWAP);
convertVFloatToInt();
loadImm(0x80000000);
mv.visitInsn(Opcodes.IAND);
mv.visitInsn(Opcodes.IXOR);
storeTmp1();
// Store the NaN value as an "int" to not loose any bit.
// Storing as float results in 0x7FC00001 instead of 0x7F800001.
mv.visitInsn(Opcodes.DUP2_X2);
mv.visitInsn(Opcodes.POP2);
loadPspNaNInt();
loadTmp1();
mv.visitInsn(Opcodes.IOR);
int preparedRegister = preparedRegisterForStore;
storeVdInt(n);
preparedRegisterForStore = preparedRegister;
mv.visitJumpInsn(Opcodes.GOTO, doneStore);
mv.visitLabel(notZeroByZeroPop);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(notZeroByZero);
}
mv.visitInsn(opcode);
if (doneOpcode != null) {
mv.visitLabel(doneOpcode);
}
}
storeVd(n);
if (doneStore != null) {
mv.visitLabel(doneStore);
}
}
endPfxCompiled(vsize, true);
}
}
public void visitHook(NativeCodeSequence nativeCodeSequence) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(nativeCodeSequence.getNativeCodeSequenceClass()), nativeCodeSequence.getMethodName(), "()V");
}
@Override
public boolean compileVFPULoad(int registerIndex, int offset, int vt, int count) {
if (!RuntimeContext.hasMemoryInt()) {
// Can only generate an optimized code sequence for memoryInt
return false;
}
if ((vt & 32) != 0) {
// Optimization possible only for column access
return false;
}
// Build parameters for
// System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// i.e.
// System.arraycopy(RuntimeContext.memoryInt,
// RuntimeContext.checkMemoryRead32(rs + simm14, pc) >>> 2,
// RuntimeContext.vprInt,
// vprIndex,
// countSequence * 4);
loadMemoryInt();
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (checkMemoryAccess()) {
loadImm(getCodeInstruction().getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(RuntimeContext.class), "checkMemoryRead32", "(II)I");
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
} else {
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
loadVprInt();
int vprIndex = VfpuState.getVprIndex((vt >> 2) & 7, vt & 3, (vt & 64) >> 6);
loadImm(vprIndex);
loadImm(count);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(System.class), "arraycopy", arraycopyDescriptor);
// Set the VPR float values
for (int i = 0; i < count; i++) {
loadVprFloat();
loadImm(vprIndex + i);
loadVprInt();
loadImm(vprIndex + i);
mv.visitInsn(Opcodes.IALOAD);
convertVIntToFloat();
mv.visitInsn(Opcodes.FASTORE);
}
return true;
}
@Override
public boolean compileVFPUStore(int registerIndex, int offset, int vt, int count) {
if (!RuntimeContext.hasMemoryInt()) {
// Can only generate an optimized code sequence for memoryInt
return false;
}
if ((vt & 32) != 0) {
// Optimization possible only for column access
return false;
}
// Build parameters for
// System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// i.e.
// System.arraycopy(RuntimeContext.vprInt,
// vprIndex,
// RuntimeContext.memoryInt,
// RuntimeContext.checkMemoryWrite32(rs + simm14, pc) >>> 2,
// countSequence * 4);
loadVprInt();
int vprIndex = VfpuState.getVprIndex((vt >> 2) & 7, vt & 3, (vt & 64) >> 6);
loadImm(vprIndex);
loadMemoryInt();
loadRegister(registerIndex);
if (offset != 0) {
loadImm(offset);
mv.visitInsn(Opcodes.IADD);
}
if (checkMemoryAccess()) {
loadImm(getCodeInstruction().getAddress());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(RuntimeContext.class), "checkMemoryWrite32", "(II)I");
loadImm(2);
mv.visitInsn(Opcodes.IUSHR);
} else {
loadImm(2);
mv.visitInsn(Opcodes.ISHL);
loadImm(4);
mv.visitInsn(Opcodes.IUSHR);
}
loadImm(count);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(System.class), "arraycopy", arraycopyDescriptor);
return true;
}
/**
* Search for a sequence of instructions saving registers onto the stack
* at the beginning of a code block and replace them by a meta
* instruction allowing a more efficient compiled code.
*
* For example, a typical code block sequence looks like this:
* addiu $sp, $sp, -32
* sw $s3, 12($sp)
* addu $s3, $a0, $zr <=> move $s3, $a0
* lui $a0, 0x0307 <=> li $a0, 0x03070000
* ori $a0, $a0, 16
* sw $s2, 8($sp)
* addu $s2, $a1, $zr <=> move $s2, $a1
* sw $s1, 4($sp)
* sw $s0, 0($sp)
* lui $s0, 0x0000 <=> li $s0, 0x00000000
* sw $ra, 16($sp)
* jal 0x0nnnnnnn
* ...
*
* This method will identify the "sw" instructions saving registers onto the
* stack and will merge them into a single meta instruction (SequenceSWCodeInstruction).
*
* In the above example:
* addiu $sp, $sp, -32
* sw $s0/$s1/$s2/$s3/$ra, 0/4/8/12/16($sp)
* addu $s3, $a0, $zr <=> move $s3, $a0
* lui $a0, 0x0307 <=> li $a0, 0x03070000
* ori $a0, $a0, 16
* addu $s2, $a1, $zr <=> move $s2, $a1
* lui $s0, 0x0000 <=> li $s0, 0x00000000
* jal 0x0nnnnnnn
* ...
*
* @param codeInstructions the list of code instruction to be optimized.
*/
public void optimizeSequence(List<CodeInstruction> codeInstructions) {
// Optimization only possible for memoryInt
if (!RuntimeContext.hasMemoryInt()) {
return;
}
// Disable optimizations when the profiler is enabled.
if (Profiler.isProfilerEnabled()) {
return;
}
// Disable optimizations when the debugger is open
if (State.debugger != null) {
return;
}
int decreaseSpInstruction = -1;
int stackSize = 0;
int currentInstructionIndex = 0;
int maxSpOffset = Integer.MAX_VALUE;
int swSequenceCount = 0;
int[] storeSpInstructions = null;
int[] storeSpRegisters = null;
List<CodeInstruction> storeSpCodeInstructions = null;
boolean[] modifiedRegisters = new boolean[GprState.NUMBER_REGISTERS];
Arrays.fill(modifiedRegisters, false);
for (CodeInstruction codeInstruction : codeInstructions) {
// Stop optimization when reaching a branch, branch target or delay slot
if (codeInstruction.isBranching() || codeInstruction.hasFlags(Instruction.FLAG_HAS_DELAY_SLOT)) {
break;
}
if (codeInstruction.isBranchTarget() && codeBlock.getStartAddress() != codeInstruction.getAddress()) {
break;
}
Instruction insn = codeInstruction.getInsn();
// Check for a "sw" instruction if we have already seen an "addiu $sp, $sp, -nn".
if (decreaseSpInstruction >= 0) {
// Check for a "sw" instruction...
if (insn == Instructions.SW) {
int rs = codeInstruction.getRsRegisterIndex();
int rt = codeInstruction.getRtRegisterIndex();
// ...saving an unmodified register to the stack...
if (rs == _sp) {
int simm16 = codeInstruction.getImm16(true);
if (!modifiedRegisters[rt]) {
// ...at a valid stack offset...
if (simm16 >= 0 && simm16 < stackSize && (simm16 & 3) == 0 && simm16 < maxSpOffset) {
int index = simm16 >> 2;
// ...at a still ununsed stack offset
if (storeSpInstructions[index] < 0) {
storeSpCodeInstructions.add(codeInstruction);
storeSpInstructions[index] = currentInstructionIndex;
storeSpRegisters[index] = rt;
swSequenceCount++;
}
}
} else {
// The register saved to the stack has already been modified.
// Do not optimize values above this stack offset.
maxSpOffset = min(maxSpOffset, simm16);
}
}
}
}
// Check for a "addiu $sp, $sp, -nn" instruction
if (insn == Instructions.ADDI || insn == Instructions.ADDIU) {
int rs = codeInstruction.getRsRegisterIndex();
int rt = codeInstruction.getRtRegisterIndex();
int simm16 = codeInstruction.getImm16(true);
if (rt == _sp && rs == _sp && simm16 < 0) {
decreaseSpInstruction = currentInstructionIndex;
stackSize = -codeInstruction.getImm16(true);
storeSpInstructions = new int[stackSize >> 2];
Arrays.fill(storeSpInstructions, -1);
storeSpRegisters = new int[storeSpInstructions.length];
Arrays.fill(storeSpRegisters, -1);
storeSpCodeInstructions = new LinkedList<CodeInstruction>();
} else if (rs == _sp && simm16 >= 0) {
// Loading a stack address into a register (e.g. "addiu $xx, $sp, nnn").
// Do not optimize values above this stack offset (nnn).
maxSpOffset = min(maxSpOffset, simm16);
}
// Check for a "addu $reg, $sp, $reg" instruction
} else if (insn == Instructions.ADD || insn == Instructions.ADDU) {
int rs = codeInstruction.getRsRegisterIndex();
int rt = codeInstruction.getRtRegisterIndex();
if (rs == _sp || rt == _sp) {
// Loading the stack address into a register (e.g. "addu $reg, $sp, $zr").
// The stack could be accessed at any address, stop optimizing.
break;
}
} else if (insn == Instructions.LW || insn == Instructions.SWC1 || insn == Instructions.LWC1) {
int rs = codeInstruction.getRsRegisterIndex();
int simm16 = codeInstruction.getImm16(true);
if (rs == _sp && simm16 >= 0) {
// Accessing the stack, do not optimize values above this stack offset.
maxSpOffset = min(maxSpOffset, simm16);
}
} else if (insn == Instructions.SVQ || insn == Instructions.LVQ) {
int rs = codeInstruction.getRsRegisterIndex();
int simm14 = codeInstruction.getImm14(true);
if (rs == _sp && simm14 >= 0) {
// Accessing the stack, do not optimize values above this stack offset.
maxSpOffset = min(maxSpOffset, simm14);
}
}
if (codeInstruction.hasFlags(Instruction.FLAG_WRITES_RT)) {
modifiedRegisters[codeInstruction.getRtRegisterIndex()] = true;
}
if (codeInstruction.hasFlags(Instruction.FLAG_WRITES_RD)) {
modifiedRegisters[codeInstruction.getRdRegisterIndex()] = true;
}
if (maxSpOffset <= 0) {
// Nothing more to do if the complete stack should not be optimized
break;
}
currentInstructionIndex++;
}
// If we have found more than one "sw" instructions, replace them by a meta code instruction.
if (swSequenceCount > 1) {
int[] offsets = new int[swSequenceCount];
int[] registers = new int[swSequenceCount];
int index = 0;
for (int i = 0; i < storeSpInstructions.length && index < swSequenceCount; i++) {
if (storeSpInstructions[i] >= 0) {
offsets[index] = i << 2;
registers[index] = storeSpRegisters[i];
index++;
}
}
// Remove all the "sw" instructions...
codeInstructions.removeAll(storeSpCodeInstructions);
// ... and replace them by a meta code instruction
SequenceSWCodeInstruction sequenceSWCodeInstruction = new SequenceSWCodeInstruction(_sp, offsets, registers);
sequenceSWCodeInstruction.setAddress(storeSpCodeInstructions.get(0).getAddress());
codeInstructions.add(decreaseSpInstruction + 1, sequenceSWCodeInstruction);
}
}
/**
* Compile a sequence
* sw $zr, n($reg)
* sw $zr, n+4($reg)
* sw $zr, n+8($reg)
* ...
* into
* System.arraycopy(FastMemory.zero, 0, memoryInt, (n + $reg) >> 2, length)
*
* @param baseRegister
* @param offsets
* @param registers
* @return true if the sequence could be compiled
* false if the sequence could not be compiled
*/
private boolean compileSWsequenceZR(int baseRegister, int[] offsets, int[] registers) {
for (int i = 0; i < registers.length; i++) {
if (registers[i] != _zr) {
return false;
}
}
for (int i = 1; i < offsets.length; i++) {
if (offsets[i] != offsets[i - 1] + 4) {
return false;
}
}
int offset = offsets[0];
int length = offsets.length;
do {
int copyLength = Math.min(length, FastMemory.zero.length);
// Build parameters for
// System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// i.e.
// System.arraycopy(FastMemory.zero,
// 0,
// RuntimeContext.memoryInt,
// RuntimeContext.checkMemoryRead32(baseRegister + offset, pc) >>> 2,
// copyLength);
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(FastMemory.class), "zero", "[I");
loadImm(0);
loadMemoryInt();
prepareMemIndex(baseRegister, offset, false, 32);
loadImm(copyLength);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(System.class), "arraycopy", arraycopyDescriptor);
length -= copyLength;
offset += copyLength;
} while (length > 0);
return true;
}
private boolean compileSWLWsequence(int baseRegister, int[] offsets, int[] registers, boolean isLW) {
// Optimization only possible for memoryInt
if (!RuntimeContext.hasMemoryInt()) {
return false;
}
// Disable optimizations when the profiler is enabled.
if (Profiler.isProfilerEnabled()) {
return false;
}
// Disable optimizations when the debugger is open
if (State.debugger != null) {
return false;
}
if (!isLW) {
if (compileSWsequenceZR(baseRegister, offsets, registers)) {
return true;
}
}
int offset = offsets[0];
prepareMemIndex(baseRegister, offset, isLW, 32);
storeTmp1();
for (int i = 0; i < offsets.length; i++) {
int rt = registers[i];
if (offset != offsets[i]) {
mv.visitIincInsn(LOCAL_TMP1, (offsets[i] - offset) >> 2);
offset = offsets[i];
}
if (isLW) {
if (rt != _zr) {
prepareRegisterForStore(rt);
loadMemoryInt();
loadTmp1();
mv.visitInsn(Opcodes.IALOAD);
storeRegister(rt);
}
} else {
loadMemoryInt();
loadTmp1();
loadRegister(rt);
mv.visitInsn(Opcodes.IASTORE);
}
}
return true;
}
@Override
public boolean compileSWsequence(int baseRegister, int[] offsets, int[] registers) {
return compileSWLWsequence(baseRegister, offsets, registers, false);
}
@Override
public boolean compileLWsequence(int baseRegister, int[] offsets, int[] registers) {
return compileSWLWsequence(baseRegister, offsets, registers, true);
}
}