DFG stops clearing `NodeMustGenerate` on checked arithmetic
Component: JSC DFG | 9f3eea6
JSC의 DFG tier는 산술 연산 결과가 Int32 범위를 벗어나지 않는다고 speculate합니다. 이때 bytecode profiling 결과에 따라 overflow와 negative-zero check(Arith::CheckOverflow, Arith::CheckOverflowAndNegativeZero)를 삽입하거나, 아예 생략하는 모드(Arith::Unchecked)를 선택하게 됩니다. 한편 NodeMustGenerate는 해당 node에 관찰되는 side effect가 있다고 표시하는 flag입니다. 이 flag가 붙어 있으면 숫자 결과가 사용되지 않더라도 DCE가 node를 제거하지 않습니다. check만 수행하는 node가 살아남는 방식이 바로 이것입니다.
기존 DFGFixupPhase는 div, mod, mul에 대해 arith mode를 선택한 뒤 NodeMustGenerate를 무조건 제거했습니다. 어떤 모드가 선택되었는지는 고려하지 않았습니다. 이번 수정에서는 flag를 제거하는 범위가 Arith::Unchecked node로 한정되었습니다.
Significance
abstract interpreter는 checked node의 overflow check를 결과가 Int32이고 negative zero가 아니라는 근거로 사용하며, 그 위에서 (y | 0) === y 같은 비교를 fold합니다. 그런데 flag를 제거하면 그 근거가 되었던 check 자체를 DCE가 삭제할 수 있게 됩니다. 결과적으로 compile된 코드에는 fold된 가정만 남고, 이를 뒷받침하던 런타임 검증은 사라집니다. Inc/Dec에서 bug 315213으로 이미 수정되었던 것과 동일한 unsound elimination 패턴입니다.
Audit directions
앞으로 주목할 패턴은, abstract interpreter의 증명이 뒤따르는 pass가 마음대로 삭제할 수 있는 node에 근거를 두는 경우입니다. abstract interpreter가 check node의 존재 자체로부터 타입이나 범위를 단정하는 지점이 있다면, 해당 node에는 DCE를 견딜 만큼 강한 liveness 보장이 필요합니다. 다만 두 가지는 서로 다른 phase에서 관리되며, 결합이 깨지는 원인도 정확히 여기에 있습니다. arith mode를 기준으로 동작하는 다른 abstract interpreter 규칙들, 그리고 그 결과를 받아 처리하는 constant folding 규칙들도 같은 관점에서 점검해 볼 필요가 있습니다. 코드 리뷰에서의 신호는 명확합니다. unchecked mode를 선택한 branch 바깥에서 clearFlags(NodeMustGenerate)나 이에 상응하는 처리가 수행되는 경우입니다.