• 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 } from "./base/vregisterCache";
20import { DebugInfo } from "./debuginfo";
21import {
22    Format,
23    IRNode,
24    MovDyn,
25    OperandKind,
26    OperandType,
27    VReg
28} from "./irnodes";
29import { PandaGen } from "./pandagen";
30
31const MAX_VREGA = 16;
32const MAX_VREGB = 256;
33const MAX_VREGC = 65536;
34
35interface VRegWithFlag {
36    vreg: VReg;
37    flag: boolean; // indicate whether it is used as a temporary register for spill
38}
39
40class RegAllocator {
41    private newInsns: IRNode[] = [];
42    private spills: VReg[] = [];
43    private vRegsId: number = 0;
44    private usedVreg: VRegWithFlag[] = [];
45    private tmpVreg: VRegWithFlag[] = [];
46
47    constructor() {
48        this.vRegsId = 0;
49    }
50
51    allocIndexForVreg(vreg: VReg) {
52        let num = this.getFreeVreg();
53        vreg.num = num;
54        this.usedVreg[num] = {vreg: vreg, flag: false};
55    }
56
57    findTmpVreg(level: number): VReg {
58        let iterCnts = Math.min(MAX_VREGB, this.usedVreg.length);
59        for (let i = 0; i < iterCnts; ++i) {
60            let value = this.usedVreg[i];
61            if (value === undefined || value.flag) {
62                continue;
63            }
64            if (level === MAX_VREGA && value.vreg.num >= MAX_VREGA) {
65                throw new Error("no available tmp vReg from A");
66            }
67            value.flag = true;
68            this.tmpVreg.push(value);
69            return value.vreg;
70        }
71        throw new Error("no available tmp vReg from B");
72    }
73
74    clearVregFlags(): void {
75        for (let v of this.tmpVreg) {
76            v.flag = false;
77        }
78        this.tmpVreg = [];
79    }
80    allocSpill(): VReg {
81        if (this.spills.length > 0) {
82            return this.spills.pop()!;
83        }
84        let v = new VReg();
85        this.allocIndexForVreg(v);
86        return v;
87    }
88    freeSpill(v: VReg): void {
89        this.spills.push(v);
90    }
91
92    getFreeVreg(): number {
93        if (this.vRegsId >= MAX_VREGC) {
94            throw new Error("vreg has been running out");
95        }
96        return this.vRegsId++;
97    }
98
99    /* check whether the operands is valid for the format,
100       return 0 if it is valid, otherwise return the total
101       number of vreg which does not meet the requirement
102    */
103    getNumOfInvalidVregs(operands: OperandType[], format: Format): number {
104        let num = 0;
105        for (let j = 0; j < operands.length; ++j) {
106            if (operands[j] instanceof VReg) {
107                if ((<VReg>operands[j]).num >= (1 << format[j][1])) {
108                    num++;
109                }
110            }
111        }
112        return num;
113    }
114
115    markVregNotAvailableAsTmp(vreg: VReg): void {
116        let num = vreg.num;
117        this.usedVreg[num].flag = true;
118        this.tmpVreg.push(this.usedVreg[num]);
119    }
120
121    doRealAdjustment(operands: OperandType[], format: Format, index: number, irNodes: IRNode[]) {
122        let head: IRNode[] = [];
123        let tail: IRNode[] = [];
124        let spills: VReg[] = [];
125
126        // mark all vreg used in the current insn as not valid for tmp register
127        for (let i = 0; i < operands.length; ++i) {
128            if (operands[i] instanceof VReg) {
129                this.markVregNotAvailableAsTmp(<VReg>operands[i]);
130            }
131        }
132        for (let j = 0; j < operands.length; ++j) {
133            if (operands[j] instanceof VReg) {
134                let vOrigin = <VReg>operands[j];
135                if (vOrigin.num >= (1 << format[j][1])) {
136                    let spill = this.allocSpill();
137                    spills.push(spill);
138                    let vTmp;
139                    try {
140                        vTmp = this.findTmpVreg(1 << format[j][1]);
141                    } catch {
142                        throw Error("no available tmp vReg");
143                    }
144                    head.push(new MovDyn(spill, vTmp));
145                    operands[j] = vTmp;
146                    if (format[j][0] == OperandKind.SrcVReg) {
147                        head.push(new MovDyn(vTmp, vOrigin));
148                    } else if (format[j][0] == OperandKind.DstVReg) {
149                        tail.push(new MovDyn(vOrigin, vTmp))
150                    } else if (format[j][0] == OperandKind.SrcDstVReg) {
151                        head.push(new MovDyn(vTmp, vOrigin));
152                        tail.push(new MovDyn(vOrigin, vTmp))
153                    } else {
154                        // here we do nothing
155                    }
156                    tail.push(new MovDyn(vTmp, spill));
157                }
158            }
159        }
160
161        // for debuginfo
162        DebugInfo.copyDebugInfo(irNodes[index], head);
163        DebugInfo.copyDebugInfo(irNodes[index], tail);
164
165        this.newInsns.push(...head, irNodes[index], ...tail);
166
167        for (let j = spills.length - 1; j >= 0; --j) {
168            this.freeSpill(spills[j]);
169        }
170        this.clearVregFlags();
171    }
172
173    checkDynRangeInstruction(irNodes: IRNode[], index: number): boolean {
174        let operands = irNodes[index].operands;
175        let rangeRegOffset = getRangeStartVregPos(irNodes[index]);
176        let level = 1 << (irNodes[index].getFormats())[0][rangeRegOffset][1];
177
178        /*
179          1. "CalliDynRange 4, v255" is a valid insn, there is no need for all 4 registers numbers to be less than 255,
180          it is also similar for NewobjDyn
181          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
182          3. if v.num is bigger than 255, it means all register less than 255 has been already used, they should have been pushed
183          into usedVreg
184        */
185        if ((<VReg>operands[1]).num >= level) {
186            // needs to be adjusted.
187            return false;
188        }
189
190        /* the first operand is an imm */
191        let startNum = (<VReg>operands[rangeRegOffset]).num;
192        let i = rangeRegOffset + 1;
193        for (; i < (irNodes[index]).operands.length; ++i) {
194            if ((startNum + 1) != (<VReg>operands[i]).num) {
195                throw Error("Warning: VReg sequence of DynRange is not continuous. Please adjust it now.");
196            }
197            startNum++;
198        }
199
200        /* If the parameters are consecutive, no adjustment is required. */
201        if (i == (irNodes[index]).operands.length) {
202            return true;
203        }
204
205        // needs to be adjusted.
206        return false;
207    }
208
209    adjustDynRangeInstruction(irNodes: IRNode[], index: number) {
210        let head: IRNode[] = [];
211        let tail: IRNode[] = [];
212        let spills: VReg[] = [];
213        let operands = irNodes[index].operands;
214
215        /* exclude operands that are not require consecutive */
216        let rangeRegOffset = getRangeStartVregPos(irNodes[index]);
217        let regNums = operands.length - getRangeStartVregPos(irNodes[index]);
218
219        let level = 1 << (irNodes[index].getFormats())[0][rangeRegOffset][1];
220        let tmp = this.findTmpVreg(level);
221
222        for (let i = 0; i < regNums; i++) {
223            let spill = this.allocSpill();
224            spills.push(spill);
225
226            /* We need to make sure that the register input in the .range instruction is continuous(small to big). */
227            head.push(new MovDyn(spill, this.usedVreg[tmp.num + i].vreg));
228            head.push(new MovDyn(this.usedVreg[tmp.num + i].vreg, <VReg>operands[i + rangeRegOffset]));
229            operands[i + rangeRegOffset] = this.usedVreg[tmp.num + i].vreg;
230            tail.push(new MovDyn(this.usedVreg[tmp.num + i].vreg, spill));
231        }
232
233        // for debuginfo
234        DebugInfo.copyDebugInfo(irNodes[index], head);
235        DebugInfo.copyDebugInfo(irNodes[index], tail);
236
237        this.newInsns.push(...head, irNodes[index], ...tail);
238        for (let i = spills.length - 1; i >= 0; --i) {
239            this.freeSpill(spills[i]);
240        }
241        this.clearVregFlags();
242    }
243
244    adjustInstructionsIfNeeded(irNodes: IRNode[]): void {
245        for (let i = 0; i < irNodes.length; ++i) {
246            let operands = irNodes[i].operands;
247            let formats = irNodes[i].getFormats();
248            if (isRangeInst(irNodes[i])) {
249                if (this.checkDynRangeInstruction(irNodes, i)) {
250                    this.newInsns.push(irNodes[i]);
251                    continue;
252                }
253                this.adjustDynRangeInstruction(irNodes, i);
254                continue;
255            }
256
257            let min = operands.length;
258            let minFormat = formats[0];
259            for (let j = 0; j < formats.length; ++j) {
260                let num = this.getNumOfInvalidVregs(operands, formats[j]);
261                if (num < min) {
262                    minFormat = formats[j];
263                    min = num;
264                }
265            }
266            if (min > 0) {
267                this.doRealAdjustment(operands, minFormat, i, irNodes);
268                continue;
269            }
270            this.newInsns.push(irNodes[i]);
271        }
272    }
273
274    getTotalRegsNum(): number {
275        return this.vRegsId;
276    }
277
278    run(pandaGen: PandaGen): void {
279        let irNodes = pandaGen.getInsns();
280        let locals = pandaGen.getLocals();
281        let temps = pandaGen.getTemps();
282        let cache = pandaGen.getVregisterCache();
283        let parametersCount = pandaGen.getParametersCount();
284        // don't mess up allocation order
285        for (let i = 0; i < locals.length; ++i) {
286            this.allocIndexForVreg(locals[i]);
287        }
288        for (let i = 0; i < temps.length; ++i) {
289            this.allocIndexForVreg(temps[i]);
290        }
291        for (let i = CacheList.MIN; i < CacheList.MAX; ++i) {
292            let cacheItem = cache.getCache(i);
293            if (cacheItem.isNeeded()) {
294                this.allocIndexForVreg(cacheItem.getCache());
295            }
296        }
297        this.adjustInstructionsIfNeeded(irNodes);
298        for (let i = 0; i < parametersCount; ++i) {
299            let v = new VReg();
300            this.allocIndexForVreg(v);
301            this.newInsns.unshift(new MovDyn(locals[i], v));
302        }
303
304        pandaGen.setInsns(this.newInsns);
305    }
306}
307
308export class RegAlloc {
309    run(pandaGen: PandaGen): void {
310        let regalloc = new RegAllocator();
311
312        regalloc.run(pandaGen);
313        pandaGen.setTotalRegsNum(regalloc.getTotalRegsNum());
314    }
315}
316