• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {
17    getRangeStartVregPos, isRangeInst
18} from "./base/util";
19import { CacheList, VregisterCache } from "./base/vregisterCache";
20import { DebugInfo } from "./debuginfo";
21import {
22    Format,
23    IRNode,
24    Mov,
25    OperandKind,
26    OperandType,
27    VReg
28} from "./irnodes";
29import { PandaGen } from "./pandagen";
30
31const MAX_VREGC = 65536;
32
33class RegAllocator {
34    private newInsns: IRNode[] = [];
35    private spills: VReg[] = [];
36    private spillId: number = 0;
37    private vRegsId: number = 0;
38    private needAdjust: boolean = false;
39
40    constructor() {
41        this.vRegsId = 0;
42    }
43
44    allocIndexForVreg(vreg: VReg): void {
45        let num = this.getFreeVreg();
46        vreg.num = num;
47    }
48
49    getSpill(): VReg {
50        return this.spills[this.spillId++];
51    }
52
53    freeSpill(): void {
54        this.spillId = 0;
55    }
56
57    getFreeVreg(): number {
58        if (this.vRegsId >= MAX_VREGC) {
59            throw new Error("vreg has been running out");
60        }
61        return this.vRegsId++;
62    }
63
64    /* check whether the operands is valid for the format,
65       return 0 if it is valid, otherwise return the total
66       number of vreg which does not meet the requirement
67    */
68    getNumOfInvalidVregs(operands: OperandType[], format: Format): number {
69        let num = 0;
70        for (let j = 0; j < operands.length; ++j) {
71            if (operands[j] instanceof VReg) {
72                if ((<VReg>operands[j]).num >= (1 << format[j][1])) {
73                    num++;
74                }
75            }
76        }
77        return num;
78    }
79
80    doRealAdjustment(operands: OperandType[], format: Format, index: number, irNodes: IRNode[]): void {
81        let head: IRNode[] = [];
82        let tail: IRNode[] = [];
83
84        for (let j = 0; j < operands.length; ++j) {
85            if (operands[j] instanceof VReg) {
86                let vOrigin = <VReg>operands[j];
87                if (vOrigin.num >= (1 << format[j][1])) {
88                    let spill = this.getSpill();
89                    operands[j] = spill;
90                    if (format[j][0] === OperandKind.SrcVReg) {
91                        head.push(new Mov(spill, vOrigin));
92                    } else if (format[j][0] === OperandKind.DstVReg) {
93                        tail.push(new Mov(vOrigin, spill));
94                    } else if (format[j][0] === OperandKind.SrcDstVReg) {
95                        head.push(new Mov(spill, vOrigin));
96                        tail.push(new Mov(vOrigin, spill));
97                    } else {
98                        // here we do nothing
99                    }
100                }
101            }
102        }
103
104        // for debuginfo
105        DebugInfo.copyDebugInfo(irNodes[index], head);
106        DebugInfo.copyDebugInfo(irNodes[index], tail);
107
108        this.newInsns.push(...head, irNodes[index], ...tail);
109        this.freeSpill();
110    }
111
112    checkDynRangeInstruction(irNodes: IRNode[], index: number): boolean {
113        let operands = irNodes[index].operands;
114        let rangeRegOffset = getRangeStartVregPos(irNodes[index]);
115        let level = 1 << (irNodes[index].getFormats())[0][rangeRegOffset][1];
116
117        /*
118          1. "CalliDynRange 4, v255" is a valid insn, there is no need for all 4 registers numbers to be less than 255,
119          it is also similar for NewobjDyn
120          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
121        */
122        if ((<VReg>operands[rangeRegOffset]).num >= level) {
123            // needs to be adjusted.
124            return false;
125        }
126
127        /* the first operand is an imm */
128        let startNum = (<VReg>operands[rangeRegOffset]).num;
129        let i = rangeRegOffset + 1;
130        for (; i < (irNodes[index]).operands.length; ++i) {
131            if ((startNum + 1) != (<VReg>operands[i]).num) {
132                throw Error("Warning: VReg sequence of DynRange is not continuous. Please adjust it now.");
133            }
134            startNum++;
135        }
136
137        /* If the parameters are consecutive, no adjustment is required. */
138        if (i === (irNodes[index]).operands.length) {
139            return true;
140        }
141
142        // needs to be adjusted.
143        return false;
144    }
145
146    adjustDynRangeInstruction(irNodes: IRNode[], index: number): void {
147        let head: IRNode[] = [];
148        let operands = irNodes[index].operands;
149
150        /* exclude operands that are not require consecutive */
151        let rangeRegOffset = getRangeStartVregPos(irNodes[index]);
152        let regNums = operands.length - getRangeStartVregPos(irNodes[index]);
153
154        for (let i = 0; i < regNums; i++) {
155            let spill = this.getSpill();
156            head.push(new Mov(spill, <VReg>operands[i + rangeRegOffset]));
157            operands[i + rangeRegOffset] = spill;
158        }
159
160        // for debuginfo
161        DebugInfo.copyDebugInfo(irNodes[index], head);
162
163        this.newInsns.push(...head, irNodes[index]);
164        this.freeSpill();
165    }
166
167    adjustInstructionsIfNeeded(irNodes: IRNode[]): void {
168        for (let i = 0; i < irNodes.length; ++i) {
169            let operands = irNodes[i].operands;
170            let formats = irNodes[i].getFormats();
171            if (isRangeInst(irNodes[i])) {
172                if (this.checkDynRangeInstruction(irNodes, i)) {
173                    this.newInsns.push(irNodes[i]);
174                    continue;
175                }
176                this.adjustDynRangeInstruction(irNodes, i);
177                continue;
178            }
179
180            let min = operands.length;
181            let minFormat = formats[0];
182            for (let j = 0; j < formats.length; ++j) {
183                let num = this.getNumOfInvalidVregs(operands, formats[j]);
184                if (num < min) {
185                    minFormat = formats[j];
186                    min = num;
187                }
188            }
189            if (min > 0) {
190                this.doRealAdjustment(operands, minFormat, i, irNodes);
191                continue;
192            }
193            this.newInsns.push(irNodes[i]);
194        }
195    }
196
197    getTotalRegsNum(): number {
198        return this.vRegsId;
199    }
200
201    allocIndexForVregs(locals: VReg[], temps: VReg[], cache: VregisterCache): void {
202        for (let i = 0; i < locals.length; ++i) {
203            this.allocIndexForVreg(locals[i]);
204        }
205        for (let i = 0; i < temps.length; ++i) {
206            this.allocIndexForVreg(temps[i]);
207        }
208        for (let i = CacheList.MIN; i < CacheList.MAX; ++i) {
209            let cacheItem = cache.getCache(i);
210            if (cacheItem.isNeeded()) {
211                this.allocIndexForVreg(cacheItem.getCache());
212            }
213        }
214    }
215
216    allocSpillPool(irNodes: IRNode[]): void {
217        let spillCount: number = 0;
218        for (let i = 0; i < irNodes.length; ++i) {
219            let operands = irNodes[i].operands;
220            let formats = irNodes[i].getFormats();
221            if (isRangeInst(irNodes[i])) {
222                let rangeRegOffset = getRangeStartVregPos(irNodes[i]);
223                spillCount = Math.max(spillCount, operands.length - rangeRegOffset);
224
225                let level = 1 << (irNodes[i].getFormats())[0][rangeRegOffset][1];
226                if ((<VReg>operands[rangeRegOffset]).num >= level) {
227                    this.needAdjust = true;
228                }
229                continue;
230            }
231
232            let min = operands.length;
233            spillCount = Math.max(spillCount, min);
234            for (let j = 0; j < formats.length; ++j) {
235                let num = this.getNumOfInvalidVregs(operands, formats[j]);
236                if (num < min) {
237                    min = num;
238                }
239            }
240            if (min > 0) {
241                this.needAdjust = true;
242            }
243        }
244
245        if (this.needAdjust) {
246            this.vRegsId = 0;
247            while (spillCount--) {
248                let spill = new VReg();
249                this.allocIndexForVreg(spill);
250                this.spills.push(spill);
251            }
252        }
253    }
254
255    run(pandaGen: PandaGen): void {
256        let irNodes = pandaGen.getInsns();
257        let locals = pandaGen.getLocals();
258        let temps = pandaGen.getTemps();
259        let cache = pandaGen.getVregisterCache();
260        let parametersCount = pandaGen.getParametersCount();
261
262        this.allocIndexForVregs(locals, temps, cache);
263        this.allocSpillPool(irNodes);
264        if (this.needAdjust) {
265            // assign index to Vregs again
266            this.allocIndexForVregs(locals, temps, cache);
267        }
268        this.adjustInstructionsIfNeeded(irNodes);
269        for (let i = 0; i < parametersCount; ++i) {
270            let v = new VReg();
271            this.allocIndexForVreg(v);
272            this.newInsns.unshift(new Mov(locals[i], v));
273        }
274
275        pandaGen.setInsns(this.newInsns);
276    }
277}
278
279export class RegAlloc {
280    run(pandaGen: PandaGen): void {
281        let regalloc = new RegAllocator();
282
283        regalloc.run(pandaGen);
284        pandaGen.setTotalRegsNum(regalloc.getTotalRegsNum());
285    }
286}
287