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

[API Proposal]: Make OpCode.StackChange() public #93419

Closed
buyaa-n opened this issue Oct 12, 2023 · 10 comments · Fixed by #93244
Closed

[API Proposal]: Make OpCode.StackChange() public #93419

buyaa-n opened this issue Oct 12, 2023 · 10 comments · Fixed by #93244
Labels
api-approved API was approved in API review, it can be implemented area-System.Reflection.Emit
Milestone

Comments

@buyaa-n
Copy link
Member

buyaa-n commented Oct 12, 2023

Background and motivation

OpCodes represents MS IL instructions that used for emitting IL with ILGenerator. An OpCode instruction may pop value(s) from stack as an operand of the instruction and also may push value(s) into the stack as a result. This stack change is needed for calculating MaxStack value that is essential for populating method body. I could use the StackBehaviourPop and StackBehaviourPush properties of OpCode to calculate this, but there is an internal method that gives this info directly:

internal int StackChange() =>
m_flags >> StackChangeShift;

Making this public will be useful for calculating MaxStack faster
Related to https://github.com/dotnet/runtime/pull/93244/files#r1353013060

API Proposal

namespace System.Reflection.Emit

public readonly partial struct OpCode : System.IEquatable<System.Reflection.Emit.OpCode>
{
    ...
    public StackBehaviour StackBehaviourPop { get }
    public StackBehaviour StackBehaviourPush { get }
    ...
+   public int EvaluationStackDelta { get }
}

Alternative Designs

Other naming options: EvaluationStackDelta, StackSizeDelta, StackTransitionDelta or StackDepthDelta as it is showing the difference of evaluation stack transition.

API Usage

    private void UpdateStackSize(OpCode opCode)
    {
        _currentStack += opCode.EvaluationStackDelta;
        _maxStackSize = Math.Max(_maxStackSize, _currentStack);
    }
@buyaa-n buyaa-n added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Reflection.Emit labels Oct 12, 2023
@ghost
Copy link

ghost commented Oct 12, 2023

Tagging subscribers to this area: @dotnet/area-system-reflection-emit
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

OpCodes represents MS IL instructions that used for emitting IL with ILGenerator. An OpCode instruction may pop value(s) from stack as an operand of the instruction and also may push value(s) into the stack as a result. This stack change is needed for calculating MaxStack value that is essential for populating method body. I could use the StackBehaviourPop and StackBehaviourPush properties of OpCode to calculate this, but there is an internal method that gives this info directly:

internal int StackChange() =>
m_flags >> StackChangeShift;

Making this public will be useful for calculating MaxStack faster
Related to https://github.com/dotnet/runtime/pull/93244/files#r1353013060

API Proposal

namespace System.Reflection.Emit

public readonly partial struct OpCode : System.IEquatable<System.Reflection.Emit.OpCode>
{
    ...
    public StackBehaviour StackBehaviourPop { get }
    public StackBehaviour StackBehaviourPush { get }
    ...
+   public int StackDifference()
}

API Usage

    private void UpdateStackSize(OpCode opCode)
    {
        _currentStack += opCode.StackDifference();
        _maxStackSize = Math.Max(_maxStackSize, _currentStack);
    }

Alternative Designs

It can be a property too

namespace System.Reflection.Emit

public readonly partial struct OpCode : System.IEquatable<System.Reflection.Emit.OpCode>
{
    ...
    public StackBehaviour StackBehaviourPop { get }
    public StackBehaviour StackBehaviourPush { get }
    ...
+   public int StackDifference { get }
}

Risks

No response

Author: buyaa-n
Assignees: -
Labels:

api-suggestion, area-System.Reflection.Emit

Milestone: -

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Oct 12, 2023
@buyaa-n buyaa-n added this to the 9.0.0 milestone Oct 12, 2023
@buyaa-n buyaa-n added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Oct 12, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Oct 12, 2023
@alexrp
Copy link
Contributor

alexrp commented Oct 12, 2023

Seems like this should be a property?

@buyaa-n
Copy link
Member Author

buyaa-n commented Oct 16, 2023

Seems like this should be a property?

Yes, can be a property and that is mentioned in the Alternative Designs part

@terrajobst
Copy link
Member

How expensive is the computation?

I assume it's just a switch over the op code as the values are static? In that case I'd be in favor of a property.

I also assume a positive value means the op code net-ads to the evaluation stack (pushing), a negative value means net consumption (popping), and zero means no change or equal amounts of pushing and popping.

If so, I think I'd prefer EvaluationStackDelta.

@dotnet/fxdc?

@buyaa-n
Copy link
Member Author

buyaa-n commented Oct 18, 2023

How expensive is the computation?

The OpCode has this info in the upper 4 bits of m_flags field:

internal const int StackChangeShift = 28; // XXXX0000000000000000000000000000

So the computation is just shifting those bits:
internal int StackChange() =>
m_flags >> StackChangeShift;

I also assume a positive value means the op code net-ads to the evaluation stack (pushing), a negative value means net consumption (popping), and zero means no change or equal amounts of pushing and popping.

Yep, that is right

@terrajobst
Copy link
Member

terrajobst commented Oct 18, 2023

Oh wow. I didn't realize the op code value encoded the stack change. In other metadata readers I've always seen people doing switches facepalm

Also, I didn't realize we already have properties called StackBehaviuor(Push|Pop). In that case I rescind my proposal to include the term Evalutation. I think I still prefer the word Delta over Difference though.

Anyone else on @dotnet/fxdc?

namespace System.Reflection.Emit;

public partial struct OpCode
{
    // Existing:
    // public StackBehaviour StackBehaviourPop { get; }
    // public StackBehaviour StackBehaviourPush { get; }

    public int StackDelta { get; }
}

@steveharter
Copy link
Member

Slightly more verbose, but I think StackSizeDelta rings a bit better than StackDelta which is somewhat ambiguous.

@buyaa-n
Copy link
Member Author

buyaa-n commented Oct 19, 2023

Another option could be StackTransitionDelta, as it is showing the difference of evaluation stack transition/movement, also could be StackDepthDelta.

@terrajobst
Copy link
Member

Either of those names work for me :-)

@bartonjs
Copy link
Member

bartonjs commented Oct 24, 2023

Video

Looks good as proposed.

namespace System.Reflection.Emit

public readonly partial struct OpCode : System.IEquatable<System.Reflection.Emit.OpCode>
{
    public int EvaluationStackDelta { get }
}

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Oct 24, 2023
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Oct 24, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Oct 25, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Nov 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Reflection.Emit
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants