GVN final fields even with side effects.

Two accesses of a final field can be GVN'ed even if there are
side effects between them.

Change-Id: I04495ae83c7858f4216b083ad1c29851954320ad
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 0f44af0..c04fe4e 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -816,6 +816,7 @@
         current_block_->GetLastInstruction(),
         field_type,
         resolved_field->GetOffset(),
+        resolved_field->IsFinal(),
         resolved_field->IsVolatile()));
 
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
@@ -917,13 +918,19 @@
     temps.Add(cls);
     HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
     DCHECK_EQ(value->GetType(), field_type);
-    current_block_->AddInstruction(
-        new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset(),
-            resolved_field->IsVolatile()));
+    current_block_->AddInstruction(new (arena_) HStaticFieldSet(
+        cls,
+        value,
+        field_type,
+        resolved_field->GetOffset(),
+        resolved_field->IsVolatile()));
   } else {
-    current_block_->AddInstruction(
-        new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset(),
-            resolved_field->IsVolatile()));
+    current_block_->AddInstruction(new (arena_) HStaticFieldGet(
+        cls,
+        field_type,
+        resolved_field->GetOffset(),
+        resolved_field->IsFinal(),
+        resolved_field->IsVolatile()));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
   return true;
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index a81d49a..59854d7 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -42,21 +42,21 @@
 
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   HInstruction* to_remove = block->GetLastInstruction();
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
-          MemberOffset(43), false));
+          MemberOffset(43), false, false));
   HInstruction* different_offset = block->GetLastInstruction();
   // Kill the value.
   block->AddInstruction(new (&allocator) HInstanceFieldSet(
       parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   HInstruction* use_after_kill = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HExit());
 
@@ -90,7 +90,7 @@
   entry->AddSuccessor(block);
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
 
   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
   HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -107,15 +107,15 @@
 
   then->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   then->AddInstruction(new (&allocator) HGoto());
   else_->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   else_->AddInstruction(new (&allocator) HGoto());
   join->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   join->AddInstruction(new (&allocator) HExit());
 
   graph->TryBuildingSsa();
@@ -146,7 +146,7 @@
   entry->AddSuccessor(block);
   block->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   block->AddInstruction(new (&allocator) HGoto());
 
   HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -163,7 +163,7 @@
 
   loop_header->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
 
@@ -174,13 +174,13 @@
   HInstruction* field_set = loop_body->GetLastInstruction();
   loop_body->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HGoto());
 
   exit->AddInstruction(
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
-          MemberOffset(42), false));
+          MemberOffset(42), false, false));
   HInstruction* field_get_in_exit = exit->GetLastInstruction();
   exit->AddInstruction(new (&allocator) HExit());
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3fe23e1..51c68e9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2993,8 +2993,10 @@
   HInstanceFieldGet(HInstruction* value,
                     Primitive::Type field_type,
                     MemberOffset field_offset,
+                    bool is_final,
                     bool is_volatile)
-      : HExpression(field_type, SideEffects::DependsOnSomething()),
+      : HExpression(field_type,
+                    is_final ? SideEffects::None() : SideEffects::DependsOnSomething()),
         field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, value);
   }
@@ -3404,8 +3406,10 @@
   HStaticFieldGet(HInstruction* cls,
                   Primitive::Type field_type,
                   MemberOffset field_offset,
+                  bool is_final,
                   bool is_volatile)
-      : HExpression(field_type, SideEffects::DependsOnSomething()),
+      : HExpression(field_type,
+                    is_final ? SideEffects::None() : SideEffects::DependsOnSomething()),
         field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, cls);
   }
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 8c6d904..61afb99 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -475,7 +475,7 @@
   entry->AddSuccessor(block);
 
   HInstruction* test = new (allocator) HInstanceFieldGet(
-      parameter, Primitive::kPrimBoolean, MemberOffset(22), false);
+      parameter, Primitive::kPrimBoolean, MemberOffset(22), false, false);
   block->AddInstruction(test);
   block->AddInstruction(new (allocator) HIf(test));
   HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -495,9 +495,9 @@
   *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   join->AddPhi(*phi);
   *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
-                                              MemberOffset(42), false);
+                                              MemberOffset(42), false, false);
   *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
-                                              MemberOffset(42), false);
+                                              MemberOffset(42), false, false);
   then->AddInstruction(*input1);
   else_->AddInstruction(*input2);
   join->AddInstruction(new (allocator) HExit());
@@ -605,7 +605,7 @@
   entry->AddSuccessor(block);
 
   *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
-                                             MemberOffset(42), false);
+                                             MemberOffset(42), false, false);
   block->AddInstruction(*field);
   *ret = new (allocator) HReturn(*field);
   block->AddInstruction(*ret);
diff --git a/test/483-checker-gvn/expected.txt b/test/483-checker-gvn/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/483-checker-gvn/expected.txt
diff --git a/test/483-checker-gvn/info.txt b/test/483-checker-gvn/info.txt
new file mode 100644
index 0000000..3ff7a8c
--- /dev/null
+++ b/test/483-checker-gvn/info.txt
@@ -0,0 +1 @@
+Tests that final fields can be GVNed even with side effects.
diff --git a/test/483-checker-gvn/src/Main.java b/test/483-checker-gvn/src/Main.java
new file mode 100644
index 0000000..ed474aa
--- /dev/null
+++ b/test/483-checker-gvn/src/Main.java
@@ -0,0 +1,62 @@
+/*
+* Copyright (C) 2015 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 final int staticField;
+  public final int instanceField;
+
+  Main() {
+    instanceField = 42;
+  }
+
+  static {
+    staticField = 42;
+  }
+
+  // CHECK-START: int Main.addStatic() GVN (before)
+  // CHECK-DAG:     StaticFieldGet
+  // CHECK-DAG:     StaticFieldGet
+
+  // CHECK-START: int Main.addStatic() GVN (after)
+  // CHECK-DAG:     StaticFieldGet
+  // CHECK-NOT:     StaticFieldGet
+  public static int addStatic() {
+    return staticField + doACall() + staticField;
+  }
+
+  // CHECK-START: int Main.addInstance() GVN (before)
+  // CHECK-DAG:     InstanceFieldGet
+  // CHECK-DAG:     InstanceFieldGet
+
+  // CHECK-START: int Main.addInstance() GVN (after)
+  // CHECK-DAG:     InstanceFieldGet
+  // CHECK-NOT:     InstanceFieldGet
+  public int addInstance() {
+    return instanceField + doACall() + instanceField;
+  }
+
+  public static int doACall() {
+    try {
+      // Defeat inlining.
+      Thread.sleep(0);
+    } catch (Throwable t ) {}
+    return (int) System.currentTimeMillis();
+  }
+
+  public static void main(String[] args) {
+  }
+}