Add RTCHECK node for compiler-inserted runtime check code#114369
Add RTCHECK node for compiler-inserted runtime check code#114369snickolls-arm wants to merge 1 commit intodotnet:mainfrom
Conversation
Introduces a new node type the compiler can introduce to create a runtime check that happens before a value is evaluated in the code tree. The node wraps some 'value' tree, and also depends on a 'check' tree. The check tree contains code that performs the runtime check, and the value tree is the actual value of this tree during normal execution (the runtime check does not pass). The node is marked such that it may throw an exception. Codegen should generate code for the check tree, and jump to throw an exception if the check tree evaluates to true. Otherwise, the node will be assigned a register value representing its wrapped value which can be used/forwarded as necessary. This patch implements this node for divide-by-zero and overflow checks on ARM64.
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
@jakobbotsch Follow on from #111543 This is an early draft of changes towards adding a new node to represent a runtime check. I've created a generalized node and then specialized it to divide-by-zero and division overflow. The first thing I've noticed is that it isn't able to optimize the flow graph like the QMARK implementation. Probably because processing statically decided checks happens quite late. I added some code to process these in rationalization but it would need to happen before flow graph optimizations. I haven't looked at assertion propagation, maybe this would help? |
| // | ||
| struct GenTreeRTCheck : public GenTreeOp | ||
| { | ||
| SpecialCodeKind gtThrowKind; // Kind of throw block to branch to on failure |
There was a problem hiding this comment.
Do you think using this pattern for the overflow check is going to come with any benefits? Optimizing multiple of those checks will generally require both the dividend and divisor to be equal, but in that case the whole division operation is also equal and can be CSE'd on its own.
I think the main interest is for the zero check, so I would just make it GT_ZEROCHECK and make it a normal GenTreeOp.
There was a problem hiding this comment.
I wrote it this way partly thinking about making it easier to add new checks in future. Having a common framework for if (expr) throw() could be helpful generally. I don't expect any performance benefit from doing it this way, except that it does allow the JIT to generate CMP+CCMP for the overflow check at the moment, which is good for lowering conditional branch density. I could just change the emitter to generate this pattern though, if it shows a benefit. I did something similar with the divide-by-zero checks recently (#111797).
| GenTreeRTCheck* Compiler::gtNewDivideByZeroCheck(GenTree* divisor) | ||
| { | ||
| assert(varTypeIsIntegral(divisor)); | ||
| GenTree* check = gtNewOperNode(GT_EQ, TYP_INT, gtCloneExpr(divisor), gtNewIconNode(0, genActualType(divisor))); |
There was a problem hiding this comment.
It's not legal to clone the divisor tree like this since it can have side effects. I would just remove this and call gtNewRTCheckNode directly from the importer, where we can ensure the proper side effect handling.
|
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
Introduces a new node type the compiler can introduce to create a runtime check that happens before a value is evaluated in the code tree.
The node wraps some 'value' tree, and also depends on a 'check' tree. The check tree contains code that performs the runtime check, and the value tree is the actual value of this tree during normal execution (the runtime check does not pass).
The node is marked such that it may throw an exception. Codegen should generate code for the check tree, and jump to throw an exception if the check tree evaluates to true. Otherwise, the node will be assigned a register value representing its wrapped value which can be used/forwarded as necessary.
This patch implements this node for divide-by-zero and overflow checks on ARM64.