Merge "Delete AllocationTimer"
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 4607ebe..77d6628 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1005,6 +1005,31 @@
GetMoveResolver()->EmitNativeCode(¶llel_move);
}
+void CodeGenerator::ValidateInvokeRuntime(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.
+ if (slow_path == nullptr) {
+ DCHECK(instruction->GetLocations()->WillCall());
+ DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
+ } else {
+ DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
+ DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
+ // Control flow would not come back into the code if a fatal slow
+ // path is taken, so we do not care if it triggers GC.
+ slow_path->IsFatal() ||
+ // HDeoptimize is a special case: we know we are not coming back from
+ // it into the code.
+ instruction->IsDeoptimize());
+ }
+
+ // Check the coherency of leaf information.
+ DCHECK(instruction->IsSuspendCheck()
+ || ((slow_path != nullptr) && slow_path->IsFatal())
+ || instruction->GetLocations()->CanCall()
+ || !IsLeafMethod());
+}
+
void SlowPathCode::RecordPcInfo(CodeGenerator* codegen, HInstruction* instruction, uint32_t dex_pc) {
codegen->RecordPcInfo(instruction, dex_pc, this);
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 540da1c..2582444 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -292,6 +292,8 @@
return type == Primitive::kPrimNot && !value->IsNullConstant();
}
+ void ValidateInvokeRuntime(HInstruction* instruction, SlowPathCode* slow_path);
+
void AddAllocatedRegister(Location location) {
allocated_registers_.Add(location);
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 6c0292c..1bd4216 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -953,23 +953,10 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- // Ensure that the call kind indication given to the register allocator is
- // coherent with the runtime call generated.
- if (slow_path == nullptr) {
- DCHECK(instruction->GetLocations()->WillCall());
- } else {
- DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
- }
-
+ ValidateInvokeRuntime(instruction, slow_path);
__ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
__ blx(LR);
RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || instruction->GetLocations()->CanCall()
- || !IsLeafMethod());
}
void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b44c5ba..e5b6df5 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1103,23 +1103,11 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- // Ensure that the call kind indication given to the register allocator is
- // coherent with the runtime call generated.
- if (slow_path == nullptr) {
- DCHECK(instruction->GetLocations()->WillCall());
- } else {
- DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
- }
-
+ ValidateInvokeRuntime(instruction, slow_path);
BlockPoolsScope block_pools(GetVIXLAssembler());
__ Ldr(lr, MemOperand(tr, entry_point_offset));
__ Blr(lr);
RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || !IsLeafMethod());
}
void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index b6ebeb4..e5761eb 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -977,23 +977,11 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- // Ensure that the call kind indication given to the register allocator is
- // coherent with the runtime call generated.
- if (slow_path == nullptr) {
- DCHECK(instruction->GetLocations()->WillCall());
- } else {
- DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
- }
-
+ ValidateInvokeRuntime(instruction, slow_path);
// TODO: anything related to T9/GP/GOT/PIC/.so's?
__ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
__ Jalr(T9);
RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || !IsLeafMethod());
}
void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4efdbb9..5a77c82 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -429,21 +429,9 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- // Ensure that the call kind indication given to the register allocator is
- // coherent with the runtime call generated.
- if (slow_path == nullptr) {
- DCHECK(instruction->GetLocations()->WillCall());
- } else {
- DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
- }
-
+ ValidateInvokeRuntime(instruction, slow_path);
__ fs()->call(entry_point);
RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || !IsLeafMethod());
}
CodeGeneratorX86::CodeGeneratorX86(HGraph* graph,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 1585104..3730478 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -484,21 +484,9 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- // Ensure that the call kind indication given to the register allocator is
- // coherent with the runtime call generated.
- if (slow_path == nullptr) {
- DCHECK(instruction->GetLocations()->WillCall());
- } else {
- DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal());
- }
-
+ ValidateInvokeRuntime(instruction, slow_path);
__ gs()->call(entry_point);
RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || !IsLeafMethod());
}
static constexpr int kNumberOfCpuRegisterPairs = 0;
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 6269d16..5de629d 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -128,7 +128,7 @@
for (i.Advance(); !i.Done(); i.Advance()) {
HInstruction* inst = i.Current();
DCHECK(!inst->IsControlFlow());
- if (!inst->DoesAnyWrite()
+ if (!inst->HasSideEffects()
&& !inst->CanThrow()
&& !inst->IsSuspendCheck()
// If we added an explicit barrier then we should keep it.
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 3900646..833dfb0 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -264,7 +264,7 @@
// odd buckets to speed up deletion.
size_t HashCode(HInstruction* instruction) const {
size_t hash_code = instruction->ComputeHashCode();
- if (instruction->GetSideEffects().DoesAnyRead()) {
+ if (instruction->GetSideEffects().HasDependencies()) {
return (hash_code << 1) | 0;
} else {
return (hash_code << 1) | 1;
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 5c6239b..42ef3ff 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -266,6 +266,8 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
+ static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
+
HGraph* graph = CreateGraph(&allocator);
HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
graph->AddBlock(entry);
@@ -309,7 +311,7 @@
ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
*outer_loop_header->GetLoopInformation()));
- // Check that the loops don't have side effects.
+ // Check that the only side effect of loops is to potentially trigger GC.
{
// Make one block with a side effect.
entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
@@ -327,6 +329,8 @@
ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
+ ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).Equals(kCanTriggerGC));
+ ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
}
// Check that the side effects of the outer loop does not affect the inner loop.
@@ -348,6 +352,7 @@
ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
+ ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
}
// Check that the side effects of the inner loop affects the outer loop.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ca2c998..dba9233 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1215,23 +1215,33 @@
};
/**
- * Side-effects representation for write/read dependences on fields/arrays.
+ * Side-effects representation.
*
- * The dependence analysis uses type disambiguation (e.g. a float field write
- * cannot modify the value of an integer field read) and the access type (e.g.
- * a reference array write cannot modify the value of a reference field read
- * [although it may modify the reference fetch prior to reading the field,
- * which is represented by its own write/read dependence]). The analysis
- * makes conservative points-to assumptions on reference types (e.g. two same
- * typed arrays are assumed to be the same, and any reference read depends
- * on any reference read without further regard of its type).
+ * For write/read dependences on fields/arrays, the dependence analysis uses
+ * type disambiguation (e.g. a float field write cannot modify the value of an
+ * integer field read) and the access type (e.g. a reference array write cannot
+ * modify the value of a reference field read [although it may modify the
+ * reference fetch prior to reading the field, which is represented by its own
+ * write/read dependence]). The analysis makes conservative points-to
+ * assumptions on reference types (e.g. two same typed arrays are assumed to be
+ * the same, and any reference read depends on any reference read without
+ * further regard of its type).
*
- * The internal representation uses the following 36-bit flags assignments:
+ * The internal representation uses 38-bit and is described in the table below.
+ * The first line indicates the side effect, and for field/array accesses the
+ * second line indicates the type of the access (in the order of the
+ * Primitive::Type enum).
+ * The two numbered lines below indicate the bit position in the bitfield (read
+ * vertically).
*
- * |ARRAY-R |FIELD-R |ARRAY-W |FIELD-W |
- * +---------+---------+---------+---------+
- * |543210987|654321098|765432109|876543210|
- * |DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|
+ * |Depends on GC|ARRAY-R |FIELD-R |Can trigger GC|ARRAY-W |FIELD-W |
+ * +-------------+---------+---------+--------------+---------+---------+
+ * | |DFJISCBZL|DFJISCBZL| |DFJISCBZL|DFJISCBZL|
+ * | 3 |333333322|222222221| 1 |111111110|000000000|
+ * | 7 |654321098|765432109| 8 |765432109|876543210|
+ *
+ * Note that, to ease the implementation, 'changes' bits are least significant
+ * bits, while 'dependency' bits are most significant bits.
*/
class SideEffects : public ValueObject {
public:
@@ -1242,6 +1252,22 @@
}
static SideEffects All() {
+ return SideEffects(kAllChangeBits | kAllDependOnBits);
+ }
+
+ static SideEffects AllChanges() {
+ return SideEffects(kAllChangeBits);
+ }
+
+ static SideEffects AllDependencies() {
+ return SideEffects(kAllDependOnBits);
+ }
+
+ static SideEffects AllExceptGCDependency() {
+ return AllWritesAndReads().Union(SideEffects::CanTriggerGC());
+ }
+
+ static SideEffects AllWritesAndReads() {
return SideEffects(kAllWrites | kAllReads);
}
@@ -1255,7 +1281,7 @@
static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
- ? All()
+ ? AllWritesAndReads()
: SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset));
}
@@ -1265,7 +1291,7 @@
static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
- ? All()
+ ? AllWritesAndReads()
: SideEffects(TypeFlagWithAlias(type, kFieldReadOffset));
}
@@ -1273,11 +1299,40 @@
return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset));
}
+ static SideEffects CanTriggerGC() {
+ return SideEffects(1ULL << kCanTriggerGCBit);
+ }
+
+ static SideEffects DependsOnGC() {
+ return SideEffects(1ULL << kDependsOnGCBit);
+ }
+
// Combines the side-effects of this and the other.
SideEffects Union(SideEffects other) const {
return SideEffects(flags_ | other.flags_);
}
+ SideEffects Exclusion(SideEffects other) const {
+ return SideEffects(flags_ & ~other.flags_);
+ }
+
+ bool Includes(SideEffects other) const {
+ return (other.flags_ & flags_) == other.flags_;
+ }
+
+ bool HasSideEffects() const {
+ return (flags_ & kAllChangeBits);
+ }
+
+ bool HasDependencies() const {
+ return (flags_ & kAllDependOnBits);
+ }
+
+ // Returns true if there are no side effects or dependencies.
+ bool DoesNothing() const {
+ return flags_ == 0;
+ }
+
// Returns true if something is written.
bool DoesAnyWrite() const {
return (flags_ & kAllWrites);
@@ -1288,47 +1343,81 @@
return (flags_ & kAllReads);
}
- // Returns true if nothing is written or read.
- bool DoesNothing() const {
- return flags_ == 0;
- }
-
// Returns true if potentially everything is written and read
// (every type and every kind of access).
+ bool DoesAllReadWrite() const {
+ return (flags_ & (kAllWrites | kAllReads)) == (kAllWrites | kAllReads);
+ }
+
bool DoesAll() const {
- return flags_ == (kAllWrites | kAllReads);
+ return flags_ == (kAllChangeBits | kAllDependOnBits);
}
// Returns true if this may read something written by other.
bool MayDependOn(SideEffects other) const {
- const uint64_t reads = (flags_ & kAllReads) >> kFieldReadOffset;
- return (other.flags_ & reads);
+ const uint64_t depends_on_flags = (flags_ & kAllDependOnBits) >> kChangeBits;
+ return (other.flags_ & depends_on_flags);
}
// Returns string representation of flags (for debugging only).
- // Format: |DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|
+ // Format: |x|DFJISCBZL|DFJISCBZL|y|DFJISCBZL|DFJISCBZL|
std::string ToString() const {
- static const char *kDebug = "LZBCSIJFD";
std::string flags = "|";
- for (int s = 35; s >= 0; s--) {
- const int t = s % kBits;
- if ((flags_ >> s) & 1)
- flags += kDebug[t];
- if (t == 0)
+ for (int s = kLastBit; s >= 0; s--) {
+ bool current_bit_is_set = ((flags_ >> s) & 1) != 0;
+ if ((s == kDependsOnGCBit) || (s == kCanTriggerGCBit)) {
+ // This is a bit for the GC side effect.
+ if (current_bit_is_set) {
+ flags += "GC";
+ }
flags += "|";
+ } else {
+ // This is a bit for the array/field analysis.
+ // The underscore character stands for the 'can trigger GC' bit.
+ static const char *kDebug = "LZBCSIJFDLZBCSIJFD_LZBCSIJFDLZBCSIJFD";
+ if (current_bit_is_set) {
+ flags += kDebug[s];
+ }
+ if ((s == kFieldWriteOffset) || (s == kArrayWriteOffset) ||
+ (s == kFieldReadOffset) || (s == kArrayReadOffset)) {
+ flags += "|";
+ }
+ }
}
return flags;
}
- private:
- static constexpr int kBits = 9;
- static constexpr int kFieldWriteOffset = 0 * kBits;
- static constexpr int kArrayWriteOffset = 1 * kBits;
- static constexpr int kFieldReadOffset = 2 * kBits;
- static constexpr int kArrayReadOffset = 3 * kBits;
+ bool Equals(const SideEffects& other) const { return flags_ == other.flags_; }
- static constexpr uint64_t kAllWrites = 0x0003ffff;
- static constexpr uint64_t kAllReads = kAllWrites << kFieldReadOffset;
+ private:
+ static constexpr int kFieldArrayAnalysisBits = 9;
+
+ static constexpr int kFieldWriteOffset = 0;
+ static constexpr int kArrayWriteOffset = kFieldWriteOffset + kFieldArrayAnalysisBits;
+ static constexpr int kLastBitForWrites = kArrayWriteOffset + kFieldArrayAnalysisBits - 1;
+ static constexpr int kCanTriggerGCBit = kLastBitForWrites + 1;
+
+ static constexpr int kChangeBits = kCanTriggerGCBit + 1;
+
+ static constexpr int kFieldReadOffset = kCanTriggerGCBit + 1;
+ static constexpr int kArrayReadOffset = kFieldReadOffset + kFieldArrayAnalysisBits;
+ static constexpr int kLastBitForReads = kArrayReadOffset + kFieldArrayAnalysisBits - 1;
+ static constexpr int kDependsOnGCBit = kLastBitForReads + 1;
+
+ static constexpr int kLastBit = kDependsOnGCBit;
+ static constexpr int kDependOnBits = kLastBit + 1 - kChangeBits;
+
+ // Aliases.
+
+ static_assert(kChangeBits == kDependOnBits,
+ "the 'change' bits should match the 'depend on' bits.");
+
+ static constexpr uint64_t kAllChangeBits = ((1ULL << kChangeBits) - 1);
+ static constexpr uint64_t kAllDependOnBits = ((1ULL << kDependOnBits) - 1) << kChangeBits;
+ static constexpr uint64_t kAllWrites =
+ ((1ULL << (kLastBitForWrites + 1 - kFieldWriteOffset)) - 1) << kFieldWriteOffset;
+ static constexpr uint64_t kAllReads =
+ ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset;
// Work around the fact that HIR aliases I/F and J/D.
// TODO: remove this interceptor once HIR types are clean
@@ -1610,6 +1699,7 @@
virtual bool IsControlFlow() const { return false; }
virtual bool CanThrow() const { return false; }
+ bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
bool DoesAnyWrite() const { return side_effects_.DoesAnyWrite(); }
// Does not apply for all instructions, but having this at top level greatly
@@ -2302,7 +2392,9 @@
public:
HBinaryOperation(Primitive::Type result_type,
HInstruction* left,
- HInstruction* right) : HExpression(result_type, SideEffects::None()) {
+ HInstruction* right,
+ SideEffects side_effects = SideEffects::None())
+ : HExpression(result_type, side_effects) {
SetRawInputAt(0, left);
SetRawInputAt(1, right);
}
@@ -2626,7 +2718,9 @@
HInstruction* second,
ComparisonBias bias,
uint32_t dex_pc)
- : HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias), dex_pc_(dex_pc) {
+ : HBinaryOperation(Primitive::kPrimInt, first, second, SideEffectsForArchRuntimeCalls(type)),
+ bias_(bias),
+ dex_pc_(dex_pc) {
DCHECK_EQ(type, first->GetType());
DCHECK_EQ(type, second->GetType());
}
@@ -2649,7 +2743,12 @@
bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; }
- uint32_t GetDexPc() const { return dex_pc_; }
+ uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
+
+ static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
+ // MIPS64 uses a runtime call for FP comparisons.
+ return Primitive::IsFloatingPointType(type) ? SideEffects::CanTriggerGC() : SideEffects::None();
+ }
DECLARE_INSTRUCTION(Compare);
@@ -2851,7 +2950,8 @@
uint32_t dex_pc,
uint32_t dex_method_index,
InvokeType original_invoke_type)
- : HInstruction(SideEffects::All()), // assume write/read on all fields/arrays
+ : HInstruction(
+ SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays.
number_of_arguments_(number_of_arguments),
inputs_(arena, number_of_arguments),
return_type_(return_type),
@@ -3068,7 +3168,7 @@
uint16_t type_index,
const DexFile& dex_file,
QuickEntrypointEnum entrypoint)
- : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
dex_pc_(dex_pc),
type_index_(type_index),
dex_file_(dex_file),
@@ -3131,7 +3231,7 @@
uint16_t type_index,
const DexFile& dex_file,
QuickEntrypointEnum entrypoint)
- : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
dex_pc_(dex_pc),
type_index_(type_index),
dex_file_(dex_file),
@@ -3232,7 +3332,8 @@
class HDiv : public HBinaryOperation {
public:
HDiv(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {}
+ : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
+ dex_pc_(dex_pc) {}
template <typename T>
T Compute(T x, T y) const {
@@ -3252,6 +3353,11 @@
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
+ static SideEffects SideEffectsForArchRuntimeCalls() {
+ // The generated code can use a runtime call.
+ return SideEffects::CanTriggerGC();
+ }
+
DECLARE_INSTRUCTION(Div);
private:
@@ -3263,7 +3369,8 @@
class HRem : public HBinaryOperation {
public:
HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {}
+ : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
+ dex_pc_(dex_pc) {}
template <typename T>
T Compute(T x, T y) const {
@@ -3283,6 +3390,10 @@
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
+ static SideEffects SideEffectsForArchRuntimeCalls() {
+ return SideEffects::CanTriggerGC();
+ }
+
DECLARE_INSTRUCTION(Rem);
private:
@@ -3593,7 +3704,8 @@
public:
// Instantiate a type conversion of `input` to `result_type`.
HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
- : HExpression(result_type, SideEffects::None()), dex_pc_(dex_pc) {
+ : HExpression(result_type, SideEffectsForArchRuntimeCalls(input->GetType(), result_type)),
+ dex_pc_(dex_pc) {
SetRawInputAt(0, input);
DCHECK_NE(input->GetType(), result_type);
}
@@ -3613,6 +3725,19 @@
// 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 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+ && result_type == Primitive::kPrimLong)
+ || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) {
+ return SideEffects::CanTriggerGC();
+ }
+ return SideEffects::None();
+ }
+
DECLARE_INSTRUCTION(TypeConversion);
private:
@@ -3879,7 +4004,9 @@
HInstruction* value,
Primitive::Type expected_component_type,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::ArrayWriteOfType(expected_component_type)),
+ : HTemplateInstruction(
+ SideEffects::ArrayWriteOfType(expected_component_type).Union(
+ SideEffectsForArchRuntimeCalls(value->GetType()))),
dex_pc_(dex_pc),
expected_component_type_(expected_component_type),
needs_type_check_(value->GetType() == Primitive::kPrimNot),
@@ -3932,6 +4059,10 @@
: expected_component_type_;
}
+ static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) {
+ return (value_type == Primitive::kPrimNot) ? SideEffects::CanTriggerGC() : SideEffects::None();
+ }
+
DECLARE_INSTRUCTION(ArraySet);
private:
@@ -4026,7 +4157,7 @@
class HSuspendCheck : public HTemplateInstruction<0> {
public:
explicit HSuspendCheck(uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc), slow_path_(nullptr) {}
+ : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc), slow_path_(nullptr) {}
bool NeedsEnvironment() const OVERRIDE {
return true;
@@ -4058,7 +4189,7 @@
const DexFile& dex_file,
bool is_referrers_class,
uint32_t dex_pc)
- : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
type_index_(type_index),
dex_file_(dex_file),
is_referrers_class_(is_referrers_class),
@@ -4119,6 +4250,10 @@
bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
+ static SideEffects SideEffectsForArchRuntimeCalls() {
+ return SideEffects::CanTriggerGC();
+ }
+
DECLARE_INSTRUCTION(LoadClass);
private:
@@ -4138,7 +4273,7 @@
class HLoadString : public HExpression<1> {
public:
HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
- : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
string_index_(string_index),
dex_pc_(dex_pc) {
SetRawInputAt(0, current_method);
@@ -4159,6 +4294,10 @@
bool NeedsEnvironment() const OVERRIDE { return false; }
bool NeedsDexCache() const OVERRIDE { return true; }
+ static SideEffects SideEffectsForArchRuntimeCalls() {
+ return SideEffects::CanTriggerGC();
+ }
+
DECLARE_INSTRUCTION(LoadString);
private:
@@ -4175,8 +4314,8 @@
public:
explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
: HExpression(
- Primitive::kPrimNot,
- SideEffects::AllWrites()), // assume write on all fields/arrays
+ Primitive::kPrimNot,
+ SideEffects::AllChanges()), // Assume write/read on all fields/arrays.
dex_pc_(dex_pc) {
SetRawInputAt(0, constant);
}
@@ -4305,7 +4444,7 @@
class HThrow : public HTemplateInstruction<1> {
public:
HThrow(HInstruction* exception, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) {
+ : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc) {
SetRawInputAt(0, exception);
}
@@ -4331,7 +4470,7 @@
HLoadClass* constant,
bool class_is_final,
uint32_t dex_pc)
- : HExpression(Primitive::kPrimBoolean, SideEffects::None()),
+ : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(class_is_final)),
class_is_final_(class_is_final),
must_do_null_check_(true),
dex_pc_(dex_pc) {
@@ -4357,6 +4496,10 @@
bool MustDoNullCheck() const { return must_do_null_check_; }
void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+ static SideEffects SideEffectsForArchRuntimeCalls(bool class_is_final) {
+ return class_is_final ? SideEffects::None() : SideEffects::CanTriggerGC();
+ }
+
DECLARE_INSTRUCTION(InstanceOf);
private:
@@ -4416,7 +4559,7 @@
HLoadClass* constant,
bool class_is_final,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()),
+ : HTemplateInstruction(SideEffects::CanTriggerGC()),
class_is_final_(class_is_final),
must_do_null_check_(true),
dex_pc_(dex_pc) {
@@ -4458,7 +4601,7 @@
public:
explicit HMemoryBarrier(MemBarrierKind barrier_kind)
: HTemplateInstruction(
- SideEffects::All()), // assume write/read on all fields/arrays
+ SideEffects::AllWritesAndReads()), // Assume write/read on all fields/arrays.
barrier_kind_(barrier_kind) {}
MemBarrierKind GetBarrierKind() { return barrier_kind_; }
@@ -4479,7 +4622,8 @@
};
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::All()), // assume write/read on all fields/arrays
+ : HTemplateInstruction(
+ SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays.
kind_(kind), dex_pc_(dex_pc) {
SetRawInputAt(0, object);
}
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index 9dbf638..1c3e255 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -47,8 +47,8 @@
inst_it.Advance()) {
HInstruction* instruction = inst_it.Current();
effects = effects.Union(instruction->GetSideEffects());
- // If every possible write/read is represented, scanning further
- // will not add any more information to side-effects of this block.
+ // If all side effects are represented, scanning further will not add any
+ // more information to side-effects of this block.
if (effects.DoesAll()) {
break;
}
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 8db5a8a..ec45d6b 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -77,7 +77,7 @@
EXPECT_TRUE(all.DoesAnyWrite());
EXPECT_TRUE(all.DoesAnyRead());
EXPECT_FALSE(all.DoesNothing());
- EXPECT_TRUE(all.DoesAll());
+ EXPECT_TRUE(all.DoesAllReadWrite());
}
TEST(SideEffectsTest, None) {
@@ -85,7 +85,7 @@
EXPECT_FALSE(none.DoesAnyWrite());
EXPECT_FALSE(none.DoesAnyRead());
EXPECT_TRUE(none.DoesNothing());
- EXPECT_FALSE(none.DoesAll());
+ EXPECT_FALSE(none.DoesAllReadWrite());
}
TEST(SideEffectsTest, DependencesAndNoDependences) {
@@ -176,33 +176,53 @@
s = s.Union(SideEffects::FieldReadOfType(type, false));
s = s.Union(SideEffects::ArrayReadOfType(type));
}
- EXPECT_TRUE(s.DoesAll());
+ EXPECT_TRUE(s.DoesAllReadWrite());
+}
+
+TEST(SideEffectsTest, GC) {
+ SideEffects can_trigger_gc = SideEffects::CanTriggerGC();
+ SideEffects depends_on_gc = SideEffects::DependsOnGC();
+ SideEffects all_changes = SideEffects::AllChanges();
+ SideEffects all_dependencies = SideEffects::AllDependencies();
+
+ EXPECT_TRUE(depends_on_gc.MayDependOn(can_trigger_gc));
+ EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(can_trigger_gc));
+ EXPECT_FALSE(can_trigger_gc.MayDependOn(depends_on_gc));
+
+ EXPECT_TRUE(depends_on_gc.MayDependOn(all_changes));
+ EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(all_changes));
+ EXPECT_FALSE(can_trigger_gc.MayDependOn(all_changes));
+
+ EXPECT_TRUE(all_changes.Includes(can_trigger_gc));
+ EXPECT_FALSE(all_changes.Includes(depends_on_gc));
+ EXPECT_TRUE(all_dependencies.Includes(depends_on_gc));
+ EXPECT_FALSE(all_dependencies.Includes(can_trigger_gc));
}
TEST(SideEffectsTest, BitStrings) {
EXPECT_STREQ(
- "|||||",
+ "|||||||",
SideEffects::None().ToString().c_str());
EXPECT_STREQ(
- "|DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|",
+ "|GC|DFJISCBZL|DFJISCBZL|GC|DFJISCBZL|DFJISCBZL|",
SideEffects::All().ToString().c_str());
EXPECT_STREQ(
- "|||DFJISCBZL|DFJISCBZL|",
+ "|||||DFJISCBZL|DFJISCBZL|",
SideEffects::AllWrites().ToString().c_str());
EXPECT_STREQ(
- "|DFJISCBZL|DFJISCBZL|||",
+ "||DFJISCBZL|DFJISCBZL||||",
SideEffects::AllReads().ToString().c_str());
EXPECT_STREQ(
- "||||L|",
+ "||||||L|",
SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str());
EXPECT_STREQ(
- "|||Z||",
+ "|||||Z||",
SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str());
EXPECT_STREQ(
- "||B|||",
+ "|||B||||",
SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str());
EXPECT_STREQ(
- "|DJ||||", // note: DJ alias
+ "||DJ|||||", // note: DJ alias
SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str());
SideEffects s = SideEffects::None();
s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, false));
@@ -212,7 +232,7 @@
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
EXPECT_STREQ(
- "|DFJI|FI|S|DJC|", // note: DJ/FI alias.
+ "||DFJI|FI||S|DJC|", // note: DJ/FI alias.
s.ToString().c_str());
}