Skip to content

Latest commit

 

History

History
72 lines (64 loc) · 6.17 KB

EdoOptimization.md

File metadata and controls

72 lines (64 loc) · 6.17 KB

Mechanics of the Exception Directed Optimization (EDO) Optimization

Exception-Directed Optimization (EDO) enables more aggressive inlining of methods that throw and catch exceptions to facilitate the transformation of a throw operation into a goto by Value Propagation (VP). Typically, profiling is used to determine which throw-catch pairs happen often enough to warrant extra inlining.

In OpenJ9, the EDO mechanism starts by adding a snippet that decrements the TR_PersistentJittedBodyInfo._counter every time a catch block is executed. This is done in VMgenerateCatchBlockBBStartPrologue() and is gated by a test to fej9->shouldPerformEDO(block, comp). The test makes sure that we don't instrument big methods (that have more than TR::Options::_catchSamplingSizeThreshold nodes) or if EDO is disabled with -Xjit:disableEDO. When TR_PersistentJittedBodyInfo._counter reaches zero, the snippet will execute a jump to a recompilation helper and the method will be recompiled at the next optimization level (typically hot). Since higher optimization levels are more aggressive with inlining, sometimes this is enough to bring the throw and the catch in the method being compiled. If that happens, VP may change the throw into a goto (this transformation can be disabled with -Xjit:disableThrowToGoto), and the process stops there.

The second step is to profile the frequency of the catch block in the newly compiled body with TR_CatchBlockProfiler which is created in J9::Recompilation::beforeOptimization(). This is a very simple profiler that only includes two counters: one for throws and one for catch operations (the counter for throws appears to be unused by the optimizer). These counters are applied globally for the entire method and not per catch block/throw call-site. The instrumentation is done by the recompilationModifier optimization which goes over the list of registered profilers and calls modifyTrees() on them. In particular, TR_CatchBlockProfiler::modifyTrees() allocates a TR_CatchBlockProfileInfo object with persistent memory (if it doesn't already exist) and, for each catch block it inserts an increment instruction for the TR_CatchBlockProfileInfo._catchCounter field. It's important to note that recompilationModifier is an optimization pass enabled on-demand in Optimization::switchToProfiling() if a few conditions are true:

  • the optimizer decides to switch to profiling for some reason
  • the compilation does not use "optServer" mode (unless AggressiveSwitchingToProfiling is on)

The third step is to recompile again the method and to use the collected profiling data to guide the inliner to inline more on the throw-catch call-graph. The TR_EstimateCodeSize estimator has a boolean field called _aggressivelyInlineThrows which is set to true when the method has catch-block profile information and the catch counter is greater than TR_CatchBlockProfileInfo::EDOThreshold. Under those conditions the estimator under-estimates the size of the callees with throws so that they are more likely to be inlined. If inlining succeeds and brings the throw into the code for the method being compiled, the VP may then transform the throw into a goto operation.