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