Fix some GraalVM PE issues (#1944)

* VmObject.getRootFirstObject() is a recursive call, which sometimes
  fails with "too deep inlining"
* Incorrect loop condition profile usage in some places
* Need to mark `checkedReturnTypeNode` final so that GraalVM knows
  that it is PE constant
This commit is contained in:
Daniel Chao authored and GitHub committed 2026-10-30 10:27:18 -07:00
1 parent 99c2d84c43
commit 0b582b50b4
4 files changed
+26 -33

No files matched your search

@@ -44,7 +44,6 @@ import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmObject;
import org.pkl.core.runtime.VmObjectCursor;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.runtime.VmUtils;
@@ -199,8 +198,7 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
protected void doEvalDynamic(VirtualFrame frame, ObjectData data, VmObject iterable) {
var cursor = iterable.members();
var counted = isCounted(cursor);
while (advance(cursor, counted)) {
while (loopConditionProfile.profile(cursor.advance())) {
var member = cursor.member();
var value = readCursorValueNode.execute(frame, cursor);
if (member.isElement()) {
@@ -213,8 +211,7 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
private void doEvalMapping(VirtualFrame frame, ObjectData data, VmObject iterable) {
var cursor = iterable.members();
var counted = isCounted(cursor);
while (advance(cursor, counted)) {
while (loopConditionProfile.profile(cursor.advance())) {
var member = cursor.member();
if (member.isElement() || member.isProp()) {
cannotHaveMember(BaseModule.getMappingClass(), member);
@@ -229,8 +226,8 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
private void doEvalListing(VirtualFrame frame, ObjectData data, VmObject iterable) {
var cursor = iterable.members();
var counted = isCounted(cursor);
while (advance(cursor, counted)) {
loopConditionProfile.profileCounted(cursor.getLength());
while (loopConditionProfile.inject(cursor.advance())) {
var member = cursor.member();
if (member.isEntry() || member.isProp()) {
cannotHaveMember(getListingClass(), member);
@@ -242,8 +239,7 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
private void doEvalTyped(VirtualFrame frame, VmClass clazz, ObjectData data, VmObject iterable) {
var cursor = iterable.members();
var counted = isCounted(cursor);
while (advance(cursor, counted)) {
while (loopConditionProfile.profile(cursor.advance())) {
var member = cursor.member();
if (member.isElement() || member.isEntry()) {
cannotHaveMember(clazz, member);
@@ -254,19 +250,6 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
}
}
private boolean isCounted(VmObjectCursor cursor) {
var length = cursor.getLength();
if (length == -1) return false;
loopConditionProfile.profileCounted(length);
return true;
}
private boolean advance(VmObjectCursor cursor, boolean counted) {
return counted
? loopConditionProfile.inject(cursor.advance())
: loopConditionProfile.profile(cursor.advance());
}
// handles both `List` and `Set`
private void doEvalCollection(
VirtualFrame frame, VmClass parent, ObjectData data, VmCollection iterable) {
@@ -54,7 +54,8 @@ public final class FunctionNode extends RegularMemberNode {
@Children private TypeParameterNode[] typeParameterNodes;
@Children private final TypeNode[] parameterTypeNodes;
private @Nullable TypeNode checkedReturnTypeNode;
// not marked `@Child` because it's the same as `returnTypeNode`.
private final @Nullable TypeNode checkedReturnTypeNode;
@Child private @Nullable TypeNode returnTypeNode;
@TruffleBoundary
@@ -1257,6 +1257,8 @@ public abstract class TypeNode extends PklNode {
public static final class CollectionTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode elementTypeNode;
private final LoopConditionProfile listLoopProfile = LoopConditionProfile.create();
private final LoopConditionProfile setLoopProfile = LoopConditionProfile.create();
public CollectionTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) {
super(sourceSection);
@@ -1316,13 +1318,14 @@ public abstract class TypeNode extends PklNode {
}
@SuppressWarnings("DuplicatedCode")
@ExplodeLoop
private Object evalList(VirtualFrame frame, VmList value) {
var ret = value;
var idx = 0;
listLoopProfile.profileCounted(value.getLength());
for (var elem : value) {
for (var i = 0; listLoopProfile.inject(i < value.getLength()); i++) {
TruffleSafepoint.poll(this);
var elem = value.get(i);
var result = elementTypeNode.executeLazily(frame, elem);
if (result != elem) {
ret = ret.replace(idx, result);
@@ -1330,27 +1333,31 @@ public abstract class TypeNode extends PklNode {
idx++;
}
LoopNode.reportLoopCount(this, idx);
LoopNode.reportLoopCount(this, value.getLength());
return ret;
}
private Object evalListEagerly(VirtualFrame frame, VmList value) {
for (var elem : value) {
var length = value.getLength();
listLoopProfile.profileCounted(length);
for (var i = 0; listLoopProfile.inject(i < length); i++) {
TruffleSafepoint.poll(this);
elementTypeNode.executeEagerly(frame, elem);
elementTypeNode.executeEagerly(frame, value.get(i));
}
LoopNode.reportLoopCount(this, value.getLength());
LoopNode.reportLoopCount(this, length);
return value;
}
private Object evalSet(VirtualFrame frame, VmSet value) {
for (var elem : value) {
var iterator = value.iterator();
var count = 0;
while (setLoopProfile.inject(iterator.hasNext())) {
TruffleSafepoint.poll(this);
elementTypeNode.executeEagerly(frame, elem);
elementTypeNode.executeEagerly(frame, iterator.next());
count++;
}
LoopNode.reportLoopCount(this, value.getLength());
LoopNode.reportLoopCount(this, count);
return value;
}
}
@@ -79,6 +79,8 @@ public abstract class VmObject extends VmObjectLike {
return EconomicMaps.containsKey(members, key);
}
// deeply recursive calls are bad for PE, which can happen for deeply nested objects.
@TruffleBoundary
public final @Nullable ObjectMember getRootFirstMember(Object key) {
if (parent != null) {
var parentMember = parent.getRootFirstMember(key);