Skip to content

Avoid eliding nested pointer-to-ref conversions#82263

Merged
jjonescz merged 10 commits intodotnet:mainfrom
jjonescz:82137-retrack
Mar 1, 2026
Merged

Avoid eliding nested pointer-to-ref conversions#82263
jjonescz merged 10 commits intodotnet:mainfrom
jjonescz:82137-retrack

Conversation

@jjonescz
Copy link
Member

@jjonescz jjonescz commented Feb 3, 2026

Fixes #82137.
Follow up on #79311 to handle more cases.

@hamarb123 @jkotas fyi

Some background

If there is a pointer-to-ref conversion, we need to preserve the by-ref local in IL so the CLR can "re-track" it, i.e., not garbage-collect it while it's in a managed ref-local. Previously we would sometimes optimize the local away so in IL the code would be just pointer manipulation which GC doesn't track.

For example, in Release config, this code

ref byte b = ref p->F;
b.ToString();

would be lowered to

p->F.ToString()

but that's wrong if GC happens in between p->F and .ToString() (if CLR sees the former code it has to track that local b and cannot collect it but not in the latter case). More real-world examples are in the linked issue. We previously fixed this for some cases but not for some more nested scenarios.

@jjonescz jjonescz marked this pull request as ready for review February 3, 2026 15:26
@jjonescz jjonescz requested a review from a team as a code owner February 3, 2026 15:26
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 3, 2026

Done with review pass (commit 2) #Closed

Copy link

@hamarb123 hamarb123 left a comment

Choose a reason for hiding this comment

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

lgtm other than the missing case I commented on.

Copy link

@hamarb123 hamarb123 left a comment

Choose a reason for hiding this comment

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

I find the codegen for the

ref byte b1 = ref F();
ref byte b2 = ref (b1 = ref *p);
b2.ToString();

test to be quite suspicious (for the same reason the other previously problematic code samples were suspicious) and potentially problematic if it can still be like that with other calls added between w/o forcing it to go to the byref local.

Copy link

@hamarb123 hamarb123 left a comment

Choose a reason for hiding this comment

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

The IL for all of the added & changed tested methods (I haven't checked the pre-existing ones) look good to me at least now :)

@jjonescz jjonescz requested review from a team and AlekseyTs February 16, 2026 09:55
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 17, 2026

Done with review pass (commit 6) #Closed

@jjonescz jjonescz requested a review from AlekseyTs February 18, 2026 14:01
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 18, 2026

Done with review pass (commit 7) #Closed

@jjonescz jjonescz requested a review from AlekseyTs February 18, 2026 15:55
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 18, 2026

        public override BoundNode VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node)

I am not sure what was the point in replacing this override with Visit(BoundNode node) with the given implementation. #Closed


Refers to: src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs:2050 in 3ffd5f5. [](commit_id = 3ffd5f5, deletion_comment = True)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 18, 2026

Done with review pass (commit 8) #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 9)

@jjonescz jjonescz requested a review from a team February 19, 2026 10:14
@jjonescz
Copy link
Member Author

@dotnet/roslyn-compiler for another review, thanks

return node;
}

public override BoundNode VisitCall(BoundCall node)
Copy link
Member

Choose a reason for hiding this comment

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

What about BoundFunctionPointerInvocations? Do we need to return true when one is encountered? Or what if it's funcPtr(ref *otherPtr)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Retracking is only useful if a pointer is dereferenced and stored in a by-ref local. Function pointer invocation doesn't seem to apply to that.

But I think we can ignore pointer dereferences that are passed through a function pointer invocation, just like we do for normal invocation. I will do that (and add tests for function pointer invocations).

@jjonescz jjonescz requested a review from 333fred February 27, 2026 11:02
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 10)

@jjonescz jjonescz enabled auto-merge (squash) February 28, 2026 12:37
@jjonescz jjonescz merged commit 9a3bdad into dotnet:main Mar 1, 2026
23 of 24 checks passed
@jjonescz jjonescz deleted the 82137-retrack branch March 1, 2026 08:49
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid IL sequence emitted for re-track from pointer to byref (part 2)

4 participants