content: /*
   * Copyright (C) 2017 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  public class Main {
  
    public static void main(String[] args) {
      testSimpleUse();
      testTwoUses();
      testFieldStores(doThrow);
      testFieldStoreCycle();
      testArrayStores();
      testOnlyStoreUses();
      testNoUse();
      testPhiInput();
      testVolatileStore();
      doThrow = true;
      try {
        testInstanceSideEffects();
      } catch (Error e) {
        // expected
        System.out.println(e.getMessage());
      }
      try {
        testStaticSideEffects();
      } catch (Error e) {
        // expected
        System.out.println(e.getMessage());
      }
  
      try {
        testStoreStore(doThrow);
      } catch (Error e) {
        // expected
        System.out.println(e.getMessage());
      }
    }
  
    /// CHECK-START: void Main.testSimpleUse() code_sinking (before)
    /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
    /// CHECK:                    NewInstance [<<LoadClass>>]
    /// CHECK:                    If
    /// CHECK:                    begin_block
    /// CHECK:                    Throw
  
    /// CHECK-START: void Main.testSimpleUse() code_sinking (after)
    /// CHECK-NOT:                NewInstance
    /// CHECK:                    If
    /// CHECK:                    begin_block
    /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
    /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
    /// CHECK-NOT:                begin_block
    /// CHECK:                    NewInstance [<<LoadClass>>]
    /// CHECK-NOT:                begin_block
    /// CHECK:                    NewInstance [<<Error>>]
    /// CHECK:                    Throw
    public static void testSimpleUse() {
      Object o = new Object();
      if (doThrow) {
        throw new Error(o.toString());
      }
    }
  
    /// CHECK-START: void Main.testTwoUses() code_sinking (before)
    /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
    /// CHECK:                    NewInstance [<<LoadClass>>]
    /// CHECK:                    If
    /// CHECK:                    begin_block
    /// CHECK:                    Throw
  
    /// CHECK-START: void Main.testTwoUses() code_sinking (after)
    /// CHECK-NOT:                NewInstance
    /// CHECK:                    If
    /// CHECK:                    begin_block
    /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
    /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
    /// CHECK-NOT:                begin_block
    /// CHECK:                    NewInstance [<<LoadClass>>]
    /// CHECK-NOT:                begin_block
    /// CHECK:                    NewInstance [<<Error>>]
    /// CHECK:                    Throw
    public static void testTwoUses() {
      Object o = new Object();
      if (doThrow) {
        throw new Error(o.toString() + o.toString());
      }
    }
  
    /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before)
    /// CHECK: <<Int42:i\d+>>       IntConstant 42
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK:                      Throw
  
    /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after)
    /// CHECK: <<Int42:i\d+>>       IntConstant 42
    /// CHECK-NOT:                  NewInstance
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
    /// CHECK-NOT:                  begin_block
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      NewInstance [<<Error>>]
    /// CHECK:                      Throw
    public static void testFieldStores(boolean doThrow) {
      Main m = new Main();
      m.intField = 42;
      if (doThrow) {
        throw new Error(m.toString());
      }
    }
  
    /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before)
    /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
    /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
    /// CHECK:                       If
    /// CHECK:                       begin_block
    /// CHECK:                       Throw
  
    // TODO(ngeoffray): Handle allocation/store cycles.
    /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after)
    /// CHECK: begin_block
    /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
    /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
    /// CHECK:                       If
    /// CHECK:                       begin_block
    /// CHECK:                       Throw
    public static void testFieldStoreCycle() {
      Main m1 = new Main();
      Main m2 = new Main();
      m1.objectField = m2;
      m2.objectField = m1;
      if (doThrow) {
        throw new Error(m1.toString() + m2.toString());
      }
    }
  
    /// CHECK-START: void Main.testArrayStores() code_sinking (before)
    /// CHECK: <<Int1:i\d+>>        IntConstant 1
    /// CHECK: <<Int0:i\d+>>        IntConstant 0
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
    /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
    /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK:                      Throw
  
    /// CHECK-START: void Main.testArrayStores() code_sinking (after)
    /// CHECK: <<Int1:i\d+>>        IntConstant 1
    /// CHECK: <<Int0:i\d+>>        IntConstant 0
    /// CHECK-NOT:                  NewArray
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
    /// CHECK-NOT:                  begin_block
    /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      NewInstance [<<Error>>]
    /// CHECK:                      Throw
    public static void testArrayStores() {
      Object[] o = new Object[1];
      o[0] = o;
      if (doThrow) {
        throw new Error(o.toString());
      }
    }
  
    // Make sure code sinking does not crash on dead allocations.
    public static void testOnlyStoreUses() {
      Main m = new Main();
      Object[] o = new Object[1];  // dead allocation, should eventually be removed b/35634932.
      o[0] = m;
      o = null;  // Avoid environment uses for the array allocation.
      if (doThrow) {
        throw new Error(m.toString());
      }
    }
  
    // Make sure code sinking does not crash on dead code.
    public static void testNoUse() {
      Main m = new Main();
      boolean load = Main.doLoop;  // dead code, not removed because of environment use.
      // Ensure one environment use for the static field
      $opt$noinline$foo();
      load = false;
      if (doThrow) {
        throw new Error(m.toString());
      }
    }
  
    // Make sure we can move code only used by a phi.
    /// CHECK-START: void Main.testPhiInput() code_sinking (before)
    /// CHECK: <<Null:l\d+>>        NullConstant
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
    /// CHECK:                      Throw
  
    /// CHECK-START: void Main.testPhiInput() code_sinking (after)
    /// CHECK: <<Null:l\d+>>        NullConstant
    /// CHECK-NOT:                  NewInstance
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                      begin_block
    /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
    /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
    /// CHECK:                      NewInstance [<<Error>>]
    /// CHECK:                      Throw
    public static void testPhiInput() {
      Object f = new Object();
      if (doThrow) {
        Object o = null;
        int i = 2;
        if (doLoop) {
          o = f;
          i = 42;
        }
        throw new Error(o.toString() + i);
      }
    }
  
    static void $opt$noinline$foo() {}
  
    // Check that we do not move volatile stores.
    /// CHECK-START: void Main.testVolatileStore() code_sinking (before)
    /// CHECK: <<Int42:i\d+>>        IntConstant 42
    /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
    /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK:                       If
    /// CHECK:                       begin_block
    /// CHECK:                       Throw
  
    /// CHECK-START: void Main.testVolatileStore() code_sinking (after)
    /// CHECK: <<Int42:i\d+>>        IntConstant 42
    /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
    /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
    /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK:                       If
    /// CHECK:                       begin_block
    /// CHECK:                       Throw
    public static void testVolatileStore() {
      Main m = new Main();
      m.volatileField = 42;
      if (doThrow) {
        throw new Error(m.toString());
      }
    }
  
    public static void testInstanceSideEffects() {
      int a = mainField.intField;
      $noinline$changeIntField();
      if (doThrow) {
        throw new Error("" + a);
      }
    }
  
    static void $noinline$changeIntField() {
      mainField.intField = 42;
    }
  
    public static void testStaticSideEffects() {
      Object o = obj;
      $noinline$changeStaticObjectField();
      if (doThrow) {
        throw new Error(o.getClass().toString());
      }
    }
  
    static void $noinline$changeStaticObjectField() {
      obj = new Main();
    }
  
    // Test that we preserve the order of stores.
    /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before)
    /// CHECK: <<Int42:i\d+>>       IntConstant 42
    /// CHECK: <<Int43:i\d+>>       IntConstant 43
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int43>>]
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK:                      Throw
  
    /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after)
    /// CHECK: <<Int42:i\d+>>       IntConstant 42
    /// CHECK: <<Int43:i\d+>>       IntConstant 43
    /// CHECK-NOT:                  NewInstance
    /// CHECK:                      If
    /// CHECK:                      begin_block
    /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
    /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
    /// CHECK-NOT:                  begin_block
    /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int43>>]
    /// CHECK-NOT:                  begin_block
    /// CHECK:                      NewInstance [<<Error>>]
    /// CHECK:                      Throw
    public static void testStoreStore(boolean doThrow) {
      Main m = new Main();
      m.intField = 42;
      m.intField = 43;
      if (doThrow) {
        throw new Error(m.$opt$noinline$toString());
      }
    }
  
    public String $opt$noinline$toString() {
      return "" + intField;
    }
  
    volatile int volatileField;
    int intField;
    Object objectField;
    static boolean doThrow;
    static boolean doLoop;
    static Main mainField = new Main();
    static Object obj = new Object();
  }
  