• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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