Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make suspended atom fields work for boxed atoms #8712

Merged
merged 36 commits into from
Jan 16, 2024

Conversation

JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Jan 9, 2024

Pull Request Description

Fixes #8710 by making sure suspended atom fields support works also for "normal" Atom instances without any special Layout. Moves all atom related classes into single package and hides as much of classes as possible by making them package private.

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

@JaroslavTulach JaroslavTulach self-assigned this Jan 9, 2024
@JaroslavTulach JaroslavTulach added the CI: No changelog needed Do not require a changelog entry for this PR. label Jan 9, 2024
@JaroslavTulach
Copy link
Member Author

OK, now we have two test failures that need to be fixed.

@JaroslavTulach
Copy link
Member Author

After a28daff we only expose four essential classes from the "atom" package:

javadoc

@JaroslavTulach
Copy link
Member Author

Running engine benchmarks with 08df824 fixes that optimize fields extraction in ConstructorBranchNode.

@JaroslavTulach JaroslavTulach added the CI: Clean build required CI runners will be cleaned before and after this PR is built. label Jan 16, 2024
@@ -739,6 +738,10 @@ public static FunctionPointer fromAtomConstructor(AtomConstructor atomConstructo
}

public static FunctionPointer fromFunction(Function function) {
var cons = AtomConstructor.accessorFor(function);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing the accessorFor method allows us to make QualifiedAccessorNode package private, @4e6.

if (profile.profile(matcher == target.getConstructor())) {
accept(frame, state, structs.getFields(target));
var arr = toArrayNode.executeFields(target);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmarks regressed a bit after removal of getFields, but introduction of FieldsAsArrayNode that provides a polymorphic cache for each AtomConstructor (up to limit 3 - usually there is only one anyway) and explodes the loop that reads the fields one by one seems to deliver the same results.

Example of AtomBenchmark

@@ -29,6 +30,6 @@ protected void postInitialize() {
}

public final Atom newInstance(Object... params) {
return uniqueConstructor.newInstance(params);
return AtomNewInstanceNode.getUncached().newInstance(uniqueConstructor, params);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to hide AtomConstructor.newInstance as package private and expose AtomNewInstanceNode.getUncached() instead. It was not clear that AtomConstructor.newInstance was slow (and only allocating BoxedAtom!). Now, when we follow standard Truffle conventions, it is clear getUncached() is the slow path version of AtomNewInstanceNode.create(). Functionally they are identical as AtomConstructorTest demonstrates.

EnsoObject doStruct(Atom atom, @CachedLibrary(limit = "2") StructsLibrary structs) {
return ArrayLikeHelpers.wrapObjectsWithCheckAt(structs.getFields(atom));
@TruffleBoundary
final EnsoObject doOther(Atom atom) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question of Truffle experts: If I do just the first @Specialization(limit = LIMIT, guards = {"atom.getConstructor() == cons"}) then the execute method yields UnsupportedSpecializationException when we send there more than LIMIT of different atom constructors.

I thought @Fallback would be appropriate, but DSL processor refuses it. So I ended up with @Specialization without any guards behind @TruffleBoundary calling the first specialization @TruffleBoundary. Can we do better? Should the second specialization replace the first one?

new GetFieldNode(language, field.getPosition(), this, field.getName()));
});
}
var roots = AtomConstructor.collectFieldAccessors(language, this);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduction of collectFieldAccessors function allows us to hide GetFieldWithMatchNode and GetFieldNode and make them package private.

if (Layout.isAritySupported(args.length)) {
boxedLayout = Layout.create(args.length, 0, args);
}
boxedLayout = Layout.createBoxed(args);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The major change that fixes #8710:

  • each AtomConstructor is associated with boxedLayout
  • we always use StructsLibrary (and thus layouts) to access the atom values
  • as such the SuspendedFieldGetterNode is used for BoxingAtom as well as UnboxingAtom

@@ -33,7 +36,51 @@ private SuspendedFieldGetterNode(
this.set = set;
}

static UnboxingAtom.FieldGetterNode build(
static NodeFactory<? extends UnboxingAtom.FieldGetterNode> factory(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping of FieldGetterNode now happens on the level of factories. As such it should be more consistent and the suspended wrapper shall be used everywhere.

assertAtomFactory("AtomNewInstanceNode.create", factory);
assertLessArguments("AtomNewInstanceNode.create", factory);

assertAtomFactory("AtomConstructor.newInstance with priming", factory);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two tests. First one verifies the behavior of getUncached() in isolation. The second one checks whether both cached and uncached variants co-operate well.

}

@Test
public void fiveAtomIntFields() throws Exception {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the test that simulates #8710 - fourXYZ tests were always passing. fiveXYZ tests were failing prior to this PR.

Copy link
Contributor

@hubertp hubertp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor questions about the (need for) TruffleBoundary annotations

private Text doComplexAtom(Atom atom, Object[] fields) {
Text res = Text.create("(", consName(atom.getConstructor()));
private Text doComplexAtom(Atom atom) {
var structs = StructsLibrary.getUncached();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keep on using a cached StructsLibrary in doAtom and passing it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it make anything better if doComplexAtom is inlined into PE mode? Do we really need to run this code at assembly speed? Please note that there are showObject calls that are annotated with @TruffleBoundary anyway.

@JaroslavTulach JaroslavTulach added the CI: Ready to merge This PR is eligible for automatic merge label Jan 16, 2024
@mergify mergify bot merged commit 09f484f into develop Jan 16, 2024
26 of 28 checks passed
@mergify mergify bot deleted the wip/jtulach/RichAtomFields_8710 branch January 16, 2024 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
--bug Type: bug -compiler CI: Clean build required CI runners will be cleaned before and after this PR is built. CI: No changelog needed Do not require a changelog entry for this PR. CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rich atoms don't cache suspended fields
3 participants