From d69b02b2c40b0f7a1178fc037e72467724a64d62 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 6 Apr 2009 17:48:04 +0100 Subject: [PATCH] Java implementation of 64bit ldiv and lrem bytecodes for 32bit architectures. --- .../baseline/ia32/BaselineCompilerImpl.java | 72 ++---------------- .../baseline/ia32/BaselineMagic.java | 44 +++++++++++ .../opt/hir2lir/ExpandRuntimeServices.java | 32 ++++++++ .../opt/lir2mir/ConvertLIRtoMIR.java | 28 ------- rvm/src/org/jikesrvm/runtime/BootRecord.java | 4 - rvm/src/org/jikesrvm/runtime/Entrypoints.java | 9 ++- rvm/src/org/jikesrvm/runtime/Magic.java | 20 +++++ rvm/src/org/jikesrvm/runtime/MagicNames.java | 2 + .../jikesrvm/runtime/RuntimeEntrypoints.java | 74 +++++++++++++++++++ rvm/src/org/jikesrvm/runtime/SysCall.java | 6 -- 10 files changed, 185 insertions(+), 106 deletions(-) diff --git a/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineCompilerImpl.java b/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineCompilerImpl.java index 781f331f0..6a1231718 100644 --- a/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineCompilerImpl.java +++ b/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineCompilerImpl.java @@ -1164,38 +1164,10 @@ protected final void emit_ldiv() { asm.emitIDIV_Reg_Reg_Quad(EAX, ECX); asm.emitPUSH_Reg(EAX); // push result } else { - // (1) zero check - asm.emitMOV_Reg_RegInd(T0, SP); - asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); - asm.emitBranchLikelyNextInstruction(); - ForwardReference fr1 = asm.forwardJcc(Assembler.NE); - asm.emitINT_Imm(RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO + RVM_TRAP_BASE); // trap if divisor is 0 - fr1.resolve(asm); - // (2) save RVM nonvolatiles - int numNonVols = NONVOLATILE_GPRS.length; - Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); - for (int i = 0; i < numNonVols; i++) { - asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); - } - // (3) Push args to C function (reversed) - asm.emitPUSH_RegDisp(SP, off.plus(4)); - asm.emitPUSH_RegDisp(SP, off.plus(4)); - asm.emitPUSH_RegDisp(SP, off.plus(20)); - asm.emitPUSH_RegDisp(SP, off.plus(20)); - // (4) invoke C function through bootrecord - asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); - asm.emitCALL_RegDisp(S0, Entrypoints.sysLongDivideIPField.getOffset()); - // (5) pop space for arguments - adjustStack(4 * WORDSIZE, true); - // (6) restore RVM nonvolatiles - for (int i = numNonVols - 1; i >= 0; i--) { - asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); - } - // (7) pop expression stack - adjustStack(WORDSIZE*4, true); - // (8) push results - asm.emitPUSH_Reg(T1); - asm.emitPUSH_Reg(T0); + genParameterRegisterLoad(asm, 4); // pass 4 parameter words + asm.emitCALL_Abs(Magic.getTocPointer().plus(Entrypoints.ldivMethod.getOffset())); + asm.emitPUSH_Reg(T0); // high half + asm.emitPUSH_Reg(T1); // low half } } @@ -1212,38 +1184,10 @@ protected final void emit_lrem() { asm.emitIDIV_Reg_Reg_Quad(EAX, ECX); asm.emitPUSH_Reg(EDX); // push result } else { - // (1) zero check - asm.emitMOV_Reg_RegInd(T0, SP); - asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); - asm.emitBranchLikelyNextInstruction(); - ForwardReference fr1 = asm.forwardJcc(Assembler.NE); - asm.emitINT_Imm(RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO + RVM_TRAP_BASE); // trap if divisor is 0 - fr1.resolve(asm); - // (2) save RVM nonvolatiles - int numNonVols = NONVOLATILE_GPRS.length; - Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); - for (int i = 0; i < numNonVols; i++) { - asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); - } - // (3) Push args to C function (reversed) - asm.emitPUSH_RegDisp(SP, off.plus(4)); - asm.emitPUSH_RegDisp(SP, off.plus(4)); - asm.emitPUSH_RegDisp(SP, off.plus(20)); - asm.emitPUSH_RegDisp(SP, off.plus(20)); - // (4) invoke C function through bootrecord - asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); - asm.emitCALL_RegDisp(S0, Entrypoints.sysLongRemainderIPField.getOffset()); - // (5) pop space for arguments - adjustStack(4 * WORDSIZE, true); - // (6) restore RVM nonvolatiles - for (int i = numNonVols - 1; i >= 0; i--) { - asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); - } - // (7) pop expression stack - adjustStack(WORDSIZE*4, true); - // (8) push results - asm.emitPUSH_Reg(T1); - asm.emitPUSH_Reg(T0); + genParameterRegisterLoad(asm, 4); // pass 4 parameter words + asm.emitCALL_Abs(Magic.getTocPointer().plus(Entrypoints.lremMethod.getOffset())); + asm.emitPUSH_Reg(T0); // high half + asm.emitPUSH_Reg(T1); // low half } } diff --git a/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineMagic.java b/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineMagic.java index e6a77d875..cb4f8d920 100644 --- a/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineMagic.java +++ b/rvm/src/org/jikesrvm/compilers/baseline/ia32/BaselineMagic.java @@ -2058,6 +2058,50 @@ void generateMagic(Assembler asm, MethodReference m, RVMMethod cm, Offset sd) { generators.put(getMethodReference(Magic.class, MagicNames.pause, void.class), g); } + /** + * Signed long divide by 32bit divisor giving 32bit quotient + */ + private static final class SignedDivide extends MagicGenerator { + @Override + void generateMagic(Assembler asm, MethodReference m, RVMMethod cm, Offset sd) { + // stack: value1.high = divident + // value1.low + // value2 = divisor <-- ESP + if (VM.VerifyAssertions) VM._assert(S0 != EAX && S0 != EDX); + asm.emitPOP_Reg(S0); + asm.emitPOP_Reg(EAX); + asm.emitPOP_Reg(EDX); + asm.emitIDIV_Reg_Reg(EAX, S0); + asm.emitPUSH_Reg(EAX); + } + } + static { + MagicGenerator g = new SignedDivide(); + generators.put(getMethodReference(Magic.class, MagicNames.signedDivide, long.class, int.class, int.class), g); + } + + /** + * Unsigned long divide by 32bit divisor giving 32bit quotient + */ + private static final class UnsignedDivide extends MagicGenerator { + @Override + void generateMagic(Assembler asm, MethodReference m, RVMMethod cm, Offset sd) { + // stack: value1.high = divident + // value1.low + // value2 = divisor <-- ESP + if (VM.VerifyAssertions) VM._assert(S0 != EAX && S0 != EDX); + asm.emitPOP_Reg(S0); + asm.emitPOP_Reg(EAX); + asm.emitPOP_Reg(EDX); + asm.emitDIV_Reg_Reg(EAX, S0); + asm.emitPUSH_Reg(EAX); + } + } + static { + MagicGenerator g = new UnsignedDivide(); + generators.put(getMethodReference(Magic.class, MagicNames.unsignedDivide, long.class, int.class, int.class), g); + } + /** * Floating point square root */ diff --git a/rvm/src/org/jikesrvm/compilers/opt/hir2lir/ExpandRuntimeServices.java b/rvm/src/org/jikesrvm/compilers/opt/hir2lir/ExpandRuntimeServices.java index 392c0575b..1c5e99821 100644 --- a/rvm/src/org/jikesrvm/compilers/opt/hir2lir/ExpandRuntimeServices.java +++ b/rvm/src/org/jikesrvm/compilers/opt/hir2lir/ExpandRuntimeServices.java @@ -18,6 +18,8 @@ import static org.jikesrvm.compilers.opt.ir.Operators.GETFIELD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GETSTATIC_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ASTORE; +import static org.jikesrvm.compilers.opt.ir.Operators.LONG_DIV_opcode; +import static org.jikesrvm.compilers.opt.ir.Operators.LONG_REM_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY_UNRESOLVED_opcode; @@ -54,6 +56,7 @@ import org.jikesrvm.compilers.opt.ir.Call; import org.jikesrvm.compilers.opt.ir.GetField; import org.jikesrvm.compilers.opt.ir.GetStatic; +import org.jikesrvm.compilers.opt.ir.GuardedBinary; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.IRTools; import org.jikesrvm.compilers.opt.ir.Instruction; @@ -138,6 +141,35 @@ public void perform(IR ir) { int opcode = inst.getOpcode(); switch (opcode) { + case LONG_DIV_opcode: { + if (VM.BuildFor64Addr) break; // don't reduce operator + RVMMethod target = Entrypoints.ldivMethod; + Call.mutate2(inst, + CALL, + GuardedBinary.getClearResult(inst), + IRTools.AC(target.getOffset()), + MethodOperand.STATIC(target), + GuardedBinary.getClearVal1(inst), + GuardedBinary.getClearVal2(inst)); + if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) { + inline(inst, ir); + } + } + + case LONG_REM_opcode: { + if (VM.BuildFor64Addr) break; // don't reduce operator + RVMMethod target = Entrypoints.lremMethod; + Call.mutate2(inst, + CALL, + GuardedBinary.getClearResult(inst), + IRTools.AC(target.getOffset()), + MethodOperand.STATIC(target), + GuardedBinary.getClearVal1(inst), + GuardedBinary.getClearVal2(inst)); + if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) { + inline(inst, ir); + } + } case NEW_opcode: { TypeOperand Type = New.getClearType(inst); diff --git a/rvm/src/org/jikesrvm/compilers/opt/lir2mir/ConvertLIRtoMIR.java b/rvm/src/org/jikesrvm/compilers/opt/lir2mir/ConvertLIRtoMIR.java index 0c93d44f2..9f9db7078 100644 --- a/rvm/src/org/jikesrvm/compilers/opt/lir2mir/ConvertLIRtoMIR.java +++ b/rvm/src/org/jikesrvm/compilers/opt/lir2mir/ConvertLIRtoMIR.java @@ -216,34 +216,6 @@ public void perform(IR ir) { } break; - case LONG_DIV_opcode: { - if (VM.BuildForPowerPC && VM.BuildFor64Addr) break; // don't reduce operator -- leave for BURS - Call.mutate2(s, - SYSCALL, - GuardedBinary.getClearResult(s), - null, - MethodOperand.STATIC(Entrypoints.sysLongDivideIPField), - GuardedBinary.getClearVal1(s), - GuardedBinary.getClearVal2(s)); - ConvertToLowLevelIR.expandSysCallTarget(s, ir); - CallingConvention.expandSysCall(s, ir); - } - break; - - case LONG_REM_opcode: { - if (VM.BuildForPowerPC && VM.BuildFor64Addr) break; // don't reduce operator -- leave for BURS - Call.mutate2(s, - SYSCALL, - GuardedBinary.getClearResult(s), - null, - MethodOperand.STATIC(Entrypoints.sysLongRemainderIPField), - GuardedBinary.getClearVal1(s), - GuardedBinary.getClearVal2(s)); - ConvertToLowLevelIR.expandSysCallTarget(s, ir); - CallingConvention.expandSysCall(s, ir); - } - break; - case FLOAT_REM_opcode: case DOUBLE_REM_opcode: { if (VM.BuildForPowerPC) { diff --git a/rvm/src/org/jikesrvm/runtime/BootRecord.java b/rvm/src/org/jikesrvm/runtime/BootRecord.java index 7ddeb6a2f..3970a9ba1 100644 --- a/rvm/src/org/jikesrvm/runtime/BootRecord.java +++ b/rvm/src/org/jikesrvm/runtime/BootRecord.java @@ -266,10 +266,6 @@ public void setHeapRange(int id, Address start, Address end) { // arithmetic @Entrypoint - public Address sysLongDivideIP; - @Entrypoint - public Address sysLongRemainderIP; - @Entrypoint public Address sysLongToFloatIP; @Entrypoint public Address sysLongToDoubleIP; diff --git a/rvm/src/org/jikesrvm/runtime/Entrypoints.java b/rvm/src/org/jikesrvm/runtime/Entrypoints.java index fe636637b..a12160601 100644 --- a/rvm/src/org/jikesrvm/runtime/Entrypoints.java +++ b/rvm/src/org/jikesrvm/runtime/Entrypoints.java @@ -45,6 +45,11 @@ public class Entrypoints { "objectAddressRemapper", org.jikesrvm.runtime.ObjectAddressRemapper.class); + public static final NormalMethod ldivMethod = + getMethod(org.jikesrvm.runtime.RuntimeEntrypoints.class, "ldiv", "(JJ)J"); + public static final NormalMethod lremMethod = + getMethod(org.jikesrvm.runtime.RuntimeEntrypoints.class, "lrem", "(JJ)J"); + public static final NormalMethod instanceOfMethod = getMethod(org.jikesrvm.runtime.RuntimeEntrypoints.class, "instanceOf", "(Ljava/lang/Object;I)Z"); public static final NormalMethod checkcastMethod = @@ -331,10 +336,6 @@ public class Entrypoints { getField(org.jikesrvm.runtime.BootRecord.class, "the_boot_record", org.jikesrvm.runtime.BootRecord.class); public static final RVMField externalSignalFlagField = getField(org.jikesrvm.runtime.BootRecord.class, "externalSignalFlag", int.class); - public static final RVMField sysLongDivideIPField = - getField(org.jikesrvm.runtime.BootRecord.class, "sysLongDivideIP", org.vmmagic.unboxed.Address.class); - public static final RVMField sysLongRemainderIPField = - getField(org.jikesrvm.runtime.BootRecord.class, "sysLongRemainderIP", org.vmmagic.unboxed.Address.class); public static final RVMField sysLongToFloatIPField = getField(org.jikesrvm.runtime.BootRecord.class, "sysLongToFloatIP", org.vmmagic.unboxed.Address.class); public static final RVMField sysLongToDoubleIPField = diff --git a/rvm/src/org/jikesrvm/runtime/Magic.java b/rvm/src/org/jikesrvm/runtime/Magic.java index 3415ededc..631cb00b8 100644 --- a/rvm/src/org/jikesrvm/runtime/Magic.java +++ b/rvm/src/org/jikesrvm/runtime/Magic.java @@ -877,6 +877,26 @@ public static void pause() { } } + /** + * A hardware signed long divide by 32bit divisor giving 32bit quotient + */ + public static int signedDivide(long u, int v) { + if (VM.runningVM && VM.VerifyAssertions) { + VM._assert(VM.NOT_REACHED); // call site should have been hijacked by magic in compiler + } + return 0; + } + + /** + * A hardware unsigned long divide by 32bit divisor giving 32bit quotient + */ + public static int unsignedDivide(long u, int v) { + if (VM.runningVM && VM.VerifyAssertions) { + VM._assert(VM.NOT_REACHED); // call site should have been hijacked by magic in compiler + } + return 0; + } + /** * A hardware SQRT instruction */ diff --git a/rvm/src/org/jikesrvm/runtime/MagicNames.java b/rvm/src/org/jikesrvm/runtime/MagicNames.java index 451856207..a29bdea02 100644 --- a/rvm/src/org/jikesrvm/runtime/MagicNames.java +++ b/rvm/src/org/jikesrvm/runtime/MagicNames.java @@ -62,6 +62,8 @@ public class MagicNames { public static final Atom loadObjectReference = Atom.findOrCreateAsciiAtom("loadObjectReference"); public static final Atom store = Atom.findOrCreateAsciiAtom("store"); public static final Atom pause = Atom.findOrCreateAsciiAtom("pause"); + public static final Atom signedDivide = Atom.findOrCreateAsciiAtom("signedDivide"); + public static final Atom unsignedDivide = Atom.findOrCreateAsciiAtom("unsignedDivide"); public static final Atom sqrt = Atom.findOrCreateAsciiAtom("sqrt"); public static final Atom getUnsignedByteAtOffset = Atom.findOrCreateAsciiAtom("getUnsignedByteAtOffset"); diff --git a/rvm/src/org/jikesrvm/runtime/RuntimeEntrypoints.java b/rvm/src/org/jikesrvm/runtime/RuntimeEntrypoints.java index ffdd68eca..0f3f210b2 100644 --- a/rvm/src/org/jikesrvm/runtime/RuntimeEntrypoints.java +++ b/rvm/src/org/jikesrvm/runtime/RuntimeEntrypoints.java @@ -92,6 +92,80 @@ public class RuntimeEntrypoints implements Constants, ArchitectureSpecific.Stack public static final int TRAP_STORE_CHECK = 8; // opt-compiler public static final int TRAP_STACK_OVERFLOW_FATAL = 9; // assertion checking + /** + * Perform signed division implementing the ldiv bytecode + * + * @param u dividend + * @param v divisor + * @return quotient + */ + @Entrypoint + static long ldiv(long u, long v) { + if (v == 0) { + raiseArithmeticException(); + } + long au = Math.abs(u); + long av = Math.abs(v); + if ((av >>> 31) == 0) { + if (au < (av << 31)) { + long q = Magic.signedDivide(u, (int)v); + return (q << 32) >> 32; + } + } + long q = unsignedDivide(au, av); + long t = (u ^ v) >> 63; + return (q ^ t) - t; + } + + /** + * Perform the unsigned division subcase of the ldiv bytecode + * + * @param u dividend + * @param v divisor + * @return quotient + */ + private static long unsignedDivide(long u, long v) { + if ((v >>> 32) == 0) { + if ((u >>> 32) < v) { + return Magic.unsignedDivide(u, (int)v) & 0xFFFFFFFF; + } else { + long u1 = u >>> 32; + long u0 = u & 0xFFFFFFFF; + long q1 = Magic.unsignedDivide(u1, (int)v) & 0xFFFFFFFF; + long k = u1 - q1 * v; + long q0 = Magic.unsignedDivide((k << 32) + u0, (int)v) & 0xFFFFFFFF; + return (q1 << 32) + q0; + } + } else { + int n = Long.numberOfLeadingZeros(v); + long v1 = (v << n) >>> 32; + long u1 = u >>> 1; + long q1 = Magic.unsignedDivide(u1, (int)v1) & 0xFFFFFFFF; + long q0 = (q1 << n) >>> 31; + if (q0 != 0) { + q0 = q0-1; + } + if ((u - q0*v) >= v) { + q0 = q0+1; + } + return q0; + } + } + + /** + * Perform signed remainder implementing the lrem bytecode + * + * @param u dividend + * @param v divisor + * @return remainder + */ + @Entrypoint + static long lrem(long u, long v) { + // @TODO: optimize this further + long r = ldiv(u, v); + return u - (r * v); + } + //---------------------------------------------------------------// // Type Checking. // //---------------------------------------------------------------// diff --git a/rvm/src/org/jikesrvm/runtime/SysCall.java b/rvm/src/org/jikesrvm/runtime/SysCall.java index a66cf1d0a..72850f407 100644 --- a/rvm/src/org/jikesrvm/runtime/SysCall.java +++ b/rvm/src/org/jikesrvm/runtime/SysCall.java @@ -214,12 +214,6 @@ public abstract class SysCall { public abstract void sysMonitorNotifyAll(Address monitor); // arithmetic - @SysCallTemplate - public abstract long sysLongDivide(long x, long y); - - @SysCallTemplate - public abstract long sysLongRemainder(long x, long y); - @SysCallTemplate public abstract float sysLongToFloat(long x);