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