Skip to content

Commit dcd86f3

Browse files
committed
Omit receiver type check and hoist field read.
1 parent 9070558 commit dcd86f3

File tree

3 files changed

+73
-28
lines changed

3 files changed

+73
-28
lines changed

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/FieldInfo.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import java.lang.invoke.VarHandle;
4444
import java.lang.reflect.Field;
45+
import java.util.Objects;
4546

4647
import com.oracle.truffle.api.CompilerAsserts;
4748
import com.oracle.truffle.api.CompilerDirectives;
@@ -158,4 +159,12 @@ private IllegalArgumentException illegalReceiver(DynamicObject store) {
158159
CompilerAsserts.neverPartOfCompilation();
159160
return new IllegalArgumentException("Invalid receiver type (expected " + getDeclaringClass() + ", was " + (store == null ? null : store.getClass()) + ")");
160161
}
162+
163+
DynamicObject unsafeReceiverCast(DynamicObject store) {
164+
if (ObjectStorageOptions.ReceiverCheck) {
165+
return Objects.requireNonNull(tclass.cast(store));
166+
} else {
167+
return store;
168+
}
169+
}
161170
}

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Location.java

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import java.util.Objects;
4444
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
4545

46+
import org.graalvm.nativeimage.ImageInfo;
47+
4648
import com.oracle.truffle.api.Assumption;
4749
import com.oracle.truffle.api.CompilerAsserts;
4850
import com.oracle.truffle.api.CompilerDirectives;
@@ -201,26 +203,28 @@ protected double getDouble(DynamicObject store, boolean guard) throws Unexpected
201203
* @param guard the result of the shape check or {@code false}
202204
* @return the read value
203205
*/
206+
@SuppressWarnings("hiding")
204207
final Object getInternal(DynamicObject store, Shape expectedShape, boolean guard) {
208+
DynamicObject receiver = unsafeNonNullCast(store);
205209
long idx = Integer.toUnsignedLong(index);
210+
FieldInfo field = this.field;
206211
if (this instanceof ObjectLocation objectLocation) {
207212
Object base;
208213
long offset;
209214
Object value;
210215
if (field == null) {
211-
base = getObjectArray(store, guard);
216+
base = getObjectArray(receiver, guard);
212217
offset = computeObjectArrayOffset(idx);
213218
value = UnsafeAccess.unsafeGetObject(base, offset, guard, this);
214219
} else {
215-
field.receiverCheck(store);
216-
base = store;
217-
offset = getFieldOffset();
220+
base = field.unsafeReceiverCast(receiver);
221+
offset = field.offset();
218222
value = UnsafeAccess.unsafeGetObject(base, offset, guard, this);
219223
}
220224
return CompilerDirectives.inInterpreter() ? value : objectLocation.assumedTypeCast(value, guard);
221225
} else {
222226
if (field == null) {
223-
Object array = getPrimitiveArray(store, guard);
227+
Object array = getPrimitiveArray(receiver, guard);
224228
long offset = computePrimitiveArrayOffset(idx);
225229
if (isIntLocation()) {
226230
return UnsafeAccess.unsafeGetInt(array, offset, guard, this);
@@ -229,11 +233,11 @@ final Object getInternal(DynamicObject store, Shape expectedShape, boolean guard
229233
} else if (isDoubleLocation()) {
230234
return UnsafeAccess.unsafeGetDouble(array, offset, guard, this);
231235
} else {
232-
return ((ConstantLocation) this).get(store, guard);
236+
return ((ConstantLocation) this).get(receiver, guard);
233237
}
234238
} else {
235-
field.receiverCheck(store);
236-
long longValue = UnsafeAccess.unsafeGetLong(store, getFieldOffset(), guard, this);
239+
Object base = field.unsafeReceiverCast(receiver);
240+
long longValue = UnsafeAccess.unsafeGetLong(base, field.offset(), guard, this);
237241
if (this instanceof IntLocation) {
238242
return (int) longValue;
239243
} else if (this instanceof LongLocation) {
@@ -250,18 +254,19 @@ final Object getInternal(DynamicObject store, Shape expectedShape, boolean guard
250254
* @see #getInternal(DynamicObject, Shape, boolean)
251255
*/
252256
final int getIntInternal(DynamicObject store, Shape expectedShape, boolean guard) throws UnexpectedResultException {
257+
DynamicObject receiver = unsafeNonNullCast(store);
253258
if (isIntLocation()) {
254259
if (field == null) {
255-
Object array = getPrimitiveArray(store, guard);
260+
Object array = getPrimitiveArray(receiver, guard);
256261
long offset = getPrimitiveArrayOffset();
257262
return UnsafeAccess.unsafeGetInt(array, offset, guard, this);
258263
} else {
259-
field.receiverCheck(store);
260-
long longValue = UnsafeAccess.unsafeGetLong(store, getFieldOffset(), guard, this);
264+
Object base = field.unsafeReceiverCast(receiver);
265+
long longValue = UnsafeAccess.unsafeGetLong(base, field.offset(), guard, this);
261266
return (int) longValue;
262267
}
263268
}
264-
return getIntUnexpected(store, expectedShape, guard);
269+
return getIntUnexpected(receiver, expectedShape, guard);
265270
}
266271

267272
/**
@@ -277,17 +282,18 @@ private int getIntUnexpected(DynamicObject store, Shape expectedShape, boolean g
277282
* @see #getInternal(DynamicObject, Shape, boolean)
278283
*/
279284
final long getLongInternal(DynamicObject store, Shape expectedShape, boolean guard) throws UnexpectedResultException {
285+
DynamicObject receiver = unsafeNonNullCast(store);
280286
if (isLongLocation()) {
281287
if (field == null) {
282-
Object array = getPrimitiveArray(store, guard);
288+
Object array = getPrimitiveArray(receiver, guard);
283289
long offset = getPrimitiveArrayOffset();
284290
return UnsafeAccess.unsafeGetLong(array, offset, guard, this);
285291
} else {
286-
field.receiverCheck(store);
287-
return UnsafeAccess.unsafeGetLong(store, getFieldOffset(), guard, this);
292+
Object base = field.unsafeReceiverCast(receiver);
293+
return UnsafeAccess.unsafeGetLong(base, field.offset(), guard, this);
288294
}
289295
}
290-
return getLongUnexpected(store, expectedShape, guard);
296+
return getLongUnexpected(receiver, expectedShape, guard);
291297
}
292298

293299
/**
@@ -303,18 +309,19 @@ private long getLongUnexpected(DynamicObject store, Shape expectedShape, boolean
303309
* @see #getInternal(DynamicObject, Shape, boolean)
304310
*/
305311
final double getDoubleInternal(DynamicObject store, Shape expectedShape, boolean guard) throws UnexpectedResultException {
312+
DynamicObject receiver = unsafeNonNullCast(store);
306313
if (isDoubleLocation()) {
307314
if (field == null) {
308-
Object array = getPrimitiveArray(store, guard);
315+
Object array = getPrimitiveArray(receiver, guard);
309316
long offset = getPrimitiveArrayOffset();
310317
return UnsafeAccess.unsafeGetDouble(array, offset, guard, this);
311318
} else {
312-
field.receiverCheck(store);
313-
long longValue = UnsafeAccess.unsafeGetLong(store, getFieldOffset(), guard, this);
319+
Object base = field.unsafeReceiverCast(receiver);
320+
long longValue = UnsafeAccess.unsafeGetLong(base, field.offset(), guard, this);
314321
return Double.longBitsToDouble(longValue);
315322
}
316323
}
317-
return getDoubleUnexpected(store, expectedShape, guard);
324+
return getDoubleUnexpected(receiver, expectedShape, guard);
318325
}
319326

320327
/**
@@ -436,16 +443,19 @@ final void setDoubleSafe(DynamicObject store, double value, boolean guard, boole
436443
* Stores a value in this location. Grows the object if necessary. It is the caller's
437444
* responsibility to check that the value is compatible with this location first.
438445
*
439-
* @param receiver storage object
446+
* @param store storage object
440447
* @param value the value to be stored
441448
* @param guard the result of the shape check guarding this property write or {@code false}
442449
* @param oldShape the expected shape before the set
443450
* @param newShape the expected shape after the set
444451
* @see #canStoreValue(Object).
445452
*/
446-
final void setInternal(DynamicObject receiver, Object value, boolean guard, Shape oldShape, Shape newShape) {
453+
@SuppressWarnings("hiding")
454+
final void setInternal(DynamicObject store, Object value, boolean guard, Shape oldShape, Shape newShape) {
447455
assert canStoreValue(value) : value;
456+
DynamicObject receiver = unsafeNonNullCast(store);
448457
long idx = Integer.toUnsignedLong(index);
458+
FieldInfo field = this.field;
449459
boolean init = newShape != oldShape;
450460
if (init) {
451461
DynamicObjectSupport.grow(receiver, oldShape, newShape);
@@ -467,9 +477,8 @@ final void setInternal(DynamicObject receiver, Object value, boolean guard, Shap
467477
offset = computeObjectArrayOffset(idx);
468478
UnsafeAccess.unsafePutObject(base, offset, value, this);
469479
} else {
470-
field.receiverCheck(receiver);
471-
base = receiver;
472-
offset = getFieldOffset();
480+
base = field.unsafeReceiverCast(receiver);
481+
offset = field.offset();
473482
UnsafeAccess.unsafePutObject(base, offset, value, this);
474483
}
475484
} else { // primitive location
@@ -519,9 +528,9 @@ final void setInternal(DynamicObject receiver, Object value, boolean guard, Shap
519528
assert isConstantLocation() : this;
520529
return;
521530
}
522-
field.receiverCheck(receiver);
523-
long offset = getFieldOffset();
524-
UnsafeAccess.unsafePutLong(receiver, offset, longValue, this);
531+
Object base = field.unsafeReceiverCast(receiver);
532+
long offset = field.offset();
533+
UnsafeAccess.unsafePutLong(base, offset, longValue, this);
525534
}
526535
}
527536

@@ -773,6 +782,20 @@ static Object getPrimitiveArray(DynamicObject store, boolean condition) {
773782
return UnsafeAccess.hostUnsafeCast(store.getPrimitiveStore(), int[].class, condition, true, true);
774783
}
775784

785+
private static DynamicObject unsafeNonNullCast(DynamicObject receiver) {
786+
/*
787+
* The shape check already performs an implicit null check, so the receiver is guaranteed to
788+
* be non-null here, but when compiling methods separately we might not know this yet.
789+
* Hence, we use an unsafe cast in the interpreter to avoid compiling any null code paths.
790+
* In PE OTOH, the redundant ValueAnchor+Pi would just mean extra work for the compiler.
791+
*/
792+
if (CompilerDirectives.inInterpreter() && !ObjectStorageOptions.ReceiverCheck && ImageInfo.inImageCode()) {
793+
return UnsafeAccess.hostUnsafeCast(receiver, DynamicObject.class, false, true, false);
794+
} else {
795+
return receiver;
796+
}
797+
}
798+
776799
static RuntimeException incompatibleLocationException() {
777800
CompilerDirectives.transferToInterpreterAndInvalidate();
778801
throw UncheckedIncompatibleLocationException.instance();

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/ObjectStorageOptions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ final class ObjectStorageOptions {
4747
private ObjectStorageOptions() {
4848
}
4949

50+
static final boolean ASSERTIONS_ENABLED = assertionsEnabled();
51+
5052
static final boolean PrimitiveLocations = booleanOption(OPTION_PREFIX + "PrimitiveLocations", true);
5153
static final boolean IntegerLocations = booleanOption(OPTION_PREFIX + "IntegerLocations", true);
5254
static final boolean DoubleLocations = booleanOption(OPTION_PREFIX + "DoubleLocations", true);
@@ -61,6 +63,10 @@ private ObjectStorageOptions() {
6163
* If set to true, use VarHandle rather than Unsafe to implement field accesses.
6264
*/
6365
static final boolean UseVarHandle = booleanOption(OPTION_PREFIX + "UseVarHandle", false);
66+
/**
67+
* Enforce receiver type checks for unsafe field accesses.
68+
*/
69+
static final boolean ReceiverCheck = booleanOption(OPTION_PREFIX + "ReceiverCheck", false) || ASSERTIONS_ENABLED;
6470

6571
static final boolean TriePropertyMap = booleanOption(OPTION_PREFIX + "TriePropertyMap", true);
6672
static final boolean TrieTransitionMap = booleanOption(OPTION_PREFIX + "TrieTransitionMap", true);
@@ -94,4 +100,11 @@ static boolean booleanOption(String name, boolean defaultValue) {
94100
String value = System.getProperty(name);
95101
return value == null ? defaultValue : value.equalsIgnoreCase("true");
96102
}
103+
104+
@SuppressWarnings("all")
105+
private static boolean assertionsEnabled() {
106+
boolean enabled = false;
107+
assert (enabled = true) == true;
108+
return enabled;
109+
}
97110
}

0 commit comments

Comments
 (0)