Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 829740f22fb3
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 855c25f86323
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on May 11, 2015

  1. Copy the full SHA
    09a004b View commit details
  2. Copy the full SHA
    855c25f View commit details
Original file line number Diff line number Diff line change
@@ -92,6 +92,10 @@ public boolean executeRepeating(VirtualFrame frame) {
}
}

@Override
public String toString() {
return condition.getEncapsulatingSourceSection().getShortDescription();
}
}

private static class DoWhileRepeatingNode extends WhileRepeatingBaseNode implements RepeatingNode {
Original file line number Diff line number Diff line change
@@ -10,10 +10,7 @@
package org.jruby.truffle.nodes.core.hash;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
@@ -129,7 +126,7 @@ public abstract static class GetIndexNode extends CoreMethodArrayArgumentsNode {
@Child private CallDispatchHeadNode eqlNode;
@Child private BasicObjectNodes.ReferenceEqualNode equalNode;
@Child private CallDispatchHeadNode callDefaultNode;
@Child private FindEntryNode findEntryNode;
@Child private LookupEntryNode lookupEntryNode;

private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();
private final BranchProfile notInHashProfile = BranchProfile.create();
@@ -143,7 +140,7 @@ public GetIndexNode(RubyContext context, SourceSection sourceSection) {
eqlNode = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
equalNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(context, sourceSection, null, null);
callDefaultNode = DispatchHeadNodeFactory.createMethodCall(context);
findEntryNode = new FindEntryNode(context, sourceSection);
lookupEntryNode = new LookupEntryNode(context, sourceSection);
}

public abstract Object executeGet(VirtualFrame frame, RubyHash hash, Object key);
@@ -198,10 +195,10 @@ public Object getPackedArray(VirtualFrame frame, RubyHash hash, Object key) {

@Specialization(guards = "isBucketsStorage(hash)")
public Object getBuckets(VirtualFrame frame, RubyHash hash, Object key) {
final HashSearchResult hashSearchResult = findEntryNode.search(frame, hash, key);
final HashLookupResult hashLookupResult = lookupEntryNode.lookup(frame, hash, key);

if (hashSearchResult.getEntry() != null) {
return hashSearchResult.getEntry().getValue();
if (hashLookupResult.getEntry() != null) {
return hashLookupResult.getEntry().getValue();
}

notInHashProfile.enter();
@@ -245,6 +242,7 @@ public abstract static class SetIndexNode extends CoreMethodArrayArgumentsNode {
@Child private HashNode hashNode;
@Child private CallDispatchHeadNode eqlNode;
@Child private BasicObjectNodes.ReferenceEqualNode equalNode;
@Child private LookupEntryNode lookupEntryNode;

private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();

@@ -329,13 +327,48 @@ public Object setPackedArray(VirtualFrame frame, RubyHash hash, RubyString key,
}
}

@Specialization(guards = {"isBucketsStorage(hash)", "!isRubyString(key)"})
public Object setBuckets(RubyHash hash, Object key, Object value) {
CompilerDirectives.transferToInterpreter();
// Can't be @Cached yet as we call from the RubyString specialisation
private final ConditionProfile foundProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile bucketCollisionProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile appendingProfile = ConditionProfile.createBinaryProfile();

@Specialization(guards = {"isBucketsStorage(hash)", "!isRubyString(key)"})
public Object setBuckets(VirtualFrame frame, RubyHash hash, Object key, Object value) {
assert HashOperations.verifyStore(hash);

HashOperations.verySlowSetInBuckets(hash, key, value, hash.isCompareByIdentity());
if (lookupEntryNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
lookupEntryNode = insert(new LookupEntryNode(getContext(), getEncapsulatingSourceSection()));
}

final HashLookupResult result = lookupEntryNode.lookup(frame, hash, key);

final Entry entry = result.getEntry();

if (foundProfile.profile(entry == null)) {
final Entry newEntry = new Entry(result.getHashed(), key, value);

if (bucketCollisionProfile.profile(result.getPreviousEntry() == null)) {
((Entry[]) hash.getStore())[result.getIndex()] = newEntry;
} else {
result.getPreviousEntry().setNextInLookup(newEntry);
}

final Entry lastInSequence = hash.getLastInSequence();

if (appendingProfile.profile(lastInSequence == null)) {
hash.setFirstInSequence(newEntry);
} else {
lastInSequence.setNextInSequence(newEntry);
newEntry.setPreviousInSequence(lastInSequence);
}

hash.setLastInSequence(newEntry);

hash.setSize(hash.getSize() + 1);
} else {
entry.setKeyValue(result.getHashed(), key, value);
}

assert HashOperations.verifyStore(hash);

@@ -345,9 +378,9 @@ public Object setBuckets(RubyHash hash, Object key, Object value) {
@Specialization(guards = "isBucketsStorage(hash)")
public Object setBuckets(VirtualFrame frame, RubyHash hash, RubyString key, Object value) {
if (hash.isCompareByIdentity()) {
return setBuckets(hash, key, value);
return setBuckets(frame, hash, key, value);
} else {
return setBuckets(hash, ruby(frame, "key.frozen? ? key : key.dup.freeze", "key", key), value);
return setBuckets(frame, hash, ruby(frame, "key.frozen? ? key : key.dup.freeze", "key", key), value);
}
}

@@ -431,14 +464,14 @@ public abstract static class DeleteNode extends CoreMethodArrayArgumentsNode {

@Child private HashNode hashNode;
@Child private CallDispatchHeadNode eqlNode;
@Child private FindEntryNode findEntryNode;
@Child private LookupEntryNode lookupEntryNode;
@Child private YieldDispatchHeadNode yieldNode;

public DeleteNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
hashNode = new HashNode(context, sourceSection);
eqlNode = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
findEntryNode = new FindEntryNode(context, sourceSection);
lookupEntryNode = new LookupEntryNode(context, sourceSection);
yieldNode = new YieldDispatchHeadNode(context);
}

@@ -489,17 +522,17 @@ public Object deletePackedArray(VirtualFrame frame, RubyHash hash, Object key, O
public Object delete(VirtualFrame frame, RubyHash hash, Object key, Object block) {
assert HashOperations.verifyStore(hash);

final HashSearchResult hashSearchResult = findEntryNode.search(frame, hash, key);
final HashLookupResult hashLookupResult = lookupEntryNode.lookup(frame, hash, key);

if (hashSearchResult.getEntry() == null) {
if (hashLookupResult.getEntry() == null) {
if (block == UndefinedPlaceholder.INSTANCE) {
return nil();
} else {
return yieldNode.dispatch(frame, (RubyProc) block, key);
}
}

final Entry entry = hashSearchResult.getEntry();
final Entry entry = hashLookupResult.getEntry();

// Remove from the sequence chain

@@ -519,10 +552,10 @@ public Object delete(VirtualFrame frame, RubyHash hash, Object key, Object block

// Remove from the lookup chain

if (hashSearchResult.getPreviousEntry() == null) {
((Entry[]) hash.getStore())[hashSearchResult.getIndex()] = entry.getNextInLookup();
if (hashLookupResult.getPreviousEntry() == null) {
((Entry[]) hash.getStore())[hashLookupResult.getIndex()] = entry.getNextInLookup();
} else {
hashSearchResult.getPreviousEntry().setNextInLookup(entry.getNextInLookup());
hashLookupResult.getPreviousEntry().setNextInLookup(entry.getNextInLookup());
}

hash.setSize(hash.getSize() - 1);
@@ -952,7 +985,7 @@ public RubyHash merge(VirtualFrame frame, RubyHash hash, RubyHash other, RubyPro
}

for (KeyValue keyValue : HashOperations.verySlowToKeyValues(other)) {
final HashSearchResult searchResult = HashOperations.verySlowFindBucket(merged, keyValue.getKey(), false);
final HashLookupResult searchResult = HashOperations.verySlowFindBucket(merged, keyValue.getKey(), false);

if (searchResult.getEntry() == null) {
HashOperations.verySlowSetInBuckets(merged, keyValue.getKey(), keyValue.getValue(), false);
Original file line number Diff line number Diff line change
@@ -21,24 +21,24 @@
import org.jruby.truffle.runtime.core.RubyHash;
import org.jruby.truffle.runtime.hash.BucketsStrategy;
import org.jruby.truffle.runtime.hash.Entry;
import org.jruby.truffle.runtime.hash.HashSearchResult;
import org.jruby.truffle.runtime.hash.HashLookupResult;

public class FindEntryNode extends RubyNode {
public class LookupEntryNode extends RubyNode {

@Child HashNode hashNode;
@Child CallDispatchHeadNode eqlNode;
@Child BasicObjectNodes.ReferenceEqualNode equalNode;

private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();

public FindEntryNode(RubyContext context, SourceSection sourceSection) {
public LookupEntryNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
hashNode = new HashNode(context, sourceSection);
eqlNode = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
equalNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(context, sourceSection, null, null);
}

public HashSearchResult search(VirtualFrame frame, RubyHash hash, Object key) {
public HashLookupResult lookup(VirtualFrame frame, RubyHash hash, Object key) {
final int hashed = hashNode.hash(frame, key);

final Entry[] entries = (Entry[]) hash.getStore();
@@ -50,19 +50,19 @@ public HashSearchResult search(VirtualFrame frame, RubyHash hash, Object key) {
while (entry != null) {
if (byIdentityProfile.profile(hash.isCompareByIdentity())) {
if (equalNode.executeReferenceEqual(frame, key, entry.getKey())) {
return new HashSearchResult(hashed, index, previousEntry, entry);
return new HashLookupResult(hashed, index, previousEntry, entry);
}
} else {
if (eqlNode.callBoolean(frame, key, "eql?", null, entry.getKey())) {
return new HashSearchResult(hashed, index, previousEntry, entry);
return new HashLookupResult(hashed, index, previousEntry, entry);
}
}

previousEntry = entry;
entry = entry.getNextInLookup();
}

return new HashSearchResult(hashed, index, previousEntry, null);
return new HashLookupResult(hashed, index, previousEntry, null);
}

@Override
Original file line number Diff line number Diff line change
@@ -54,6 +54,12 @@ public void setValue(Object value) {
this.value = value;
}

public void setKeyValue(int hashed, Object key, Object value) {
this.hashed = hashed;
this.key = key;
this.value = value;
}

public Entry getNextInLookup() {
return nextInLookup;
}
Original file line number Diff line number Diff line change
@@ -23,14 +23,14 @@
* previous entry will be the one in the entry chain before that one</li>
* </ul>
*/
public class HashSearchResult {
public class HashLookupResult {

private final int hashed;
private final int index;
private final Entry previousEntry;
private final Entry entry;

public HashSearchResult(int hashed, int index, Entry previousEntry, Entry entry) {
public HashLookupResult(int hashed, int index, Entry previousEntry, Entry entry) {
this.hashed = hashed;
this.index = index;
this.previousEntry = previousEntry;
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ public static List<KeyValue> verySlowToKeyValues(RubyHash hash) {
}

@CompilerDirectives.TruffleBoundary
public static HashSearchResult verySlowFindBucket(RubyHash hash, Object key, boolean byIdentity) {
public static HashLookupResult verySlowFindBucket(RubyHash hash, Object key, boolean byIdentity) {
final Object hashValue = DebugOperations.send(hash.getContext(), key, "hash", null);

final int hashed;
@@ -141,34 +141,34 @@ public static HashSearchResult verySlowFindBucket(RubyHash hash, Object key, boo
}

if ((boolean) DebugOperations.send(hash.getContext(), key, method, null, entry.getKey())) {
return new HashSearchResult(hashed, bucketIndex, previousEntry, entry);
return new HashLookupResult(hashed, bucketIndex, previousEntry, entry);
}

previousEntry = entry;
entry = entry.getNextInLookup();
}

return new HashSearchResult(hashed, bucketIndex, previousEntry, null);
return new HashLookupResult(hashed, bucketIndex, previousEntry, null);
}

@CompilerDirectives.TruffleBoundary
public static void verySlowSetAtBucket(RubyHash hash, HashSearchResult hashSearchResult, Object key, Object value) {
public static void verySlowSetAtBucket(RubyHash hash, HashLookupResult hashLookupResult, Object key, Object value) {
assert verifyStore(hash);

assert hash.getStore() instanceof Entry[];

if (hashSearchResult.getEntry() == null) {
BucketsStrategy.addNewEntry(hash, hashSearchResult.getHashed(), key, value);
if (hashLookupResult.getEntry() == null) {
BucketsStrategy.addNewEntry(hash, hashLookupResult.getHashed(), key, value);

assert verifyStore(hash);
} else {
final Entry entry = hashSearchResult.getEntry();
final Entry entry = hashLookupResult.getEntry();

// The bucket stays in the same place in the sequence

// Update the key (it overwrites even it it's eql?) and value

entry.setHashed(hashSearchResult.getHashed());
entry.setHashed(hashLookupResult.getHashed());
entry.setKey(key);
entry.setValue(value);

@@ -184,12 +184,12 @@ public static boolean verySlowSetInBuckets(RubyHash hash, Object key, Object val
key = DebugOperations.send(hash.getContext(), DebugOperations.send(hash.getContext(), key, "dup", null), "freeze", null);
}

final HashSearchResult hashSearchResult = verySlowFindBucket(hash, key, byIdentity);
verySlowSetAtBucket(hash, hashSearchResult, key, value);
final HashLookupResult hashLookupResult = verySlowFindBucket(hash, key, byIdentity);
verySlowSetAtBucket(hash, hashLookupResult, key, value);

assert verifyStore(hash);

return hashSearchResult.getEntry() == null;
return hashLookupResult.getEntry() == null;
}

@CompilerDirectives.TruffleBoundary
14 changes: 14 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/array.rb
Original file line number Diff line number Diff line change
@@ -907,6 +907,20 @@ def rotate!(cnt=1)
replace ary
end

class SampleRandom
def initialize(rng)
@rng = rng
end

def rand(size)
random = Rubinius::Type.coerce_to_collection_index @rng.rand(size)
raise RangeError, "random value must be >= 0" if random < 0
raise RangeError, "random value must be less than Array size" unless random < size

random
end
end

def sample(count=undefined, options=undefined)
return at Kernel.rand(size) if undefined.equal? count