Skip to content

Commit 3604e2a

Browse files
Nicolas Laurenteregon
authored andcommitted
Make sure PE loops are properly profiled
* Use Truffle ArrayUtils#regionEqualsWithOrMask() and ArrayUtils#indexOf().
1 parent e1c7078 commit 3604e2a

16 files changed

+310
-151
lines changed

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

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
import java.util.List;
1515

1616
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
17+
import com.oracle.truffle.api.nodes.Node;
18+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1719
import org.truffleruby.RubyLanguage;
20+
import org.truffleruby.language.RubyBaseNode;
1821

1922
public abstract class ArrayUtils {
2023

@@ -230,29 +233,47 @@ public static Object[] append(Object[] array, Object element) {
230233
return newArray;
231234
}
232235

233-
public static int memcmp(final byte[] first, final int firstStart, final byte[] second, final int secondStart,
234-
int size) {
236+
/** Like {@link java.util.Arrays#fill(Object[], int, int, Object)} but includes profiling. */
237+
public static void fill(Object[] array, int from, int to, Object value, Node node,
238+
LoopConditionProfile loopProfile) {
239+
assert from <= to;
240+
int i = from;
241+
try {
242+
for (; loopProfile.inject(i < to); i++) {
243+
array[i] = value;
244+
}
245+
} finally {
246+
RubyBaseNode.profileAndReportLoopCount(node, loopProfile, i - from);
247+
}
248+
}
249+
250+
public static boolean regionEquals(byte[] first, int firstStart, byte[] second, int secondStart, int size) {
251+
return com.oracle.truffle.api.ArrayUtils
252+
.regionEqualsWithOrMask(first, firstStart, second, secondStart, size, null);
253+
}
254+
255+
public static int memcmp(byte[] first, int firstStart, byte[] second, int secondStart, int size, Node node,
256+
LoopConditionProfile loopProfile) {
235257
assert firstStart + size <= first.length;
236258
assert secondStart + size <= second.length;
237259

238-
int cmp;
239-
240-
for (int i = 0; i < size; i++) {
241-
if ((cmp = (first[firstStart + i] & 0xff) - (second[secondStart + i] & 0xff)) != 0) {
242-
return cmp;
260+
int i = 0;
261+
try {
262+
for (; loopProfile.inject(i < size); i++) {
263+
final int cmp = (first[firstStart + i] & 0xff) - (second[secondStart + i] & 0xff);
264+
if (cmp != 0) {
265+
return cmp;
266+
}
243267
}
268+
} finally {
269+
RubyBaseNode.profileAndReportLoopCount(node, loopProfile, i);
244270
}
245271

246272
return 0;
247273
}
248274

249-
public static int memchr(byte[] array, int start, byte find, int size) {
250-
for (int i = start; i < start + size; i++) {
251-
if (array[i] == find) {
252-
return i;
253-
}
254-
}
255-
return -1;
275+
public static int memchr(byte[] array, int start, int size, byte find) {
276+
return com.oracle.truffle.api.ArrayUtils.indexOf(array, start, start + size, find);
256277
}
257278

258279
@TruffleBoundary

src/main/java/org/truffleruby/core/array/library/ArrayStoreLibrary.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010

1111
package org.truffleruby.core.array.library;
1212

13+
import com.oracle.truffle.api.CompilerAsserts;
1314
import com.oracle.truffle.api.CompilerDirectives;
1415
import com.oracle.truffle.api.library.GenerateLibrary;
1516
import com.oracle.truffle.api.library.Library;
1617
import com.oracle.truffle.api.library.LibraryFactory;
1718
import com.oracle.truffle.api.library.GenerateLibrary.Abstract;
1819
import com.oracle.truffle.api.library.GenerateLibrary.DefaultExport;
20+
import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
21+
import com.oracle.truffle.api.nodes.Node;
1922

2023
/** Library for accessing and manipulating the storage used for representing arrays. This includes reading, modifying,
2124
* and copy the storage. */
@@ -184,4 +187,14 @@ public abstract static class ArrayAllocator {
184187
public abstract boolean isDefaultValue(Object value);
185188

186189
}
190+
191+
public final Node getNode() {
192+
boolean adoptable = this.isAdoptable();
193+
CompilerAsserts.partialEvaluationConstant(adoptable);
194+
if (adoptable) {
195+
return this;
196+
} else {
197+
return EncapsulatingNodeReference.getCurrent().get();
198+
}
199+
}
187200
}

src/main/java/org/truffleruby/core/array/library/DelegatedArrayStorage.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
import java.util.Set;
1313

1414
import com.oracle.truffle.api.CompilerDirectives;
15+
import com.oracle.truffle.api.dsl.Cached;
16+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1517
import org.truffleruby.core.array.ArrayGuards;
1618
import org.truffleruby.core.array.library.ArrayStoreLibrary.ArrayAllocator;
19+
import org.truffleruby.language.RubyBaseNode;
1720
import org.truffleruby.language.objects.ObjectGraph;
1821
import org.truffleruby.language.objects.ObjectGraphNode;
1922

@@ -81,10 +84,17 @@ protected Object[] boxedCopyOfRange(int start, int length,
8184

8285
@ExportMessage
8386
protected void copyContents(int srcStart, Object destStore, int destStart, int length,
87+
@CachedLibrary("this") ArrayStoreLibrary node,
88+
@Cached LoopConditionProfile loopProfile,
8489
@CachedLibrary(limit = "1") ArrayStoreLibrary srcStores,
8590
@CachedLibrary(limit = "storageStrategyLimit()") ArrayStoreLibrary destStores) {
86-
for (int i = 0; i < length; i++) {
87-
destStores.write(destStore, i + destStart, srcStores.read(storage, srcStart + offset + i));
91+
int i = 0;
92+
try {
93+
for (; loopProfile.inject(i < length); i++) {
94+
destStores.write(destStore, i + destStart, srcStores.read(storage, srcStart + offset + i));
95+
}
96+
} finally {
97+
RubyBaseNode.profileAndReportLoopCount(node.getNode(), loopProfile, i);
8898
}
8999
}
90100

src/main/java/org/truffleruby/core/array/library/DoubleArrayStore.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.Iterator;
1515
import java.util.NoSuchElementException;
1616

17+
import com.oracle.truffle.api.dsl.Cached;
18+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1719
import org.truffleruby.core.array.ArrayGuards;
1820
import org.truffleruby.core.array.ArrayUtils;
1921
import org.truffleruby.core.array.library.ArrayStoreLibrary.ArrayAllocator;
@@ -26,6 +28,7 @@
2628
import com.oracle.truffle.api.library.CachedLibrary;
2729
import com.oracle.truffle.api.library.ExportLibrary;
2830
import com.oracle.truffle.api.library.ExportMessage;
31+
import org.truffleruby.language.RubyBaseNode;
2932

3033
@ExportLibrary(value = ArrayStoreLibrary.class, receiverType = double[].class)
3134
@GenerateUncached
@@ -116,9 +119,15 @@ protected static void copyContents(
116119

117120
@Specialization(guards = "!isDoubleStore(destStore)", limit = "storageStrategyLimit()")
118121
protected static void copyContents(double[] srcStore, int srcStart, Object destStore, int destStart, int length,
122+
@Cached LoopConditionProfile loopProfile,
119123
@CachedLibrary("destStore") ArrayStoreLibrary destStores) {
120-
for (int i = 0; i < length; i++) {
121-
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
124+
int i = 0;
125+
try {
126+
for (; loopProfile.inject(i < length); i++) {
127+
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
128+
}
129+
} finally {
130+
RubyBaseNode.profileAndReportLoopCount(destStores.getNode(), loopProfile, i);
122131
}
123132
}
124133

src/main/java/org/truffleruby/core/array/library/IntegerArrayStore.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.Iterator;
1515
import java.util.NoSuchElementException;
1616

17+
import com.oracle.truffle.api.dsl.Cached;
18+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1719
import org.truffleruby.core.array.ArrayGuards;
1820
import org.truffleruby.core.array.ArrayUtils;
1921
import org.truffleruby.core.array.library.ArrayStoreLibrary.ArrayAllocator;
@@ -26,6 +28,7 @@
2628
import com.oracle.truffle.api.library.CachedLibrary;
2729
import com.oracle.truffle.api.library.ExportLibrary;
2830
import com.oracle.truffle.api.library.ExportMessage;
31+
import org.truffleruby.language.RubyBaseNode;
2932

3033
@ExportLibrary(value = ArrayStoreLibrary.class, receiverType = int[].class)
3134
@GenerateUncached
@@ -115,9 +118,15 @@ protected static void copyContents(int[] srcStore, int srcStart, int[] destStore
115118

116119
@Specialization(guards = "!isIntStore(destStore)", limit = "storageStrategyLimit()")
117120
protected static void copyContents(int[] srcStore, int srcStart, Object destStore, int destStart, int length,
121+
@Cached LoopConditionProfile loopProfile,
118122
@CachedLibrary("destStore") ArrayStoreLibrary destStores) {
119-
for (int i = 0; i < length; i++) {
120-
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
123+
int i = 0;
124+
try {
125+
for (; loopProfile.inject(i < length); i++) {
126+
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
127+
}
128+
} finally {
129+
RubyBaseNode.profileAndReportLoopCount(destStores.getNode(), loopProfile, i);
121130
}
122131
}
123132

src/main/java/org/truffleruby/core/array/library/LongArrayStore.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.Iterator;
1515
import java.util.NoSuchElementException;
1616

17+
import com.oracle.truffle.api.dsl.Cached;
18+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1719
import org.truffleruby.core.array.ArrayGuards;
1820
import org.truffleruby.core.array.ArrayUtils;
1921
import org.truffleruby.core.array.library.ArrayStoreLibrary.ArrayAllocator;
@@ -26,6 +28,7 @@
2628
import com.oracle.truffle.api.library.CachedLibrary;
2729
import com.oracle.truffle.api.library.ExportLibrary;
2830
import com.oracle.truffle.api.library.ExportMessage;
31+
import org.truffleruby.language.RubyBaseNode;
2932

3033
@ExportLibrary(value = ArrayStoreLibrary.class, receiverType = long[].class)
3134
@GenerateUncached
@@ -128,9 +131,15 @@ protected static void copyContents(long[] srcStore, int srcStart, long[] destSto
128131

129132
@Specialization(guards = "!isLongStore(destStore)", limit = "storageStrategyLimit()")
130133
protected static void copyContents(long[] srcStore, int srcStart, Object destStore, int destStart, int length,
134+
@Cached LoopConditionProfile loopProfile,
131135
@CachedLibrary("destStore") ArrayStoreLibrary destStores) {
132-
for (int i = 0; i < length; i++) {
133-
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
136+
int i = 0;
137+
try {
138+
for (; loopProfile.inject(i < length); i++) {
139+
destStores.write(destStore, destStart + i, srcStore[srcStart + i]);
140+
}
141+
} finally {
142+
RubyBaseNode.profileAndReportLoopCount(destStores.getNode(), loopProfile, i);
134143
}
135144
}
136145

src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.oracle.truffle.api.CompilerDirectives;
1717
import com.oracle.truffle.api.profiles.ConditionProfile;
18+
import com.oracle.truffle.api.profiles.LoopConditionProfile;
1819
import org.truffleruby.cext.UnwrapNode;
1920
import org.truffleruby.cext.UnwrapNodeGen.UnwrapNativeNodeGen;
2021
import org.truffleruby.cext.ValueWrapper;
@@ -23,14 +24,14 @@
2324
import org.truffleruby.core.array.ArrayUtils;
2425
import org.truffleruby.core.array.library.ArrayStoreLibrary.ArrayAllocator;
2526
import org.truffleruby.extra.ffi.Pointer;
27+
import org.truffleruby.language.RubyBaseNode;
2628
import org.truffleruby.language.objects.ObjectGraph;
2729
import org.truffleruby.language.objects.ObjectGraphNode;
2830

2931
import com.oracle.truffle.api.dsl.Cached;
3032
import com.oracle.truffle.api.dsl.Cached.Shared;
3133
import com.oracle.truffle.api.dsl.GenerateUncached;
3234
import com.oracle.truffle.api.dsl.ImportStatic;
33-
import com.oracle.truffle.api.dsl.Specialization;
3435
import com.oracle.truffle.api.interop.InteropLibrary;
3536
import com.oracle.truffle.api.interop.UnsupportedMessageException;
3637
import com.oracle.truffle.api.library.CachedLibrary;
@@ -39,6 +40,7 @@
3940

4041
@ExportLibrary(ArrayStoreLibrary.class)
4142
@GenerateUncached
43+
@ImportStatic(ArrayGuards.class)
4244
public final class NativeArrayStorage implements ObjectGraphNode {
4345

4446
private final Pointer pointer;
@@ -82,39 +84,31 @@ protected static String toString(NativeArrayStorage storage) {
8284
}
8385

8486
@ExportMessage
85-
public abstract static class Read {
86-
87-
@Specialization
88-
protected static Object read(NativeArrayStorage storage, int index,
89-
@Shared("unwrap") @Cached UnwrapNode unwrapNode) {
90-
return unwrapNode.execute(storage.readElement(index));
91-
}
87+
protected Object read(int index,
88+
@Shared("unwrap") @Cached UnwrapNode unwrapNode) {
89+
return unwrapNode.execute(readElement(index));
9290
}
9391

9492
@ExportMessage
95-
public abstract static class Write {
96-
97-
@Specialization
98-
protected static void write(NativeArrayStorage storage, int index, Object value,
99-
@CachedLibrary(limit = "1") InteropLibrary wrappers,
100-
@Cached WrapNode wrapNode,
101-
@Cached ConditionProfile isPointerProfile) {
102-
final ValueWrapper wrapper = wrapNode.execute(value);
103-
if (!isPointerProfile.profile(wrappers.isPointer(wrapper))) {
104-
wrappers.toNative(wrapper);
105-
}
106-
107-
final long address;
108-
try {
109-
assert wrappers.isPointer(wrapper);
110-
address = wrappers.asPointer(wrapper);
111-
} catch (UnsupportedMessageException e) {
112-
CompilerDirectives.transferToInterpreterAndInvalidate();
113-
throw new UnsupportedOperationException();
114-
}
93+
protected void write(int index, Object value,
94+
@CachedLibrary(limit = "1") InteropLibrary wrappers,
95+
@Cached WrapNode wrapNode,
96+
@Cached ConditionProfile isPointerProfile) {
97+
final ValueWrapper wrapper = wrapNode.execute(value);
98+
if (!isPointerProfile.profile(wrappers.isPointer(wrapper))) {
99+
wrappers.toNative(wrapper);
100+
}
115101

116-
storage.writeElement(index, address);
102+
final long address;
103+
try {
104+
assert wrappers.isPointer(wrapper);
105+
address = wrappers.asPointer(wrapper);
106+
} catch (UnsupportedMessageException e) {
107+
CompilerDirectives.transferToInterpreterAndInvalidate();
108+
throw new UnsupportedOperationException();
117109
}
110+
111+
writeElement(index, address);
118112
}
119113

120114
@ExportMessage
@@ -123,18 +117,15 @@ protected int capacity() {
123117
}
124118

125119
@ExportMessage
126-
public abstract static class Expand {
127-
128-
@Specialization
129-
protected static NativeArrayStorage expand(NativeArrayStorage storage, int newCapacity) {
130-
Pointer newPointer = Pointer.malloc(storage.capacity());
131-
newPointer.writeBytes(0, storage.pointer, 0, storage.capacity());
132-
newPointer.writeBytes(storage.capacity(), newCapacity - storage.capacity(), (byte) 0);
133-
/* We copy the contents of the marked objects to ensure the references will be kept alive even if the old
134-
* store becomes unreachable. */
135-
Object[] newMarkedObjects = ArrayUtils.grow(storage.markedObjects, newCapacity);
136-
return new NativeArrayStorage(newPointer, newCapacity, newMarkedObjects);
137-
}
120+
protected NativeArrayStorage expand(int newCapacity) {
121+
final int capacity = this.length;
122+
Pointer newPointer = Pointer.malloc(capacity);
123+
newPointer.writeBytes(0, pointer, 0, capacity);
124+
newPointer.writeBytes(capacity, newCapacity - capacity, (byte) 0);
125+
/* We copy the contents of the marked objects to ensure the references will be kept alive even if the old store
126+
* becomes unreachable. */
127+
Object[] newMarkedObjects = ArrayUtils.grow(markedObjects, newCapacity);
128+
return new NativeArrayStorage(newPointer, newCapacity, newMarkedObjects);
138129
}
139130

140131
@ExportMessage
@@ -148,17 +139,17 @@ protected Object[] boxedCopyOfRange(int start, int length,
148139
}
149140

150141
@ExportMessage
151-
@ImportStatic(ArrayGuards.class)
152-
public abstract static class CopyContents {
153-
154-
@Specialization
155-
protected static void copyContents(
156-
NativeArrayStorage srcStore, int srcStart, Object destStore, int destStart, int length,
157-
@CachedLibrary(limit = "1") ArrayStoreLibrary srcStores,
158-
@CachedLibrary(limit = "storageStrategyLimit()") ArrayStoreLibrary destStores) {
159-
for (int i = 0; i < length; i++) {
160-
destStores.write(destStore, destStart + i, srcStores.read(srcStore, srcStart + i));
142+
protected void copyContents(int srcStart, Object destStore, int destStart, int length,
143+
@CachedLibrary("this") ArrayStoreLibrary srcStores,
144+
@Cached LoopConditionProfile loopProfile,
145+
@CachedLibrary(limit = "storageStrategyLimit()") ArrayStoreLibrary destStores) {
146+
int i = 0;
147+
try {
148+
for (; loopProfile.inject(i < length); i++) {
149+
destStores.write(destStore, destStart + i, srcStores.read(this, srcStart + i));
161150
}
151+
} finally {
152+
RubyBaseNode.profileAndReportLoopCount(srcStores.getNode(), loopProfile, i);
162153
}
163154
}
164155

0 commit comments

Comments
 (0)