1/* 2 * Copyright (c) 2022 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 { expect } from 'chai'; 17import 'mocha'; 18import * as ts from "typescript"; 19import { 20 Callrange, 21 Returnundefined, 22 Imm, 23 IRNode, 24 Jmp, 25 Label, 26 Ldai, 27 Lda, 28 Ldglobalvar, 29 Mov, 30 Sta, 31 Throw, 32 VReg 33} from "../src/irnodes"; 34import { PandaGen } from "../src/pandagen"; 35import { CacheExpander } from "../src/pass/cacheExpander"; 36import { RegAlloc } from "../src/regAllocator"; 37import { basicChecker, checkInstructions, compileAllSnippet, SnippetCompiler } from "./utils/base"; 38import { creatAstFromSnippet } from "./utils/asthelper"; 39 40function checkRegisterNumber(left: IRNode, right: IRNode): boolean { 41 if (!basicChecker(left, right)) { 42 return false; 43 } 44 let lo = left.operands; 45 let ro = right.operands; 46 if (lo.length !== ro.length) { 47 return false; 48 } 49 for (let i = 0; i < lo.length; ++i) { 50 let l = lo[i]; 51 let r = ro[i]; 52 if (l instanceof VReg && r instanceof VReg) { 53 if (!((<VReg>l).num === (<VReg>r).num)) { 54 return false; 55 } 56 } 57 } 58 return true; 59} 60describe("RegAllocator", function () { 61 it("make spill for Dst register & Src register", function () { 62 let string = "function test() {"; 63 for (let i = 0; i < 256; ++i) { 64 string += "let a" + i + " = " + i + ";"; 65 } 66 string += "a255;}"; 67 68 let snippetCompiler = new SnippetCompiler(); 69 snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); 70 let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); 71 72 IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); 73 IRNode.pg.updateIcSize(0); 74 75 let v = []; 76 for (let i = 0; i < 260; ++i) { 77 v[i] = new VReg(); 78 v[i].num = i; 79 } 80 81 let expected: IRNode[] = [ 82 new Ldai(new Imm(252)), 83 new Sta(v[0]), 84 new Mov(v[256], v[0]), 85 new Ldai(new Imm(253)), 86 new Sta(v[0]), 87 new Mov(v[257], v[0]), 88 new Ldai(new Imm(254)), 89 new Sta(v[0]), 90 new Mov(v[258], v[0]), 91 new Ldai(new Imm(255)), 92 new Sta(v[0]), 93 new Mov(v[259], v[0]), 94 // load a255 95 new Mov(v[0], v[259]), 96 new Lda(v[0]), 97 new Returnundefined() 98 ] 99 100 expect(checkInstructions(insns.slice(insns.length - 15), expected, checkRegisterNumber)).to.be.true; 101 }); 102 103 it("make spill for SrcDst register", function () { 104 /* the only possible instruction whose operand register type could be SrcDstVReg is INCI, 105 * but we do not use it at all by now 106 */ 107 expect(true).to.be.true; 108 }); 109 110 it("make spill for CalliRange", function () { 111 /* since the bitwidth for CalliRange source register is 16 now, we do not need to make spill at all. 112 but in case later 16 might be changed to 8, then spill operation will be needed in some cases. this testcase is designed 113 for 8bits constraints. 114 */ 115 let string = "function test() {"; 116 for (let i = 0; i < 256; ++i) { 117 string += "let a" + i + " = " + i + ";"; 118 } 119 string += "test(a252, a253, a254, a255);}"; 120 121 let snippetCompiler = new SnippetCompiler(); 122 snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); 123 let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); 124 125 IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); 126 IRNode.pg.updateIcSize(0); 127 let v = []; 128 for (let i = 0; i < 268; ++i) { 129 v[i] = new VReg(); 130 v[i].num = i; 131 } 132 let expected = [ 133 new Ldai(new Imm(252)), 134 new Sta(v[0]), 135 new Mov(v[259], v[0]), 136 new Ldai(new Imm(253)), 137 new Sta(v[0]), 138 new Mov(v[260], v[0]), 139 new Ldai(new Imm(254)), 140 new Sta(v[0]), 141 new Mov(v[261], v[0]), 142 new Ldai(new Imm(255)), 143 new Sta(v[0]), 144 new Mov(v[262], v[0]), 145 new Ldglobalvar(new Imm(0), "test"), 146 new Sta(v[0]), 147 new Mov(v[263], v[0]), 148 // call test with [a252, a253, a254, a255] 149 new Mov(v[0], v[259]), 150 new Lda(v[0]), 151 new Sta(v[0]), 152 new Mov(v[264], v[0]), 153 new Mov(v[0], v[260]), 154 new Lda(v[0]), 155 new Sta(v[0]), 156 new Mov(v[265], v[0]), 157 new Mov(v[0], v[261]), 158 new Lda(v[0]), 159 new Sta(v[0]), 160 new Mov(v[266], v[0]), 161 new Mov(v[0], v[262]), 162 new Lda(v[0]), 163 new Sta(v[0]), 164 new Mov(v[267], v[0]), 165 new Mov(v[0], v[263]), 166 new Lda(v[0]), 167 new Mov(v[0], v[264]), 168 new Mov(v[1], v[265]), 169 new Mov(v[2], v[266]), 170 new Mov(v[3], v[267]), 171 new Callrange(new Imm(1), new Imm(4), [v[0], v[1], v[2], v[3]]), 172 new Returnundefined(), 173 ]; 174 175 expect(checkInstructions(insns.slice(insns.length - 39), expected, checkRegisterNumber)).to.be.true; 176 }); 177 178 it("make spill for control-flow change", function () { 179 let string = "function test() {"; 180 for (let i = 0; i < 256; ++i) { 181 string += "let a" + i + " = " + i + ";"; 182 } 183 string += `try { throw a0; } catch { a0 }};`; 184 185 let snippetCompiler = new SnippetCompiler(); 186 snippetCompiler.compile(string, [new CacheExpander(), new RegAlloc()]); 187 let insns = snippetCompiler.getPandaGenByName("UnitTest.test").getInsns(); 188 189 IRNode.pg = new PandaGen("", creatAstFromSnippet(""), 0, undefined); 190 IRNode.pg.updateIcSize(0); 191 let v = []; 192 for (let i = 0; i < 261; ++i) { 193 v[i] = new VReg(); 194 v[i].num = i; 195 } 196 let tryBeginLabel = new Label(); 197 let tryEndLabel = new Label(); 198 let catchBeginLabel = new Label(); 199 let catchEndLabel = new Label(); 200 201 let expected = [ 202 new Ldai(new Imm(252)), 203 new Sta(v[0]), 204 new Mov(v[256], v[0]), 205 new Ldai(new Imm(253)), 206 new Sta(v[0]), 207 new Mov(v[257], v[0]), 208 new Ldai(new Imm(254)), 209 new Sta(v[0]), 210 new Mov(v[258], v[0]), 211 new Ldai(new Imm(255)), 212 new Sta(v[0]), 213 new Mov(v[259], v[0]), 214 tryBeginLabel, 215 new Lda(v[4]), 216 new Throw(), 217 tryEndLabel, 218 new Jmp(catchEndLabel), 219 catchBeginLabel, 220 new Lda(v[4]), 221 catchEndLabel, 222 new Returnundefined(), 223 ]; 224 225 expect(checkInstructions(insns.slice(insns.length - 21), expected, checkRegisterNumber)).to.be.true; 226 }); 227 228 it("VReg sequence of CalliDynRange is not continuous", function () { 229 let pandaGen = new PandaGen('', creatAstFromSnippet(""), 0); 230 let para1 = pandaGen.getTemp(); 231 let para2 = pandaGen.getTemp(); 232 let para3 = pandaGen.getTemp(); 233 let para4 = pandaGen.getTemp(); 234 let para5 = pandaGen.getTemp(); 235 let para6 = pandaGen.getTemp(); 236 237 pandaGen.call(ts.createNode(0), [para1, para2, para3, para4, para5, para6], false); 238 239 pandaGen.freeTemps(para1, para3, para2); 240 241 try { 242 new RegAlloc().run(pandaGen); 243 } catch (err) { 244 expect(true).to.be.true; 245 return; 246 } 247 expect(true).to.be.false; 248 }); 249 250 it("VReg sequence of DynRange is not continuous", function () { 251 let pandaGen = new PandaGen('', creatAstFromSnippet(""), 0); 252 let para1 = pandaGen.getTemp(); 253 let para2 = pandaGen.getTemp(); 254 let para3 = pandaGen.getTemp(); 255 256 pandaGen.getInsns().push(new Callrange(new Imm(0), new Imm(3), [para1, para2, para3])); 257 258 pandaGen.freeTemps(para1, para3, para2); 259 260 try { 261 new RegAlloc().run(pandaGen); 262 } catch (err) { 263 expect(true).to.be.true; 264 return; 265 } 266 expect(true).to.be.false; 267 }); 268}); 269