Among many other things, morph (fgMorphTree, fgMorphSmpOp, fgMorphSmpOpOptional and others) performs simple optimizing tree transformations, like turning MOD(x, 2^k) !=/== 0 into AND(x, 2^k - 1) !=/== 0. Because morphing functions are called during the optimizations phases, they have to be careful when maintaining the IR state.
- Trees that are marked as CSE candidates cannot be deleted from the IR and their type cannot change. Use
gtIsActiveCSE_Candidateto check if any particular node can be removed. For non-trivial transforms, a full!optValnumCSE_phaseguard may be more appropriate. - Value numbers must be maintained (including when the tree is being retyped). If you are changing a value of a constant, you can update it via the
fgUpdateConstTreeValueNumberhelper method.SetVNsFromNodemethod can be used to quickly transfer a VN from one tree to another, for example if the root tree is being replaced with one of its children. For transforms that cannot easily preserve VNs,fgGlobalMorphguard is needed (note:vnStore != nullptrwould be a better guard but it is not used). Remember that VNs need to be updated when the set of exceptions the tree produces changes. - Execution order of side effecting trees must be maintained, always. Remember to take the possibility of
GTF_REVERSE_OPSbeing set into account. - Side effect flags must be maintained. The
GTF_RELOP_JMP_USEDflag must be preserved on relops. - Some trees cannot be modified as some part of the Jit expects them to have a certain shape.
GT_MULs on 32 bit marked with theGT_MUL_64RSLTmust have the following shape:MUL(CAST(int <- long), CAST(int <- long) | CONST). Relops marked withGTF_RELOP_JMP_USEDmust not be detached from their parentGT_JTRUE. Constant expressions involving handles cannot always be folded (useop->AsIntConCommon()->ImmedValCanBeFolded(compiler, oper)to check). - Field sequences must be accounted for and maintained. Watch out for the zero-offset cases (call
fgAddFieldSeqForZeroOffsetas appropriate)! - In global morphing, implicit byrefs are rewritten into indirections, while their addresses are collapsed. Remember to take this into account, especially if your transform looks down
IND(ADDR(x))trees in pre-order! If you simplify it tox, the implicit byrefs morphing will not kick in. Remember to take the possibility of demoted promoted fields into account - they are in many respects very similar to implicit byrefs themselves.
- Prefer guarding optimizing transforms with
opts.OptimizationEnabled(). - Use
DEBUG_DESTROY_NODEon removed nodes. - Prefer bashing (
SetOper/ChangeOper- don't forget to usePRESERVE_VNwhen that's appropriate) to creating a new node when possible, as that's beneficial for throughput. Remember that the source oper must have the default node size not less than the target one.