Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JIT: Support some assignment decomposition in physical promotion (#85105
) Add support for directly initializing and copying into replacements instead of doing a struct local and read back. Physically promoted struct locals used as sources are still handled conservatively (by first writing them back to stack, then doing the copy). For example, for a case like ``` void Foo() { S s = _field; s.A = s.B + 3; Consume(s); } struct S { public int A, B; } ``` We see the following: ``` STMT00000 ( 0x000[E-] ... 0x006 ) [000003] -A-XG------ ▌ ASG struct (copy) [000002] D------N--- ├──▌ LCL_VAR struct<Program+S, 8> V01 loc0 [000001] ---XG------ └──▌ FIELD struct Program:_field [000000] ----------- └──▌ LCL_VAR ref V00 this (last use) Processing block operation [000003] that involves replacements New statement: STMT00000 ( 0x000[E-] ... 0x006 ) [000029] -A-XG------ ▌ COMMA int [000021] -A-XG------ ├──▌ ASG int [000015] D------N--- │ ├──▌ LCL_VAR int V03 tmp1 [000020] ---XG------ │ └──▌ IND int [000018] ----------- │ └──▌ ADD ref [000016] ----------- │ ├──▌ LCL_VAR ref V00 this [000017] ----------- │ └──▌ CNS_INT long 8 [000028] -A-XG------ └──▌ ASG int [000022] D------N--- ├──▌ LCL_VAR int V04 tmp2 [000027] ---XG------ └──▌ IND int [000025] ----------- └──▌ ADD ref [000023] ----------- ├──▌ LCL_VAR ref V00 this [000024] ----------- └──▌ CNS_INT long 12 ``` The logic is currently quite rudimentary when it comes to holes/uncovered parts of the struct. For example, in the above case if we add another unused field at the end of S then the result is: ``` STMT00000 ( 0x000[E-] ... 0x006 ) [000003] -A-XG------ ▌ ASG struct (copy) [000002] D------N--- ├──▌ LCL_VAR struct<Program+S, 12> V01 loc0 [000001] ---XG------ └──▌ FIELD struct Program:_field [000000] ----------- └──▌ LCL_VAR ref V00 this (last use) Processing block operation [000003] that involves replacements Struct operation is not fully covered by replaced fields. Keeping struct operation. New statement: STMT00000 ( 0x000[E-] ... 0x006 ) [000030] -A-XG------ ▌ COMMA struct [000021] -A-XG------ ├──▌ ASG int [000015] D------N--- │ ├──▌ LCL_VAR int V03 tmp1 [000020] ---XG------ │ └──▌ IND int [000018] ----------- │ └──▌ ADD ref [000016] ----------- │ ├──▌ LCL_VAR ref V00 this [000017] ----------- │ └──▌ CNS_INT long 8 [000029] -A-XG------ └──▌ COMMA struct [000028] -A-XG------ ├──▌ ASG int [000022] D------N--- │ ├──▌ LCL_VAR int V04 tmp2 [000027] ---XG------ │ └──▌ IND int [000025] ----------- │ └──▌ ADD ref [000023] ----------- │ ├──▌ LCL_VAR ref V00 this [000024] ----------- │ └──▌ CNS_INT long 12 [000003] -A-XG------ └──▌ ASG struct (copy) [000002] D------N--- ├──▌ LCL_VAR struct<Program+S, 12> V01 loc0 [000001] ---XG------ └──▌ FIELD struct Program:_field [000000] ----------- └──▌ LCL_VAR ref V00 this ``` In this case it would be significantly more efficient to copy only the remainder, which is just a small part of the struct. However, in the general case it is not easy to predict the most efficient way to do this, and in some cases we cannot even represent the hole in JIT IR (if it involves GC pointers), so I have left this for a future change for now. Liveness should also be beneficial for that as there are many cases where we would expect the remainder to be dead.
- Loading branch information