From 46b9fd2079725c131880d7fb62243c099ad286c5 Mon Sep 17 00:00:00 2001 From: Michael Bebenita Date: Fri, 6 Mar 2015 01:45:06 -0800 Subject: [PATCH 1/2] A more compact encoding of unwind code. --- bytecodes.ts | 2 +- jit/baseline.ts | 36 ++++++++++++++++++++++++++---------- jit/blockMap.ts | 5 +++++ vm/runtime.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/bytecodes.ts b/bytecodes.ts index a4c9b6e0..18802d72 100644 --- a/bytecodes.ts +++ b/bytecodes.ts @@ -744,7 +744,7 @@ module J2ME.Bytecode { * Determines if a given opcode denotes an instruction that stores a value to a local variable * after popping it from the operand stack. */ - function isInvoke(opcode: Bytecodes): boolean { + export function isInvoke(opcode: Bytecodes): boolean { return (flags[opcode & 0xff] & Flags.INVOKE) != 0; } diff --git a/jit/baseline.ts b/jit/baseline.ts index 67e164bc..fa767c27 100644 --- a/jit/baseline.ts +++ b/jit/baseline.ts @@ -312,6 +312,8 @@ module J2ME { */ static stackNames = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "_O", "P", "Q", "R", "S", "T", "_U", "V", "W", "X", "Y", "Z"]; + private hasUnwindThrow; + constructor(methodInfo: MethodInfo, target: CompilationTarget) { this.methodInfo = methodInfo; this.local = []; @@ -330,6 +332,7 @@ module J2ME { this.bodyEmitter = new Emitter(target !== CompilationTarget.Runtime); this.blockEmitter = new Emitter(target !== CompilationTarget.Runtime); this.target = target; + this.hasUnwindThrow = false; } compile(): CompiledMethodInfo { @@ -388,11 +391,6 @@ module J2ME { this.bodyEmitter.writeLn("J2ME.baselineMethodCounter.count(\"" + this.methodInfo.implKey + "\");"); } - needsWhile && this.bodyEmitter.enter("while (1) {"); - needsTry && this.bodyEmitter.enter("try {"); - - this.bodyEmitter.writeLn("var label = 0;"); - var blocks = blockMap.blocks; for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; @@ -407,12 +405,24 @@ module J2ME { this.emitBlockBody(stream, block); } + if (this.hasUnwindThrow) { + needsTry = true; + } + + needsWhile && this.bodyEmitter.enter("while (1) {"); + needsTry && this.bodyEmitter.enter("try {"); + this.bodyEmitter.writeLn("var label = 0;"); this.bodyEmitter.writeLns(Relooper.render(this.entryBlock)); emitCompilerAssertions && this.bodyEmitter.writeLn("J2ME.Debug.assert(false, 'Invalid PC: ' + pc)"); if (needsTry) { this.bodyEmitter.leaveAndEnter("} catch (ex) {"); + if (this.hasUnwindThrow) { + var local = this.local.join(", "); + var stack = this.stack.join(", "); + this.bodyEmitter.writeLn("if (U) { $.T(ex, [" + local + "], [" + stack + "], " + this.lockObject + "); return; }"); + } this.bodyEmitter.writeLn(this.getStackName(0) + " = TE(ex);"); this.blockStack = [this.getStackName(0)]; this.sp = 1; @@ -970,10 +980,16 @@ module J2ME { this.emitPush(Kind.Reference, "NM(" + classConstant(classInfo) + ", [" + dimensions.join(", ") + "])", Precedence.Call); } - private emitUnwind(emitter: Emitter, pc: string, nextPC: string) { - var local = this.local.join(", "); - var stack = this.blockStack.slice(0, this.sp).join(", "); - emitter.writeLn("if (U) { $.B(" + pc + ", " + nextPC + ", [" + local + "], [" + stack + "], " + this.lockObject + "); return; }"); + private emitUnwind(emitter: Emitter, pc: string, nextPC: string, forceInline: boolean = false) { + if (!forceInline && this.blockMap.invokeCount > 2) { + this.flushBlockStack(); + emitter.writeLn("U && TU(" + UnwindThrowLocation.encode(pc, nextPC, this.sp) + ");"); + this.hasUnwindThrow = true; + } else { + var local = this.local.join(", "); + var stack = this.blockStack.slice(0, this.sp).join(", "); + emitter.writeLn("if (U) { $.B(" + pc + ", " + nextPC + ", [" + local + "], [" + stack + "], " + this.lockObject + "); return; }"); + } baselineCounter && baselineCounter.count("emitUnwind"); } @@ -987,7 +1003,7 @@ module J2ME { this.needsVariable("lk"); emitter.writeLn("lk = " + object + "._lock;"); emitter.enter("if (lk && lk.level === 0) { lk.thread = th; lk.level = 1; } else { ME(" + object + ");"); - this.emitUnwind(emitter, String(this.pc), String(nextPC)); + this.emitUnwind(emitter, String(this.pc), String(nextPC), true); emitter.leave("}"); } diff --git a/jit/blockMap.ts b/jit/blockMap.ts index 043fd1ba..9012c311 100644 --- a/jit/blockMap.ts +++ b/jit/blockMap.ts @@ -87,6 +87,7 @@ module J2ME.Bytecode { method: MethodInfo; blocks: Block []; hasBackwardBranches: boolean; + invokeCount: number; private blockMap: Block []; private startBlock: Block; private canTrap: Uint32ArrayBitSet; @@ -96,6 +97,7 @@ module J2ME.Bytecode { this.blocks = []; this.method = method; this.hasBackwardBranches = false; + this.invokeCount = 0; this.blockMap = new Array(method.code.length); this.canTrap = new Uint32ArrayBitSet(this.blockMap.length); this.exceptionHandlers = this.method.exception_table; @@ -293,6 +295,9 @@ module J2ME.Bytecode { if (this.canTrapAt(opcode, bci)) { this.canTrap.set(bci); } + if (Bytecode.isInvoke(opcode)) { + this.invokeCount ++; + } } } bci += lengthAt(code, bci); diff --git a/vm/runtime.ts b/vm/runtime.ts index c6abd654..22c5cbfb 100644 --- a/vm/runtime.ts +++ b/vm/runtime.ts @@ -737,10 +737,20 @@ module J2ME { /** * Bailout callback whenever a JIT frame is unwound. */ - B(bci: number, nextBCI: number, local: any [], stack: any [], lockObject: java.lang.Object) { + B(pc: number, nextPC: number, local: any [], stack: any [], lockObject: java.lang.Object) { var methodInfo = jitMethodInfos[(arguments.callee.caller).name]; release || assert(methodInfo !== undefined); - $.ctx.bailout(methodInfo, bci, nextBCI, local, stack, lockObject); + $.ctx.bailout(methodInfo, pc, nextPC, local, stack, lockObject); + } + + /** + * Bailout callback whenever a JIT frame is unwound that uses a slightly different calling + * convetion that makes it more convenient to emit in some cases. + */ + T(location: UnwindThrowLocation, local: any [], stack: any [], lockObject: java.lang.Object) { + var methodInfo = jitMethodInfos[(arguments.callee.caller).name]; + release || assert(methodInfo !== undefined); + $.ctx.bailout(methodInfo, location.getPC(), location.getNextPC(), local, stack.slice(0, location.getSP()), lockObject); } yield(reason: string) { @@ -1865,6 +1875,39 @@ module J2ME { $.yield("preemption"); } } + + export class UnwindThrowLocation { + static instance: UnwindThrowLocation = new UnwindThrowLocation(); + constructor() { + this.location = 0; + } + static encode(pc, nextPC, sp) { + var delta = nextPC - pc; + release || assert (delta >= 0 && delta < 8, delta); + return pc << 11 | delta << 8 | sp; + } + location: number; + set(location: number) { + this.location = location; + return this; + } + getPC() { + return (this.location >> 11) & 0xffff; + } + getNextPC() { + return this.getPC() + ((this.location >> 8) & 0x07); + } + getSP() { + return this.location & 0xff; + } + } + + export function throwUnwind(location: number) { + if (location === 0) { + debugger; + } + throw UnwindThrowLocation.instance.set(location); + } } var Runtime = J2ME.Runtime; @@ -1878,6 +1921,8 @@ var AOTMD = J2ME.aotMetaData; */ var U: J2ME.VMState = J2ME.VMState.Running; +var TU = J2ME.throwUnwind; + /** * OSR Frame. */ From 85e33fd2151dd415b1cded2248a153ae48176d7d Mon Sep 17 00:00:00 2001 From: Michael Bebenita Date: Fri, 6 Mar 2015 01:53:25 -0800 Subject: [PATCH 2/2] Comments and assertions. --- jit/baseline.ts | 3 ++- vm/runtime.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/jit/baseline.ts b/jit/baseline.ts index fa767c27..bc32fe59 100644 --- a/jit/baseline.ts +++ b/jit/baseline.ts @@ -981,7 +981,8 @@ module J2ME { } private emitUnwind(emitter: Emitter, pc: string, nextPC: string, forceInline: boolean = false) { - if (!forceInline && this.blockMap.invokeCount > 2) { + // Only emit throw unwinds if it saves on code size. + if (!forceInline && this.blockMap.invokeCount > 2 && this.stack.length < 256) { this.flushBlockStack(); emitter.writeLn("U && TU(" + UnwindThrowLocation.encode(pc, nextPC, this.sp) + ");"); this.hasUnwindThrow = true; diff --git a/vm/runtime.ts b/vm/runtime.ts index 22c5cbfb..ee81a063 100644 --- a/vm/runtime.ts +++ b/vm/runtime.ts @@ -1884,6 +1884,7 @@ module J2ME { static encode(pc, nextPC, sp) { var delta = nextPC - pc; release || assert (delta >= 0 && delta < 8, delta); + release || assert (sp >= 0 && sp < 256, sp); return pc << 11 | delta << 8 | sp; } location: number;