Skip to content

Commit 314f8ae

Browse files
author
Nicolas Laurent
committed
[GR-23533] Refactor integer conversion nodes.
PullRequest: truffleruby/1613
2 parents 53f175e + f0ae824 commit 314f8ae

File tree

22 files changed

+384
-258
lines changed

22 files changed

+384
-258
lines changed

spec/mspec/lib/mspec/helpers/numeric.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ def bignum_value(plus = 0)
1212
0x8000_0000_0000_0000 + plus
1313
end
1414

15+
def max_long
16+
2**(0.size * 8 - 1) - 1
17+
end
18+
19+
def min_long
20+
-(2**(0.size * 8 - 1))
21+
end
22+
1523
# This is a bit hairy, but we need to be able to write specs that cover the
1624
# boundary between Fixnum and Bignum for operations like Fixnum#<<. Since
1725
# this boundary is implementation-dependent, we use these helpers to write

spec/ruby/core/array/shared/slice.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,12 @@ def to.to_int() -2 end
466466

467467
obj = 8e19
468468
-> { array.send(@method, obj) }.should raise_error(RangeError)
469+
470+
# boundary value when longs are 64 bits
471+
-> { array.send(@method, 2.0**63) }.should raise_error(RangeError)
472+
473+
# just under the boundary value when longs are 64 bits
474+
array.send(@method, max_long.to_f.prev_float).should == nil
469475
end
470476

471477
it "raises a RangeError when the length is out of range of Fixnum" do

spec/ruby/core/integer/left_shift_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@
7171
it "calls #to_int to convert the argument to an Integer" do
7272
obj = mock("4")
7373
obj.should_receive(:to_int).and_return(4)
74-
7574
(3 << obj).should == 48
75+
76+
obj = mock("to_int_neg_bignum")
77+
obj.should_receive(:to_int).and_return(-bignum_value)
78+
(3 << obj).should == 0
7679
end
7780

7881
it "raises a TypeError when #to_int does not return an Integer" do

spec/ruby/core/integer/right_shift_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@
7171
it "calls #to_int to convert the argument to an Integer" do
7272
obj = mock("2")
7373
obj.should_receive(:to_int).and_return(2)
74-
7574
(8 >> obj).should == 2
75+
76+
obj = mock("to_int_bignum")
77+
obj.should_receive(:to_int).and_return(bignum_value)
78+
(8 >> obj).should == 0
7679
end
7780

7881
it "raises a TypeError when #to_int does not return an Integer" do

spec/ruby/shared/string/times.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ class MyString < String; end
2020
it "raises an ArgumentError when given integer is negative" do
2121
-> { @object.call("cool", -3) }.should raise_error(ArgumentError)
2222
-> { @object.call("cool", -3.14) }.should raise_error(ArgumentError)
23+
-> { @object.call("cool", min_long) }.should raise_error(ArgumentError)
2324
end
2425

2526
it "raises a RangeError when given integer is a Bignum" do
2627
-> { @object.call("cool", 999999999999999999999) }.should raise_error(RangeError)
28+
-> { @object.call("", 999999999999999999999) }.should raise_error(RangeError)
29+
end
30+
31+
it "works with huge long values when string is empty" do
32+
@object.call("", max_long).should == ""
2733
end
2834

2935
it "returns subclass instances" do

src/main/java/org/truffleruby/core/array/ArrayNodes.java

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
import org.truffleruby.core.array.library.NativeArrayStorage;
3434
import org.truffleruby.core.cast.BooleanCastNode;
3535
import org.truffleruby.core.cast.CmpIntNode;
36+
import org.truffleruby.core.cast.ToIntNode;
37+
import org.truffleruby.core.cast.ToLongNode;
3638
import org.truffleruby.core.cast.ToAryNode;
3739
import org.truffleruby.core.cast.ToAryNodeGen;
38-
import org.truffleruby.core.cast.ToIntNode;
39-
import org.truffleruby.core.cast.ToIntNodeGen;
4040
import org.truffleruby.core.cast.ToStrNodeGen;
4141
import org.truffleruby.core.format.BytesResult;
4242
import org.truffleruby.core.format.FormatExceptionTranslator;
@@ -281,7 +281,7 @@ public abstract static class AtNode extends CoreMethodNode {
281281

282282
@CreateCast("index")
283283
protected RubyNode coerceOtherToInt(RubyNode index) {
284-
return FixnumLowerNodeGen.create(ToIntNodeGen.create(index));
284+
return FixnumLowerNodeGen.create(ToIntNode.create(index));
285285
}
286286

287287
@Specialization
@@ -569,7 +569,7 @@ public abstract static class DeleteAtNode extends CoreMethodNode {
569569

570570
@CreateCast("index")
571571
protected RubyNode coerceOtherToInt(RubyNode index) {
572-
return ToIntNodeGen.create(index);
572+
return ToIntNode.create(index);
573573
}
574574

575575
@Specialization(
@@ -835,39 +835,25 @@ public abstract static class HashNode extends ArrayCoreMethodNode {
835835

836836
private static final int CLASS_SALT = 42753062; // random number, stops hashes for similar values but different classes being the same, static because we want deterministic hashes
837837

838-
@Child private ToIntNode toIntNode;
839-
840838
@Specialization(limit = "storageStrategyLimit()")
841839
protected long hash(VirtualFrame frame, DynamicObject array,
842840
@CachedLibrary("getStore(array)") ArrayStoreLibrary stores,
843-
@Cached("createPrivate()") CallDispatchHeadNode toHashNode) {
841+
@Cached("createPrivate()") CallDispatchHeadNode toHashNode,
842+
@Cached ToLongNode toLongNode) {
844843
final int size = Layouts.ARRAY.getSize(array);
845844
long h = getContext().getHashing(this).start(size);
846845
h = Hashing.update(h, CLASS_SALT);
847846
final Object store = Layouts.ARRAY.getStore(array);
848847

849848
for (int n = 0; n < size; n++) {
850849
final Object value = stores.read(store, n);
851-
final long valueHash = toLong(toHashNode.call(value, "hash"));
850+
final long valueHash = toLongNode.execute(toHashNode.call(value, "hash"));
852851
h = Hashing.update(h, valueHash);
853852
}
854853

855854
return Hashing.end(h);
856855
}
857856

858-
private long toLong(Object indexObject) {
859-
if (toIntNode == null) {
860-
CompilerDirectives.transferToInterpreterAndInvalidate();
861-
toIntNode = insert(ToIntNode.create());
862-
}
863-
final Object result = toIntNode.executeIntOrLong(indexObject);
864-
if (result instanceof Integer) {
865-
return (int) result;
866-
} else {
867-
return (long) result;
868-
}
869-
}
870-
871857
}
872858

873859
@CoreMethod(names = "include?", required = 1)
@@ -1091,7 +1077,7 @@ protected int toInt(Object value) {
10911077
CompilerDirectives.transferToInterpreterAndInvalidate();
10921078
toIntNode = insert(ToIntNode.create());
10931079
}
1094-
return toIntNode.doInt(value);
1080+
return toIntNode.execute(value);
10951081
}
10961082

10971083
}
@@ -1437,19 +1423,12 @@ protected int getCacheLimit() {
14371423
@ReportPolymorphism
14381424
public abstract static class PopNode extends ArrayCoreMethodNode {
14391425

1440-
@Child private ToIntNode toIntNode;
1441-
@Child private ArrayPopOneNode popOneNode;
1442-
14431426
public abstract Object executePop(DynamicObject array, Object n);
14441427

14451428
@Specialization
14461429
@ReportPolymorphism.Exclude
1447-
protected Object pop(DynamicObject array, NotProvided n) {
1448-
if (popOneNode == null) {
1449-
CompilerDirectives.transferToInterpreterAndInvalidate();
1450-
popOneNode = insert(ArrayPopOneNodeGen.create());
1451-
}
1452-
1430+
protected Object pop(DynamicObject array, NotProvided n,
1431+
@Cached ArrayPopOneNode popOneNode) {
14531432
return popOneNode.executePopOne(array);
14541433
}
14551434

@@ -1513,16 +1492,9 @@ protected Object popNotEmptyUnsharedStorage(DynamicObject array, int n,
15131492
}
15141493

15151494
@Specialization(guards = { "wasProvided(n)", "!isInteger(n)", "!isLong(n)" })
1516-
protected Object popNToInt(DynamicObject array, Object n) {
1517-
return executePop(array, toInt(n));
1518-
}
1519-
1520-
private int toInt(Object indexObject) {
1521-
if (toIntNode == null) {
1522-
CompilerDirectives.transferToInterpreterAndInvalidate();
1523-
toIntNode = insert(ToIntNode.create());
1524-
}
1525-
return toIntNode.doInt(indexObject);
1495+
protected Object popNToInt(DynamicObject array, Object n,
1496+
@Cached ToIntNode toIntNode) {
1497+
return executePop(array, toIntNode.execute(n));
15261498
}
15271499

15281500
}
@@ -1926,16 +1898,9 @@ protected Object shiftMany(DynamicObject array, int n,
19261898
}
19271899

19281900
@Specialization(guards = { "wasProvided(n)", "!isInteger(n)", "!isLong(n)" })
1929-
protected Object shiftNToInt(DynamicObject array, Object n) {
1930-
return executeShift(array, toInt(n));
1931-
}
1932-
1933-
private int toInt(Object indexObject) {
1934-
if (toIntNode == null) {
1935-
CompilerDirectives.transferToInterpreterAndInvalidate();
1936-
toIntNode = insert(ToIntNode.create());
1937-
}
1938-
return toIntNode.doInt(indexObject);
1901+
protected Object shiftNToInt(DynamicObject array, Object n,
1902+
@Cached ToIntNode toIntNode) {
1903+
return executeShift(array, toIntNode.execute(n));
19391904
}
19401905

19411906
}

src/main/java/org/truffleruby/core/cast/IntegerCastNode.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
import org.truffleruby.language.RubyGuards;
1616
import org.truffleruby.language.control.RaiseException;
1717

18-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1918
import com.oracle.truffle.api.dsl.CachedContext;
2019
import com.oracle.truffle.api.dsl.GenerateUncached;
2120
import com.oracle.truffle.api.dsl.ImportStatic;
2221
import com.oracle.truffle.api.dsl.Specialization;
23-
import com.oracle.truffle.api.object.DynamicObject;
22+
import org.truffleruby.utils.Utils;
2423

25-
/** Casts a value into an int. */
24+
/** See {@link ToIntNode} for a comparison of different integer conversion nodes. */
2625
@GenerateUncached
2726
@ImportStatic(RubyGuards.class)
2827
public abstract class IntegerCastNode extends RubyBaseNode {
@@ -46,18 +45,16 @@ protected int doLong(long value) {
4645
@Specialization(guards = "!fitsInInteger(value)")
4746
protected int doLongToBig(long value,
4847
@CachedContext(RubyLanguage.class) RubyContext context) {
49-
throw new RaiseException(context, notAFixnum(context, value));
48+
throw new RaiseException(
49+
context,
50+
context.getCoreExceptions().rangeError("long too big to convert into `int'", this));
5051
}
5152

52-
@Specialization(guards = { "!isBasicInteger(value)" })
53+
@Specialization(guards = "!isBasicInteger(value)")
5354
protected int doBasicObject(Object value,
5455
@CachedContext(RubyLanguage.class) RubyContext context) {
55-
throw new RaiseException(context, notAFixnum(context, value));
56+
throw new RaiseException(
57+
context,
58+
context.getCoreExceptions().typeErrorIsNotA(Utils.toString(value), "Integer (fitting in int)", this));
5659
}
57-
58-
@TruffleBoundary
59-
private DynamicObject notAFixnum(RubyContext context, Object object) {
60-
return context.getCoreExceptions().typeErrorIsNotA(object.toString(), "Fixnum (fitting in int)", this);
61-
}
62-
6360
}

src/main/java/org/truffleruby/core/cast/LongCastNode.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@
2020
import com.oracle.truffle.api.dsl.GenerateUncached;
2121
import com.oracle.truffle.api.dsl.ImportStatic;
2222
import com.oracle.truffle.api.dsl.Specialization;
23-
import com.oracle.truffle.api.object.DynamicObject;
2423

25-
/** Casts a value into a long. */
24+
/** See {@link ToIntNode} for a comparison of different integer conversion nodes. */
2625
@GenerateUncached
2726
@ImportStatic(RubyGuards.class)
2827
public abstract class LongCastNode extends RubyBaseNode {
@@ -43,18 +42,12 @@ protected long doLong(long value) {
4342
return value;
4443
}
4544

45+
@TruffleBoundary
4646
@Specialization(guards = "!isBasicInteger(value)")
4747
protected long doBasicObject(Object value,
4848
@CachedContext(RubyLanguage.class) RubyContext context) {
49-
throw new RaiseException(context, notAFixnum(context, value));
49+
throw new RaiseException(
50+
context,
51+
context.getCoreExceptions().typeErrorIsNotA(value.toString(), "Integer (fitting in long)", this));
5052
}
51-
52-
@TruffleBoundary
53-
private DynamicObject notAFixnum(RubyContext context, Object object) {
54-
return context.getCoreExceptions().typeErrorIsNotA(
55-
object.toString(),
56-
"Fixnum (fitting in long)",
57-
this);
58-
}
59-
6053
}

0 commit comments

Comments
 (0)