Remove the `CanTriggerGC` side-effects on a few instructions.

The side-effect was specified for these instructions as they call
runtime. We now have a list of entrypoints that we know cannot trigger
GC. We can avoid requiring the side-effect for those.

Test: Run ART test suite on Nexus 5X and host.

Change-Id: I0e0e6a4d701ce6c75aff486cb0d1bc7fe2e8dda4
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 6732670..51ba187 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1179,37 +1179,51 @@
   GetMoveResolver()->EmitNativeCode(&parallel_move);
 }
 
-void CodeGenerator::ValidateInvokeRuntime(HInstruction* instruction, SlowPathCode* slow_path) {
+void CodeGenerator::ValidateInvokeRuntime(QuickEntrypointEnum entrypoint,
+                                          HInstruction* instruction,
+                                          SlowPathCode* slow_path) {
   // Ensure that the call kind indication given to the register allocator is
-  // coherent with the runtime call generated, and that the GC side effect is
-  // set when required.
+  // coherent with the runtime call generated.
   if (slow_path == nullptr) {
     DCHECK(instruction->GetLocations()->WillCall())
         << "instruction->DebugName()=" << instruction->DebugName();
-    DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()))
-        << "instruction->DebugName()=" << instruction->DebugName()
-        << " instruction->GetSideEffects().ToString()=" << instruction->GetSideEffects().ToString();
   } else {
     DCHECK(instruction->GetLocations()->CallsOnSlowPath() || slow_path->IsFatal())
         << "instruction->DebugName()=" << instruction->DebugName()
         << " slow_path->GetDescription()=" << slow_path->GetDescription();
-    DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
-           // When (non-Baker) read barriers are enabled, some instructions
-           // use a slow path to emit a read barrier, which does not trigger
-           // GC.
-           (kEmitCompilerReadBarrier &&
-            !kUseBakerReadBarrier &&
-            (instruction->IsInstanceFieldGet() ||
-             instruction->IsStaticFieldGet() ||
-             instruction->IsArrayGet() ||
-             instruction->IsLoadClass() ||
-             instruction->IsLoadString() ||
-             instruction->IsInstanceOf() ||
-             instruction->IsCheckCast() ||
-             (instruction->IsInvokeVirtual() && instruction->GetLocations()->Intrinsified()))))
-        << "instruction->DebugName()=" << instruction->DebugName()
-        << " instruction->GetSideEffects().ToString()=" << instruction->GetSideEffects().ToString()
-        << " slow_path->GetDescription()=" << slow_path->GetDescription();
+  }
+
+  // Check that the GC side effect is set when required.
+  // TODO: Reverse EntrypointCanTriggerGC
+  if (EntrypointCanTriggerGC(entrypoint)) {
+    if (slow_path == nullptr) {
+      DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()))
+          << "instruction->DebugName()=" << instruction->DebugName()
+          << " instruction->GetSideEffects().ToString()="
+          << instruction->GetSideEffects().ToString();
+    } else {
+      DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
+             // When (non-Baker) read barriers are enabled, some instructions
+             // use a slow path to emit a read barrier, which does not trigger
+             // GC.
+             (kEmitCompilerReadBarrier &&
+              !kUseBakerReadBarrier &&
+              (instruction->IsInstanceFieldGet() ||
+               instruction->IsStaticFieldGet() ||
+               instruction->IsArrayGet() ||
+               instruction->IsLoadClass() ||
+               instruction->IsLoadString() ||
+               instruction->IsInstanceOf() ||
+               instruction->IsCheckCast() ||
+               (instruction->IsInvokeVirtual() && instruction->GetLocations()->Intrinsified()))))
+          << "instruction->DebugName()=" << instruction->DebugName()
+          << " instruction->GetSideEffects().ToString()="
+          << instruction->GetSideEffects().ToString()
+          << " slow_path->GetDescription()=" << slow_path->GetDescription();
+    }
+  } else {
+    // The GC side effect is not required for the instruction. But the instruction might still have
+    // it, for example if it calls other entrypoints requiring it.
   }
 
   // Check the coherency of leaf information.
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b4d4b9b..22b5c9c 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -404,7 +404,9 @@
 
 
   // Perfoms checks pertaining to an InvokeRuntime call.
-  void ValidateInvokeRuntime(HInstruction* instruction, SlowPathCode* slow_path);
+  void ValidateInvokeRuntime(QuickEntrypointEnum entrypoint,
+                             HInstruction* instruction,
+                             SlowPathCode* slow_path);
 
   // Perfoms checks pertaining to an InvokeRuntimeWithoutRecordingPcInfo call.
   static void ValidateInvokeRuntimeWithoutRecordingPcInfo(HInstruction* instruction,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 40c2b9c..8b2d6fd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1177,7 +1177,7 @@
                                      HInstruction* instruction,
                                      uint32_t dex_pc,
                                      SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
   if (EntrypointRequiresStackMap(entrypoint)) {
     RecordPcInfo(instruction, dex_pc, slow_path);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 599185a..7f542da 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1452,7 +1452,7 @@
                                        HInstruction* instruction,
                                        uint32_t dex_pc,
                                        SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   GenerateInvokeRuntime(GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value());
   if (EntrypointRequiresStackMap(entrypoint)) {
     RecordPcInfo(instruction, dex_pc, slow_path);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index b767aa5..018cc45 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1224,7 +1224,7 @@
                                       HInstruction* instruction,
                                       uint32_t dex_pc,
                                       SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   bool reordering = __ SetReorder(false);
   __ LoadFromOffset(kLoadWord, T9, TR, GetThreadOffset<kMipsPointerSize>(entrypoint).Int32Value());
   __ Jalr(T9);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 4d87523..557e5da 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -946,7 +946,7 @@
                                         HInstruction* instruction,
                                         uint32_t dex_pc,
                                         SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   // TODO: anything related to T9/GP/GOT/PIC/.so's?
   __ LoadFromOffset(kLoadDoubleword,
                     T9,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0518213..172ce4a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -754,7 +754,7 @@
                                      HInstruction* instruction,
                                      uint32_t dex_pc,
                                      SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   GenerateInvokeRuntime(GetThreadOffset<kX86PointerSize>(entrypoint).Int32Value());
   if (EntrypointRequiresStackMap(entrypoint)) {
     RecordPcInfo(instruction, dex_pc, slow_path);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 506a7b1..1943ddc 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -981,7 +981,7 @@
                                         HInstruction* instruction,
                                         uint32_t dex_pc,
                                         SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(instruction, slow_path);
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
   GenerateInvokeRuntime(GetThreadOffset<kX86_64PointerSize>(entrypoint).Int32Value());
   if (EntrypointRequiresStackMap(entrypoint)) {
     RecordPcInfo(instruction, dex_pc, slow_path);
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index 8f7778f..04e063c 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -259,7 +259,8 @@
   HIntConstant* offset = graph->GetIntConstant(data_offset);
   HIntermediateAddress* address =
       new (arena) HIntermediateAddress(array, offset, kNoDexPc);
-  address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
+  // TODO: Is it ok to not have this on the intermediate address?
+  // address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
   access->GetBlock()->InsertInstructionBefore(address, access);
   access->ReplaceInput(address, 0);
   // Both instructions must depend on GC to prevent any instruction that can
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index caecc57..6d207765 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4374,7 +4374,7 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T>
   T ComputeIntegral(T x, T y) const {
@@ -4409,11 +4409,6 @@
         ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
-  static SideEffects SideEffectsForArchRuntimeCalls() {
-    // The generated code can use a runtime call.
-    return SideEffects::CanTriggerGC();
-  }
-
   DECLARE_INSTRUCTION(Div);
 
  private:
@@ -4426,7 +4421,7 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
+      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
 
   template <typename T>
   T ComputeIntegral(T x, T y) const {
@@ -4461,10 +4456,6 @@
         ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
   }
 
-  static SideEffects SideEffectsForArchRuntimeCalls() {
-    return SideEffects::CanTriggerGC();
-  }
-
   DECLARE_INSTRUCTION(Rem);
 
  private:
@@ -4917,9 +4908,7 @@
  public:
   // Instantiate a type conversion of `input` to `result_type`.
   HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
-      : HExpression(result_type,
-                    SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
-                    dex_pc) {
+      : HExpression(result_type, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
     // Invariant: We should never generate a conversion to a Boolean value.
     DCHECK_NE(Primitive::kPrimBoolean, result_type);
@@ -4938,18 +4927,6 @@
   // containing the result.  If the input cannot be converted, return nullptr.
   HConstant* TryStaticEvaluation() const;
 
-  static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type input_type,
-                                                    Primitive::Type result_type) {
-    // Some architectures may not require the 'GC' side effects, but at this point
-    // in the compilation process we do not know what architecture we will
-    // generate code for, so we must be conservative.
-    if ((Primitive::IsFloatingPointType(input_type) && Primitive::IsIntegralType(result_type))
-        || (input_type == Primitive::kPrimLong && Primitive::IsFloatingPointType(result_type))) {
-      return SideEffects::CanTriggerGC();
-    }
-    return SideEffects::None();
-  }
-
   DECLARE_INSTRUCTION(TypeConversion);
 
  private:
@@ -5031,9 +5008,7 @@
                     const DexFile& dex_file,
                     Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
-      : HExpression(field_type,
-                    SideEffectsForArchRuntimeCalls(field_type, is_volatile),
-                    dex_pc),
+      : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset,
                     field_type,
                     is_volatile,
@@ -5064,16 +5039,6 @@
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
   bool IsVolatile() const { return field_info_.IsVolatile(); }
 
-  static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type field_type, bool is_volatile) {
-    SideEffects side_effects = SideEffects::FieldReadOfType(field_type, is_volatile);
-
-    // MIPS delegates volatile kPrimLong and kPrimDouble loads to a runtime helper.
-    if (Primitive::Is64BitType(field_type)) {
-      side_effects.Add(SideEffects::CanTriggerGC());
-    }
-    return side_effects;
-  }
-
   DECLARE_INSTRUCTION(InstanceFieldGet);
 
  private:
@@ -5094,8 +5059,7 @@
                     const DexFile& dex_file,
                     Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
-      : HTemplateInstruction(SideEffectsForArchRuntimeCalls(field_type, is_volatile),
-                             dex_pc),
+      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset,
                     field_type,
                     is_volatile,
@@ -5120,16 +5084,6 @@
   bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
   void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
 
-  static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type field_type, bool is_volatile) {
-    SideEffects side_effects = SideEffects::FieldWriteOfType(field_type, is_volatile);
-
-    // MIPS delegates volatile kPrimLong and kPrimDouble stores to a runtime helper.
-    if (Primitive::Is64BitType(field_type)) {
-      side_effects.Add(SideEffects::CanTriggerGC());
-    }
-    return side_effects;
-  }
-
   DECLARE_INSTRUCTION(InstanceFieldSet);
 
  private:
@@ -5934,9 +5888,7 @@
                   const DexFile& dex_file,
                   Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
-      : HExpression(field_type,
-                    SideEffectsForArchRuntimeCalls(field_type, is_volatile),
-                    dex_pc),
+      : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset,
                     field_type,
                     is_volatile,
@@ -5964,16 +5916,6 @@
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
   bool IsVolatile() const { return field_info_.IsVolatile(); }
 
-  static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type field_type, bool is_volatile) {
-    SideEffects side_effects = SideEffects::FieldReadOfType(field_type, is_volatile);
-
-    // MIPS delegates volatile kPrimLong and kPrimDouble loads to a runtime helper.
-    if (Primitive::Is64BitType(field_type)) {
-      side_effects.Add(SideEffects::CanTriggerGC());
-    }
-    return side_effects;
-  }
-
   DECLARE_INSTRUCTION(StaticFieldGet);
 
  private:
@@ -5994,8 +5936,7 @@
                   const DexFile& dex_file,
                   Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
-      : HTemplateInstruction(SideEffectsForArchRuntimeCalls(field_type, is_volatile),
-                             dex_pc),
+      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset,
                     field_type,
                     is_volatile,
@@ -6017,16 +5958,6 @@
   bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
   void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
 
-  static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type field_type, bool is_volatile) {
-    SideEffects side_effects = SideEffects::FieldWriteOfType(field_type, is_volatile);
-
-    // MIPS delegates volatile kPrimLong and kPrimDouble stores to a runtime helper.
-    if (Primitive::Is64BitType(field_type)) {
-      side_effects.Add(SideEffects::CanTriggerGC());
-    }
-    return side_effects;
-  }
-
   DECLARE_INSTRUCTION(StaticFieldSet);
 
  private:
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 8bd8667..73085f8 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -117,10 +117,15 @@
 // This instruction computes an intermediate address pointing in the 'middle' of an object. The
 // result pointer cannot be handled by GC, so extra care is taken to make sure that this value is
 // never used across anything that can trigger GC.
+// The result of this instruction is not a pointer in the sense of `Primitive::kPrimNot`. So we
+// represent it by the type `Primitive::kPrimInt`.
 class HIntermediateAddress FINAL : public HExpression<2> {
  public:
   HIntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc)
-      : HExpression(Primitive::kPrimNot, SideEffects::DependsOnGC(), dex_pc) {
+      : HExpression(Primitive::kPrimInt, SideEffects::DependsOnGC(), dex_pc) {
+        DCHECK_EQ(Primitive::ComponentSize(Primitive::kPrimInt),
+                  Primitive::ComponentSize(Primitive::kPrimNot))
+            << "kPrimInt and kPrimNot have different sizes.";
     SetRawInputAt(0, base_address);
     SetRawInputAt(1, offset);
   }