This pass propagates indirect loads through the PHI node for its
address to make the load source possibly non-addressable and to
allow for PHI optimization to trigger.
For example the pass changes
# addr_1 = PHI <&a, &b>
tmp_1 = *addr_1;
to
# tmp_1 = PHI <a, b>
but also handles more complex scenarios like
D.2077_2 = &this_1(D)->a1;
...
# b_12 = PHI <&c(2), D.2077_2(3)>
D.2114_13 = *b_12;
...
# b_15 = PHI <b_12(4), &b(5)>
D.2080_5 = &this_1(D)->a0;
...
# b_18 = PHI <D.2080_5(6), &c(7)>
...
# b_21 = PHI <b_15(8), b_18(9)>
D.2076_8 = *b_21;
where the addresses loaded are defined by PHIs itself.
The above happens for
std::max(std::min(a0, c), std::min(std::max(a1, c), b))
where this pass transforms it to a form later PHI optimization
recognizes and transforms it to the simple
D.2109_10 = this_1(D)->a1;
D.2110_11 = c;
D.2114_31 = MAX_EXPR <D.2109_10, D.2110_11>;
D.2115_14 = b;
D.2125_17 = MIN_EXPR <D.2115_14, D.2114_31>;
D.2119_16 = this_1(D)->a0;
D.2124_32 = MIN_EXPR <D.2110_11, D.2119_16>;
D.2076_33 = MAX_EXPR <D.2125_17, D.2124_32>;
The pass does a dominator walk processing loads using a basic-block
local analysis and stores the result for use by transformations on
dominated basic-blocks.
Structure to keep track of the value of a dereferenced PHI result
and the virtual operand used for that dereference.