1// Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14import { PandaGen } from "./pandagen" 15% def get_node_kind(mnemonic) 16% return "#{mnemonic.gsub('.', '_').upcase}" 17% end 18% 19% def insn2node(insn) 20% mnemonic = insn.mnemonic.split('.') 21% return mnemonic.map{|el| el == '64' ? 'Wide' : el.capitalize}.join() 22% end 23% 24% def get_result_type(insn) 25% if insn.mnemonic.start_with? "call" 26% return "ResultType.Unknown" 27% end 28% case insn.dtype 29% when 'i32' 30% return "ResultType.Int" 31% when 'i64', 'b64' 32% return "ResultType.Long" 33% when 'f64' 34% return "ResultType.Float" 35% when 'obj' 36% return "ResultType.Obj" 37% else 38% return "ResultType.None" 39% end 40% end 41% 42% def get_result_dst(insn) 43% if insn.properties.include? "acc_write" 44% return "ResultDst.Acc" 45% end 46% dst_operands = insn.operands.select { |op| op.dst? } 47% if dst_operands.length > 0 and dst_operands[0].reg? 48% return "ResultDst.VReg" 49% end 50% return "ResultDst.None" 51% end 52% 53% def is_VReg(name) 54% if name == :v 55% return true 56% end 57% end 58% 59% def is_Acc(name) 60% if name == :acc 61% return true 62% end 63% end 64% 65% def is_Imm(name) 66% if name == :imm 67% return true 68% end 69% end 70% 71% def is_Id(name) 72% if %i[method_id type_id field_id string_id literalarray_id callsite_id].include?(name) 73% return true 74% end 75% end 76% 77% def is_Call(insn) 78% if insn.mnemonic.start_with? "callruntime" 79% return false 80% end 81% if insn.mnemonic.start_with? "call" 82% return true 83% end 84% return false 85% end 86% 87% def is_CallRange(insn) 88% if insn.mnemonic == "callrange" or insn.mnemonic == "wide.callrange" or 89% insn.mnemonic == "callthisrange" or insn.mnemonic == "wide.callthisrange" or 90% insn.mnemonic == "newobjrange" or insn.mnemonic == "wide.newobjrange" or 91% insn.mnemonic == "createobjectwithexcludedkeys" or insn.mnemonic == "wide.createobjectwithexcludedkeys" or 92% insn.mnemonic == "supercallthisrange" or insn.mnemonic == "wide.supercallthisrange" or 93% insn.mnemonic == "supercallarrowrange" or insn.mnemonic == "wide.supercallarrowrange" 94% return true 95% end 96% return false 97% end 98% 99% def get_operand_type(name, insn) 100% if is_VReg(name) 101% return "VReg" 102% elsif is_Imm(name) 103% is_jump = insn.properties.include? 'jump' 104% return is_jump ? "Label" : "Imm" 105% elsif is_Id(name) 106% return "string" 107% else 108% return nil 109% end 110% end 111% 112% def get_operands(sig) 113% return [] unless sig.include? ' ' 114% _, operands = sig.match(/(\S+) (.+)/).captures 115% operands = operands.split(', ') 116% end 117% 118% def get_ctor_args(insn) 119% operands = get_operands(insn.sig) 120% ops = Array.new 121% ctor_args = Array.new 122% operands.map do |operand| 123% operand_parts = operand.split(':') 124% case operand_parts.size 125% when 1 126% name = operand_parts[0] 127% when 2 128% name, _ = operand_parts 129% when 3 130% name, _, _ = operand_parts 131% else 132% raise 'Unexpected operand string' 133% end 134% ops.push(name) 135% name_tmp = name.to_s.gsub(/[0-9]/, '').to_sym 136% type = get_operand_type(name_tmp,insn) 137% if is_Call(insn) and !is_CallRange(insn) and is_VReg(name_tmp) and !insn.mnemonic.include?('acc') 138% ctor_args.push("#{name}?: #{type}") 139% else 140% ctor_args.push("#{name}: #{type}") 141% end 142% end 143% return ops,ctor_args 144% end 145% 146% def get_operand_kind(op, insn) 147% if op.reg? 148% if op.src? and op.dst? 149% return 2 150% elsif op.src? 151% return 0 152% elsif op.dst? 153% return 1 154% end 155% return nil 156% elsif op.imm? 157% is_jump = insn.properties.include? 'jump' 158% return is_jump ? 6 : 3 159% elsif op.id? 160% is_string_id = insn.properties.include? 'string_id' 161% return is_string_id ? 5 : 4 162% else 163% return nil 164% end 165% end 166% 167% def make_format(fmt, insn) 168% operands = fmt.operands.map {|op| "[#{get_operand_kind(op, insn)}, #{op.width}]"} 169% return "[" + operands.join(", ") + "]" 170% end 171% 172// Autogenerated file -- DO NOT EDIT! 173import * as ts from "typescript"; 174import { 175 DebugPosInfo, 176 NodeKind 177} from "./debuginfo"; 178import { Scope } from "./scope"; 179 180export enum IRNodeKind { 181% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| 182 <%= get_node_kind(mnemonic) %>, 183% end 184 VREG, 185 IMM, 186 LABEL, 187 VIRTUALSTARTINS_DYN, 188 VIRTUALENDINS_DYN, 189 DEFINE_GLOBAL_VAR, 190} 191 192export function getInstructionSize(opcode: IRNodeKind) { 193 switch(opcode) { 194% Panda::instructions.each do |insn| 195% node_kind = get_node_kind(insn.mnemonic) 196% kind = "IRNodeKind." + node_kind 197 case <%= kind %>: 198 return <%= insn.format.size %>; 199% end 200 default: 201 // LOGE("getInstructionSize: Unknown opcode:" + opcode); 202 return 0; 203 } 204} 205 206export enum ResultType { 207 None, 208 Unknown, 209 Int, 210 Long, 211 Float, 212 Obj, 213 Boolean 214} 215 216export enum ResultDst { 217 None, 218 Acc, 219 VReg 220} 221 222export enum BuiltIns { 223 NaN, 224 Infinity, 225 globalThis, 226 undefined, 227 Boolean, 228 Number, 229 String, 230 BigInt, 231 Symbol, 232 Null, 233 Object, 234 Function, 235 Global, 236 True, 237 False, 238 LexEnv, 239 MAX_BUILTIN, 240} 241 242export type OperandType = VReg | Imm | Label | string | number 243 244export enum OperandKind { 245 // the least significant bit indicates vreg 246 // the second bit indicates src or dst 247 SrcVReg = 0, DstVReg, SrcDstVReg, Imm, Id, StringId, Label 248} 249 250export namespace OperandKind { 251 // @ts-ignore 252 function isVReg(kind: OperandKind): boolean { 253 return kind === OperandKind.SrcVReg || kind === OperandKind.DstVReg || kind === OperandKind.SrcDstVReg; 254 } 255} 256 257export type Format = number[][] 258 259export function getInsnMnemonic(opcode: IRNodeKind): string { 260 switch(opcode) { 261% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| 262% node_kind = get_node_kind(mnemonic) 263% kind = "IRNodeKind." + node_kind 264 case <%= kind %>: 265 return "<%= mnemonic %>"; 266% end 267 default: 268 return ''; 269 } 270} 271 272export function getInsnFormats(opcode: IRNodeKind) { 273 switch(opcode) { 274% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| 275% insn = group.first 276% formats = group.map {|i| make_format(i,insn) } 277% node_kind = get_node_kind(mnemonic) 278% kind = "IRNodeKind." + node_kind 279 case <%= kind %>: { 280 return [ 281 <%= formats.join(",\n ") %> 282 ]; 283 } 284% end 285 default: 286 return []; 287 } 288} 289 290export abstract class IRNode { 291 private node: ts.Node | NodeKind = NodeKind.NORMAL; 292 public static pg: PandaGen | undefined = undefined; 293 constructor( 294 readonly kind: IRNodeKind, 295 readonly operands: OperandType[], 296 ) {} 297 // for debuginfo 298 public debugPosInfo: DebugPosInfo = new DebugPosInfo(); 299 300 toString(): string { 301 let mnemonic = this.getMnemonic(); 302 let out = mnemonic + "\t"; 303 if (mnemonic.length < 8) { 304 out += "\t"; 305 } 306 307 this.operands.forEach((element) => { 308 out = out + element.toString() + ", "; 309 }); 310 311 return out; 312 } 313 314 setNode(node: ts.Node | NodeKind) { 315 this.node = node; 316 } 317 318 getNodeName() { 319 if (this.node != NodeKind.INVALID && 320 this.node != NodeKind.FIRST_NODE_OF_FUNCTION && 321 this.node != NodeKind.NORMAL) { 322 return ts.SyntaxKind[(<ts.Node>this.node).kind]; 323 } 324 325 return "undefined"; 326 } 327 328 getMnemonic() { 329 return getInsnMnemonic(this.kind); 330 } 331 332 getFormats() { 333 return getInsnFormats(this.kind); 334 } 335 336 static setPandagen(pg: PandaGen) { 337 IRNode.pg = pg; 338 } 339} 340 341export class VReg { 342 private typeIndex: number | undefined; 343 private variableName: string | undefined; 344 num: number = -1; 345 346 toString(): string { 347 return "V" + this.num; 348 } 349 350 constructor() { 351 } 352 353 getTypeIndex() { 354 return this.typeIndex; 355 } 356 357 setTypeIndex(typeIndex: number) { 358 this.typeIndex = typeIndex; 359 } 360 361 getVariableName() { 362 return this.variableName; 363 } 364 365 setVariableName(variableName: string) { 366 this.variableName = variableName; 367 } 368} 369 370export class Imm extends IRNode { 371 readonly value: number; 372 373 constructor(value: number) { 374 super(IRNodeKind.IMM, []); 375 this.value = value; 376 } 377 378 toString(): string { 379 return "#" + this.value; 380 } 381} 382 383export class Label extends IRNode { 384 private static global_id = 0; 385 readonly id: number; 386 387 constructor() { 388 super(IRNodeKind.LABEL, []); 389 this.id = Label.global_id++; 390 } 391 392 static resetGlobalId() { 393 Label.global_id = 0; 394 } 395 396 toString(): string { 397 return "LABEL_" + this.id; 398 } 399} 400 401export class DebugInsStartPlaceHolder extends IRNode { 402 private scope: Scope; 403 404 constructor(scope: Scope) { 405 super(IRNodeKind.VIRTUALSTARTINS_DYN, []); 406 this.scope = scope; 407 } 408 409 getScope() { 410 return this.scope; 411 } 412} 413 414export class DebugInsEndPlaceHolder extends IRNode { 415 private scope: Scope; 416 417 constructor(scope: Scope) { 418 super(IRNodeKind.VIRTUALENDINS_DYN, []); 419 this.scope = scope; 420 } 421 422 getScope() { 423 return this.scope; 424 } 425} 426 427% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| 428% insn = group.first 429% jump = insn.properties.include? 'jump' 430% node_kind = get_node_kind(mnemonic) 431% kind = "IRNodeKind." + node_kind 432% ops_list,ctor_arg_list = get_ctor_args(insn) 433% ctor_args = ctor_arg_list.map {|arg| "#{arg}"}.join(", ") 434% ops = ops_list.map { |op| "#{op}"}.join(", ") 435% result_type = get_result_type(insn) 436% result_dst = get_result_dst(insn); 437% is_call_op = is_Call(insn) 438% is_callrange_op = is_CallRange(insn) 439% parent_type = "IRNode" 440export class <%= insn2node(insn) %> extends <%= parent_type %> { 441% if is_callrange_op 442% op_last = ops_list.pop 443% ops_length = ops_list.size 444 constructor(<%= ctor_arg_list.join(", ")%>[]) { 445 var ctors = [<%=ops_list.join(", ")%>, ...<%=op_last %>] 446 var operands:OperandType[] = [<%=ops_list.join(", ")%>] 447% for i in 1..ops_length do 448 ctors.shift() 449% end 450 while (!!(ctors && ctors.length)){ 451 let ctor = ctors.shift() 452 if (ctor != undefined) { 453 operands.push(ctor) 454 } 455 } 456% else 457 constructor(<%= ctor_args %>) { 458% if is_call_op and ops.length() != 0 459 var ctors = [<%= ops %>] 460 var operands:OperandType[] = [<%=ops_list[0] %>] 461 ctors.shift() 462 while (!!(ctors && ctors.length)){ 463 let ctor = ctors.shift() 464 if (ctor != undefined) { 465 operands.push(ctor) 466 } 467 } 468% end 469% end 470 super( 471 <%= kind %>, 472% if is_call_op or is_callrange_op 473 operands, 474% else 475 [<%= ops %>] 476% end 477 ); 478% is_jit_ic = insn.properties.include?("jit_ic_slot") 479% is_ic = (!is_jit_ic) && insn.properties.include?("ic_slot") 480% if (is_ic) 481% ret = insn.properties.include?("one_slot") ? 1 : 2; 482 let invalid = 255; 483 let icSize = (<PandaGen>(IRNode.pg)).getIcSize(); 484 if (icSize <= invalid) { 485 if ((icSize + <%= ret %>) > 255) { 486 this.operands[0] = new Imm(256); 487 let inc = <%= ret %> + 256 - icSize; 488 (<PandaGen>(IRNode.pg)).updateIcSize(inc); 489 } else { 490 this.operands[0] = new Imm(icSize); 491 (<PandaGen>(IRNode.pg)).updateIcSize(<%= ret %>); 492 } 493 } else if(icSize > invalid && icSize <= 65535) { 494 this.operands[0] = new Imm(icSize); 495 (<PandaGen>(IRNode.pg)).updateIcSize(<%= ret %>); 496 } else { 497 this.operands[0] = new Imm(invalid); 498 } 499% end 500% if (is_jit_ic && !is_ic) 501% ret = insn.properties.include?("one_slot") ? 1 : 2; 502 let invalid = 255; 503 let icSize = (<PandaGen>(IRNode.pg)).getIcSize(); 504 if (icSize < invalid) { 505 if ((icSize + <%= ret %>) > invalid) { 506 this.operands[0] = new Imm(invalid); 507 (<PandaGen>(IRNode.pg)).updateIcSize((256 - icSize)); 508 } else { 509 this.operands[0] = new Imm(icSize); 510 (<PandaGen>(IRNode.pg)).updateIcSize(<%= ret %>); 511 } 512 } else { 513 this.operands[0] = new Imm(invalid); 514 } 515% end 516 } 517% if jump 518% target_index = insn.operands.index {|op| op.imm? } 519 520 getTarget(): Label { 521 return <Label> this.operands[<%= target_index %>]; 522 } 523% end 524} 525 526% end 527