[optimizing] Add memory barriers in constructors when needed

If a class has final fields we must add a memory barrier before
returning from constructor. This makes sure the fields are visible to
other threads.

Bug: 19851497
Change-Id: If8c485092fc512efb9636cd568cb0543fb27688e
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 641d174..538d969 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2381,7 +2381,7 @@
 }
 
 bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
-                                                uint16_t class_def_index) {
+                                                uint16_t class_def_index) const {
   ReaderMutexLock mu(self, freezing_constructor_lock_);
   return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0;
 }
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1a4ae13..bd9d7c1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -187,7 +187,8 @@
 
   void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
                                      uint16_t class_def_index);
-  bool RequiresConstructorBarrier(Thread* self, const DexFile* dex_file, uint16_t class_def_index);
+  bool RequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
+                                  uint16_t class_def_index) const;
 
   // Callbacks from compiler to see what runtime checks must be generated.
 
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 8a64d81..fff2906 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -520,8 +520,19 @@
   UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
 }
 
+static bool RequiresConstructorBarrier(const DexCompilationUnit& cu, const CompilerDriver& driver) {
+  Thread* self = Thread::Current();
+  return cu.IsConstructor()
+      && driver.RequiresConstructorBarrier(self, cu.GetDexFile(), cu.GetClassDefIndex());
+}
+
 void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type type) {
   if (type == Primitive::kPrimVoid) {
+    // Note that we might insert redundant barriers when inlining `super` calls.
+    // TODO: add a data flow analysis to get rid of duplicate barriers.
+    if (RequiresConstructorBarrier(*dex_compilation_unit_, *compiler_driver_)) {
+      current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore));
+    }
     current_block_->AddInstruction(new (arena_) HReturnVoid());
   } else {
     HInstruction* value = LoadLocal(instruction.VRegA(), type);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2ea9203..037a4ae 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1214,6 +1214,14 @@
   UNUSED(constant);
 }
 
+void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  memory_barrier->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+}
+
 void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
   ret->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index efc41e7..779340d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2430,6 +2430,14 @@
   }
 }
 
+void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  memory_barrier->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+}
+
 void LocationsBuilderARM64::VisitReturn(HReturn* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   Primitive::Type return_type = instruction->InputAt(0)->GetType();
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 879216d..6ac4ad7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1120,6 +1120,14 @@
   UNUSED(constant);
 }
 
+void LocationsBuilderX86::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  memory_barrier->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+}
+
 void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) {
   ret->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index a3d3490..65b128a 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1145,6 +1145,14 @@
   UNUSED(constant);
 }
 
+void LocationsBuilderX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  memory_barrier->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+}
+
 void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) {
   ret->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index fc3dd01..9499040 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -38,6 +38,7 @@
       if (!inst->HasSideEffects()
           && !inst->CanThrow()
           && !inst->IsSuspendCheck()
+          && !inst->IsMemoryBarrier()  // If we added an explicit barrier then we should keep it.
           && !inst->HasUses()) {
         block->RemoveInstruction(inst);
       }
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4c28378..ca9cbc3 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -192,6 +192,10 @@
     output_ << " " << phi->GetRegNumber();
   }
 
+  void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE {
+    output_ << " " << barrier->GetBarrierKind();
+  }
+
   bool IsPass(const char* name) {
     return strcmp(pass_name_, name) == 0;
   }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 649038b..c3c35ac 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -19,6 +19,7 @@
 
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
+#include "dex/compiler_enums.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "handle.h"
 #include "handle_scope.h"
@@ -718,6 +719,7 @@
   M(LoadString, Instruction)                                            \
   M(Local, Instruction)                                                 \
   M(LongConstant, Constant)                                             \
+  M(MemoryBarrier, Instruction)                                         \
   M(MonitorOperation, Instruction)                                      \
   M(Mul, BinaryOperation)                                               \
   M(Neg, UnaryOperation)                                                \
@@ -908,6 +910,12 @@
   HUseListNode<T>* use_node_;
 };
 
+// TODO: Add better documentation to this class and maybe refactor with more suggestive names.
+// - Has(All)SideEffects suggests that all the side effects are present but only ChangesSomething
+//   flag is consider.
+// - DependsOn suggests that there is a real dependency between side effects but it only
+//   checks DependendsOnSomething flag.
+//
 // Represents the side effects an instruction may have.
 class SideEffects : public ValueObject {
  public:
@@ -3437,6 +3445,22 @@
   DISALLOW_COPY_AND_ASSIGN(HCheckCast);
 };
 
+class HMemoryBarrier : public HTemplateInstruction<0> {
+ public:
+  explicit HMemoryBarrier(MemBarrierKind barrier_kind)
+      : HTemplateInstruction(SideEffects::None()),
+        barrier_kind_(barrier_kind) {}
+
+  MemBarrierKind GetBarrierKind() { return barrier_kind_; }
+
+  DECLARE_INSTRUCTION(MemoryBarrier);
+
+ private:
+  const MemBarrierKind barrier_kind_;
+
+  DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
+};
+
 class HMonitorOperation : public HTemplateInstruction<1> {
  public:
   enum OperationKind {
diff --git a/test/476-checker-ctor-memory-barrier/expected.txt b/test/476-checker-ctor-memory-barrier/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/476-checker-ctor-memory-barrier/expected.txt
diff --git a/test/476-checker-ctor-memory-barrier/info.txt b/test/476-checker-ctor-memory-barrier/info.txt
new file mode 100644
index 0000000..9bd311f
--- /dev/null
+++ b/test/476-checker-ctor-memory-barrier/info.txt
@@ -0,0 +1,2 @@
+Tests if we add memory barriers on constructors when needed (i.e when the
+class has final fields).
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
new file mode 100644
index 0000000..10aa2ab
--- /dev/null
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -0,0 +1,147 @@
+/*
+* 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.
+*/
+
+
+class ClassWithoutFinals {
+  // CHECK-START: void ClassWithoutFinals.<init>() register (after)
+  // CHECK-NOT: MemoryBarrier {{StoreStore}}
+  public ClassWithoutFinals() {}
+}
+
+class ClassWithFinals {
+  public final int x;
+  public ClassWithFinals obj;
+
+  // CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+  public ClassWithFinals(boolean cond) {
+    x = 0;
+    if (cond) {
+      // avoid inlining
+      throw new RuntimeException();
+    }
+  }
+
+  // CHECK-START: void ClassWithFinals.<init>() register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+  public ClassWithFinals() {
+    x = 0;
+  }
+
+  // CHECK-START: void ClassWithFinals.<init>(int) register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+  public ClassWithFinals(int x) {
+    // This should have two barriers:
+    //   - one for the constructor
+    //   - one for the `new` which should be inlined.
+    obj = new ClassWithFinals();
+    this.x = x;
+  }
+}
+
+class InheritFromClassWithFinals extends ClassWithFinals {
+  // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+
+  // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
+  // CHECK-NOT: InvokeStaticOrDirect
+  public InheritFromClassWithFinals() {
+    // Should inline the super constructor.
+  }
+
+  // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
+  // CHECK:     InvokeStaticOrDirect
+
+  // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
+  // CHECK-NOT: MemoryBarrier {{StoreStore}}
+  public InheritFromClassWithFinals(boolean cond) {
+    super(cond);
+    // should not inline the super constructor
+  }
+}
+
+class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals {
+  final int y;
+
+  // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+
+  // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
+  // CHECK-NOT: InvokeStaticOrDirect
+  public HaveFinalsAndInheritFromClassWithFinals() {
+    // Should inline the super constructor.
+    y = 0;
+  }
+
+  // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after)
+  // CHECK:     InvokeStaticOrDirect
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     ReturnVoid
+  public HaveFinalsAndInheritFromClassWithFinals(boolean cond) {
+    super(cond);
+    // should not inline the super constructor
+    y = 0;
+  }
+}
+
+public class Main {
+
+  // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
+  // CHECK:     InvokeStaticOrDirect
+
+  // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
+  // CHECK-NOT: MemoryBarrier {{StoreStore}}
+  public static ClassWithFinals noInlineNoConstructorBarrier() {
+    return new ClassWithFinals(false);
+  }
+
+  // CHECK-START: ClassWithFinals Main.inlineConstructorBarrier() register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     Return
+
+  // CHECK-START: ClassWithFinals Main.inlineConstructorBarrier() register (after)
+  // CHECK-NOT: InvokeStaticOrDirect
+  public static ClassWithFinals inlineConstructorBarrier() {
+    return new ClassWithFinals();
+  }
+
+  // CHECK-START: InheritFromClassWithFinals Main.doubleInlineConstructorBarrier() register (after)
+  // CHECK:     MemoryBarrier {{StoreStore}}
+  // CHECK-NOT: {{.*}}
+  // CHECK:     Return
+
+  // CHECK-START: InheritFromClassWithFinals Main.doubleInlineConstructorBarrier() register (after)
+  // CHECK-NOT: InvokeStaticOrDirect
+  public static InheritFromClassWithFinals doubleInlineConstructorBarrier() {
+    return new InheritFromClassWithFinals();
+  }
+
+  public static void main(String[] args) {  }
+}