Navigation Menu

Skip to content

Commit

Permalink
Merge branch 'master' into truffle-head
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Feb 8, 2015
2 parents 8330e22 + 290e3f1 commit fc3ac5e
Show file tree
Hide file tree
Showing 72 changed files with 1,974 additions and 1,503 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyFile.java
Expand Up @@ -586,7 +586,7 @@ public static IRubyObject chmod(ThreadContext context, IRubyObject recv, IRubyOb
return runtime.newFixnum(count);
}

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

Expand Down
18 changes: 14 additions & 4 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Expand Up @@ -1407,7 +1407,15 @@ public void setProfilingService( String service ) {
}

private static ClassLoader setupLoader() {
return RubyInstanceConfig.class.getClassLoader();
ClassLoader loader = RubyInstanceConfig.class.getClassLoader();

// loader can be null for example when jruby comes from the boot-classLoader

if (loader == null) {
loader = Thread.currentThread().getContextClassLoader();
}

return loader;
}

////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1454,9 +1462,11 @@ private static ClassLoader setupLoader() {
private ProfileOutput profileOutput = new ProfileOutput(System.err);
private String profilingService;

private ClassLoader thisLoader = setupLoader();
// thisLoader can be null for example when jruby comes from the boot-classLoader
private ClassLoader loader = thisLoader == null ? Thread.currentThread().getContextClassLoader() : thisLoader;
private ClassLoader loader = setupLoader();

public ClassLoader getCurrentThreadClassLoader() {
return Thread.currentThread().getContextClassLoader();
}

// from CommandlineParser
private List<String> loadPaths = new ArrayList<String>();
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Expand Up @@ -782,7 +782,7 @@ public Operand buildAnd(final AndNode andNode) {
public Operand buildArray(Node node) {
List<Operand> elts = new ArrayList<>();
for (Node e: node.childNodes())
elts.add(build(e));
elts.add(copyAndReturnValue(build(e)));

return copyAndReturnValue(new Array(elts));
}
Expand Down Expand Up @@ -2393,10 +2393,10 @@ public Operand buildHash(HashNode hashNode) {
splatKeywordArgument = build(pair.getValue());
break;
} else {
keyOperand = build(key);
keyOperand = copyAndReturnValue(build(key));
}

args.add(new KeyValuePair<>(keyOperand, build(pair.getValue())));
args.add(new KeyValuePair<Operand, Operand>(keyOperand, copyAndReturnValue(build(pair.getValue()))));
}

if (splatKeywordArgument != null) { // splat kwargs merge with any explicit kwargs
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/IRManager.java
Expand Up @@ -18,7 +18,7 @@
public class IRManager {
public static final String SAFE_COMPILER_PASSES = "";
public static final String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
public static final String DEFAULT_JIT_PASSES = "DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
public static final String DEFAULT_JIT_PASSES = "OptimizeDelegationPass,DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
public static final String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";

private int dummyMetaClassCount = 0;
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ir/IRScope.java
Expand Up @@ -566,6 +566,8 @@ private void optimizeSimpleScopes() {
// stymied by escaped bindings. We can also eliminate
// dynscopes for these scopes.
if (!isUnsafeScope() && !flags.contains(REQUIRES_DYNSCOPE)) {
if (flags.contains(RECEIVES_CLOSURE_ARG))
(new OptimizeDelegationPass()).run(this);
(new DeadCodeElimination()).run(this);
(new OptimizeDynScopesPass()).run(this);
}
Expand Down
100 changes: 100 additions & 0 deletions core/src/main/java/org/jruby/ir/passes/OptimizeDelegationPass.java
@@ -0,0 +1,100 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRFlags;
import org.jruby.ir.instructions.*;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;

import java.util.*;

public class OptimizeDelegationPass extends CompilerPass {
public static List<Class<? extends CompilerPass>> DEPENDENCIES = Arrays.<Class<? extends CompilerPass>>asList(CFGBuilder.class);

@Override
public String getLabel() {
return "Delegated Variable Removal";
}

@Override
public List<Class<? extends CompilerPass>> getDependencies() {
return DEPENDENCIES;
}

@Override
public Object execute(IRScope s, Object... data) {
for (IRClosure c: s.getClosures()) {
run(c, false, true);
}

s.computeScopeFlags();

if (s.getFlags().contains(IRFlags.BINDING_HAS_ESCAPED))
return null;

if (!s.getFlags().contains(IRFlags.RECEIVES_CLOSURE_ARG))
return null;

optimizeDelegatedVars(s);

return true;
}

@Override
public boolean invalidate(IRScope s) {
// Not reversible right now
return false;
}

private static void optimizeDelegatedVars(IRScope s) {
Map<Operand, Operand> unusedExplicitBlocks = new HashMap<Operand, Operand>();

for (BasicBlock bb: s.cfg().getBasicBlocks()) {
for (Instr i: bb.getInstrs()) {
if (i instanceof ReifyClosureInstr) {
ReifyClosureInstr ri = (ReifyClosureInstr) i;
unusedExplicitBlocks.put(ri.getResult(), ri.getSource());
} else {
Iterator<Operand> it = unusedExplicitBlocks.keySet().iterator();
while (it.hasNext()) {
Variable explicitBlock = (Variable) it.next();
if (usesVariableAsNonClosureArg(i, explicitBlock)) {
it.remove();
}
}
}
}
}

for (BasicBlock bb: s.cfg().getBasicBlocks()) {
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
while (instrs.hasNext()) {
Instr i = instrs.next();
if (i instanceof ReifyClosureInstr) {
ReifyClosureInstr ri = (ReifyClosureInstr) i;
Variable procVar = ri.getResult();
Operand blockVar = unusedExplicitBlocks.get(procVar);

if (blockVar != null) {
ri.markDead();
instrs.set(new CopyInstr(procVar, blockVar));
}
}
}
}
}

private static boolean usesVariableAsNonClosureArg(Instr i, Variable v) {
List<Variable> usedVariables = i.getUsedVariables();
if (usedVariables.contains(v)) {
if (i instanceof ClosureAcceptingInstr) {
return usedVariables.indexOf(v) != usedVariables.lastIndexOf(v) ||
v != ((ClosureAcceptingInstr) i).getClosureArg();
} else
return true;
}
return false;
}
}
61 changes: 43 additions & 18 deletions core/src/main/java/org/jruby/ir/passes/OptimizeTempVarsPass.java
Expand Up @@ -3,6 +3,7 @@
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.instructions.*;
import org.jruby.ir.operands.ImmutableLiteral;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.TemporaryVariable;
import org.jruby.ir.operands.Variable;
Expand Down Expand Up @@ -123,13 +124,26 @@ else if (use != NopInstr.NOP && def != null && def != NopInstr.NOP && i instance
if (!(use instanceof ReturnInstr)) {
CopyInstr ci = (CopyInstr)i;
Operand src = ci.getSource();
i.markDead();
instrs.remove();
// Only tmp vars are in SSA form post IR-building and it is safe to
// replace uses with defs without examining intervening instrs. But,
// not true for local vars and other operands that use local vars.
// a = 0
// %v_1 = a
// a = 1
// x = %v_1
// In that snippet, it would be buggy to rewrite it to:
// a = 0
// a = 1
// x = a
if (src instanceof TemporaryVariable || src instanceof ImmutableLiteral) {
i.markDead();
instrs.remove();

// Fix up use
Map<Operand, Operand> copyMap = new HashMap<>();
copyMap.put(v, src);
use.simplifyOperands(copyMap, true);
// Fix up use
Map<Operand, Operand> copyMap = new HashMap<>();
copyMap.put(v, src);
use.simplifyOperands(copyMap, true);
}
}
}
}
Expand All @@ -138,25 +152,36 @@ else if (use != NopInstr.NOP && def != null && def != NopInstr.NOP && i instance
// 2: x = %v
// If %v is not used anywhere else, the result of 1. can be updated to use x and 2. can be removed
//
// NOTE: consider this pattern:
// %v = <operand> (copy instr)
// x = %v
// This code will have been captured in the previous if branch which would have deleted %v = 5
// Hence the check for whether the src def instr is dead
// CAVEATS:
// --------
// 1. We only do this if 'x' is a temporary variable since only tmp vars are in SSA form.
// %v = ...(not a copy-1)
// x = .. (not a copy-2)
// x = %v
// In that snippet above, it would be buggy to replace it with:
// x = ...(not a copy-1)
// x = .. (not a copy-2)
//
// 2. Consider this pattern
// %v = <operand> (copy instr)
// x = %v
// This code will have been captured in the previous if branch which would have deleted %v = 5
// Hence the check for whether the src def instr is dead
else if (i instanceof CopyInstr) {
CopyInstr ci = (CopyInstr)i;
Operand src = ci.getSource();
if (src instanceof TemporaryVariable) {
TemporaryVariable vsrc = (TemporaryVariable)src;
Instr use = tmpVarUses.get(vsrc);
Instr def = tmpVarDefs.get(vsrc);
if ((use != null && use != NopInstr.NOP) && (def != null && def != NopInstr.NOP)) {
if (!def.isDead()) {
// Fix up def
((ResultInstr) def).updateResult(ci.getResult());
ci.markDead();
instrs.remove();
}
if (use != null && use != NopInstr.NOP &&
def != null && def != NopInstr.NOP &&
!def.isDead() && ((ResultInstr)def).getResult() instanceof TemporaryVariable)
{
// Fix up def
((ResultInstr) def).updateResult(ci.getResult());
ci.markDead();
instrs.remove();
}
}
}
Expand Down
1 change: 0 additions & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Expand Up @@ -138,7 +138,6 @@ public class Options {
public static final Option<Integer> TRUFFLE_HASHES_SMALL = integer(TRUFFLE, "truffle.hashes.small", 3, "Maximum size of a Hash to consider small for optimisations.");

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

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.");
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");
Expand Down
1 change: 1 addition & 0 deletions lib/ruby/truffle/mri/enumerator.rb
@@ -0,0 +1 @@
# Provided by default
9 changes: 9 additions & 0 deletions lib/ruby/truffle/shims/thread.rb
@@ -0,0 +1,9 @@
# Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1

# Empty thread file - everything is loaded by default at the moment
17 changes: 17 additions & 0 deletions spec/regression/GH-2574_dont_clobber_variable.rb
@@ -0,0 +1,17 @@
# https://github.com/jruby/jruby/issues/2574
describe 'Local variable assignments should not get clobbered' do
it 'returns the right value for array literals' do
a = 0
b = [a,a=1]
expect(b).to eq([0,1])
end

it 'returns the right value for hash literals' do
a = 0
b = { a => a, (a = 1) => a } # => { 1 => 1 } (MRI: {0=>0, 1=>1})
c = { a => a, a => (a = 2) } # => { 2 => 2 } (MRI: {1=>2})
expect(b).to eq({0=>0, 1=>1})
expect(c).to eq({1=>2})
end
end

4 changes: 0 additions & 4 deletions spec/truffle/tags/core/array/eql_tags.txt

This file was deleted.

4 changes: 0 additions & 4 deletions spec/truffle/tags/core/array/equal_value_tags.txt

This file was deleted.

23 changes: 0 additions & 23 deletions spec/truffle/tags/core/array/flatten_tags.txt
@@ -1,28 +1,5 @@
fails:Array#flatten returns a one-dimensional flattening recursively
fails:Array#flatten takes an optional argument that determines the level of recursion
fails:Array#flatten returns dup when the level of recursion is 0
fails:Array#flatten ignores negative levels
fails:Array#flatten tries to convert passed Objects to Integers using #to_int
fails:Array#flatten raises a TypeError when the passed Object can't be converted to an Integer
fails:Array#flatten does not call flatten on elements
fails:Array#flatten raises an ArgumentError on recursive arrays
fails:Array#flatten flattens any element which responds to #to_ary, using the return value of said method
fails:Array#flatten returns subclass instance for Array subclasses
fails:Array#flatten returns a tainted array if self is tainted
fails:Array#flatten returns an untrusted array if self is untrusted
fails:Array#flatten with a non-Array object in the Array ignores the return value of #to_ary if it is nil
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
fails:Array#flatten! modifies array to produce a one-dimensional flattening recursively
fails:Array#flatten! returns self if made some modifications
fails:Array#flatten! returns nil if no modifications took place
fails:Array#flatten! should not check modification by size
fails:Array#flatten! takes an optional argument that determines the level of recursion
fails:Array#flatten! returns nil when the level of recursion is 0
fails:Array#flatten! treats negative levels as no arguments
fails:Array#flatten! tries to convert passed Objects to Integers using #to_int
fails:Array#flatten! raises a TypeError when the passed Object can't be converted to an Integer
fails:Array#flatten! does not call flatten! on elements
fails:Array#flatten! raises an ArgumentError on recursive arrays
fails:Array#flatten! flattens any elements which responds to #to_ary, using the return value of said method
fails:Array#flatten! raises a RuntimeError on frozen arrays when the array is modified
fails:Array#flatten! raises a RuntimeError on frozen arrays when the array would not be modified
1 change: 1 addition & 0 deletions spec/truffle/tags/core/array/hash_tags.txt
Expand Up @@ -5,3 +5,4 @@ fails:Array#hash calls to_int on result of calling hash on each element
fails:Array#hash ignores array class differences
fails:Array#hash returns same hash code for arrays with the same content
fails:Array#hash returns the same value if arrays are #eql?
fails:Array#hash properly handles recursive arrays
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/array/intersection_tags.txt
@@ -1,4 +1,2 @@
fails:Array#& creates an array with no duplicates
fails:Array#& creates an array with elements in order they are first encountered
fails:Array#& properly handles recursive arrays
fails:Array#& determines equivalence between elements in the sense of eql?
6 changes: 1 addition & 5 deletions spec/truffle/tags/core/array/minus_tags.txt
@@ -1,5 +1 @@
fails:Array#- tries to convert the passed arguments to Arrays using #to_ary
fails:Array#- does not return subclass instance for Array subclasses
fails:Array#- does not call to_ary on array subclasses
fails:Array#- removes an item identified as equivalent via #hash and #eql?
fails:Array#- doesn't remove an item with the same hash but not #eql?
fails:Array#- properly handles recursive arrays
12 changes: 0 additions & 12 deletions spec/truffle/tags/core/array/permutation_tags.txt

This file was deleted.

13 changes: 0 additions & 13 deletions spec/truffle/tags/core/encoding/default_internal_tags.txt

This file was deleted.

0 comments on commit fc3ac5e

Please sign in to comment.