Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unsafe expressions after store sinking #7573

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

hzongaro
Copy link
Contributor

In the process of moving stores whose results are not needed on all paths, the General Store Sinking optimization will decide whether the tree representing the store should be copied or simply moved. A tree might be copied if nodes in its subtrees are commoned — leaving the part of the tree that represents the value anchored beneath a TR::treetop node ensures the order of evaluation is maintained.

However, it is possible for a tree that was left in place like this to contain a load of some symbol whose store had preceded the current tree, but that earlier store was also sunk past the current tree. This usually doesn't pose a problem, as the load must have been unneeded at that point, and a subsequent pass of Dead Trees Elimination would ordinarily remove the unneeded uses of the load of the symbol. However, it might happen that Dead Trees Elimination fails to remove that load, as was described in OpenJ9 issue 17515.

Even then the load that was left behind would often not cause a problem — the value would have to be used in a situation where use of an uninitialized symbol might result in a crash. For example, if the result of the load is used as the child of an arraylength operation, as one of the operands of an arraycmp operation, or as the denominator of an idiv or ldiv operation. It could also cause a problem for garbage collection if the symbol was thought to contain a valid reference.

The Isolated Store Elimination optimization deals with the same situation using its UnsafeSubexpressionRemoval class.
The UnsafeSubexpressionRemoval class is used to handle trees that might contain references to symbols that would be unset following a transformation that removed dead stores, anchoring the parts of the tree that are still safe to evaluate and removing those parts that are unsafe to evaluate.

This change factors out UnsafeSubexpressionRemoval from Isolated Store Elimination, and it adds changes to General Store Sinking to test whether a load of such a dead symbol might remain in a tree that was left behind by Store Sinking, anchoring safe parts and removing unsafe parts.

I will add some further examples and details about the IL that will result below.

Fixes eclipse-openj9/openj9#17515
Fixes eclipse-openj9/openj9#20283

The Store Sinking optimization contain calls to dumpMethodTrees that
were explicitly disabled.  This hampers debugging of the optimization.
This change dumps the method trees before and after the optimization if
tracing of the optimization has been enabled.

Signed-off-by:  Henry Zongaro <[email protected]>
UnsafeSubexpressionRemoval is currently used in IsolatedStoreElimination
where it is defined.  The Store Sinking optimization requires the utility
provided by UnsafeSubexpressionRemoval, so this change moves the
declaration and implementation of that class into there own files.

Signed-off-by:  Henry Zongaro <[email protected]>
The nextStoreWasMoved parameter of sinkStorePlacement is never
referenced.  This change removes this unused parameter.

Signed-off-by:  Henry Zongaro <[email protected]>
Store Sinking might leave behind trees that contain commoned loads
if necessary to preserve the order of evaluation of operations.  It
is possible for such trees to contain a load of a symbol that had a
value assigned to it in a tree before the current tree, but that was
also moved by Store Sinking.

Typically such a load would end up being removed by Dead Trees
Elimination, but if that optimization fails to run or fails to run to
completion, such a load of an uninitialized symbol might be left behind.
That can be especially problematic if it is used in an indirect
reference.

This change checks for such cases and makes use of
UnsafeSubexpressionRemover to rework such a tree.  It will keep only the
parts that are safe to evaluate and vandalize any loads that are unsafe
to evaluate.

Signed-off-by:  Henry Zongaro <[email protected]>
@hzongaro
Copy link
Contributor Author

hzongaro commented Nov 29, 2024

Following is an example that can be used to reproduce the problematic IL in method sub — the method junk exists only in the hope that stack slots used by sub might end up containing zeros, resulting in an unexpected division by zero:

TestStoreSinking3.java
public class TestStoreSinking3 {
    public static final int junk(int val) {
        int i = 0, j = 0, k = 0, l = 0;
        switch (val) {
        case 0: i++;
        case 1: j++;
        case 2: k++;
        case 3: l++;
        default:
        }
        return i + j + k + l;
    }

    public static final int sub(int p, int q, boolean cond) {
        int qp1 = 0;
        if (q <= 0 || q > 100) return 1;  // Ensure DIVCHK can be removed
        qp1 = q + 1;
        int r = 19;
        if (p > 1000) return 2;
        int s = (p + q + 1) + (p - q + 2)/qp1 + (r = p + 3);
        if (cond) return 17;
        return s + r + qp1;
    }

    public static final void caller() {
        junk(4);
        System.out.println(sub(60, 5, true));
    }

    public static final void main(String[] args) {
//        System.out.println(sub(60, 5, true));
//        System.out.println(sub(60, 5, true));
//        System.out.println(sub(60, 5, true));
        caller();
        caller();
        caller();
    }
}

Compiled with the options

-Xjit:{TestStoreSinking3.[sc]*}\(traceGeneralStoreSinking,optDetails,log=teststoresinking3.old.log,dontInline={*}\),optLevel=hot,count=1,disableAsyncCompilation,lastOptIndex=180,disableLocalCSE -XX:-EnableHCR

the IL that results for the assignments

        qp1 = q + 1;
         …
        int s = (p + q + 1) + (p - q + 2)/qp1 + (r = p + 3);

in sub prior to General Store Sinking looks like this:

Part of IL for sub prior to General Store Sinking
n3n       BBStart <block_5> (freq 7001) (extension of previous block)                         [0x7f4a502b59d0] bci=[-1,14,17] rc=0 vc=330 vn=- li=- udi=- nc=0
n26n      istore  <auto slot 3>[#424  Auto] [flags 0x3 0x0 ]                                  [0x7f4a502b6100] bci=[-1,17,17] rc=0 vc=330 vn=- li=3 udi=1 nc=1
n25n        isub (X>=0 cannotOverflow )                                                       [0x7f4a502b60b0] bci=[-1,16,17] rc=1 vc=330 vn=- li=- udi=- nc=2 flg=0x1100
n23n          iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )    [0x7f4a502b6010] bci=[-1,14,17] rc=1 vc=330 vn=- li=- udi=6 nc=0 flg=0x1100
n24n          iconst -1 (X!=0 X<=0 )                                                          [0x7f4a502b6060] bci=[-1,15,17] rc=1 vc=330 vn=- li=- udi=- nc=0 flg=0x204
  …
n5n       BBStart <block_7> (freq 6501) (extension of previous block)                         [0x7f4a502b5a70] bci=[-1,31,20] rc=0 vc=330 vn=- li=- udi=- nc=0
n53n      istore  <auto slot 4>[#425  Auto] [flags 0x3 0x0 ]                                  [0x7f4a50331050] bci=[-1,48,20] rc=0 vc=330 vn=- li=4 udi=2 nc=1
n52n        isub (cannotOverflow )                                                            [0x7f4a50331000] bci=[-1,46,20] rc=2 vc=330 vn=- li=- udi=- nc=2 flg=0x1000
n50n          iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )         [0x7f4a502b6880] bci=[-1,44,20] rc=1 vc=330 vn=- li=- udi=8 nc=0 flg=0x1000
n51n          iconst -3 (X!=0 X<=0 )                                                          [0x7f4a502b68d0] bci=[-1,45,20] rc=1 vc=330 vn=- li=- udi=- nc=0 flg=0x204
n55n      istore  <auto slot 5>[#426  Auto] [flags 0x3 0x0 ]                                  [0x7f4a503310f0] bci=[-1,51,20] rc=0 vc=330 vn=- li=5 udi=3 nc=1
n54n        isub                                                                              [0x7f4a503310a0] bci=[-1,50,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n49n          iadd                                                                            [0x7f4a502b6830] bci=[-1,43,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n40n            iadd                                                                          [0x7f4a502b6560] bci=[-1,35,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n38n              iadd (cannotOverflow )                                                      [0x7f4a502b64c0] bci=[-1,33,20] rc=1 vc=330 vn=- li=- udi=- nc=2 flg=0x1000
n36n                iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )   [0x7f4a502b6420] bci=[-1,31,20] rc=1 vc=330 vn=- li=- udi=9 nc=0 flg=0x1000
n37n                iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6470] bci=[-1,32,20] rc=1 vc=330 vn=- li=- udi=10 nc=0 flg=0x1100
n47n              idiv                                                                        [0x7f4a502b6790] bci=[-1,42,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n45n                isub                                                                      [0x7f4a502b66f0] bci=[-1,40,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n43n                  isub                                                                    [0x7f4a502b6650] bci=[-1,38,20] rc=1 vc=330 vn=- li=- udi=- nc=2
n41n                    iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )  [0x7f4a502b65b0] bci=[-1,36,20] rc=1 vc=330 vn=- li=- udi=11 nc=0 flg=0x1000
n42n                    iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6600] bci=[-1,37,20] rc=1 vc=330 vn=- li=- udi=12 nc=0 flg=0x1100
n44n                  iconst -2 (X!=0 X<=0 )                                                  [0x7f4a502b66a0] bci=[-1,39,20] rc=1 vc=330 vn=- li=- udi=- nc=0 flg=0x204
n46n                iload  <auto slot 3>[#424  Auto] [flags 0x3 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6740] bci=[-1,41,20] rc=1 vc=330 vn=- li=- udi=13 nc=0 flg=0x1100
n52n            ==>isub
n39n          iconst -1 (X!=0 X<=0 )                                                          [0x7f4a502b6510] bci=[-1,34,20] rc=1 vc=330 vn=- li=- udi=- nc=0 flg=0x204

without the fix, this becomes:

Part of IL for sub following General Store Sinking, without the fix
n3n       BBStart <block_5> (freq 7001) (extension of previous block)                         [0x7f4a502b59d0] bci=[-1,14,17] rc=0 vc=334 vn=- li=- udi=- nc=0
n26n      treetop                                                                             [0x7f4a502b6100] bci=[-1,17,17] rc=0 vc=343 vn=- li=- udi=1 nc=1
n25n        isub (X>=0 cannotOverflow )                                                       [0x7f4a502b60b0] bci=[-1,16,17] rc=1 vc=343 vn=- li=- udi=- nc=2 flg=0x1100
n23n          iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )    [0x7f4a502b6010] bci=[-1,14,17] rc=1 vc=343 vn=- li=- udi=6 nc=0 flg=0x1100
n24n          iconst -1 (X!=0 X<=0 )                                                          [0x7f4a502b6060] bci=[-1,15,17] rc=1 vc=343 vn=- li=- udi=- nc=0 flg=0x204
   …
n5n       BBStart <block_7> (freq 6501) (extension of previous block)                         [0x7f4a502b5a70] bci=[-1,31,20] rc=0 vc=334 vn=- li=- udi=- nc=0
n53n      treetop                                                                             [0x7f4a50331050] bci=[-1,48,20] rc=0 vc=341 vn=- li=- udi=2 nc=1
n52n        isub (cannotOverflow )                                                            [0x7f4a50331000] bci=[-1,46,20] rc=2 vc=341 vn=- li=- udi=- nc=2 flg=0x1000
n50n          iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )         [0x7f4a502b6880] bci=[-1,44,20] rc=1 vc=341 vn=- li=- udi=8 nc=0 flg=0x1000
n51n          iconst -3 (X!=0 X<=0 )                                                          [0x7f4a502b68d0] bci=[-1,45,20] rc=1 vc=341 vn=- li=- udi=- nc=0 flg=0x204
n55n      treetop                                                                             [0x7f4a503310f0] bci=[-1,51,20] rc=0 vc=340 vn=- li=- udi=3 nc=1
n54n        isub                                                                              [0x7f4a503310a0] bci=[-1,50,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n49n          iadd                                                                            [0x7f4a502b6830] bci=[-1,43,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n40n            iadd                                                                          [0x7f4a502b6560] bci=[-1,35,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n38n              iadd (cannotOverflow )                                                      [0x7f4a502b64c0] bci=[-1,33,20] rc=1 vc=340 vn=- li=- udi=- nc=2 flg=0x1000
n36n                iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )   [0x7f4a502b6420] bci=[-1,31,20] rc=1 vc=340 vn=- li=- udi=9 nc=0 flg=0x1000
n37n                iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6470] bci=[-1,32,20] rc=1 vc=340 vn=- li=- udi=10 nc=0 flg=0x1100
n47n              idiv                                                                        [0x7f4a502b6790] bci=[-1,42,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n45n                isub                                                                      [0x7f4a502b66f0] bci=[-1,40,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n43n                  isub                                                                    [0x7f4a502b6650] bci=[-1,38,20] rc=1 vc=340 vn=- li=- udi=- nc=2
n41n                    iload  <parm 0 I>[#421  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )  [0x7f4a502b65b0] bci=[-1,36,20] rc=1 vc=340 vn=- li=- udi=11 nc=0 flg=0x1000
n42n                    iload  <parm 1 I>[#422  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6600] bci=[-1,37,20] rc=1 vc=340 vn=- li=- udi=12 nc=0 flg=0x1100
n44n                  iconst -2 (X!=0 X<=0 )                                                  [0x7f4a502b66a0] bci=[-1,39,20] rc=1 vc=340 vn=- li=- udi=- nc=0 flg=0x204
n46n                iload  <auto slot 3>[#424  Auto] [flags 0x3 0x0 ] (X>=0 cannotOverflow )  [0x7f4a502b6740] bci=[-1,41,20] rc=1 vc=340 vn=- li=- udi=13 nc=0 flg=0x1100
n52n            ==>isub
n39n          iconst -1 (X!=0 X<=0 )                                                          [0x7f4a502b6510] bci=[-1,34,20] rc=1 vc=340 vn=- li=- udi=- nc=0 flg=0x204

Notice the load of the now uninitialized #424 in n46n that remains as a child of the idiv operation in n47n. After the fix is applied, the IL that results after General Store Sinking looks like the following. [Edit: Moved the preceding sentence from beneath this subsection.]

Part of IL for sub following General Store Sinking, with the fix
n3n       BBStart <block_5> (freq 7001) (extension of previous block)                         [0x7fd8bf0049b0] bci=[-1,14,17] rc=0 vc=342 vn=- li=- udi=- nc=0
n26n      treetop                                                                             [0x7fd8bf0050e0] bci=[-1,17,17] rc=0 vc=351 vn=- li=- udi=1 nc=1
n25n        isub (X>=0 cannotOverflow )                                                       [0x7fd8bf005090] bci=[-1,16,17] rc=1 vc=351 vn=- li=- udi=- nc=2 flg=0x1100
n23n          iload  <parm 1 I>[#418  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )    [0x7fd8bf004ff0] bci=[-1,14,17] rc=1 vc=351 vn=- li=- udi=6 nc=0 flg=0x1100
n24n          iconst -1 (X!=0 X<=0 )                                                          [0x7fd8bf005040] bci=[-1,15,17] rc=1 vc=351 vn=- li=- udi=- nc=0 flg=0x204
   …
n5n       BBStart <block_7> (freq 6501) (extension of previous block)                         [0x7fd8bf004a50] bci=[-1,31,20] rc=0 vc=342 vn=- li=- udi=- nc=0
n53n      treetop                                                                             [0x7fd8bf080050] bci=[-1,48,20] rc=0 vc=349 vn=- li=- udi=2 nc=1
n52n        isub (cannotOverflow )                                                            [0x7fd8bf080000] bci=[-1,46,20] rc=2 vc=349 vn=- li=- udi=- nc=2 flg=0x1000
n50n          iload  <parm 0 I>[#417  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )         [0x7fd8bf005860] bci=[-1,44,20] rc=1 vc=349 vn=- li=- udi=8 nc=0 flg=0x1000
n51n          iconst -3 (X!=0 X<=0 )                                                          [0x7fd8bf0058b0] bci=[-1,45,20] rc=1 vc=349 vn=- li=- udi=- nc=0 flg=0x204
n133n     treetop                                                                             [0x7fd8bf081950] bci=[-1,40,20] rc=0 vc=0 vn=- li=- udi=- nc=1
n45n        isub                                                                              [0x7fd8bf0056d0] bci=[-1,40,20] rc=1 vc=348 vn=- li=- udi=- nc=2
n43n          isub                                                                            [0x7fd8bf005630] bci=[-1,38,20] rc=1 vc=348 vn=- li=- udi=- nc=2
n41n            iload  <parm 0 I>[#417  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )       [0x7fd8bf005590] bci=[-1,36,20] rc=1 vc=348 vn=- li=- udi=11 nc=0 flg=0x1000
n42n            iload  <parm 1 I>[#418  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )  [0x7fd8bf0055e0] bci=[-1,37,20] rc=1 vc=348 vn=- li=- udi=12 nc=0 flg=0x1100
n44n          iconst -2 (X!=0 X<=0 )                                                          [0x7fd8bf005680] bci=[-1,39,20] rc=1 vc=348 vn=- li=- udi=- nc=0 flg=0x204
n134n     treetop                                                                             [0x7fd8bf0819a0] bci=[-1,33,20] rc=0 vc=0 vn=- li=- udi=- nc=1
n38n        iadd (cannotOverflow )                                                            [0x7fd8bf0054a0] bci=[-1,33,20] rc=1 vc=348 vn=- li=- udi=- nc=2 flg=0x1000
n36n          iload  <parm 0 I>[#417  Parm] [flags 0x40000103 0x0 ] (cannotOverflow )         [0x7fd8bf005400] bci=[-1,31,20] rc=1 vc=348 vn=- li=- udi=9 nc=0 flg=0x1000
n37n          iload  <parm 1 I>[#418  Parm] [flags 0x40000103 0x0 ] (X>=0 cannotOverflow )    [0x7fd8bf005450] bci=[-1,32,20] rc=1 vc=348 vn=- li=- udi=10 nc=0 flg=0x1100
n135n     treetop                                                                             [0x7fd8bf0819f0] bci=[-1,46,20] rc=0 vc=0 vn=- li=- udi=- nc=1
n52n        ==>isub
n136n     treetop                                                                             [0x7fd8bf081a40] bci=[-1,34,20] rc=0 vc=0 vn=- li=- udi=- nc=1
n39n        iconst -1 (X!=0 X<=0 )                                                            [0x7fd8bf0054f0] bci=[-1,34,20] rc=1 vc=348 vn=- li=- udi=- nc=0 flg=0x204
n55n      treetop                                                                             [0x7fd8bf0800f0] bci=[-1,51,20] rc=0 vc=348 vn=- li=- udi=3 nc=1
n137n       iconst 0xbad1dead (X!=0 X<=0 )                                                    [0x7fd8bf081a90] bci=[-1,50,20] rc=1 vc=0 vn=- li=- udi=- nc=0 flg=0x204

After the fix is applied, the IL that results after General Store Sinking looks like the following. The safe n45n child of the idiv is anchored, but the n46n child that contained a now unsafe load of #424 has been removed. Moving up the tree, the safe iadd operation in the n38n child of the iadd at n40nis anchored, but its idiv child at n47n has been removed as unsafe; the safe isub operation in the n52n child of the iaddoperation at n49nis anchored, but its unsafe child n38n is removed; and the safe iconst -1 node n39nwhich is the child of the isub operation at n54n is anchored, but its unsafe child n49n is removed.

@vijaysun-omr, may I ask you to review this change?

@vijaysun-omr vijaysun-omr self-assigned this Nov 29, 2024
@vijaysun-omr
Copy link
Contributor

vijaysun-omr commented Nov 29, 2024

I'll review this and probably will find the answer in the commit moving the pass inside isolated store elimination to its own pass, but I thought I'd ask here anyway, to make it easy for anyone reading the above discussion : what is the definition of safe/unsafe nodes as used in the prior description ? Is it "exception causing" essentially ?

@hzongaro
Copy link
Contributor Author

what is the definition of safe/unsafe nodes as used in the prior description ? Is it "exception causing" essentially ?

No, it's any load of a symbol whose definition has been removed/moved past the point of its use, but whose use has remained in place. Uses that might cause an exception allowed the problem to be detected, but this is treating anything that might be loading garbage as unsafe, even if it was only used in an operation that would never result in exception - like an integer add operation, say.

@vijaysun-omr
Copy link
Contributor

jenkins build all

@vijaysun-omr
Copy link
Contributor

jenkins build riscv

// anchored because it's not safe to evaluate.
//
child->recursivelyDecReferenceCount();
TR::Node *dummyChild = node->setAndIncChild(0, TR::Node::createConstDead(child, TR::Int32, 0xbad1 /* eyecatcher */));
Copy link
Contributor

@vijaysun-omr vijaysun-omr Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is an int value for the vandalized case correct to be an TR::Int32 in every case ? I mean, is the node tree even guaranteed to be type correct ?

I ask, since in escape analysis (I believe) vandalization is done in code that is deemed unreachable, whereas here (in store sinking) I don't think we are talking about unreachable code as such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. The code below recreates the top-level node as either a PassThrough or a treetop node. If the top-level node's reference count was zero, it becomes a treetop, with the vandalized constant as its only child, so that's safe.

However, if the top-level node's reference count was greater than zero, the store might be, as noted by point 2 in the comment above, a commoned child of a CHK node perhaps or a compressedrefs. I'm not sure what will happen during later optimization passes or code generation with something like this.

  NULLCHK  or  compressedrefs
    PassThrough
      iconst  0xbad1dead

I can rework this to ensure the type of the constant matches whatever type that child node previously represented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants