8193508: Expressions in split literals must never be optimistic

Reviewed-by: jlaskey, sundar
This commit is contained in:
Hannes Wallnöfer 2017-12-20 21:40:11 +01:00
parent fc13efa1f3
commit a37fb7b942
8 changed files with 150 additions and 26 deletions

View File

@ -1279,7 +1279,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
boolean useOptimisticTypes() {
return !lc.inSplitNode() && compiler.useOptimisticTypes();
return !lc.inSplitLiteral() && compiler.useOptimisticTypes();
}
@Override
@ -2917,12 +2917,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit().
final int literalSlot = fixScopeSlot(currentFunction, 3);
lc.enterSplitNode();
lc.enterSplitLiteral();
creator.populateRange(method, literalType, literalSlot, splitRange.getLow(), splitRange.getHigh());
method._return();
lc.exitSplitNode();
lc.exitSplitLiteral();
method.end();
lc.releaseSlots();
popMethodEmitter();
@ -4654,10 +4654,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
this.optimistic = optimistic;
this.expression = (Expression)optimistic;
this.resultBounds = resultBounds;
this.isOptimistic = isOptimistic(optimistic) && useOptimisticTypes() &&
this.isOptimistic = isOptimistic(optimistic)
// Operation is only effectively optimistic if its type, after being coerced into the result bounds
// is narrower than the upper bound.
resultBounds.within(Type.generic(((Expression)optimistic).getType())).narrowerThan(resultBounds.widest);
&& resultBounds.within(Type.generic(((Expression)optimistic).getType())).narrowerThan(resultBounds.widest);
// Optimistic operations need to be executed in optimistic context, else unwarranted optimism will go unnoticed
assert !this.isOptimistic || useOptimisticTypes();
}
MethodEmitter emit() {

View File

@ -68,7 +68,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
private final IntDeque splitNodes = new IntDeque();
private final IntDeque splitLiterals = new IntDeque();
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
* currently on the lexical context stack. */
@ -89,18 +89,18 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
if (((FunctionNode)node).inDynamicContext()) {
dynamicScopeCount++;
}
splitNodes.push(0);
splitLiterals.push(0);
}
return super.push(node);
}
void enterSplitNode() {
splitNodes.getAndIncrement();
void enterSplitLiteral() {
splitLiterals.getAndIncrement();
pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
}
void exitSplitNode() {
final int count = splitNodes.decrementAndGet();
void exitSplitLiteral() {
final int count = splitLiterals.decrementAndGet();
assert count >= 0;
}
@ -115,8 +115,8 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
dynamicScopeCount--;
assert dynamicScopeCount >= 0;
}
assert splitNodes.peek() == 0;
splitNodes.pop();
assert splitLiterals.peek() == 0;
splitLiterals.pop();
}
return popped;
}
@ -125,8 +125,8 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
return dynamicScopeCount > 0;
}
boolean inSplitNode() {
return !splitNodes.isEmpty() && splitNodes.peek() > 0;
boolean inSplitLiteral() {
return !splitLiterals.isEmpty() && splitLiterals.peek() > 0;
}
MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {

View File

@ -42,8 +42,10 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.Symbol;
@ -189,6 +191,23 @@ final class OptimisticTypesCalculator extends SimpleNodeVisitor {
return true;
}
@Override
public boolean enterObjectNode(ObjectNode objectNode) {
if (objectNode.getSplitRanges() != null) {
return false;
}
return super.enterObjectNode(objectNode);
}
@Override
public boolean enterLiteralNode(LiteralNode<?> literalNode) {
if (literalNode.isArray() && ((LiteralNode.ArrayLiteralNode) literalNode).getSplitRanges() != null) {
return false;
}
return super.enterLiteralNode(literalNode);
}
@Override
public boolean enterWhileNode(final WhileNode whileNode) {
// Test is never optimistic (always coerced to boolean).

View File

@ -112,7 +112,7 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
assert lc.isEmpty() : "LexicalContext not empty";
if (weight >= SPLIT_THRESHOLD) {
log.info("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
log.info("Splitting function '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
functionNode = (FunctionNode)functionNode.accept(this);
if (functionNode.isSplit()) {
@ -287,7 +287,7 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
@SuppressWarnings("rawtypes")
@Override
public Node leaveLiteralNode(final LiteralNode literal) {
long weight = WeighNodes.weigh(literal);
final long weight = WeighNodes.weigh(literal);
if (weight < SPLIT_THRESHOLD) {
return literal;
@ -310,14 +310,14 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
final int postset = postsets[i];
final Node element = value[postset];
weight = WeighNodes.weigh(element);
totalWeight += WeighNodes.AASTORE_WEIGHT + weight;
final long elementWeight = WeighNodes.weigh(element);
totalWeight += WeighNodes.AASTORE_WEIGHT + elementWeight;
if (totalWeight >= SPLIT_THRESHOLD) {
final CompileUnit unit = compiler.findUnit(totalWeight - weight);
final CompileUnit unit = compiler.findUnit(totalWeight - elementWeight);
ranges.add(new Splittable.SplitRange(unit, lo, i));
lo = i;
totalWeight = weight;
totalWeight = elementWeight;
}
}
@ -326,6 +326,8 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
ranges.add(new Splittable.SplitRange(unit, lo, postsets.length));
}
log.info("Splitting array literal in '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
return arrayLiteralNode.setSplitRanges(lc, ranges);
}
@ -334,7 +336,7 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
long weight = WeighNodes.weigh(objectNode);
final long weight = WeighNodes.weigh(objectNode);
if (weight < SPLIT_THRESHOLD) {
return objectNode;
@ -355,14 +357,14 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
final boolean isConstant = LiteralNode.isConstant(property.getValue());
if (!isConstant || !isSpillObject) {
weight = isConstant ? 0 : WeighNodes.weigh(property.getValue());
totalWeight += WeighNodes.AASTORE_WEIGHT + weight;
final long propertyWeight = isConstant ? 0 : WeighNodes.weigh(property.getValue());
totalWeight += WeighNodes.AASTORE_WEIGHT + propertyWeight;
if (totalWeight >= SPLIT_THRESHOLD) {
final CompileUnit unit = compiler.findUnit(totalWeight - weight);
final CompileUnit unit = compiler.findUnit(totalWeight - propertyWeight);
ranges.add(new Splittable.SplitRange(unit, lo, i));
lo = i;
totalWeight = weight;
totalWeight = propertyWeight;
}
}
}
@ -372,6 +374,8 @@ final class Splitter extends SimpleNodeVisitor implements Loggable {
ranges.add(new Splittable.SplitRange(unit, lo, properties.size()));
}
log.info("Splitting object node in '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
return objectNode.setSplitRanges(lc, ranges);
}

View File

@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Scope;
@ -39,6 +40,7 @@ public final class GetSplitState extends Expression {
private static final long serialVersionUID = 1L;
/** The sole instance of this AST node. */
@Ignore
public final static GetSplitState INSTANCE = new GetSplitState();
private GetSplitState() {

View File

@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.objects.NativeArray;
@ -633,6 +634,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
private final int[] postsets;
/** Ranges for splitting up large literals in code generation */
@Ignore
private final List<Splittable.SplitRange> splitRanges;
/** Does this array literal have a spread element? */

View File

@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@ -43,6 +44,7 @@ public final class ObjectNode extends Expression implements LexicalContextNode,
private final List<PropertyNode> elements;
/** Ranges for splitting large literals over multiple compile units in codegen. */
@Ignore
private final List<Splittable.SplitRange> splitRanges;
/**

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8193508: Expressions in split literals must never be optimistic
*
* @test
* @run
* @option -Dnashorn.compiler.splitter.threshold=100
* @fork
*/
function f() {
return 'a';
}
var o = {
a: f(),
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: f(),
h: 1,
i: 2,
j: 3,
k: 4,
l: 5,
m: f(),
n: 1,
o: 2,
p: 3,
q: 4,
r: 5,
s: f(),
t: 1,
u: 2,
v: 3,
w: 4,
x: 5,
y: f(),
z: 1,
A: 2,
B: 3,
C: 4,
D: 5,
E: f(),
F: 1,
G: 2,
H: 3,
I: 4,
J: 5,
K: f(),
L: 1,
M: 2,
N: 3,
O: 4,
P: 5,
Q: f(),
R: 1,
S: 2,
T: 3,
U: 4,
V: 5,
W: f(),
X: 1,
Y: 2,
Z: 3
};
Assert.assertTrue(o.a === 'a');