-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.js
executable file
·243 lines (225 loc) · 7.79 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
const fs = require('fs');
const app = require('commander');
const Parse = require('./src/js/Parse');
const Static = require('./src/js/Static');
const Generate = require('./src/js/Generate');
const AstToString = require('./src/js/AstToString');
const { CFG, LLVM } = require('./src/js/Generate');
const Optimize = require('./src/js/Optimize');
const ARM = require('./src/js/ARM');
/* add options to a command */
function glue(opts) {
for (const opt of opts)
this.option.apply(this, opt);
return this;
}
/* common; make sure we can write the output before doing stuff */
function withOutfile(output, cb) {
/* stdout */
if (!output) {
cb(null, process.stdout.fd);
} else {
fs.open(output, 'w', (err, fd) => {
if (err) {
console.error('ERROR: could not open output file for writing.');
console.error(err);
process.exit(1);
}
cb(null, fd);
});
}
}
/* do very bad stuff because it makes things look nicer later */
app.glue = glue;
app.Command.prototype.glue = glue;
/* These options are available regardless of the command. */
const universalOptions = [
['-o, --output [file]', 'Output file', './ast.out.json'],
['-W, --no-write', 'Do not save any output, just see if it compiles'],
['-v, --verbose', 'Increase chattiness'],
['-t, --target-architecture <target>', 'Specify target architecture', 'i686'],
];
app.glue(universalOptions);
/* Parse phase */
const parse = app.command('parse <mini>').description('Parse a mini program and output an AST');
const parseOptions = [
['-U, --no-collapse-double-unary', 'Do not collapse double-negations and double-nots'],
];
parse.glue(universalOptions);
parse.glue(parseOptions);
parse.action((mini, opts) => {
console.log('Parsing...');
if (opts.parent.write) {
withOutfile(opts.parent.output, (err, fd) => {
let ast = Parse.parseFile(mini);
fs.writeSync(fd, JSON.stringify(ast));
});
} else {
Parse.parseFile(mini);
}
});
/* Static Anaylsis phase */
const staticAnalysis = app.command('static <ast>').description('Perform static analysis on an AST');
const staticAnalysisOptions = [
['-T, --no-type-analysis', 'Do not perform type analysis'],
['-R, --no-return-analysis', 'Do not perform function return checking'],
['--Wall', 'Enable all warnings'],
['--Wboolean-comparison', 'Warn on boolean comparison'],
];
staticAnalysis.glue(universalOptions);
staticAnalysis.glue(staticAnalysisOptions);
staticAnalysis.action((astfile, opts) => {
const ast = JSON.parse(fs.readFileSync(astfile));
console.log('Performing static analysis...');
let typeErrs = (new Static.TypeChecker(opts)).check(ast);
console.log('Type checking results:');
if (!typeErrs.length) {
console.log('ALL TESTS PASSED');
} else {
console.log(`${typeErrs.length} ERRORS:`);
for (let err of typeErrs) {
console.log(err);
}
}
let returnErrs = (new Static.ReturnChecker(opts)).check(ast);
console.log('Return analysis results:');
if (!returnErrs.length) {
console.log('ALL TESTS PASSED');
} else {
console.log(`${returnErrs.length} ERRORS:`);
for (let err of returnErrs) {
console.log(err);
}
}
});
const generateCFG = app.command('cfg <ast>').description('Generate a Control Flow Graph from an AST');
const generateCFGOptions = [
];
generateCFG.glue(universalOptions);
generateCFG.glue(generateCFGOptions);
generateCFG.action((astfile, opts) => {
const ast = JSON.parse(fs.readFileSync(astfile));
console.log('Generating CFG...');
const CFG = new Generate.CFG(opts);
const cfgs = CFG.generate(ast);
for (const cfg of cfgs) {
console.log(`FUNCTION ${cfg.id}()`);
for (const block of cfg.blockList) {
console.log(`${block.label}:`);
for (const statement of block.body) {
process.stdout.write(AstToString.stringify(statement, 1));
}
console.log(` -> ${block.successors.map(b => b.label).join(', ')}\n`);
}
}
// TODO: serialize CFG
//console.log(util.inspect(cfg));
});
const generateLLVM = app.command('llvm <ast>').description('Generate a Control Flow Graph from an AST');
const generateLLVMOptions = [
['--stack', 'Generate stack-based code'],
['--llvm', 'Generate LLVM code'],
['--old-llvm', 'Target old LLVM syntax'],
];
generateLLVM.glue(universalOptions);
generateLLVM.glue(generateLLVMOptions);
generateLLVM.action(() => {
//const ast = JSON.parse(fs.readFileSync(cfgfile));
// TODO: deserialize CFG
/*console.log('Generating CFG...');
const CFG = new Generate.CFG(opts);
const cfgs = CFG.generate(ast);
for (const cfg of cfgs) {
console.log(`FUNCTION ${cfg.id}()`)
for (const block of cfg.blockList) {
console.log(`${block.label}:`);
for (const statement of block.body) {
process.stdout.write(AstToString.stringify(statement, 1));
}
console.log(` -> ${block.successors.map(b => b.label).join(', ')}\n`);
}
}*/
//console.log(util.inspect(cfg));
});
const optimizationOptions = [
['-O, --no-optimization', 'Do not perform any optimization'],
['--no-sscp', 'Do not perform Sparse Simple Constant Propagation (SSCP)'],
['--no-uce', 'Do not perform Unused Code Elimination (SSA Unused Result)'],
['--no-cfg-simplification', 'Do not perform CFG Simplification'],
['--arm', 'Generate ARM assembly'],
];
const all = app.command('all <mini>');
all
.glue(parseOptions)
.glue(staticAnalysisOptions)
.glue(generateLLVMOptions)
.glue(optimizationOptions);
all.action((mini, opts) => {
console.log(`Compiling file '${mini}'`);
console.log('Parsing...');
const ast = Parse.parseFile(mini);
if (!ast) {
console.error('ERROR: failed to parse file, aborting');
return;
}
console.log('Performing static analysis: Type Checking + Semantics');
const tc = new Static.TypeChecker(opts);
const tcErrs = tc.check(ast);
if (!tcErrs.length) {
console.log('All checks passed.');
} else {
console.log(`${tcErrs.length} errors during type checking and semantic analyis:`);
for (let err of tcErrs) {
console.log(err);
}
return;
}
console.log('Performing static analyis: Return Analysis');
const rc = new Static.ReturnChecker(opts);
let rcErrs = rc.check(ast);
if (!rcErrs.length) {
console.log('All checks passed.');
} else {
console.log(`${rcErrs.length} errors during return analysis:`);
for (let err of rcErrs) {
console.log(err);
}
return;
}
if (opts.llvm || opts.parent.llvm || opts.arm || opts.parent.arm) {
console.log('Building Control Flow Graph');
const cfgGenerator = new CFG(opts);
const cfgs = cfgGenerator.generate(ast);
console.log(`Generating ${(opts.stack || opts.parent.stack) ? 'stack-based' : 'register-based'} LLVM assembly`);
const llvmGenerator = new LLVM(opts);
const cfgsWithLLVM = llvmGenerator.generate(cfgs);
if (opts.optimization || opts.parent.optimization) {
console.log('Performing optimizations...');
for (const func of cfgsWithLLVM.functions) {
if (opts.sscp || opts.parent.sscp) {
Optimize.SSCP(func);
Optimize.SSCP(func);
}
if (opts.uce || opts.parent.uce) {
Optimize.SSAUnusedResult(func);
}
if (opts.cfgSimplification || opts.parent.cfgSimplification) {
Optimize.CFGCleanup(func);
if (opts.uce || opts.parent.uce) {
Optimize.SSAUnusedResult(func);
Optimize.CFGCleanup(func);
}
}
}
}
if (opts.arm || opts.parent.arm) {
console.log('Translating to ARM assembly');
ARM.translateAll(cfgsWithLLVM);
}
if (opts.parent.write) {
console.log(`Writing output to ${opts.parent.output ? opts.parent.output : 'STDOUT'}`);
withOutfile(opts.parent.output, (err, file) => fs.writeSync(file, `${cfgsWithLLVM}`));
}
}
});
app.parse(process.argv);