Skip to content

Commit

Permalink
Showing 10 changed files with 127 additions and 59 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -246,7 +246,7 @@ public class Options {
public static final Option<Integer> TRUFFLE_PACK_CACHE = integer(TRUFFLE, "truffle.pack.cache", TRUFFLE_DEFAULT_CACHE.load(), "Array#pack cache size");
public static final Option<Integer> TRUFFLE_UNPACK_CACHE = integer(TRUFFLE, "truffle.unpack.cache", TRUFFLE_DEFAULT_CACHE.load(), "String#unpack cache size");
public static final Option<Integer> TRUFFLE_EVAL_CACHE = integer(TRUFFLE, "truffle.eval.cache", TRUFFLE_DEFAULT_CACHE.load(), "eval lookup cache size");
public static final Option<Integer> ENCODING_COMPATIBLE_QUERY_CACHE = integer(TRUFFLE, "truffle.encoding_compatible_query.cache", TRUFFLE_DEFAULT_CACHE.load(), "Encoding.compatible? cache size");
public static final Option<Integer> TRUFFLE_ENCODING_COMPATIBLE_QUERY_CACHE = integer(TRUFFLE, "truffle.encoding_compatible_query.cache", TRUFFLE_DEFAULT_CACHE.load(), "Encoding.compatible? cache size");

public static final Option<Boolean> TRUFFLE_CLONE_DEFAULT = bool(TRUFFLE, "truffle.clone.default", true, "Default option for cloning.");
public static final Option<Boolean> TRUFFLE_INLINE_DEFAULT = bool(TRUFFLE, "truffle.inline.default", true, "Default option for inlining.");
17 changes: 15 additions & 2 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -188,7 +188,7 @@ def mspec(command, *args)
args.unshift "-ttool/jruby_eclipse"
end

sh env_vars, 'ruby', 'spec/mspec/bin/mspec', command, '--config', 'spec/truffle/truffle.mspec', *args
sh env_vars, Utilities.find_jruby, 'spec/mspec/bin/mspec', command, '--config', 'spec/truffle/truffle.mspec', *args
end
end

@@ -230,7 +230,10 @@ def help
puts 'jt tag all spec/ruby/language tag all specs in this file, without running them'
puts 'jt untag spec/ruby/language untag passing specs in this directory'
puts 'jt untag spec/ruby/language/while_spec.rb untag passing specs in this file'
puts 'jt bench debug [--ruby-backtrace] [vm-args] benchmark run a single benchmark with options for compiler debugging'
puts 'jt bench debug [options] [vm-args] benchmark run a single benchmark with options for compiler debugging'
puts ' --igv make sure IGV is running and dump Graal graphs after partial escape (implies --graal)'
puts ' --full show all phases, not just up to the Truffle partial escape'
puts ' --ruby-backtrace print a Ruby backtrace on any compilation failures'
puts 'jt bench reference [benchmarks] run a set of benchmarks and record a reference point'
puts 'jt bench compare [benchmarks] run a set of benchmarks and compare against a reference point'
puts ' benchmarks can be any benchmarks or group of benchmarks supported'
@@ -483,6 +486,16 @@ def bench(command, *args)
case command
when 'debug'
vm_args = ['-G:+TraceTruffleCompilation', '-G:+DumpOnError']
if args.delete '--igv'
warn "warning: --igv might not work on master - if it does not, use truffle-head instead which builds against latest graal" if Utilities.git_branch == 'master'
Utilities.ensure_igv_running

if args.delete('--full')
vm_args.push '-G:Dump=Truffle'
else
vm_args.push '-G:Dump=TrufflePartialEscape'
end
end
if args.delete '--ruby-backtrace'
vm_args.push '-G:+TruffleCompilationExceptionsAreThrown'
else
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.utilities.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyGuards;
@@ -22,6 +23,8 @@
import org.jruby.truffle.nodes.objects.IsTaintedNodeGen;
import org.jruby.truffle.nodes.objects.TaintNode;
import org.jruby.truffle.nodes.objects.TaintNodeGen;
import org.jruby.truffle.nodes.rubinius.StringPrimitiveNodes;
import org.jruby.truffle.nodes.rubinius.StringPrimitiveNodesFactory;
import org.jruby.truffle.runtime.RubyContext;

/**
@@ -31,7 +34,7 @@ public final class InterpolatedStringNode extends RubyNode {

@Children private final ToSNode[] children;

@Child private CallDispatchHeadNode appendNode;
@Child private StringPrimitiveNodes.StringAppendPrimitiveNode appendNode;
@Child private CallDispatchHeadNode dupNode;
@Child private IsTaintedNode isTaintedNode;
@Child private TaintNode taintNode;
@@ -41,7 +44,7 @@ public final class InterpolatedStringNode extends RubyNode {
public InterpolatedStringNode(RubyContext context, SourceSection sourceSection, ToSNode[] children) {
super(context, sourceSection);
this.children = children;
appendNode = DispatchHeadNodeFactory.createMethodCall(context);
appendNode = StringPrimitiveNodesFactory.StringAppendPrimitiveNodeFactory.create(context, sourceSection, new RubyNode[] {});
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
isTaintedNode = IsTaintedNodeGen.create(context, sourceSection, null);
taintNode = TaintNodeGen.create(context, sourceSection, null);
@@ -72,16 +75,16 @@ public Object execute(VirtualFrame frame) {
private Object concat(VirtualFrame frame, Object[] strings) {
// TODO(CS): there is a lot of copying going on here - and I think this is sometimes inner loop stuff

Object builder = null;
DynamicObject builder = null;

// TODO (nirvdrum 11-Jan-16) Rewrite to avoid massively unbalanced trees.
for (Object string : strings) {
assert RubyGuards.isRubyString(string);

if (builder == null) {
builder = dupNode.call(frame, string, "dup", null);
builder = (DynamicObject) dupNode.call(frame, string, "dup", null);
} else {
builder = appendNode.call(frame, builder, "append", null, string);
builder = appendNode.executeStringAppend(frame, builder, (DynamicObject) string);
}
}

26 changes: 24 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.rope.AsciiOnlyLeafRope;
@@ -308,7 +309,25 @@ public LeafRope makeInvalidLeafRope(byte[] bytes, Encoding encoding, int codeRan
return new InvalidLeafRope(bytes, encoding);
}

@Specialization(guards = "isUnknown(codeRange)")
@Specialization(guards = { "isUnknown(codeRange)", "isBinaryString(encoding)" })
public LeafRope makeUnknownLeafRopeBinary(byte[] bytes, Encoding encoding, int codeRange,
@Cached("createBinaryProfile()") ConditionProfile discovered7BitProfile) {
int newCodeRange = StringSupport.CR_7BIT;
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] < 0) {
newCodeRange = StringSupport.CR_VALID;
break;
}
}

if (discovered7BitProfile.profile(newCodeRange == StringSupport.CR_7BIT)) {
return new AsciiOnlyLeafRope(bytes, encoding);
}

return new ValidLeafRope(bytes, encoding, bytes.length);
}

@Specialization(guards = { "isUnknown(codeRange)", "!isBinaryString(encoding)" })
public LeafRope makeUnknownLeafRope(byte[] bytes, Encoding encoding, int codeRange,
@Cached("createBinaryProfile()") ConditionProfile discovered7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile discoveredValidProfile) {
@@ -327,7 +346,6 @@ public LeafRope makeUnknownLeafRope(byte[] bytes, Encoding encoding, int codeRan
return new InvalidLeafRope(bytes, encoding);
}


protected static boolean is7Bit(int codeRange) {
return codeRange == StringSupport.CR_7BIT;
}
@@ -343,6 +361,10 @@ protected static boolean isBroken(int codeRange) {
protected static boolean isUnknown(int codeRange) {
return codeRange == StringSupport.CR_UNKNOWN;
}

protected static boolean isBinaryString(Encoding encoding) {
return encoding == ASCIIEncoding.INSTANCE;
}
}

@NodeChildren({
Original file line number Diff line number Diff line change
@@ -1332,12 +1332,14 @@ public InitializeCopyNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isRubyString(from)")
public Object initializeCopy(DynamicObject self, DynamicObject from) {
if (self == from) {
return self;
}
@Specialization(guards = "self == from")
public Object initializeCopySelfIsSameAsFrom(DynamicObject self, DynamicObject from) {
return self;
}


@Specialization(guards = { "self != from", "isRubyString(from)" })
public Object initializeCopy(DynamicObject self, DynamicObject from) {
StringOperations.setRope(self, rope(from));

return self;
Original file line number Diff line number Diff line change
@@ -498,9 +498,8 @@ public FlattenRopeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization(guards = "isRubyString(string)")
public DynamicObject debugPrint(DynamicObject string) {
public DynamicObject flattenRope(DynamicObject string) {
final Rope flattened = RopeOperations.flatten(StringOperations.rope(string));

return createString(flattened);
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@
import com.oracle.truffle.api.utilities.BranchProfile;
import com.oracle.truffle.api.utilities.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.format.parser.PackCompiler;
@@ -65,6 +66,7 @@
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.Memo;
import org.jruby.util.StringSupport;

import java.util.Arrays;

@@ -2398,10 +2400,12 @@ public CallTarget getCallTarget() {
@ImportStatic(StringCachingGuards.class)
public abstract static class PackNode extends ArrayCoreMethodNode {

@Child private RopeNodes.MakeLeafRopeNode makeLeafRopeNode;
@Child private TaintNode taintNode;

public PackNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
makeLeafRopeNode = RopeNodesFactory.MakeLeafRopeNodeGen.create(context, sourceSection, null, null, null);
}

@Specialization(guards = {"isRubyString(format)", "ropesEqual(format, cachedFormat)"}, limit = "getCacheLimit()")
@@ -2461,7 +2465,13 @@ private RuntimeException handleException(PackException exception) {
}

private DynamicObject finishPack(int formatLength, PackResult result) {
final DynamicObject string = createString(new ByteList((byte[]) result.getOutput(), 0, result.getOutputLength()));
byte[] bytes = (byte[]) result.getOutput();
if (bytes.length != result.getOutputLength()) {
bytes = Arrays.copyOf(bytes, result.getOutputLength());
}

final Rope rope = makeLeafRopeNode.executeMake(bytes, ASCIIEncoding.INSTANCE, StringSupport.CR_UNKNOWN);
final DynamicObject string = createString(rope);

if (formatLength == 0) {
StringOperations.forceEncoding(string, USASCIIEncoding.INSTANCE);
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ public class Options {
public final int PACK_CACHE = org.jruby.util.cli.Options.TRUFFLE_PACK_CACHE.load();
public final int UNPACK_CACHE = org.jruby.util.cli.Options.TRUFFLE_UNPACK_CACHE.load();
public final int EVAL_CACHE = org.jruby.util.cli.Options.TRUFFLE_EVAL_CACHE.load();
public final int ENCODING_COMPATIBILE_QUERY_CACHE = org.jruby.util.cli.Options.ENCODING_COMPATIBLE_QUERY_CACHE.load();
public final int ENCODING_COMPATIBILE_QUERY_CACHE = org.jruby.util.cli.Options.TRUFFLE_ENCODING_COMPATIBLE_QUERY_CACHE.load();

// Cloning and inlining

Original file line number Diff line number Diff line change
@@ -155,7 +155,6 @@ public static Encoding checkEncoding(DynamicObject string, CodeRangeable other)
}

public static void forceEncoding(DynamicObject string, Encoding encoding) {
modify(string);
final Rope oldRope = Layouts.STRING.getRope(string);
StringOperations.setRope(string, RopeOperations.withEncoding(oldRope, encoding, StringSupport.CR_UNKNOWN));
}
Original file line number Diff line number Diff line change
@@ -119,6 +119,8 @@ public static Encoding STR_ENC_GET(Rope rope) {
public static long calculateCodeRangeAndLength(Encoding encoding, byte[] bytes, int start, int end) {
if (bytes.length == 0) {
return StringSupport.pack(0, encoding.isAsciiCompatible() ? StringSupport.CR_7BIT : StringSupport.CR_VALID);
} else if (encoding == ASCIIEncoding.INSTANCE) {
return strLengthWithCodeRangeBinaryString(bytes, start, end);
} else if (encoding.isAsciiCompatible()) {
return StringSupport.strLengthWithCodeRangeAsciiCompatible(encoding, bytes, start, end);
} else {
@@ -131,6 +133,19 @@ public static int strLength(Encoding enc, byte[] bytes, int p, int end) {
return StringSupport.strLength(enc, bytes, p, end);
}

private static long strLengthWithCodeRangeBinaryString(byte[] bytes, int start, int end) {
int codeRange = StringSupport.CR_7BIT;

for (int i = start; i < end; i++) {
if (bytes[i] < 0) {
codeRange = StringSupport.CR_VALID;
break;
}
}

return StringSupport.pack(end - start, codeRange);
}

public static LeafRope flatten(Rope rope) {
if (rope instanceof LeafRope) {
return (LeafRope) rope;
@@ -139,7 +154,6 @@ public static LeafRope flatten(Rope rope) {
return create(flattenBytes(rope), rope.getEncoding(), rope.getCodeRange());
}

@TruffleBoundary
/**
* Performs an iterative depth first search of the Rope tree to calculate its byte[] without needing to populate
* the byte[] for each level beneath. Every LeafRope has its byte[] populated by definition. The goal is to determine
@@ -150,6 +164,7 @@ public static LeafRope flatten(Rope rope) {
* of stack frame management. Additionally, a recursive algorithm will eventually overflow the stack if the Rope
* tree is too deep.
*/
@TruffleBoundary
public static byte[] flattenBytes(Rope rope) {
if (rope instanceof LeafRope) {
return rope.getRawBytes();
@@ -179,6 +194,47 @@ public static byte[] flattenBytes(Rope rope) {
continue;
}

if (current.getRawBytes() != null) {
// In the absence of any SubstringRopes, we always take the full contents of the current rope.
if (substringLengths.isEmpty()) {
System.arraycopy(current.getRawBytes(), offset, buffer, bufferPosition, current.byteLength());
bufferPosition += current.byteLength();
} else {
int bytesToCopy = substringLengths.pop();
final int currentBytesToCopy;

// If we reach here, this rope is a descendant of a SubstringRope at some level. Based on
// the currently calculated byte[] offset and the number of bytes to extract, determine how many
// bytes we can copy to the buffer.
if (bytesToCopy > (current.byteLength() - offset)) {
currentBytesToCopy = current.byteLength() - offset;
} else {
currentBytesToCopy = bytesToCopy;
}

System.arraycopy(current.getRawBytes(), offset, buffer, bufferPosition, currentBytesToCopy);
bufferPosition += currentBytesToCopy;
bytesToCopy -= currentBytesToCopy;

// If this rope wasn't able to satisfy the remaining byte count from the ancestor SubstringRope,
// update the byte count for the next item in the work queue.
if (bytesToCopy > 0) {
substringLengths.push(bytesToCopy);
}
}

// By definition, offsets only affect the start of the rope. Once we've copied bytes out of a rope,
// we need to reset the offset or subsequent items in the work queue will copy from the wrong location.
//
// NB: In contrast to the number of bytes to extract, the offset can be shared and updated by multiple
// levels of SubstringRopes. Thus, we do not need to maintain offsets in a stack and it is appropriate
// to clear the offset after the first time we use it, since it will have been updated accordingly at
// each SubstringRope encountered for this SubstringRope ancestry chain.
offset = 0;

continue;
}

if (current instanceof ConcatRope) {
final ConcatRope concatRope = (ConcatRope) current;

@@ -210,7 +266,8 @@ public static byte[] flattenBytes(Rope rope) {
final SubstringRope substringRope = (SubstringRope) current;

// If this SubstringRope is a descendant of another SubstringRope, we need to increment the offset
// so that when we finally reach a LeafRope, we're extracting bytes from the correct location.
// so that when we finally reach a rope with its byte[] filled, we're extracting bytes from the correct
// location.
offset += substringRope.getOffset();

workStack.push(substringRope.getChild());
@@ -248,43 +305,6 @@ public static byte[] flattenBytes(Rope rope) {
substringLengths.push(adjustedByteLength);
}
}
} else if (current instanceof LeafRope) {
// In the absence of any SubstringRopes, we always take the full contents of the LeafRope.
if (substringLengths.isEmpty()) {
System.arraycopy(current.getRawBytes(), offset, buffer, bufferPosition, current.byteLength());
bufferPosition += current.byteLength();
} else {
int bytesToCopy = substringLengths.pop();
final int currentBytesToCopy;

// If we reach here, this LeafRope is a descendant of a SubstringRope at some level. Based on
// the currently calculated byte[] offset and the number of bytes to extract, determine how many
// bytes we can copy to the buffer.
if (bytesToCopy > (current.byteLength() - offset)) {
currentBytesToCopy = current.byteLength() - offset;
} else {
currentBytesToCopy = bytesToCopy;
}

System.arraycopy(current.getRawBytes(), offset, buffer, bufferPosition, currentBytesToCopy);
bufferPosition += currentBytesToCopy;
bytesToCopy -= currentBytesToCopy;

// If this LeafRope wasn't able to satisfy the remaining byte count from the ancestor SubstringRope,
// update the byte count for the next item in the work queue.
if (bytesToCopy > 0) {
substringLengths.push(bytesToCopy);
}
}

// By definition, offsets only affect the start of the rope. Once we've copied bytes out of a LeafRope,
// we need to reset the offset or subsequent items in the work queue will copy from the wrong location.
//
// NB: In contrast to the number of bytes to extract, the offset can be shared and updated by multiple
// levels of SubstringRopes. Thus, we do not need to maintain offsets in a stack and it is appropriate
// to clear the offset after the first time we use it, since it will have been updated accordingly at
// each SubstringRope encountered for this SubstringRope ancestry chain.
offset = 0;
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException("Don't know how to flatten rope of type: " + rope.getClass().getName());

0 comments on commit f3460a2

Please sign in to comment.