Skip to content

Commit fc3ac5e

Browse files
committed
Merge branch 'master' into truffle-head
2 parents 8330e22 + 290e3f1 commit fc3ac5e

File tree

72 files changed

+1974
-1503
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1974
-1503
lines changed

core/src/main/java/org/jruby/RubyFile.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ public static IRubyObject chmod(ThreadContext context, IRubyObject recv, IRubyOb
586586
return runtime.newFixnum(count);
587587
}
588588

589-
@JRubyMethod(required = 3, rest = true, meta = true)
589+
@JRubyMethod(required = 2, rest = true, meta = true)
590590
public static IRubyObject chown(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
591591
Ruby runtime = context.runtime;
592592

core/src/main/java/org/jruby/RubyInstanceConfig.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,15 @@ public void setProfilingService( String service ) {
14071407
}
14081408

14091409
private static ClassLoader setupLoader() {
1410-
return RubyInstanceConfig.class.getClassLoader();
1410+
ClassLoader loader = RubyInstanceConfig.class.getClassLoader();
1411+
1412+
// loader can be null for example when jruby comes from the boot-classLoader
1413+
1414+
if (loader == null) {
1415+
loader = Thread.currentThread().getContextClassLoader();
1416+
}
1417+
1418+
return loader;
14111419
}
14121420

14131421
////////////////////////////////////////////////////////////////////////////
@@ -1454,9 +1462,11 @@ private static ClassLoader setupLoader() {
14541462
private ProfileOutput profileOutput = new ProfileOutput(System.err);
14551463
private String profilingService;
14561464

1457-
private ClassLoader thisLoader = setupLoader();
1458-
// thisLoader can be null for example when jruby comes from the boot-classLoader
1459-
private ClassLoader loader = thisLoader == null ? Thread.currentThread().getContextClassLoader() : thisLoader;
1465+
private ClassLoader loader = setupLoader();
1466+
1467+
public ClassLoader getCurrentThreadClassLoader() {
1468+
return Thread.currentThread().getContextClassLoader();
1469+
}
14601470

14611471
// from CommandlineParser
14621472
private List<String> loadPaths = new ArrayList<String>();

core/src/main/java/org/jruby/ir/IRBuilder.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ public Operand buildAnd(final AndNode andNode) {
782782
public Operand buildArray(Node node) {
783783
List<Operand> elts = new ArrayList<>();
784784
for (Node e: node.childNodes())
785-
elts.add(build(e));
785+
elts.add(copyAndReturnValue(build(e)));
786786

787787
return copyAndReturnValue(new Array(elts));
788788
}
@@ -2393,10 +2393,10 @@ public Operand buildHash(HashNode hashNode) {
23932393
splatKeywordArgument = build(pair.getValue());
23942394
break;
23952395
} else {
2396-
keyOperand = build(key);
2396+
keyOperand = copyAndReturnValue(build(key));
23972397
}
23982398

2399-
args.add(new KeyValuePair<>(keyOperand, build(pair.getValue())));
2399+
args.add(new KeyValuePair<Operand, Operand>(keyOperand, copyAndReturnValue(build(pair.getValue()))));
24002400
}
24012401

24022402
if (splatKeywordArgument != null) { // splat kwargs merge with any explicit kwargs

core/src/main/java/org/jruby/ir/IRManager.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
public class IRManager {
1919
public static final String SAFE_COMPILER_PASSES = "";
2020
public static final String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
21-
public static final String DEFAULT_JIT_PASSES = "DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
21+
public static final String DEFAULT_JIT_PASSES = "OptimizeDelegationPass,DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
2222
public static final String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";
2323

2424
private int dummyMetaClassCount = 0;

core/src/main/java/org/jruby/ir/IRScope.java

+2
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ private void optimizeSimpleScopes() {
566566
// stymied by escaped bindings. We can also eliminate
567567
// dynscopes for these scopes.
568568
if (!isUnsafeScope() && !flags.contains(REQUIRES_DYNSCOPE)) {
569+
if (flags.contains(RECEIVES_CLOSURE_ARG))
570+
(new OptimizeDelegationPass()).run(this);
569571
(new DeadCodeElimination()).run(this);
570572
(new OptimizeDynScopesPass()).run(this);
571573
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.jruby.ir.passes;
2+
3+
import org.jruby.ir.IRClosure;
4+
import org.jruby.ir.IRScope;
5+
import org.jruby.ir.IRFlags;
6+
import org.jruby.ir.instructions.*;
7+
import org.jruby.ir.operands.Operand;
8+
import org.jruby.ir.operands.Variable;
9+
import org.jruby.ir.representations.BasicBlock;
10+
11+
import java.util.*;
12+
13+
public class OptimizeDelegationPass extends CompilerPass {
14+
public static List<Class<? extends CompilerPass>> DEPENDENCIES = Arrays.<Class<? extends CompilerPass>>asList(CFGBuilder.class);
15+
16+
@Override
17+
public String getLabel() {
18+
return "Delegated Variable Removal";
19+
}
20+
21+
@Override
22+
public List<Class<? extends CompilerPass>> getDependencies() {
23+
return DEPENDENCIES;
24+
}
25+
26+
@Override
27+
public Object execute(IRScope s, Object... data) {
28+
for (IRClosure c: s.getClosures()) {
29+
run(c, false, true);
30+
}
31+
32+
s.computeScopeFlags();
33+
34+
if (s.getFlags().contains(IRFlags.BINDING_HAS_ESCAPED))
35+
return null;
36+
37+
if (!s.getFlags().contains(IRFlags.RECEIVES_CLOSURE_ARG))
38+
return null;
39+
40+
optimizeDelegatedVars(s);
41+
42+
return true;
43+
}
44+
45+
@Override
46+
public boolean invalidate(IRScope s) {
47+
// Not reversible right now
48+
return false;
49+
}
50+
51+
private static void optimizeDelegatedVars(IRScope s) {
52+
Map<Operand, Operand> unusedExplicitBlocks = new HashMap<Operand, Operand>();
53+
54+
for (BasicBlock bb: s.cfg().getBasicBlocks()) {
55+
for (Instr i: bb.getInstrs()) {
56+
if (i instanceof ReifyClosureInstr) {
57+
ReifyClosureInstr ri = (ReifyClosureInstr) i;
58+
unusedExplicitBlocks.put(ri.getResult(), ri.getSource());
59+
} else {
60+
Iterator<Operand> it = unusedExplicitBlocks.keySet().iterator();
61+
while (it.hasNext()) {
62+
Variable explicitBlock = (Variable) it.next();
63+
if (usesVariableAsNonClosureArg(i, explicitBlock)) {
64+
it.remove();
65+
}
66+
}
67+
}
68+
}
69+
}
70+
71+
for (BasicBlock bb: s.cfg().getBasicBlocks()) {
72+
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
73+
while (instrs.hasNext()) {
74+
Instr i = instrs.next();
75+
if (i instanceof ReifyClosureInstr) {
76+
ReifyClosureInstr ri = (ReifyClosureInstr) i;
77+
Variable procVar = ri.getResult();
78+
Operand blockVar = unusedExplicitBlocks.get(procVar);
79+
80+
if (blockVar != null) {
81+
ri.markDead();
82+
instrs.set(new CopyInstr(procVar, blockVar));
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
private static boolean usesVariableAsNonClosureArg(Instr i, Variable v) {
90+
List<Variable> usedVariables = i.getUsedVariables();
91+
if (usedVariables.contains(v)) {
92+
if (i instanceof ClosureAcceptingInstr) {
93+
return usedVariables.indexOf(v) != usedVariables.lastIndexOf(v) ||
94+
v != ((ClosureAcceptingInstr) i).getClosureArg();
95+
} else
96+
return true;
97+
}
98+
return false;
99+
}
100+
}

core/src/main/java/org/jruby/ir/passes/OptimizeTempVarsPass.java

+43-18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.jruby.ir.IRClosure;
44
import org.jruby.ir.IRScope;
55
import org.jruby.ir.instructions.*;
6+
import org.jruby.ir.operands.ImmutableLiteral;
67
import org.jruby.ir.operands.Operand;
78
import org.jruby.ir.operands.TemporaryVariable;
89
import org.jruby.ir.operands.Variable;
@@ -123,13 +124,26 @@ else if (use != NopInstr.NOP && def != null && def != NopInstr.NOP && i instance
123124
if (!(use instanceof ReturnInstr)) {
124125
CopyInstr ci = (CopyInstr)i;
125126
Operand src = ci.getSource();
126-
i.markDead();
127-
instrs.remove();
127+
// Only tmp vars are in SSA form post IR-building and it is safe to
128+
// replace uses with defs without examining intervening instrs. But,
129+
// not true for local vars and other operands that use local vars.
130+
// a = 0
131+
// %v_1 = a
132+
// a = 1
133+
// x = %v_1
134+
// In that snippet, it would be buggy to rewrite it to:
135+
// a = 0
136+
// a = 1
137+
// x = a
138+
if (src instanceof TemporaryVariable || src instanceof ImmutableLiteral) {
139+
i.markDead();
140+
instrs.remove();
128141

129-
// Fix up use
130-
Map<Operand, Operand> copyMap = new HashMap<>();
131-
copyMap.put(v, src);
132-
use.simplifyOperands(copyMap, true);
142+
// Fix up use
143+
Map<Operand, Operand> copyMap = new HashMap<>();
144+
copyMap.put(v, src);
145+
use.simplifyOperands(copyMap, true);
146+
}
133147
}
134148
}
135149
}
@@ -138,25 +152,36 @@ else if (use != NopInstr.NOP && def != null && def != NopInstr.NOP && i instance
138152
// 2: x = %v
139153
// If %v is not used anywhere else, the result of 1. can be updated to use x and 2. can be removed
140154
//
141-
// NOTE: consider this pattern:
142-
// %v = <operand> (copy instr)
143-
// x = %v
144-
// This code will have been captured in the previous if branch which would have deleted %v = 5
145-
// Hence the check for whether the src def instr is dead
155+
// CAVEATS:
156+
// --------
157+
// 1. We only do this if 'x' is a temporary variable since only tmp vars are in SSA form.
158+
// %v = ...(not a copy-1)
159+
// x = .. (not a copy-2)
160+
// x = %v
161+
// In that snippet above, it would be buggy to replace it with:
162+
// x = ...(not a copy-1)
163+
// x = .. (not a copy-2)
164+
//
165+
// 2. Consider this pattern
166+
// %v = <operand> (copy instr)
167+
// x = %v
168+
// This code will have been captured in the previous if branch which would have deleted %v = 5
169+
// Hence the check for whether the src def instr is dead
146170
else if (i instanceof CopyInstr) {
147171
CopyInstr ci = (CopyInstr)i;
148172
Operand src = ci.getSource();
149173
if (src instanceof TemporaryVariable) {
150174
TemporaryVariable vsrc = (TemporaryVariable)src;
151175
Instr use = tmpVarUses.get(vsrc);
152176
Instr def = tmpVarDefs.get(vsrc);
153-
if ((use != null && use != NopInstr.NOP) && (def != null && def != NopInstr.NOP)) {
154-
if (!def.isDead()) {
155-
// Fix up def
156-
((ResultInstr) def).updateResult(ci.getResult());
157-
ci.markDead();
158-
instrs.remove();
159-
}
177+
if (use != null && use != NopInstr.NOP &&
178+
def != null && def != NopInstr.NOP &&
179+
!def.isDead() && ((ResultInstr)def).getResult() instanceof TemporaryVariable)
180+
{
181+
// Fix up def
182+
((ResultInstr) def).updateResult(ci.getResult());
183+
ci.markDead();
184+
instrs.remove();
160185
}
161186
}
162187
}

core/src/main/java/org/jruby/util/cli/Options.java

-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ public class Options {
138138
public static final Option<Integer> TRUFFLE_HASHES_SMALL = integer(TRUFFLE, "truffle.hashes.small", 3, "Maximum size of a Hash to consider small for optimisations.");
139139

140140
public static final Option<Boolean> TRUFFLE_LOAD_CORE = bool(TRUFFLE, "truffle.load_core", true, "Load the Truffle core library.");
141-
public static final Option<Boolean> TRUFFLE_PROC_BINDING = bool(TRUFFLE, "truffle.proc.binding", true, "Enable Proc#binding.");
142141

143142
public static final Option<Integer> TRUFFLE_PASSALOT = integer(TRUFFLE, "truffle.passalot", 0, "Probabilty between 0 and 100 to randomly insert Thread.pass at a given line.");
144143
public static final Option<Integer> TRUFFLE_STACK_SERVER_PORT = integer(TRUFFLE, "truffle.stack_server_port", 0, "Port number to run an HTTP server on that returns stack traces");

lib/ruby/truffle/mri/enumerator.rb

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Provided by default

lib/ruby/truffle/shims/thread.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 1.0
6+
# GNU General Public License version 2
7+
# GNU Lesser General Public License version 2.1
8+
9+
# Empty thread file - everything is loaded by default at the moment
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# https://github.com/jruby/jruby/issues/2574
2+
describe 'Local variable assignments should not get clobbered' do
3+
it 'returns the right value for array literals' do
4+
a = 0
5+
b = [a,a=1]
6+
expect(b).to eq([0,1])
7+
end
8+
9+
it 'returns the right value for hash literals' do
10+
a = 0
11+
b = { a => a, (a = 1) => a } # => { 1 => 1 } (MRI: {0=>0, 1=>1})
12+
c = { a => a, a => (a = 2) } # => { 2 => 2 } (MRI: {1=>2})
13+
expect(b).to eq({0=>0, 1=>1})
14+
expect(c).to eq({1=>2})
15+
end
16+
end
17+

spec/truffle/tags/core/array/eql_tags.txt

-4
This file was deleted.

spec/truffle/tags/core/array/equal_value_tags.txt

-4
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,5 @@
1-
fails:Array#flatten returns a one-dimensional flattening recursively
2-
fails:Array#flatten takes an optional argument that determines the level of recursion
3-
fails:Array#flatten returns dup when the level of recursion is 0
4-
fails:Array#flatten ignores negative levels
5-
fails:Array#flatten tries to convert passed Objects to Integers using #to_int
6-
fails:Array#flatten raises a TypeError when the passed Object can't be converted to an Integer
7-
fails:Array#flatten does not call flatten on elements
8-
fails:Array#flatten raises an ArgumentError on recursive arrays
9-
fails:Array#flatten flattens any element which responds to #to_ary, using the return value of said method
101
fails:Array#flatten returns subclass instance for Array subclasses
112
fails:Array#flatten returns a tainted array if self is tainted
123
fails:Array#flatten returns an untrusted array if self is untrusted
13-
fails:Array#flatten with a non-Array object in the Array ignores the return value of #to_ary if it is nil
14-
fails:Array#flatten with a non-Array object in the Array raises a TypeError if the return value of #to_ary is not an Array
15-
fails:Array#flatten! modifies array to produce a one-dimensional flattening recursively
16-
fails:Array#flatten! returns self if made some modifications
17-
fails:Array#flatten! returns nil if no modifications took place
18-
fails:Array#flatten! should not check modification by size
19-
fails:Array#flatten! takes an optional argument that determines the level of recursion
20-
fails:Array#flatten! returns nil when the level of recursion is 0
21-
fails:Array#flatten! treats negative levels as no arguments
22-
fails:Array#flatten! tries to convert passed Objects to Integers using #to_int
23-
fails:Array#flatten! raises a TypeError when the passed Object can't be converted to an Integer
24-
fails:Array#flatten! does not call flatten! on elements
25-
fails:Array#flatten! raises an ArgumentError on recursive arrays
26-
fails:Array#flatten! flattens any elements which responds to #to_ary, using the return value of said method
274
fails:Array#flatten! raises a RuntimeError on frozen arrays when the array is modified
285
fails:Array#flatten! raises a RuntimeError on frozen arrays when the array would not be modified

spec/truffle/tags/core/array/hash_tags.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ fails:Array#hash calls to_int on result of calling hash on each element
55
fails:Array#hash ignores array class differences
66
fails:Array#hash returns same hash code for arrays with the same content
77
fails:Array#hash returns the same value if arrays are #eql?
8+
fails:Array#hash properly handles recursive arrays
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
fails:Array#& creates an array with no duplicates
2-
fails:Array#& creates an array with elements in order they are first encountered
32
fails:Array#& properly handles recursive arrays
4-
fails:Array#& determines equivalence between elements in the sense of eql?
+1-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
fails:Array#- tries to convert the passed arguments to Arrays using #to_ary
2-
fails:Array#- does not return subclass instance for Array subclasses
3-
fails:Array#- does not call to_ary on array subclasses
4-
fails:Array#- removes an item identified as equivalent via #hash and #eql?
5-
fails:Array#- doesn't remove an item with the same hash but not #eql?
1+
fails:Array#- properly handles recursive arrays

spec/truffle/tags/core/array/permutation_tags.txt

-12
This file was deleted.

spec/truffle/tags/core/encoding/default_internal_tags.txt

-13
This file was deleted.

0 commit comments

Comments
 (0)