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 { 17 Imm, 18 IRNode, 19 IRNodeKind, 20 Label, 21 OperandKind, 22 VReg 23} from "./irnodes"; 24import { generateCatchTables } from "./statement/tryStatement"; 25import { PandaGen } from "./pandagen"; 26import { 27 isRangeInst, 28 getRangeExplicitVregNums, 29} from "./base/util"; 30 31export class IntrinsicInfo { 32 readonly intrinsicName: string; 33 readonly argsNum: number; 34 readonly returnType: string; 35 36 constructor(intrinsicName: string, argsNum: number, returnType: string) { 37 this.intrinsicName = intrinsicName; 38 this.argsNum = argsNum; 39 this.returnType = returnType; 40 } 41} 42 43export class AssemblyDumper { 44 private labels: Map<number, string>; // Label.id : Label string name 45 private labelId: number; 46 private pg: PandaGen; 47 readonly labelPrefix = "LABEL_"; 48 static intrinsicRec: Map<string, IntrinsicInfo> = new Map<string, IntrinsicInfo>(); 49 private output: string; 50 51 constructor(pg: PandaGen) { 52 this.pg = pg; 53 this.labels = new Map<number, string>(); 54 this.labelId = 0; 55 this.output = ""; 56 } 57 58 static writeLanguageTag(out: any): void { 59 out.str += ".language ECMAScript\n"; 60 out.str += "\n"; 61 } 62 63 writeFunctionHeader(): void { 64 let parametersCount = this.pg.getParametersCount(); 65 this.output += ".function any " + this.pg.internalName + "("; 66 for (let i = 0; i < parametersCount; ++i) { 67 this.output += "any a" + i.toString(); 68 if (i !== parametersCount - 1) { 69 this.output += ", "; 70 } 71 } 72 this.output += ") {\n"; 73 } 74 75 writeFunctionBody(): void { 76 let irNodes: IRNode[] = this.pg.getInsns(); 77 let parametersCount = this.pg.getParametersCount(); 78 79 /* the first parametersCount insns are move insn for argument initialization, 80 we can directly dump them into text 81 */ 82 for (let i = 0; i < parametersCount; ++i) { 83 let node = irNodes[i]; 84 this.output += "\t"; 85 let paramIdx = parametersCount - i - 1; 86 this.output += node.getMnemonic() + " v" + (<VReg>node.operands[0]).num + ", a" + paramIdx + "\n"; 87 } 88 89 for (let i = parametersCount; i < irNodes.length; ++i) { 90 let node = irNodes[i]; 91 if (node.kind === IRNodeKind.VREG || node.kind === IRNodeKind.IMM) { 92 continue; 93 } 94 if (node.kind === IRNodeKind.LABEL) { 95 this.writeLabel(<Label>node); 96 continue; 97 } 98 99 this.output += "\t"; 100 this.output += node.getMnemonic() + " "; 101 let operands = node.operands; 102 let formats = node.getFormats(); 103 let outputRangeVregNum = getRangeExplicitVregNums(node); 104 for (let j = 0; j < operands.length; ++j) { 105 if (outputRangeVregNum === 0) { 106 break; 107 } 108 let format = formats[0]; 109 let kind = format[j][0]; 110 let op = operands[j]; 111 112 if (kind === OperandKind.Imm) { 113 let imm = <Imm>op; 114 this.output += imm.value.toString(); 115 } else if (kind === OperandKind.Id) { 116 this.output += op; 117 } else if (kind === OperandKind.StringId) { 118 let escapedOp = op.toString().replace(/\\/g, "\\\\").replace(/\t/g, "\\t") 119 .replace(/\n/g, "\\n").replace(/\"/g, "\\\"") 120 this.output += "\"" + escapedOp + "\""; 121 } else if (kind === OperandKind.DstVReg || 122 kind === OperandKind.SrcDstVReg || 123 kind === OperandKind.SrcVReg) { 124 let v = <VReg>op; 125 if (v.num < 0) { 126 throw Error("invalid register, please check your insn!\n"); 127 } 128 this.output += "v" + v.num.toString(); 129 // we don't need to print all the registers for range inst, just the first one 130 if (isRangeInst(node)) { 131 outputRangeVregNum--; 132 continue; 133 } 134 } else if (kind === OperandKind.Label) { 135 this.output += this.getLabelName(<Label>op); 136 } else { 137 throw new Error("Unexpected OperandKind"); 138 } 139 if (j < operands.length - 1) { 140 this.output += ", "; 141 } 142 } 143 this.output += "\n"; 144 } 145 } 146 147 writeFunctionTail(): void { 148 this.output += "}\n"; 149 } 150 151 writeFunctionCatchTable(): void { 152 let catchTables = generateCatchTables(this.pg.getCatchMap()); 153 if (catchTables.length === 0) { 154 return; 155 } 156 157 this.output += "\n"; 158 catchTables.forEach((catchTable) => { 159 let catchBeginLabel = catchTable.getCatchBeginLabel(); 160 let labelPairs = catchTable.getLabelPairs(); 161 labelPairs.forEach((labelPair) => { 162 this.output += ".catchall " + this.getLabelName(labelPair.getBeginLabel()) + 163 ", " + this.getLabelName(labelPair.getEndLabel()) + 164 ", " + this.getLabelName(catchBeginLabel) + 165 "\n"; 166 }); 167 }); 168 } 169 170 getLabelName(label: Label): string { 171 let labelName: string; 172 if (!this.labels.has(label.id)) { 173 labelName = this.labelPrefix + this.labelId++; 174 this.labels.set(label.id, labelName); 175 } else { 176 labelName = this.labels.get(label.id)!; 177 } 178 return labelName; 179 } 180 181 writeLabel(label: Label): void { 182 let labelName = this.getLabelName(label); 183 this.output += labelName + ":\n"; 184 } 185 186 dump(): void { 187 this.writeFunctionHeader(); 188 this.writeFunctionBody(); 189 this.writeFunctionCatchTable(); 190 this.writeFunctionTail(); 191 192 console.log(this.output); 193 } 194 195 static dumpHeader(): void { 196 let out = { str: "" }; 197 AssemblyDumper.writeLanguageTag(out); 198 console.log(out.str); 199 } 200} 201