-
Notifications
You must be signed in to change notification settings - Fork 119
/
scope.js
325 lines (273 loc) · 9.55 KB
/
scope.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
(function (exports) {
var util, T, Types;
if (typeof process !== "undefined") {
util = require("./util.js");
T = require("./estransform.js");
Types = require("./types.js");
} else {
util = this.util;
T = estransform;
Types = this.Types;
}
/**
* Import nodes.
*/
const Node = T.Node;
const Literal = T.Literal;
const Identifier = T.Identifier;
const VariableDeclaration = T.VariableDeclaration;
const VariableDeclarator = T.VariableDeclarator;
const MemberExpression = T.MemberExpression;
const BinaryExpression = T.BinaryExpression;
const SequenceExpression = T.SequenceExpression;
const CallExpression = T.CallExpression;
const AssignmentExpression = T.AssignmentExpression;
const ExpressionStatement = T.ExpressionStatement;
const ReturnStatement = T.ReturnStatement;
const Program = T.Program;
const FunctionDeclaration = T.FunctionDeclaration;
const FunctionExpression = T.FunctionExpression;
const ConditionalExpression = T.ConditionalExpression;
const ObjectExpression = T.ObjectExpression;
const UnaryExpression = T.UnaryExpression;
const NewExpression = T.NewExpression;
const UpdateExpression = T.UpdateExpression;
const ForStatement = T.ForStatement;
const BlockStatement = T.BlockStatement;
const CatchClause = T.CatchClause;
const ThisExpression = T.ThisExpression;
const TypeAliasDirective = T.TypeAliasDirective;
const CastExpression = T.CastExpression;
/**
* Import utilities.
*/
const assert = util.assert;
const cast = util.cast;
const alignTo = util.alignTo;
const dereference = util.dereference;
/**
* Import types.
*/
const TypeAlias = Types.TypeAlias;
const PrimitiveType = Types.PrimitiveType;
const StructType = Types.StructType;
const PointerType = Types.PointerType;
const ArrayType = Types.ArrayType;
const ArrowType = Types.ArrowType;
/**
* Scopes and Variables
*/
function Variable(name, type) {
this.name = name;
this.type = type;
this.isStackAllocated = (type instanceof StructType || type instanceof ArrayType);
}
Variable.prototype.toString = function () {
return Types.tystr(this.type, 0) + " " + this.name;
};
Variable.prototype.getStackAccess = function getStackAccess(scope, loc) {
assert(this.isStackAllocated);
assert(typeof this.wordOffset !== "undefined", "stack-allocated variable offset not computed.");
var byteOffset = this.wordOffset * Types.wordTy.size;
return dereference(scope.SP(), byteOffset, this.type, scope, loc);
};
function Scope(parent, name) {
this.name = name;
this.parent = parent;
this.root = parent.root;
this.variables = Object.create(null);
this.frame = parent.frame;
assert(this.frame instanceof Frame);
}
Scope.prototype.getVariable = function getVariable(name, local) {
var variable = this.variables[name];
if (variable instanceof Variable) {
return variable;
}
if (this.parent && !local) {
return this.parent.getVariable(name);
}
return null;
};
Scope.prototype.freshName = function freshName(name, variable) {
var mangles = this.frame.mangles;
var fresh = 0;
var freshName = name;
while (mangles[freshName]) {
freshName = name + "$" + ++fresh;
}
if (variable) {
mangles[freshName] = variable;
}
return freshName;
};
Scope.prototype.freshVariable = function freshVariable(name, type) {
var variable = new Variable(name, type);
variable.name = this.freshName(name, variable);
return variable;
};
Scope.prototype.freshTemp = function freshTemp(ty, loc, inDeclarator) {
var t = this.freshVariable("_", ty);
var id = cast(new Identifier(t.name), ty);
if (!inDeclarator) {
var cachedLocals = this.frame.cachedLocals;
cachedLocals[t.name] = new VariableDeclarator(id);
}
return id;
};
Scope.prototype.cacheReference = function cacheReference(node) {
assert(node);
var def, use;
if (node instanceof MemberExpression && !(node.object instanceof Identifier)) {
assert(!node.computed);
var t = this.freshTemp(node.object.ty, node.object.loc);
node.object = new AssignmentExpression(t, "=", node.object, node.object.loc);
var use = new MemberExpression(t, node.property, false, "[]", node.property.loc);
return { def: node, use: use };
}
return { def: node, use: node };
};
Scope.prototype.addVariable = function addVariable(variable, external) {
assert(variable);
assert(!variable.frame);
assert(!this.variables[variable.name], "Scope already has a variable named " + variable.name);
variable.frame = this.frame;
var variables = this.variables;
var name = variable.name;
variables[name] = variable;
if (!external) {
variable.name = this.freshName(name, variable);
}
// console.log("added variable " + variable + " to scope " + this);
};
Scope.prototype.MEMORY = function MEMORY() {
return this.root.MEMORY();
};
Scope.prototype.getView = function getView(type) {
return this.frame.getView(type);
};
Scope.prototype.MALLOC = function MALLOC() {
return this.frame.MALLOC();
};
Scope.prototype.FREE = function FREE() {
return this.frame.FREE();
};
Scope.prototype.MEMCPY = function MEMCPY(size) {
return this.frame.MEMCPY(size);
};
Scope.prototype.MEMSET = function MEMSET(size) {
return this.frame.MEMSET(size);
};
Scope.prototype.MEMCHECK_CALL_PUSH = function MEMCHECK_CALL_PUSH() {
return this.frame.MEMCHECK_CALL_PUSH();
};
Scope.prototype.MEMCHECK_CALL_RESET = function MEMCHECK_CALL_RESET() {
return this.frame.MEMCHECK_CALL_RESET();
};
Scope.prototype.MEMCHECK_CALL_POP = function MEMCHECK_CALL_POP() {
return this.frame.MEMCHECK_CALL_POP();
};
Scope.prototype.toString = function () {
return this.name;
};
function Frame(parent, name) {
this.name = name;
this.parent = parent;
this.root = parent ? parent.root : this;
this.variables = Object.create(null);
this.cachedLocals = Object.create(null);
this.frame = this;
this.mangles = Object.create(null);
}
Frame.prototype = Object.create(Scope.prototype);
function getCachedLocal(frame, name, ty) {
var cachedLocals = frame.cachedLocals;
var cname = "$" + name;
if (!cachedLocals[cname]) {
var id = cast(new Identifier(frame.freshVariable(cname, ty).name), ty);
var init = new MemberExpression(frame.root.MEMORY(), new Identifier(name), false);
cachedLocals[cname] = new VariableDeclarator(id, init, false);
}
return cachedLocals[cname].id;
}
Frame.prototype.MEMORY = function MEMORY() {
assert(this.root === this);
if (!this.cachedMEMORY) {
this.cachedMEMORY = new Identifier(this.freshVariable("$M").name);
}
return this.cachedMEMORY;
};
Frame.prototype.MALLOC = function MALLOC() {
return getCachedLocal(this, "malloc", Types.mallocTy);
};
Frame.prototype.FREE = function FREE() {
return getCachedLocal(this, "free", Types.freeTy);
};
Frame.prototype.MEMCPY = function MEMCPY(size) {
assert(size === 1 || size === 2 || size === 4);
var name, ty;
switch (size) {
case 1: name = "memcpy"; ty = Types.memcpyTy; break;
case 2: name = "memcpy2"; ty = Types.memcpy2Ty; break;
case 4: name = "memcpy4"; ty = Types.memcpy4Ty; break;
}
return getCachedLocal(this, name, ty);
};
Frame.prototype.MEMSET = function MEMSET(size) {
assert(size === 1 || size === 2 || size === 4);
var name, ty;
switch (size) {
case 1: name = "memset"; ty = Types.memsetTy; break;
case 2: name = "memset2"; ty = Types.memset2Ty; break;
case 4: name = "memset4"; ty = Types.memset4Ty; break;
}
return getCachedLocal(this, name, ty);
};
Frame.prototype.MEMCHECK_CALL_PUSH = function MEMCHECK_CALL_PUSH() {
return getCachedLocal(this, "memcheck_call_push", "dyn");
};
Frame.prototype.MEMCHECK_CALL_RESET = function MEMCHECK_CALL_RESET() {
return getCachedLocal(this, "memcheck_call_reset", "dyn");
};
Frame.prototype.MEMCHECK_CALL_POP = function MEMCHECK_CALL_POP() {
return getCachedLocal(this, "memcheck_call_pop", "dyn");
};
Frame.prototype.getView = function getView(ty) {
assert(ty);
assert(ty.align);
var alignType = ty.align;
if (typeof alignType.signed === "undefined") {
return getCachedLocal(this, "F" + alignType.size);
}
return getCachedLocal(this, (alignType.signed ? "I" : "U") + alignType.size);
};
Frame.prototype.SP = function SP() {
if (!this.cachedSP) {
this.cachedSP = cast(new Identifier(this.freshVariable("$SP").name), Types.spTy);
}
return this.cachedSP;
};
Frame.prototype.realSP = function realSP() {
return cast(new MemberExpression(this.getView(Types.builtinTypes.uint), new Literal(1), true), Types.spTy);
};
Frame.prototype.close = function close() {
const wordSize = Types.wordTy.size;
var wordOffset = 0;
var mangles = this.mangles;
// The SP and frame sizes are in *words*, since we expect most accesses
// are to ints, but the alignment is by *double word*, to fit doubles.
for (var name in mangles) {
var variable = mangles[name];
if (mangles[name].isStackAllocated) {
var size = variable.type.size;
variable.wordOffset = wordOffset;
wordOffset += alignTo(size, wordSize * 2) / wordSize;
}
}
this.frameSizeInWords = wordOffset;
};
exports.Variable = Variable;
exports.Scope = Scope;
exports.Frame = Frame;
exports.getCachedLocal = getCachedLocal;
}).call(this, typeof exports === "undefined" ? (scope = {}) : exports);