diff --git a/ts2panda/src/assemblyDumper.ts b/ts2panda/src/assemblyDumper.ts index a11bc0b7506fb60b6c4e207f86791b8eae81be78..6b2fc9d7932499c3f87709cea21d47a4de824f3f 100644 --- a/ts2panda/src/assemblyDumper.ts +++ b/ts2panda/src/assemblyDumper.ts @@ -82,7 +82,8 @@ export class AssemblyDumper { for (let i = 0; i < parametersCount; ++i) { let node = irNodes[i]; this.output += "\t"; - this.output += node.getMnemonic() + " v" + (node.operands[0]).num + ", a" + ((node.operands[0]).num) + "\n"; + let paramIdx = parametersCount - i - 1; + this.output += node.getMnemonic() + " v" + (node.operands[0]).num + ", a" + paramIdx + "\n"; } for (let i = parametersCount; i < irNodes.length; ++i) { diff --git a/ts2panda/src/regAllocator.ts b/ts2panda/src/regAllocator.ts index 9d5ac4f11ffaf7918829209640bcfdeaf16c6aa5..790745246c363ea5f208adb7aff516a322dc875f 100644 --- a/ts2panda/src/regAllocator.ts +++ b/ts2panda/src/regAllocator.ts @@ -16,7 +16,7 @@ import { getRangeStartVregPos, isRangeInst } from "./base/util"; -import { CacheList } from "./base/vregisterCache"; +import { CacheList, VregisterCache } from "./base/vregisterCache"; import { DebugInfo } from "./debuginfo"; import { Format, @@ -28,21 +28,14 @@ import { } from "./irnodes"; import { PandaGen } from "./pandagen"; -const MAX_VREGA = 16; -const MAX_VREGB = 256; const MAX_VREGC = 65536; -interface VRegWithFlag { - vreg: VReg; - flag: boolean; // indicate whether it is used as a temporary register for spill -} - class RegAllocator { private newInsns: IRNode[] = []; private spills: VReg[] = []; + private spillId: number = 0; private vRegsId: number = 0; - private usedVreg: VRegWithFlag[] = []; - private tmpVreg: VRegWithFlag[] = []; + private needAdjust: boolean = false; constructor() { this.vRegsId = 0; @@ -51,42 +44,14 @@ class RegAllocator { allocIndexForVreg(vreg: VReg) { let num = this.getFreeVreg(); vreg.num = num; - this.usedVreg[num] = {vreg: vreg, flag: false}; } - findTmpVreg(level: number): VReg { - let iterCnts = Math.min(MAX_VREGB, this.usedVreg.length); - for (let i = 0; i < iterCnts; ++i) { - let value = this.usedVreg[i]; - if (value === undefined || value.flag) { - continue; - } - if (level === MAX_VREGA && value.vreg.num >= MAX_VREGA) { - throw new Error("no available tmp vReg from A"); - } - value.flag = true; - this.tmpVreg.push(value); - return value.vreg; - } - throw new Error("no available tmp vReg from B"); + getSpill(): VReg { + return this.spills[this.spillId++]; } - clearVregFlags(): void { - for (let v of this.tmpVreg) { - v.flag = false; - } - this.tmpVreg = []; - } - allocSpill(): VReg { - if (this.spills.length > 0) { - return this.spills.pop()!; - } - let v = new VReg(); - this.allocIndexForVreg(v); - return v; - } - freeSpill(v: VReg): void { - this.spills.push(v); + freeSpill(): void { + this.spillId = 0; } getFreeVreg(): number { @@ -112,48 +77,26 @@ class RegAllocator { return num; } - markVregNotAvailableAsTmp(vreg: VReg): void { - let num = vreg.num; - this.usedVreg[num].flag = true; - this.tmpVreg.push(this.usedVreg[num]); - } - doRealAdjustment(operands: OperandType[], format: Format, index: number, irNodes: IRNode[]) { let head: IRNode[] = []; let tail: IRNode[] = []; - let spills: VReg[] = []; - // mark all vreg used in the current insn as not valid for tmp register - for (let i = 0; i < operands.length; ++i) { - if (operands[i] instanceof VReg) { - this.markVregNotAvailableAsTmp(operands[i]); - } - } for (let j = 0; j < operands.length; ++j) { if (operands[j] instanceof VReg) { let vOrigin = operands[j]; if (vOrigin.num >= (1 << format[j][1])) { - let spill = this.allocSpill(); - spills.push(spill); - let vTmp; - try { - vTmp = this.findTmpVreg(1 << format[j][1]); - } catch { - throw Error("no available tmp vReg"); - } - head.push(new Mov(spill, vTmp)); - operands[j] = vTmp; + let spill = this.getSpill(); + operands[j] = spill; if (format[j][0] == OperandKind.SrcVReg) { - head.push(new Mov(vTmp, vOrigin)); + head.push(new Mov(spill, vOrigin)); } else if (format[j][0] == OperandKind.DstVReg) { - tail.push(new Mov(vOrigin, vTmp)) + tail.push(new Mov(vOrigin, spill)) } else if (format[j][0] == OperandKind.SrcDstVReg) { - head.push(new Mov(vTmp, vOrigin)); - tail.push(new Mov(vOrigin, vTmp)) + head.push(new Mov(spill, vOrigin)); + tail.push(new Mov(vOrigin, spill)) } else { // here we do nothing } - tail.push(new Mov(vTmp, spill)); } } } @@ -163,11 +106,7 @@ class RegAllocator { DebugInfo.copyDebugInfo(irNodes[index], tail); this.newInsns.push(...head, irNodes[index], ...tail); - - for (let j = spills.length - 1; j >= 0; --j) { - this.freeSpill(spills[j]); - } - this.clearVregFlags(); + this.freeSpill(); } checkDynRangeInstruction(irNodes: IRNode[], index: number): boolean { @@ -179,8 +118,6 @@ class RegAllocator { 1. "CalliDynRange 4, v255" is a valid insn, there is no need for all 4 registers numbers to be less than 255, it is also similar for NewobjDyn 2. we do not need to mark any register to be invalid for tmp register, since no other register is used in calli.dyn.range - 3. if v.num is bigger than 255, it means all register less than 255 has been already used, they should have been pushed - into usedVreg */ if ((operands[rangeRegOffset]).num >= level) { // needs to be adjusted. @@ -208,37 +145,23 @@ class RegAllocator { adjustDynRangeInstruction(irNodes: IRNode[], index: number) { let head: IRNode[] = []; - let tail: IRNode[] = []; - let spills: VReg[] = []; let operands = irNodes[index].operands; /* exclude operands that are not require consecutive */ let rangeRegOffset = getRangeStartVregPos(irNodes[index]); let regNums = operands.length - getRangeStartVregPos(irNodes[index]); - let level = 1 << (irNodes[index].getFormats())[0][rangeRegOffset][1]; - let tmp = this.findTmpVreg(level); - for (let i = 0; i < regNums; i++) { - let spill = this.allocSpill(); - spills.push(spill); - - /* We need to make sure that the register input in the .range instruction is continuous(small to big). */ - head.push(new Mov(spill, this.usedVreg[tmp.num + i].vreg)); - head.push(new Mov(this.usedVreg[tmp.num + i].vreg, operands[i + rangeRegOffset])); - operands[i + rangeRegOffset] = this.usedVreg[tmp.num + i].vreg; - tail.push(new Mov(this.usedVreg[tmp.num + i].vreg, spill)); + let spill = this.getSpill(); + head.push(new Mov(spill, operands[i + rangeRegOffset])); + operands[i + rangeRegOffset] = spill; } // for debuginfo DebugInfo.copyDebugInfo(irNodes[index], head); - DebugInfo.copyDebugInfo(irNodes[index], tail); - this.newInsns.push(...head, irNodes[index], ...tail); - for (let i = spills.length - 1; i >= 0; --i) { - this.freeSpill(spills[i]); - } - this.clearVregFlags(); + this.newInsns.push(...head, irNodes[index]); + this.freeSpill(); } adjustInstructionsIfNeeded(irNodes: IRNode[]): void { @@ -275,13 +198,7 @@ class RegAllocator { return this.vRegsId; } - run(pandaGen: PandaGen): void { - let irNodes = pandaGen.getInsns(); - let locals = pandaGen.getLocals(); - let temps = pandaGen.getTemps(); - let cache = pandaGen.getVregisterCache(); - let parametersCount = pandaGen.getParametersCount(); - // don't mess up allocation order + allocIndexForVregs(locals: VReg[], temps: VReg[], cache: VregisterCache): void { for (let i = 0; i < locals.length; ++i) { this.allocIndexForVreg(locals[i]); } @@ -294,6 +211,60 @@ class RegAllocator { this.allocIndexForVreg(cacheItem.getCache()); } } + } + + allocSpillPool(irNodes: IRNode[]): void { + let spillCount: number = 0; + for (let i = 0; i < irNodes.length; ++i) { + let operands = irNodes[i].operands; + let formats = irNodes[i].getFormats(); + if (isRangeInst(irNodes[i])) { + let rangeRegOffset = getRangeStartVregPos(irNodes[i]); + spillCount = Math.max(spillCount, operands.length - rangeRegOffset); + + let level = 1 << (irNodes[i].getFormats())[0][rangeRegOffset][1]; + if ((operands[rangeRegOffset]).num >= level) { + this.needAdjust = true; + } + continue; + } + + let min = operands.length; + spillCount = Math.max(spillCount, min); + for (let j = 0; j < formats.length; ++j) { + let num = this.getNumOfInvalidVregs(operands, formats[j]); + if (num < min) { + min = num; + } + } + if (min > 0) { + this.needAdjust = true; + } + } + + if (this.needAdjust) { + this.vRegsId = 0; + while (spillCount--) { + let spill = new VReg(); + this.allocIndexForVreg(spill); + this.spills.push(spill); + } + } + } + + run(pandaGen: PandaGen): void { + let irNodes = pandaGen.getInsns(); + let locals = pandaGen.getLocals(); + let temps = pandaGen.getTemps(); + let cache = pandaGen.getVregisterCache(); + let parametersCount = pandaGen.getParametersCount(); + + this.allocIndexForVregs(locals, temps, cache); + this.allocSpillPool(irNodes); + if (this.needAdjust) { + // assign index to Vregs again + this.allocIndexForVregs(locals, temps, cache); + } this.adjustInstructionsIfNeeded(irNodes); for (let i = 0; i < parametersCount; ++i) { let v = new VReg(); diff --git a/ts2panda/tests/regAllocator.test.ts b/ts2panda/tests/regAllocator.test.ts index b20940b14b053d5de4f9994885aab3be6a67fdf3..185ebd3594afc137b880494157d04220fd31deed 100644 --- a/ts2panda/tests/regAllocator.test.ts +++ b/ts2panda/tests/regAllocator.test.ts @@ -19,19 +19,22 @@ import * as ts from "typescript"; import { Callrange, Returnundefined, - Sttoglobalrecord, - Tryldglobalbyname, Imm, IRNode, + Jmp, + Label, Ldai, Lda, + Ldglobalvar, + Mov, Sta, + Throw, VReg } from "../src/irnodes"; import { PandaGen } from "../src/pandagen"; import { CacheExpander } from "../src/pass/cacheExpander"; import { RegAlloc } from "../src/regAllocator"; -import { basicChecker, checkInstructions, compileAllSnippet } from "./utils/base"; +import { basicChecker, checkInstructions, compileAllSnippet, SnippetCompiler } from "./utils/base"; import { creatAstFromSnippet } from "./utils/asthelper"; function checkRegisterNumber(left: IRNode, right: IRNode): boolean { @@ -55,32 +58,46 @@ function checkRegisterNumber(left: IRNode, right: IRNode): boolean { return true; } describe("RegAllocator", function () { - it("make spill for Src register", function () { - let string: string = ""; + it("make spill for Dst register & Src register", function () { + let string = "function test() {"; for (let i = 0; i < 256; ++i) { string += "let a" + i + " = " + i + ";"; } - string += "a255;"; + string += "a255;}"; + + let snippetCompiler = new SnippetCompiler(); + snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); + let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); - let pgs = compileAllSnippet(string, [new CacheExpander(), new RegAlloc()]); - let insns = pgs[0].getInsns(); IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); - IRNode.pg.updateIcSize(252); + IRNode.pg.updateIcSize(0); + + let v = []; + for (let i = 0; i < 260; ++i) { + v[i] = new VReg(); + v[i].num = i; + } let expected: IRNode[] = [ new Ldai(new Imm(252)), - new Sttoglobalrecord(new Imm(0), 'a252'), + new Sta(v[0]), + new Mov(v[256], v[0]), new Ldai(new Imm(253)), - new Sttoglobalrecord(new Imm(1), 'a253'), + new Sta(v[0]), + new Mov(v[257], v[0]), new Ldai(new Imm(254)), - new Sttoglobalrecord(new Imm(2), 'a254'), + new Sta(v[0]), + new Mov(v[258], v[0]), new Ldai(new Imm(255)), - new Sttoglobalrecord(new Imm(3), 'a255'), - new Tryldglobalbyname(new Imm(4), 'a255'), + new Sta(v[0]), + new Mov(v[259], v[0]), + // load a255 + new Mov(v[0], v[259]), + new Lda(v[0]), new Returnundefined() ] - expect(checkInstructions(insns.slice(insns.length - 10), expected, checkRegisterNumber)).to.be.true; + expect(checkInstructions(insns.slice(insns.length - 15), expected, checkRegisterNumber)).to.be.true; }); it("make spill for SrcDst register", function () { @@ -95,41 +112,113 @@ describe("RegAllocator", function () { but in case later 16 might be changed to 8, then spill operation will be needed in some cases. this testcase is designed for 8bits constraints. */ - let string = ""; + let string = "function test() {"; for (let i = 0; i < 256; ++i) { string += "let a" + i + " = " + i + ";"; } - string += "call(a252, a253, a254, a255);"; - let pgs = compileAllSnippet(string, [new CacheExpander(), new RegAlloc()]); - let insns = pgs[0].getInsns(); + string += "test(a252, a253, a254, a255);}"; + + let snippetCompiler = new SnippetCompiler(); + snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); + let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); + IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); - IRNode.pg.updateIcSize(252); + IRNode.pg.updateIcSize(0); let v = []; - for (let i = 0; i < 8; ++i) { + for (let i = 0; i < 268; ++i) { v[i] = new VReg(); v[i].num = i; } let expected = [ new Ldai(new Imm(252)), - new Sttoglobalrecord(new Imm(252), 'a252'), + new Sta(v[0]), + new Mov(v[259], v[0]), + new Ldai(new Imm(253)), + new Sta(v[0]), + new Mov(v[260], v[0]), + new Ldai(new Imm(254)), + new Sta(v[0]), + new Mov(v[261], v[0]), + new Ldai(new Imm(255)), + new Sta(v[0]), + new Mov(v[262], v[0]), + new Ldglobalvar(new Imm(0), "test"), + new Sta(v[0]), + new Mov(v[263], v[0]), + // call test with [a252, a253, a254, a255] + new Mov(v[0], v[259]), + new Lda(v[0]), + new Sta(v[0]), + new Mov(v[264], v[0]), + new Mov(v[0], v[260]), + new Lda(v[0]), + new Sta(v[0]), + new Mov(v[265], v[0]), + new Mov(v[0], v[261]), + new Lda(v[0]), + new Sta(v[0]), + new Mov(v[266], v[0]), + new Mov(v[0], v[262]), + new Lda(v[0]), + new Sta(v[0]), + new Mov(v[267], v[0]), + new Mov(v[0], v[263]), + new Lda(v[0]), + new Mov(v[0], v[264]), + new Mov(v[1], v[265]), + new Mov(v[2], v[266]), + new Mov(v[3], v[267]), + new Callrange(new Imm(1), new Imm(4), [v[0], v[1], v[2], v[3]]), + new Returnundefined(), + ]; + + expect(checkInstructions(insns.slice(insns.length - 39), expected, checkRegisterNumber)).to.be.true; + }); + + it("make spill for control-flow change", function () { + let string = "function test() {"; + for (let i = 0; i < 256; ++i) { + string += "let a" + i + " = " + i + ";"; + } + string += `try { throw a0; } catch { a0 }};`; + + let snippetCompiler = new SnippetCompiler(); + snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); + let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); + + IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); + IRNode.pg.updateIcSize(0); + let v = []; + for (let i = 0; i < 261; ++i) { + v[i] = new VReg(); + v[i].num = i; + } + let tryBeginLabel = new Label(); + let tryEndLabel = new Label(); + let catchBeginLabel = new Label(); + let catchEndLabel = new Label(); + + let expected = [ + new Ldai(new Imm(252)), + new Sta(v[0]), + new Mov(v[256], v[0]), new Ldai(new Imm(253)), - new Sttoglobalrecord(new Imm(253), 'a253'), + new Sta(v[0]), + new Mov(v[257], v[0]), new Ldai(new Imm(254)), - new Sttoglobalrecord(new Imm(254), 'a254'), + new Sta(v[0]), + new Mov(v[258], v[0]), new Ldai(new Imm(255)), - new Sttoglobalrecord(new Imm(256), 'a255'), - new Tryldglobalbyname(new Imm(257), 'call'), - new Sta(v[3]), - new Tryldglobalbyname(new Imm(258), 'a252'), - new Sta(v[4]), - new Tryldglobalbyname(new Imm(259), 'a253'), - new Sta(v[5]), - new Tryldglobalbyname(new Imm(260), 'a254'), - new Sta(v[6]), - new Tryldglobalbyname(new Imm(261), 'a255'), - new Sta(v[7]), - new Lda(v[3]), - new Callrange(new Imm(255), new Imm(4), [v[4], v[5], v[6], v[7]]), + new Sta(v[0]), + new Mov(v[259], v[0]), + tryBeginLabel, + new Lda(v[4]), + new Throw(), + tryEndLabel, + new Jmp(catchEndLabel), + catchBeginLabel, + new Lda(v[4]), + catchEndLabel, new Returnundefined(), ];