KEMBAR78
Use type hint in pointer arithmetic by ds5678 · Pull Request #3413 · icsharpcode/ILSpy · GitHub
Skip to content

Conversation

@ds5678
Copy link
Contributor

@ds5678 ds5678 commented Feb 25, 2025

Resolves #3411

Problem

This makes pointer arithmetic prettier with the help of available type hints.

Solution

  • Any comments on the approach taken, its consistency with surrounding code, etc.
    • I tried to be as surgical as possible.
  • Which part of this PR is most in need of attention/improvement?
    • There was a slight behavior change involving sbyte* vs byte*. I want to know if that's okay or not. Previously, byte* got picked because it's the default when things go wrong. However, now the type hint (which is often signed) prevents some of those situations.
    • I think the type hints could be better if they had sign information in more cases, but I was hesitant to change the code that sets the type hint.
  • At least one test covering the code changed

@ElektroKill
Copy link
Contributor

Could we have an option for this? I think it's a matter of preference which version of the decompilation is preferred (using only byte offsets or the offsets using the size of the type of the pointer), and it could be something that is configurable.

@ds5678
Copy link
Contributor Author

ds5678 commented Mar 15, 2025

An option for what specifically?

  • My change (using expression result type hints to influence code generation of pointer arithmetic)
  • Assuming all pointers are byte*
  • Something else?

@ElektroKill
Copy link
Contributor

An option for what specifically?

* My change (using expression result type hints to influence code generation of pointer arithmetic)

* Assuming all pointers are `byte*`

* Something else?

Your change makes the decompiler change the pointer arithmetic to be based on the size of the pointer type, while previously, it used to use byte offsets and then cast it to the necessary type pointer. I suggest an option to make this new behavior a toggleable option since, in my opinion, it's really a matter of preference.

TLDR: Make the change in PR toggleable with a deecompiler option.

@ds5678
Copy link
Contributor Author

ds5678 commented Mar 15, 2025

I don't see why this:

*(int*)((byte*)ptr + (nint)2 * (nint)4) = 1;

would be preferable to:

((int*)ptr)[2] = 1;

@ElektroKill
Copy link
Contributor

I don't see why this:

*(int*)((byte*)ptr + (nint)2 * (nint)4) = 1;

would be preferable to:

((int*)ptr)[2] = 1;

Yes, this is clearer. However, this is not what initially sparked my suggestion for the option to be added.

What made me suggest the option was the changes that occurred in this test as a effect of the change:
https://github.com/icsharpcode/ILSpy/pull/3413/files#diff-efb55f39bb23d80b28a5b409a98149cef496df7c6cf653dbdf26f22dc6f46226L225-R226

The lines:

*(int*)(num - 4) = 2;
*(int*)(num - 8) = 3;

got changed to:

*((int*)num - 1) = 2;
*((int*)num - 2) = 3;

In my opinion, the code decompilation before the change was clearer as we are working on a byte* pointer in this context, and it would make more sense to do the pointer math on the byte pointer rather than first casting the pointer.

@ds5678
Copy link
Contributor Author

ds5678 commented Mar 15, 2025

While there is room for tweaking the rules about when to use the type hint instead of the natural type, I disagree that it carries enough importance to be elevated to a configuration option.

I have a few suggestions for a rule change:

  • When the natural type is byte*, ignore the type hint.
  • When the natural type and type hint are both numeric primitives, ignore the type hint if the natural type is the same size or smaller.
  • When the natural type and type hint are both numeric primitives, ignore the type hint.
  • When the natural type and type hint are both numeric primitives, ignore the type hint unless one is integral and the other is floating point.

What are your thoughts?

@ElektroKill
Copy link
Contributor

While there is room for tweaking the rules about when to use the type hint instead of the natural type, I disagree that it carries enough importance to be elevated to a configuration option.

I have a few suggestions for a rule change:

* When the natural type is `byte*`, ignore the type hint.

* When the natural type and type hint are both numeric primitives, ignore the type hint if the natural type is the same size or smaller.

* When the natural type and type hint are both numeric primitives, ignore the type hint.

* When the natural type and type hint are both numeric primitives, ignore the type hint unless one is integral and the other is floating point.

What are your thoughts?

I think tweaking the output of pointer arithmetic is indeed the best option. In my opinion something like this would be good:

  • If the pointer type is byte* and the type of the pointer operation is a primitive, we should keep the pointer arithmetic on byte* pointer.
  • If the pointer is void*, then we should just use the type of the pointer operation since we can't do arithmetic on void*.
  • If the pointer is of a primitive type and the pointer operation is of the same type, or of the same size but different sign, I think using the pointer type is best.
  • if the pointer operation type is different than the pointer type, then we use the operation type for arithmetic.

Let me know what you think!

@ds5678
Copy link
Contributor Author

ds5678 commented Mar 15, 2025

When the natural type is byte* and the type hint is a primitive, ignore the type hint.

It sounds like you prefer this modification to the current rules. The rest of your bullet points are already part of the current rule set.

@ds5678
Copy link
Contributor Author

ds5678 commented Mar 15, 2025

Actually, I'm not sure that's the best change. It results in your desired outcome.

// Source and decompiled the same
*(int*)(ptr - 8) = 3;

However, it also makes this code decompile poorly.

// Source
*((int*)ptr - 2) = 3;

// Decompiled
*(int*)(ptr - (nint)2 * (nint)4) = 3;

@ElektroKill
Copy link
Contributor

ElektroKill commented Mar 15, 2025

Oh, I'd expect the decompiler here to simplify the multiplication there, considering it already does simplify some simple arithmetic when converting the ILAst to C#. I think this is because of the nint cast coming from the conv.i instruction inserted by the C# compiler. I'm puzzled as to why the decompiler chooses to maintain these casts since they are not changing the behavior.
These two are equivalent.

*(int*)(ptr - (nint)2 * (nint)4) = 3;
*(int*)(ptr - 8) = 3;

There really is no way to decompile these kinds of expressions the same way they are written, and we just need to find the best compromise, so I'm not sure what the best way is to approach this.

@siegfriedpammer
Copy link
Member

@ds5678 I don't want to comment on the changes in this PR, because it's @dgrunwald's area of expertise. Thank you for your understanding...

@ds5678 ds5678 force-pushed the use-type-hint-in-pointer-arithmetic branch from 91050e0 to 4c4a02d Compare March 30, 2025 19:08
@ds5678 ds5678 force-pushed the use-type-hint-in-pointer-arithmetic branch from 0055cb5 to 8de6585 Compare April 5, 2025 19:14
@ds5678
Copy link
Contributor Author

ds5678 commented Apr 6, 2025

I don't want to comment on the changes in this PR, because it's dgrunwald's area of expertise.

I notice that he doesn't seem to be active on GitHub. To set proper expectations, how long should we wait for his review before moving forward?

Copy link
Member

@dgrunwald dgrunwald left a comment

Choose a reason for hiding this comment

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

I think this change looks good except for the small detail with the repeated call.

@ds5678
Copy link
Contributor Author

ds5678 commented Apr 6, 2025

Thank you for the review! I implemented the change you requested.

@dgrunwald dgrunwald merged commit b50d68c into icsharpcode:master Apr 8, 2025
5 checks passed
@ds5678 ds5678 deleted the use-type-hint-in-pointer-arithmetic branch April 8, 2025 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pointer Arithmetic Prettiness

4 participants