114
115 JumpOrigin(final JoinPredecessor node, final Map<Symbol, LvarType> types) {
116 this.node = node;
117 this.types = types;
118 }
119 }
120
121 private static class JumpTarget {
122 private final List<JumpOrigin> origins = new LinkedList<>();
123 private Map<Symbol, LvarType> types = Collections.emptyMap();
124
125 void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes) {
126 origins.add(new JumpOrigin(originNode, originTypes));
127 this.types = getUnionTypes(this.types, originTypes);
128 }
129 }
130 private enum LvarType {
131 UNDEFINED(Type.UNDEFINED),
132 BOOLEAN(Type.BOOLEAN),
133 INT(Type.INT),
134 LONG(Type.LONG),
135 DOUBLE(Type.NUMBER),
136 OBJECT(Type.OBJECT);
137
138 private final Type type;
139 private final TypeHolderExpression typeExpression;
140
141 private LvarType(final Type type) {
142 this.type = type;
143 this.typeExpression = new TypeHolderExpression(type);
144 }
145 }
146
147 /**
148 * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their
149 * types by creating temporary copies of them and replacing their operands with instances of these. An alternative
150 * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType)
151 * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in
152 * generation of higher number of temporary short lived nodes, though.
153 */
154 private static class TypeHolderExpression extends Expression {
255 union.putAll(types2);
256 } else {
257 union = cloneMap(types2);
258 union.putAll(types1);
259 }
260 for(final Symbol symbol: commonSymbols) {
261 final LvarType type1 = types1.get(symbol);
262 final LvarType type2 = types2.get(symbol);
263 union.put(symbol, widestLvarType(type1, type2));
264 }
265 return union;
266 }
267
268 private static void symbolIsUsed(final Symbol symbol, final LvarType type) {
269 if(type != LvarType.UNDEFINED) {
270 symbol.setHasSlotFor(type.type);
271 }
272 }
273
274 private static class SymbolConversions {
275 private static final byte I2L = 1 << 0;
276 private static final byte I2D = 1 << 1;
277 private static final byte I2O = 1 << 2;
278 private static final byte L2D = 1 << 3;
279 private static final byte L2O = 1 << 4;
280 private static final byte D2O = 1 << 5;
281
282 private byte conversions;
283
284 void recordConversion(final LvarType from, final LvarType to) {
285 switch (from) {
286 case UNDEFINED:
287 return;
288 case INT:
289 case BOOLEAN:
290 switch (to) {
291 case LONG:
292 recordConversion(I2L);
293 return;
294 case DOUBLE:
295 recordConversion(I2D);
296 return;
297 case OBJECT:
298 recordConversion(I2O);
299 return;
300 default:
301 illegalConversion(from, to);
302 return;
303 }
304 case LONG:
305 switch (to) {
306 case DOUBLE:
307 recordConversion(L2D);
308 return;
309 case OBJECT:
310 recordConversion(L2O);
311 return;
312 default:
313 illegalConversion(from, to);
314 return;
315 }
316 case DOUBLE:
317 if(to == LvarType.OBJECT) {
318 recordConversion(D2O);
319 }
320 return;
321 default:
322 illegalConversion(from, to);
323 }
324 }
325
326 private static void illegalConversion(final LvarType from, final LvarType to) {
327 throw new AssertionError("Invalid conversion from " + from + " to " + to);
328 }
329
330 void recordConversion(final byte convFlag) {
331 conversions = (byte)(conversions | convFlag);
332 }
333
334 boolean hasConversion(final byte convFlag) {
335 return (conversions & convFlag) != 0;
336 }
337
338 void calculateTypeLiveness(final Symbol symbol) {
339 if(symbol.hasSlotFor(Type.OBJECT)) {
340 if(hasConversion(D2O)) {
341 symbol.setHasSlotFor(Type.NUMBER);
342 }
343 if(hasConversion(L2O)) {
344 symbol.setHasSlotFor(Type.LONG);
345 }
346 if(hasConversion(I2O)) {
347 symbol.setHasSlotFor(Type.INT);
348 }
349 }
350 if(symbol.hasSlotFor(Type.NUMBER)) {
351 if(hasConversion(L2D)) {
352 symbol.setHasSlotFor(Type.LONG);
353 }
354 if(hasConversion(I2D)) {
355 symbol.setHasSlotFor(Type.INT);
356 }
357 }
358 if(symbol.hasSlotFor(Type.LONG)) {
359 if(hasConversion(I2L)) {
360 symbol.setHasSlotFor(Type.INT);
361 }
362 }
363 }
364 }
365
366 private void symbolIsConverted(final Symbol symbol, final LvarType from, final LvarType to) {
367 SymbolConversions conversions = symbolConversions.get(symbol);
368 if(conversions == null) {
369 conversions = new SymbolConversions();
370 symbolConversions.put(symbol, conversions);
371 }
372 conversions.recordConversion(from, to);
373 }
374
375 private static LvarType toLvarType(final Type type) {
376 assert type != null;
377 final LvarType lvarType = TO_LVAR_TYPE.get(type);
378 if(lvarType != null) {
379 return lvarType;
380 }
381 assert type.isObject();
382 return LvarType.OBJECT;
383 }
384 private static LvarType widestLvarType(final LvarType t1, final LvarType t2) {
385 if(t1 == t2) {
386 return t1;
387 }
388 // Undefined or boolean to anything always widens to object.
389 if(t1.ordinal() < LvarType.INT.ordinal() || t2.ordinal() < LvarType.INT.ordinal()) {
390 return LvarType.OBJECT;
391 }
392 // NOTE: we allow "widening" of long to double even though it can lose precision. ECMAScript doesn't have an
393 // Int64 type anyway, so this loss of precision is actually more conformant to the specification...
394 return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())];
395 }
396 private final Compiler compiler;
397 private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>();
398 // Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always
399 // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
400 // value.
401 private Map<Symbol, LvarType> localVariableTypes = new IdentityHashMap<>();
|
114
115 JumpOrigin(final JoinPredecessor node, final Map<Symbol, LvarType> types) {
116 this.node = node;
117 this.types = types;
118 }
119 }
120
121 private static class JumpTarget {
122 private final List<JumpOrigin> origins = new LinkedList<>();
123 private Map<Symbol, LvarType> types = Collections.emptyMap();
124
125 void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes) {
126 origins.add(new JumpOrigin(originNode, originTypes));
127 this.types = getUnionTypes(this.types, originTypes);
128 }
129 }
130 private enum LvarType {
131 UNDEFINED(Type.UNDEFINED),
132 BOOLEAN(Type.BOOLEAN),
133 INT(Type.INT),
134 DOUBLE(Type.NUMBER),
135 OBJECT(Type.OBJECT);
136
137 private final Type type;
138 private final TypeHolderExpression typeExpression;
139
140 private LvarType(final Type type) {
141 this.type = type;
142 this.typeExpression = new TypeHolderExpression(type);
143 }
144 }
145
146 /**
147 * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their
148 * types by creating temporary copies of them and replacing their operands with instances of these. An alternative
149 * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType)
150 * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in
151 * generation of higher number of temporary short lived nodes, though.
152 */
153 private static class TypeHolderExpression extends Expression {
254 union.putAll(types2);
255 } else {
256 union = cloneMap(types2);
257 union.putAll(types1);
258 }
259 for(final Symbol symbol: commonSymbols) {
260 final LvarType type1 = types1.get(symbol);
261 final LvarType type2 = types2.get(symbol);
262 union.put(symbol, widestLvarType(type1, type2));
263 }
264 return union;
265 }
266
267 private static void symbolIsUsed(final Symbol symbol, final LvarType type) {
268 if(type != LvarType.UNDEFINED) {
269 symbol.setHasSlotFor(type.type);
270 }
271 }
272
273 private static class SymbolConversions {
274 private static final byte I2D = 1 << 0;
275 private static final byte I2O = 1 << 1;
276 private static final byte D2O = 1 << 2;
277
278 private byte conversions;
279
280 void recordConversion(final LvarType from, final LvarType to) {
281 switch (from) {
282 case UNDEFINED:
283 return;
284 case INT:
285 case BOOLEAN:
286 switch (to) {
287 case DOUBLE:
288 recordConversion(I2D);
289 return;
290 case OBJECT:
291 recordConversion(I2O);
292 return;
293 default:
294 illegalConversion(from, to);
295 return;
296 }
297 case DOUBLE:
298 if(to == LvarType.OBJECT) {
299 recordConversion(D2O);
300 }
301 return;
302 default:
303 illegalConversion(from, to);
304 }
305 }
306
307 private static void illegalConversion(final LvarType from, final LvarType to) {
308 throw new AssertionError("Invalid conversion from " + from + " to " + to);
309 }
310
311 void recordConversion(final byte convFlag) {
312 conversions = (byte)(conversions | convFlag);
313 }
314
315 boolean hasConversion(final byte convFlag) {
316 return (conversions & convFlag) != 0;
317 }
318
319 void calculateTypeLiveness(final Symbol symbol) {
320 if(symbol.hasSlotFor(Type.OBJECT)) {
321 if(hasConversion(D2O)) {
322 symbol.setHasSlotFor(Type.NUMBER);
323 }
324 if(hasConversion(I2O)) {
325 symbol.setHasSlotFor(Type.INT);
326 }
327 }
328 if(symbol.hasSlotFor(Type.NUMBER)) {
329 if(hasConversion(I2D)) {
330 symbol.setHasSlotFor(Type.INT);
331 }
332 }
333 }
334 }
335
336 private void symbolIsConverted(final Symbol symbol, final LvarType from, final LvarType to) {
337 SymbolConversions conversions = symbolConversions.get(symbol);
338 if(conversions == null) {
339 conversions = new SymbolConversions();
340 symbolConversions.put(symbol, conversions);
341 }
342 conversions.recordConversion(from, to);
343 }
344
345 private static LvarType toLvarType(final Type type) {
346 assert type != null;
347 final LvarType lvarType = TO_LVAR_TYPE.get(type);
348 if(lvarType != null) {
349 return lvarType;
350 }
351 assert type.isObject() : "Unsupported primitive type: " + type;
352 return LvarType.OBJECT;
353 }
354 private static LvarType widestLvarType(final LvarType t1, final LvarType t2) {
355 if(t1 == t2) {
356 return t1;
357 }
358 // Undefined or boolean to anything always widens to object.
359 if(t1.ordinal() < LvarType.INT.ordinal() || t2.ordinal() < LvarType.INT.ordinal()) {
360 return LvarType.OBJECT;
361 }
362 // NOTE: we allow "widening" of long to double even though it can lose precision. ECMAScript doesn't have an
363 // Int64 type anyway, so this loss of precision is actually more conformant to the specification...
364 return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())];
365 }
366 private final Compiler compiler;
367 private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>();
368 // Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always
369 // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
370 // value.
371 private Map<Symbol, LvarType> localVariableTypes = new IdentityHashMap<>();
|