Removed MaxStackSize setter. #892
Loading…
Reference in a new issue
No description provided.
Delete branch "kekyo/fix-maxstack-ignored"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When generating assemblies using cecil, I manually specified
.maxstackfor MethodBody, but it is not reflected. It is always overwritten with the calculated result. This problem has been fixed in this PR.Example:
The
CodeReadersetsmax_stack_sizeto a nonzero value for methods with a fat header:github.com/jbevain/cecil@870ce3ed2b/Mono.Cecil.Cil/CodeReader.cs (L118)This will certainly break workflows such as loading an assembly, adding instructions which increase the max stack size, then writing the assembly. The writer would assume the max stack size was set explicitly by the user.
The following would be a safer alternative:
But then, changes that decrease the max stack size wouldn't be taken into account automatically. So maybe you need a "maxstack is set explicitly" flag.
Also, note this only applies to fat headers. Cecil will always write a tiny header if possible. I'm actually curious why you need this change in the first place.
I'm writing an assembler tooling and using cecil for output. One time I noticed that the value of
.maxstackoutput by cecil sometimes differed from the expected value.As it turns out, the problem was a mistake in my assembler code, but it made me think a bit about calculating the value of maxstack. For example, which is a rather aggressive:
Even if a static execution flow analysis is performed, complete maxstack size cannot be calculated in advance. Therefore, the person writing the assembly code must manually set an appropriate size for maxstack.
Of course in most cases, the size should be set to the size obtained from the static execution flow analysis, so I believe we only need to deal with this exceptional situation. So, I explicitly specified a value for the
MaxStackSizeproperty only if a size was specified, but this was not reflected.I completely agree and it was my mistake that the existing implementation was affected. May I make additional changes? I will use the "maxstack is set explicitly" flag as suggested.
I wasn't sure if the setter for the
MaxStackSizeproperty should check to see if the value has been changed, but I determined that the intention was to change it when the setter was called. How about this?Oh, you're misunderstanding what maxstack means. It's not the max size of the execution stack, but the max size of the IL evaluation stack. The IL evaluation stack size is constant for each IL instruction.
See ECMA-335 III.1.7.4:
TL;DR: It can always be calculated statically, and Cecil does the right thing. 🙂
I'm not the maintainer, just a guy on the internet trying to help.
Thanks for reviewing my code! I await comments from the maintainer.
I understand the difference between the runtime stack (as indicated by
rspandrbpin x64) and the evaluation stack. And I also understand that the topic about maxstack is about the evaluation stack.The sample code indicates that "If the max stack size of the evaluation stack changes at "run-time", max stack size cannot be calculated by static analysis."
I could not read the meaning from the description in the ECMA specification that the evaluation stack consumption is always statically determined. In fact, if we write code like the sample code above, we will see that it is not possible to estimate it in advance. And it causes
InvalidProgramExceptionwhen maxstack is insufficient.I thought there might be a need to explicitly specify maxstack when outputting assemblies.
Ok, I see. If you're trying to write custom tooling (something that replaces the .NET JIT), then this makes sense. Your code could accept a variable evaluation stack depth.
Just be aware this is not correct ECMA-335 IL though, and is not accepted by the .NET JIT. You could support that as a custom extension to the standard.
Here's an example project: ConsoleApp.zip, it does an inifite loop while pushing an item to the stack. The .NET JIT immediately rejects it with an
InvalidProgramException.See the III.1.7.5 paragraph from ECMA-335 which defines the related correctness rule (emphasis mine):
The rules for merging evaluation stack frames described in III.1.8.1.3 could also be relevant, even though they determine verifiability (and not correctness).
Thanks for showing the pointer. I referred to the text of ECMA, as it seems I only looked at the quoted part and misinterpreted it.
And I understood that in III.1.7.4 and III.1.7.5, the statically analyzed maxstack must be set and its value must be correct.
I now understand that this means that an instruction stream like the sample code I showed can be written, but it is not guaranteed (nor should it be) that the CLR can execute it. Your point is absolutely correct!!! 😄 If someone asks me, I can now explain to them that they should not write such CIL code.
So, back to cecil, I always feel that the
MaxStackSizeproperty should not have a setter, since the maxstack can be calculated by static analysis. TheCoreReaderstores the value directly in the backing store of theMaxStackSizeproperty. So without a setter, cecil users will be able to treatMaxStackSizeas a view in the true sense.I pushed the code modified to do so. Thank you explain it real issue again!
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.