@minduscript/compiler 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +2 -0
- package/dist/asm/gen-asm.d.ts +2 -0
- package/dist/asm/gen-asm.js +432 -0
- package/dist/counter.d.ts +5 -0
- package/dist/counter.js +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/ir/build-ir.d.ts +3 -0
- package/dist/ir/build-ir.js +1057 -0
- package/dist/ir/gather-macros.d.ts +8 -0
- package/dist/ir/gather-macros.js +204 -0
- package/dist/ir/ir.d.ts +478 -0
- package/dist/ir/ir.js +120 -0
- package/dist/ir/project.d.ts +24 -0
- package/dist/ir/project.js +339 -0
- package/dist/ir/symbol-table.d.ts +19 -0
- package/dist/ir/symbol-table.js +385 -0
- package/dist/macro/check-macro.d.ts +14 -0
- package/dist/macro/check-macro.js +136 -0
- package/dist/macro/expand-macros.d.ts +6 -0
- package/dist/macro/expand-macros.js +285 -0
- package/dist/optimizer/default-passes.d.ts +2 -0
- package/dist/optimizer/default-passes.js +20 -0
- package/dist/optimizer/merge-labels.d.ts +2 -0
- package/dist/optimizer/merge-labels.js +45 -0
- package/dist/optimizer/optimizer.d.ts +4 -0
- package/dist/optimizer/optimizer.js +26 -0
- package/dist/optimizer/passes/constant-folding.d.ts +2 -0
- package/dist/optimizer/passes/constant-folding.js +160 -0
- package/dist/optimizer/passes/control-flow.d.ts +5 -0
- package/dist/optimizer/passes/control-flow.js +138 -0
- package/dist/optimizer/passes/copy-propagation.d.ts +2 -0
- package/dist/optimizer/passes/copy-propagation.js +127 -0
- package/dist/optimizer/passes/dead-code.d.ts +2 -0
- package/dist/optimizer/passes/dead-code.js +200 -0
- package/dist/optimizer/passes/merge-labels-pass.d.ts +2 -0
- package/dist/optimizer/passes/merge-labels-pass.js +14 -0
- package/dist/optimizer/types.d.ts +10 -0
- package/dist/optimizer/types.js +3 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +14 -0
- package/package.json +22 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.expandMacros = void 0;
|
|
4
|
+
const counter_1 = require("../counter");
|
|
5
|
+
const ir_1 = require("../ir/ir");
|
|
6
|
+
const deepCopy = (obj) => {
|
|
7
|
+
return JSON.parse(JSON.stringify(obj));
|
|
8
|
+
};
|
|
9
|
+
const expandSingleMacro = (body, inputParams, outputParams, inputArgs, outputArgs, returnTarget) => {
|
|
10
|
+
const macroCallId = counter_1.counter.next();
|
|
11
|
+
const varNameMap = {};
|
|
12
|
+
const labelNameMap = {};
|
|
13
|
+
const inputArgVars = {};
|
|
14
|
+
const inputArgLiterals = {};
|
|
15
|
+
for (let i = 0; i < inputArgs.length; i++) {
|
|
16
|
+
const arg = inputArgs[i];
|
|
17
|
+
if (arg.isLiteral) {
|
|
18
|
+
inputArgLiterals[i] = arg;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
inputArgVars[i] = arg;
|
|
22
|
+
}
|
|
23
|
+
const getVarName = (original) => {
|
|
24
|
+
if (!original.startsWith('$')) {
|
|
25
|
+
return original;
|
|
26
|
+
}
|
|
27
|
+
if (!varNameMap[original]) {
|
|
28
|
+
if (inputParams.includes(original)) {
|
|
29
|
+
const index = inputParams.indexOf(original);
|
|
30
|
+
varNameMap[original] = inputArgs[index].name;
|
|
31
|
+
}
|
|
32
|
+
else if (outputParams.includes(original)) {
|
|
33
|
+
const index = outputParams.indexOf(original);
|
|
34
|
+
varNameMap[original] = outputArgs[index];
|
|
35
|
+
}
|
|
36
|
+
else if (returnTarget && original === '$return') {
|
|
37
|
+
varNameMap[original] = returnTarget;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
varNameMap[original] = `$$${macroCallId}_${original}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return varNameMap[original];
|
|
44
|
+
};
|
|
45
|
+
const getLabelName = (original) => {
|
|
46
|
+
if (!labelNameMap[original]) {
|
|
47
|
+
labelNameMap[original] = `_${macroCallId}_${original}`;
|
|
48
|
+
}
|
|
49
|
+
return labelNameMap[original];
|
|
50
|
+
};
|
|
51
|
+
const transformValue = (original) => {
|
|
52
|
+
if (original.isLiteral || original.isMindustry) {
|
|
53
|
+
return original;
|
|
54
|
+
}
|
|
55
|
+
if (inputParams.includes(original.name)) {
|
|
56
|
+
const index = inputParams.indexOf(original.name);
|
|
57
|
+
if (inputArgVars[index]) {
|
|
58
|
+
return { ...inputArgVars[index] };
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return { ...inputArgLiterals[index] };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
...original,
|
|
66
|
+
name: getVarName(original.name),
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const outputBody = [];
|
|
70
|
+
for (const node of body) {
|
|
71
|
+
const newNode = deepCopy(node);
|
|
72
|
+
if (newNode.type == ir_1.IRNodeType.JUMP) {
|
|
73
|
+
newNode.label = getLabelName(newNode.label);
|
|
74
|
+
}
|
|
75
|
+
else if (newNode.type == ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
76
|
+
newNode.left = transformValue(newNode.left);
|
|
77
|
+
newNode.right = transformValue(newNode.right);
|
|
78
|
+
newNode.label = getLabelName(newNode.label);
|
|
79
|
+
}
|
|
80
|
+
else if (newNode.type == ir_1.IRNodeType.LABEL) {
|
|
81
|
+
newNode.name = getLabelName(newNode.name);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const temp = newNode;
|
|
85
|
+
for (const key in temp) {
|
|
86
|
+
if (typeof temp[key] == 'string') {
|
|
87
|
+
temp[key] = getVarName(temp[key]);
|
|
88
|
+
}
|
|
89
|
+
else if (typeof temp[key] == 'object' && 'isLiteral' in temp[key]) {
|
|
90
|
+
temp[key] = transformValue(temp[key]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
outputBody.push(newNode);
|
|
95
|
+
}
|
|
96
|
+
return outputBody;
|
|
97
|
+
};
|
|
98
|
+
const expandMacrosInBody = (body, macroDefinitions) => {
|
|
99
|
+
const output = [];
|
|
100
|
+
for (const node of body) {
|
|
101
|
+
if (node.type == ir_1.IRNodeType.MACRO_CALL) {
|
|
102
|
+
const macroDef = macroDefinitions[node.name];
|
|
103
|
+
output.push(...expandSingleMacro(macroDef.body, macroDef.inputParams, macroDef.outputParams, node.inputArgs, node.outputArgs));
|
|
104
|
+
}
|
|
105
|
+
else if (node.type == ir_1.IRNodeType.MACRO_CALL_ASSIGN) {
|
|
106
|
+
const macroDef = macroDefinitions[node.name];
|
|
107
|
+
const expanded = expandSingleMacro(macroDef.body, macroDef.inputParams, macroDef.outputParams, node.inputArgs, node.outputArgs, node.returnTarget);
|
|
108
|
+
output.push(...expanded);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
output.push(deepCopy(node));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return output;
|
|
115
|
+
};
|
|
116
|
+
const simplifyNames = (body, inputParams, outputParams) => {
|
|
117
|
+
counter_1.counter.reset();
|
|
118
|
+
const varNameMap = {};
|
|
119
|
+
const labelNameMap = {};
|
|
120
|
+
const getVarName = (original) => {
|
|
121
|
+
if (!original.startsWith('$') || original == '$return') {
|
|
122
|
+
return original;
|
|
123
|
+
}
|
|
124
|
+
if (!varNameMap[original]) {
|
|
125
|
+
if (inputParams.includes(original) || outputParams.includes(original)) {
|
|
126
|
+
varNameMap[original] = original;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
varNameMap[original] = `$${counter_1.counter.next()}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return varNameMap[original];
|
|
133
|
+
};
|
|
134
|
+
const getLabelName = (original) => {
|
|
135
|
+
if (!labelNameMap[original]) {
|
|
136
|
+
labelNameMap[original] = `${counter_1.counter.next()}`;
|
|
137
|
+
}
|
|
138
|
+
return labelNameMap[original];
|
|
139
|
+
};
|
|
140
|
+
const transformValue = (original) => {
|
|
141
|
+
if (original.isLiteral || original.isMindustry) {
|
|
142
|
+
return original;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
...original,
|
|
146
|
+
name: getVarName(original.name),
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
for (const node of body) {
|
|
150
|
+
if (node.type == ir_1.IRNodeType.JUMP) {
|
|
151
|
+
node.label = getLabelName(node.label);
|
|
152
|
+
}
|
|
153
|
+
else if (node.type == ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
154
|
+
node.left = transformValue(node.left);
|
|
155
|
+
node.right = transformValue(node.right);
|
|
156
|
+
node.label = getLabelName(node.label);
|
|
157
|
+
}
|
|
158
|
+
else if (node.type == ir_1.IRNodeType.LABEL) {
|
|
159
|
+
node.name = getLabelName(node.name);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const temp = node;
|
|
163
|
+
for (const key in temp) {
|
|
164
|
+
if (typeof temp[key] == 'string') {
|
|
165
|
+
temp[key] = getVarName(temp[key]);
|
|
166
|
+
}
|
|
167
|
+
else if (typeof temp[key] == 'object' && 'isLiteral' in temp[key]) {
|
|
168
|
+
temp[key] = transformValue(temp[key]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const expandAllMacros = (macroDefinitions) => {
|
|
175
|
+
const result = {};
|
|
176
|
+
const macroMap = {};
|
|
177
|
+
// 创建宏定义的映射表
|
|
178
|
+
for (const macro of macroDefinitions) {
|
|
179
|
+
macroMap[macro.name] = {
|
|
180
|
+
...deepCopy(macro),
|
|
181
|
+
dependencies: new Set(macro.dependencies),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// 拓扑排序状态
|
|
185
|
+
const visited = new Set();
|
|
186
|
+
const order = [];
|
|
187
|
+
// DFS 拓扑排序
|
|
188
|
+
const dfs = (name) => {
|
|
189
|
+
if (visited.has(name)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
visited.add(name);
|
|
193
|
+
const macro = macroMap[name];
|
|
194
|
+
if (macro && macro.dependencies) {
|
|
195
|
+
for (const dep of macro.dependencies) {
|
|
196
|
+
dfs(dep);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
order.push(name);
|
|
200
|
+
};
|
|
201
|
+
// 获取拓扑排序后的遍历顺序
|
|
202
|
+
for (const macro of macroDefinitions) {
|
|
203
|
+
dfs(macro.name);
|
|
204
|
+
}
|
|
205
|
+
// 按顺序展开宏
|
|
206
|
+
for (const macroName of order) {
|
|
207
|
+
const macro = macroMap[macroName];
|
|
208
|
+
// 展开宏体中的所有宏调用
|
|
209
|
+
const expandedBody = expandMacrosInBody(macro.body, result);
|
|
210
|
+
simplifyNames(expandedBody, macro.inputParams, macro.outputParams);
|
|
211
|
+
// 保存展开后的宏
|
|
212
|
+
result[macroName] = {
|
|
213
|
+
...macro,
|
|
214
|
+
body: expandedBody,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
};
|
|
219
|
+
const simplifyMainNames = (body, boundVariables) => {
|
|
220
|
+
counter_1.counter.reset();
|
|
221
|
+
const varNameMap = {};
|
|
222
|
+
const labelNameMap = {};
|
|
223
|
+
const shouldKeepVarName = (name) => {
|
|
224
|
+
return name.startsWith('@') || boundVariables.has(name);
|
|
225
|
+
};
|
|
226
|
+
const getVarName = (original) => {
|
|
227
|
+
if (shouldKeepVarName(original)) {
|
|
228
|
+
return original;
|
|
229
|
+
}
|
|
230
|
+
if (!varNameMap[original]) {
|
|
231
|
+
varNameMap[original] = `$${counter_1.counter.next()}`;
|
|
232
|
+
}
|
|
233
|
+
return varNameMap[original];
|
|
234
|
+
};
|
|
235
|
+
const getLabelName = (original) => {
|
|
236
|
+
if (!labelNameMap[original]) {
|
|
237
|
+
labelNameMap[original] = `${counter_1.counter.next()}`;
|
|
238
|
+
}
|
|
239
|
+
return labelNameMap[original];
|
|
240
|
+
};
|
|
241
|
+
const transformValue = (original) => {
|
|
242
|
+
if (original.isLiteral || original.isMindustry) {
|
|
243
|
+
return original;
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
...original,
|
|
247
|
+
name: getVarName(original.name),
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
for (const node of body) {
|
|
251
|
+
if (node.type == ir_1.IRNodeType.JUMP) {
|
|
252
|
+
node.label = getLabelName(node.label);
|
|
253
|
+
}
|
|
254
|
+
else if (node.type == ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
255
|
+
node.left = transformValue(node.left);
|
|
256
|
+
node.right = transformValue(node.right);
|
|
257
|
+
node.label = getLabelName(node.label);
|
|
258
|
+
}
|
|
259
|
+
else if (node.type == ir_1.IRNodeType.LABEL) {
|
|
260
|
+
node.name = getLabelName(node.name);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const temp = node;
|
|
264
|
+
for (const key in temp) {
|
|
265
|
+
if (typeof temp[key] == 'string') {
|
|
266
|
+
temp[key] = getVarName(temp[key]);
|
|
267
|
+
}
|
|
268
|
+
else if (typeof temp[key] == 'object' && 'isLiteral' in temp[key]) {
|
|
269
|
+
temp[key] = transformValue(temp[key]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
body,
|
|
276
|
+
boundVariables,
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
const expandMacros = (program) => {
|
|
280
|
+
const expandedMacros = expandAllMacros(Object.values(program.macros));
|
|
281
|
+
const expandedBody = expandMacrosInBody(program.main, expandedMacros);
|
|
282
|
+
return simplifyMainNames(expandedBody, program.boundVariables);
|
|
283
|
+
};
|
|
284
|
+
exports.expandMacros = expandMacros;
|
|
285
|
+
//# sourceMappingURL=expand-macros.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PASSES = void 0;
|
|
4
|
+
const control_flow_1 = require("./passes/control-flow");
|
|
5
|
+
const copy_propagation_1 = require("./passes/copy-propagation");
|
|
6
|
+
const constant_folding_1 = require("./passes/constant-folding");
|
|
7
|
+
const dead_code_1 = require("./passes/dead-code");
|
|
8
|
+
const merge_labels_pass_1 = require("./passes/merge-labels-pass");
|
|
9
|
+
exports.DEFAULT_PASSES = [
|
|
10
|
+
merge_labels_pass_1.mergeLabelsPass,
|
|
11
|
+
control_flow_1.foldConstantConditionalJumps,
|
|
12
|
+
control_flow_1.removeJumpToNextLabel,
|
|
13
|
+
control_flow_1.removeUnreachableAfterJump,
|
|
14
|
+
copy_propagation_1.propagateAssignValues,
|
|
15
|
+
constant_folding_1.foldConstants,
|
|
16
|
+
dead_code_1.removeDeadCode,
|
|
17
|
+
merge_labels_pass_1.mergeLabelsPass,
|
|
18
|
+
control_flow_1.removeUnusedLabels,
|
|
19
|
+
];
|
|
20
|
+
//# sourceMappingURL=default-passes.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeLabels = void 0;
|
|
4
|
+
const ir_1 = require("../ir/ir");
|
|
5
|
+
const mergeLabels = (ir) => {
|
|
6
|
+
const labelAlias = new Map();
|
|
7
|
+
const merged = [];
|
|
8
|
+
for (const node of ir.body) {
|
|
9
|
+
if (node.type === ir_1.IRNodeType.LABEL) {
|
|
10
|
+
const prev = merged[merged.length - 1];
|
|
11
|
+
if (prev && prev.type === ir_1.IRNodeType.LABEL) {
|
|
12
|
+
labelAlias.set(node.name, prev.name);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
merged.push(node);
|
|
17
|
+
}
|
|
18
|
+
const resolveLabel = (name) => {
|
|
19
|
+
let resolved = name;
|
|
20
|
+
while (labelAlias.has(resolved)) {
|
|
21
|
+
resolved = labelAlias.get(resolved);
|
|
22
|
+
}
|
|
23
|
+
return resolved;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
body: merged.map((node) => {
|
|
27
|
+
if (node.type === ir_1.IRNodeType.JUMP) {
|
|
28
|
+
return {
|
|
29
|
+
...node,
|
|
30
|
+
label: resolveLabel(node.label),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (node.type === ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
34
|
+
return {
|
|
35
|
+
...node,
|
|
36
|
+
label: resolveLabel(node.label),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return node;
|
|
40
|
+
}),
|
|
41
|
+
boundVariables: ir.boundVariables,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
exports.mergeLabels = mergeLabels;
|
|
45
|
+
//# sourceMappingURL=merge-labels.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type SimplifiedIRProgram } from '../macro/expand-macros';
|
|
2
|
+
import type { OptimizeOptions } from './types';
|
|
3
|
+
export type { OptimizerPass, OptimizeOptions } from './types';
|
|
4
|
+
export declare const optimizeIR: (program: SimplifiedIRProgram, options?: OptimizeOptions) => SimplifiedIRProgram;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.optimizeIR = void 0;
|
|
4
|
+
const default_passes_1 = require("./default-passes");
|
|
5
|
+
const optimizeIR = (program, options = {}) => {
|
|
6
|
+
const maxIterations = options.maxIterations ?? 5;
|
|
7
|
+
const passes = options.passes ?? default_passes_1.DEFAULT_PASSES;
|
|
8
|
+
let current = {
|
|
9
|
+
body: [...program.body],
|
|
10
|
+
boundVariables: program.boundVariables,
|
|
11
|
+
};
|
|
12
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
13
|
+
let changedInIteration = false;
|
|
14
|
+
for (const pass of passes) {
|
|
15
|
+
const result = pass(current);
|
|
16
|
+
current = result.program;
|
|
17
|
+
changedInIteration = changedInIteration || result.changed;
|
|
18
|
+
}
|
|
19
|
+
if (!changedInIteration) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return current;
|
|
24
|
+
};
|
|
25
|
+
exports.optimizeIR = optimizeIR;
|
|
26
|
+
//# sourceMappingURL=optimizer.js.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.foldConstants = void 0;
|
|
4
|
+
const ir_1 = require("../../ir/ir");
|
|
5
|
+
const literal = (value) => ({
|
|
6
|
+
isLiteral: true,
|
|
7
|
+
value,
|
|
8
|
+
});
|
|
9
|
+
const asNumber = (value) => {
|
|
10
|
+
if (!value.isLiteral || typeof value.value !== 'number') {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return value.value;
|
|
14
|
+
};
|
|
15
|
+
const foldUnary = (node) => {
|
|
16
|
+
if (node.type === ir_1.IRNodeType.ASSIGN) {
|
|
17
|
+
if (node.value.isLiteral) {
|
|
18
|
+
return node;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
if (node.type !== ir_1.IRNodeType.FLIP &&
|
|
23
|
+
node.type !== ir_1.IRNodeType.ABS &&
|
|
24
|
+
node.type !== ir_1.IRNodeType.LOG &&
|
|
25
|
+
node.type !== ir_1.IRNodeType.LOG10 &&
|
|
26
|
+
node.type !== ir_1.IRNodeType.FLOOR &&
|
|
27
|
+
node.type !== ir_1.IRNodeType.CEIL &&
|
|
28
|
+
node.type !== ir_1.IRNodeType.SQRT &&
|
|
29
|
+
node.type !== ir_1.IRNodeType.SIN &&
|
|
30
|
+
node.type !== ir_1.IRNodeType.COS &&
|
|
31
|
+
node.type !== ir_1.IRNodeType.TAN &&
|
|
32
|
+
node.type !== ir_1.IRNodeType.ASIN &&
|
|
33
|
+
node.type !== ir_1.IRNodeType.ACOS &&
|
|
34
|
+
node.type !== ir_1.IRNodeType.ATAN) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const n = asNumber(node.value);
|
|
38
|
+
if (n === undefined) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
let output;
|
|
42
|
+
switch (node.type) {
|
|
43
|
+
case ir_1.IRNodeType.FLIP:
|
|
44
|
+
output = -n;
|
|
45
|
+
break;
|
|
46
|
+
case ir_1.IRNodeType.ABS:
|
|
47
|
+
output = Math.abs(n);
|
|
48
|
+
break;
|
|
49
|
+
case ir_1.IRNodeType.LOG:
|
|
50
|
+
output = Math.log(n);
|
|
51
|
+
break;
|
|
52
|
+
case ir_1.IRNodeType.LOG10:
|
|
53
|
+
output = Math.log10(n);
|
|
54
|
+
break;
|
|
55
|
+
case ir_1.IRNodeType.FLOOR:
|
|
56
|
+
output = Math.floor(n);
|
|
57
|
+
break;
|
|
58
|
+
case ir_1.IRNodeType.CEIL:
|
|
59
|
+
output = Math.ceil(n);
|
|
60
|
+
break;
|
|
61
|
+
case ir_1.IRNodeType.SQRT:
|
|
62
|
+
output = Math.sqrt(n);
|
|
63
|
+
break;
|
|
64
|
+
case ir_1.IRNodeType.SIN:
|
|
65
|
+
output = Math.sin(n);
|
|
66
|
+
break;
|
|
67
|
+
case ir_1.IRNodeType.COS:
|
|
68
|
+
output = Math.cos(n);
|
|
69
|
+
break;
|
|
70
|
+
case ir_1.IRNodeType.TAN:
|
|
71
|
+
output = Math.tan(n);
|
|
72
|
+
break;
|
|
73
|
+
case ir_1.IRNodeType.ASIN:
|
|
74
|
+
output = Math.asin(n);
|
|
75
|
+
break;
|
|
76
|
+
case ir_1.IRNodeType.ACOS:
|
|
77
|
+
output = Math.acos(n);
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
output = Math.atan(n);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
type: ir_1.IRNodeType.ASSIGN,
|
|
85
|
+
target: node.target,
|
|
86
|
+
value: literal(output),
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
const foldBinary = (node) => {
|
|
90
|
+
if (node.type !== ir_1.IRNodeType.ADD &&
|
|
91
|
+
node.type !== ir_1.IRNodeType.SUB &&
|
|
92
|
+
node.type !== ir_1.IRNodeType.MUL &&
|
|
93
|
+
node.type !== ir_1.IRNodeType.DIV &&
|
|
94
|
+
node.type !== ir_1.IRNodeType.IDIV &&
|
|
95
|
+
node.type !== ir_1.IRNodeType.MOD &&
|
|
96
|
+
node.type !== ir_1.IRNodeType.POW) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const left = asNumber(node.left);
|
|
100
|
+
const right = asNumber(node.right);
|
|
101
|
+
if (left === undefined || right === undefined) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
let output;
|
|
105
|
+
switch (node.type) {
|
|
106
|
+
case ir_1.IRNodeType.ADD:
|
|
107
|
+
output = left + right;
|
|
108
|
+
break;
|
|
109
|
+
case ir_1.IRNodeType.SUB:
|
|
110
|
+
output = left - right;
|
|
111
|
+
break;
|
|
112
|
+
case ir_1.IRNodeType.MUL:
|
|
113
|
+
output = left * right;
|
|
114
|
+
break;
|
|
115
|
+
case ir_1.IRNodeType.DIV:
|
|
116
|
+
output = left / right;
|
|
117
|
+
break;
|
|
118
|
+
case ir_1.IRNodeType.IDIV:
|
|
119
|
+
output = Math.trunc(left / right);
|
|
120
|
+
break;
|
|
121
|
+
case ir_1.IRNodeType.MOD:
|
|
122
|
+
output = left % right;
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
output = left ** right;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
type: ir_1.IRNodeType.ASSIGN,
|
|
130
|
+
target: node.target,
|
|
131
|
+
value: literal(output),
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const foldConstants = (program) => {
|
|
135
|
+
let changed = false;
|
|
136
|
+
const body = program.body.map((node) => {
|
|
137
|
+
const unaryFolded = foldUnary(node);
|
|
138
|
+
if (unaryFolded) {
|
|
139
|
+
if (unaryFolded !== node) {
|
|
140
|
+
changed = true;
|
|
141
|
+
}
|
|
142
|
+
return unaryFolded;
|
|
143
|
+
}
|
|
144
|
+
const binaryFolded = foldBinary(node);
|
|
145
|
+
if (binaryFolded) {
|
|
146
|
+
changed = true;
|
|
147
|
+
return binaryFolded;
|
|
148
|
+
}
|
|
149
|
+
return node;
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
program: {
|
|
153
|
+
body,
|
|
154
|
+
boundVariables: program.boundVariables,
|
|
155
|
+
},
|
|
156
|
+
changed,
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
exports.foldConstants = foldConstants;
|
|
160
|
+
//# sourceMappingURL=constant-folding.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OptimizerPass } from '../types';
|
|
2
|
+
export declare const foldConstantConditionalJumps: OptimizerPass;
|
|
3
|
+
export declare const removeJumpToNextLabel: OptimizerPass;
|
|
4
|
+
export declare const removeUnreachableAfterJump: OptimizerPass;
|
|
5
|
+
export declare const removeUnusedLabels: OptimizerPass;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeUnusedLabels = exports.removeUnreachableAfterJump = exports.removeJumpToNextLabel = exports.foldConstantConditionalJumps = void 0;
|
|
4
|
+
const ir_1 = require("../../ir/ir");
|
|
5
|
+
const evaluateCondition = (node) => {
|
|
6
|
+
if (!node.left.isLiteral || !node.right.isLiteral) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const left = node.left.value;
|
|
10
|
+
const right = node.right.value;
|
|
11
|
+
switch (node.condition) {
|
|
12
|
+
case ir_1.JumpCondition.EQ:
|
|
13
|
+
return left == right;
|
|
14
|
+
case ir_1.JumpCondition.NE:
|
|
15
|
+
return left != right;
|
|
16
|
+
case ir_1.JumpCondition.LESS:
|
|
17
|
+
return left < right;
|
|
18
|
+
case ir_1.JumpCondition.LE:
|
|
19
|
+
return left <= right;
|
|
20
|
+
case ir_1.JumpCondition.GREATER:
|
|
21
|
+
return left > right;
|
|
22
|
+
case ir_1.JumpCondition.GE:
|
|
23
|
+
return left >= right;
|
|
24
|
+
case ir_1.JumpCondition.STRICT_EQ:
|
|
25
|
+
return left === right;
|
|
26
|
+
default:
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const foldConstantConditionalJumps = (program) => {
|
|
31
|
+
let changed = false;
|
|
32
|
+
const body = [];
|
|
33
|
+
for (const node of program.body) {
|
|
34
|
+
if (node.type !== ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
35
|
+
body.push(node);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const result = evaluateCondition(node);
|
|
39
|
+
if (result === undefined) {
|
|
40
|
+
body.push(node);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
changed = true;
|
|
44
|
+
if (result) {
|
|
45
|
+
body.push({
|
|
46
|
+
type: ir_1.IRNodeType.JUMP,
|
|
47
|
+
label: node.label,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
program: {
|
|
53
|
+
body,
|
|
54
|
+
boundVariables: program.boundVariables,
|
|
55
|
+
},
|
|
56
|
+
changed,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
exports.foldConstantConditionalJumps = foldConstantConditionalJumps;
|
|
60
|
+
const removeJumpToNextLabel = (program) => {
|
|
61
|
+
let changed = false;
|
|
62
|
+
const body = [];
|
|
63
|
+
for (let i = 0; i < program.body.length; i++) {
|
|
64
|
+
const node = program.body[i];
|
|
65
|
+
if (node.type === ir_1.IRNodeType.JUMP) {
|
|
66
|
+
const next = program.body[i + 1];
|
|
67
|
+
if (next && next.type === ir_1.IRNodeType.LABEL && next.name === node.label) {
|
|
68
|
+
changed = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
body.push(node);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
program: {
|
|
76
|
+
body,
|
|
77
|
+
boundVariables: program.boundVariables,
|
|
78
|
+
},
|
|
79
|
+
changed,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
exports.removeJumpToNextLabel = removeJumpToNextLabel;
|
|
83
|
+
const removeUnreachableAfterJump = (program) => {
|
|
84
|
+
let changed = false;
|
|
85
|
+
const body = [];
|
|
86
|
+
let inUnreachableRegion = false;
|
|
87
|
+
for (const node of program.body) {
|
|
88
|
+
if (node.type === ir_1.IRNodeType.LABEL) {
|
|
89
|
+
inUnreachableRegion = false;
|
|
90
|
+
body.push(node);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (inUnreachableRegion) {
|
|
94
|
+
changed = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
body.push(node);
|
|
98
|
+
if (node.type === ir_1.IRNodeType.JUMP) {
|
|
99
|
+
inUnreachableRegion = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
program: {
|
|
104
|
+
body,
|
|
105
|
+
boundVariables: program.boundVariables,
|
|
106
|
+
},
|
|
107
|
+
changed,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
exports.removeUnreachableAfterJump = removeUnreachableAfterJump;
|
|
111
|
+
const removeUnusedLabels = (program) => {
|
|
112
|
+
const usedLabels = new Set();
|
|
113
|
+
for (const node of program.body) {
|
|
114
|
+
if (node.type === ir_1.IRNodeType.JUMP || node.type === ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
115
|
+
usedLabels.add(node.label);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let changed = false;
|
|
119
|
+
const body = program.body.filter((node) => {
|
|
120
|
+
if (node.type !== ir_1.IRNodeType.LABEL) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const keep = usedLabels.has(node.name);
|
|
124
|
+
if (!keep) {
|
|
125
|
+
changed = true;
|
|
126
|
+
}
|
|
127
|
+
return keep;
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
program: {
|
|
131
|
+
body,
|
|
132
|
+
boundVariables: program.boundVariables,
|
|
133
|
+
},
|
|
134
|
+
changed,
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
exports.removeUnusedLabels = removeUnusedLabels;
|
|
138
|
+
//# sourceMappingURL=control-flow.js.map
|