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(¶llel_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);
}