Commit 6c2a5cae authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[flags] Print detected cycle in flag implications

Instead of just failing with a CHECK failure, do print the actual cycle.

Before:
# Check failed: iteration++ < 1000.

After:
# Cycle in flag implications:
--assert-types -> --no-concurrent-recompilation
--stress-concurrent-inlining -> --concurrent-recompilation

R=tebbi@chromium.org

Bug: chromium:1336577
Change-Id: I9707fbe19fbc3c27b54cf2ef7626a5f8825e8c60
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3707275
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81577}
parent ad89010f
...@@ -80,9 +80,9 @@ ...@@ -80,9 +80,9 @@
#define DEFINE_GENERIC_IMPLICATION(whenflag, statement) \ #define DEFINE_GENERIC_IMPLICATION(whenflag, statement) \
if (FLAG_##whenflag) statement; if (FLAG_##whenflag) statement;
#define DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, value) \ #define DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, value) \
changed |= TriggerImplication(!FLAG_##whenflag, #whenflag, &FLAG_##thenflag, \ changed |= TriggerImplication(!FLAG_##whenflag, "!" #whenflag, \
value, false); &FLAG_##thenflag, value, false);
// We apply a generic macro to the flags. // We apply a generic macro to the flags.
#elif defined(FLAG_MODE_APPLY) #elif defined(FLAG_MODE_APPLY)
...@@ -893,7 +893,7 @@ DEFINE_VALUE_IMPLICATION(stress_inline, max_inlined_bytecode_size_cumulative, ...@@ -893,7 +893,7 @@ DEFINE_VALUE_IMPLICATION(stress_inline, max_inlined_bytecode_size_cumulative,
999999) 999999)
DEFINE_VALUE_IMPLICATION(stress_inline, max_inlined_bytecode_size_absolute, DEFINE_VALUE_IMPLICATION(stress_inline, max_inlined_bytecode_size_absolute,
999999) 999999)
DEFINE_VALUE_IMPLICATION(stress_inline, min_inlining_frequency, 0) DEFINE_VALUE_IMPLICATION(stress_inline, min_inlining_frequency, 0.)
DEFINE_IMPLICATION(stress_inline, polymorphic_inlining) DEFINE_IMPLICATION(stress_inline, polymorphic_inlining)
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining") DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_BOOL(turbo_inline_array_builtins, true, DEFINE_BOOL(turbo_inline_array_builtins, true,
...@@ -972,7 +972,7 @@ DEFINE_BOOL(turboshaft_trace_reduction, false, ...@@ -972,7 +972,7 @@ DEFINE_BOOL(turboshaft_trace_reduction, false,
DEFINE_BOOL(optimize_for_size, false, DEFINE_BOOL(optimize_for_size, false,
"Enables optimizations which favor memory size over execution " "Enables optimizations which favor memory size over execution "
"speed") "speed")
DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, 1) DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, size_t{1})
// Flags for WebAssembly. // Flags for WebAssembly.
#if V8_ENABLE_WEBASSEMBLY #if V8_ENABLE_WEBASSEMBLY
...@@ -2242,8 +2242,10 @@ DEFINE_NEG_IMPLICATION(predictable, parallel_compile_tasks_for_lazy) ...@@ -2242,8 +2242,10 @@ DEFINE_NEG_IMPLICATION(predictable, parallel_compile_tasks_for_lazy)
DEFINE_BOOL(predictable_gc_schedule, false, DEFINE_BOOL(predictable_gc_schedule, false,
"Predictable garbage collection schedule. Fixes heap growing, " "Predictable garbage collection schedule. Fixes heap growing, "
"idle, and memory reducing behavior.") "idle, and memory reducing behavior.")
DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, min_semi_space_size, 4) DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, min_semi_space_size,
DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, max_semi_space_size, 4) size_t{4})
DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, max_semi_space_size,
size_t{4})
DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, heap_growing_percent, 30) DEFINE_VALUE_IMPLICATION(predictable_gc_schedule, heap_growing_percent, 30)
DEFINE_NEG_IMPLICATION(predictable_gc_schedule, memory_reducer) DEFINE_NEG_IMPLICATION(predictable_gc_schedule, memory_reducer)
......
...@@ -370,7 +370,7 @@ Flag flags[] = { ...@@ -370,7 +370,7 @@ Flag flags[] = {
#include "src/flags/flag-definitions.h" // NOLINT(build/include) #include "src/flags/flag-definitions.h" // NOLINT(build/include)
}; };
const size_t num_flags = sizeof(flags) / sizeof(*flags); constexpr size_t kNumFlags = arraysize(flags);
bool EqualNames(const char* a, const char* b) { bool EqualNames(const char* a, const char* b) {
for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) { for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
...@@ -382,14 +382,14 @@ bool EqualNames(const char* a, const char* b) { ...@@ -382,14 +382,14 @@ bool EqualNames(const char* a, const char* b) {
} }
Flag* FindFlagByName(const char* name) { Flag* FindFlagByName(const char* name) {
for (size_t i = 0; i < num_flags; ++i) { for (size_t i = 0; i < kNumFlags; ++i) {
if (EqualNames(name, flags[i].name())) return &flags[i]; if (EqualNames(name, flags[i].name())) return &flags[i];
} }
return nullptr; return nullptr;
} }
Flag* FindFlagByPointer(const void* ptr) { Flag* FindFlagByPointer(const void* ptr) {
for (size_t i = 0; i < num_flags; ++i) { for (size_t i = 0; i < kNumFlags; ++i) {
if (flags[i].PointsTo(ptr)) return &flags[i]; if (flags[i].PointsTo(ptr)) return &flags[i];
} }
return nullptr; return nullptr;
...@@ -486,7 +486,7 @@ namespace { ...@@ -486,7 +486,7 @@ namespace {
static std::atomic<uint32_t> flag_hash{0}; static std::atomic<uint32_t> flag_hash{0};
static std::atomic<bool> flags_frozen{false}; static std::atomic<bool> flags_frozen{false};
void ComputeFlagListHash() { uint32_t ComputeFlagListHash() {
std::ostringstream modified_args_as_string; std::ostringstream modified_args_as_string;
if (COMPRESS_POINTERS_BOOL) modified_args_as_string << "ptr-compr"; if (COMPRESS_POINTERS_BOOL) modified_args_as_string << "ptr-compr";
if (DEBUG_BOOL) modified_args_as_string << "debug"; if (DEBUG_BOOL) modified_args_as_string << "debug";
...@@ -505,7 +505,7 @@ void ComputeFlagListHash() { ...@@ -505,7 +505,7 @@ void ComputeFlagListHash() {
args.c_str(), args.c_str() + args.length())) | args.c_str(), args.c_str() + args.length())) |
1; 1;
DCHECK_NE(hash, 0); DCHECK_NE(hash, 0);
flag_hash.store(hash, std::memory_order_relaxed); return hash;
} }
} // namespace } // namespace
...@@ -782,7 +782,7 @@ bool FlagList::IsFrozen() { ...@@ -782,7 +782,7 @@ bool FlagList::IsFrozen() {
// static // static
void FlagList::ReleaseDynamicAllocations() { void FlagList::ReleaseDynamicAllocations() {
flag_hash = 0; flag_hash = 0;
for (size_t i = 0; i < num_flags; ++i) { for (size_t i = 0; i < kNumFlags; ++i) {
flags[i].ReleaseDynamicAllocations(); flags[i].ReleaseDynamicAllocations();
} }
} }
...@@ -820,19 +820,72 @@ void FlagList::PrintValues() { ...@@ -820,19 +820,72 @@ void FlagList::PrintValues() {
namespace { namespace {
template <class A, class B> class ImplicationProcessor {
bool TriggerImplication(bool premise, const char* premise_name, public:
A* conclusion_pointer, B value, bool weak_implication) { // Returns {true} if any flag value was changed.
if (!premise) return false; bool EnforceImplications() {
bool change_flag = *conclusion_pointer != implicit_cast<A>(value); bool changed = false;
Flag* conclusion_flag = FindFlagByPointer(conclusion_pointer); #define FLAG_MODE_DEFINE_IMPLICATIONS
change_flag = conclusion_flag->CheckFlagChange( #include "src/flags/flag-definitions.h" // NOLINT(build/include)
weak_implication ? Flag::SetBy::kWeakImplication #undef FLAG_MODE_DEFINE_IMPLICATIONS
: Flag::SetBy::kImplication, CheckForCycle();
change_flag, premise_name); return changed;
if (change_flag) *conclusion_pointer = value; }
return change_flag;
} private:
// Called from {DEFINE_*_IMPLICATION} in flag-definitions.h.
template <class T>
bool TriggerImplication(bool premise, const char* premise_name,
FlagValue<T>* conclusion_value, T value,
bool weak_implication) {
if (!premise) return false;
Flag* conclusion_flag = FindFlagByPointer(conclusion_value);
if (!conclusion_flag->CheckFlagChange(
weak_implication ? Flag::SetBy::kWeakImplication
: Flag::SetBy::kImplication,
conclusion_value->value() != value, premise_name)) {
return false;
}
if (V8_UNLIKELY(num_iterations_ >= kMaxNumIterations)) {
cycle_ << "\n"
<< premise_name << " -> " << conclusion_flag->name() << " = "
<< value;
}
*conclusion_value = value;
return true;
}
void CheckForCycle() {
// Make sure flag implications reach a fixed point within
// {kMaxNumIterations} iterations.
if (++num_iterations_ < kMaxNumIterations) return;
if (num_iterations_ == kMaxNumIterations) {
// Start cycle detection.
DCHECK(cycle_.str().empty());
cycle_start_hash_ = ComputeFlagListHash();
return;
}
DCHECK_NE(0, cycle_start_hash_);
// We accept spurious but highly unlikely hash collisions here. This is
// only a debug output anyway.
if (ComputeFlagListHash() == cycle_start_hash_) {
DCHECK(!cycle_.str().empty());
// {cycle_} starts with a newline.
FATAL("Cycle in flag implications:%s", cycle_.str().c_str());
}
// We must have found a cycle within another {kMaxNumIterations}.
DCHECK_GE(2 * kMaxNumIterations, num_iterations_);
}
static constexpr size_t kMaxNumIterations = kNumFlags;
size_t num_iterations_ = 0;
// After {kMaxNumIterations} we use the following two fields for finding
// cycles in flags.
uint32_t cycle_start_hash_;
std::ostringstream cycle_;
};
} // namespace } // namespace
...@@ -840,23 +893,18 @@ bool TriggerImplication(bool premise, const char* premise_name, ...@@ -840,23 +893,18 @@ bool TriggerImplication(bool premise, const char* premise_name,
void FlagList::EnforceFlagImplications() { void FlagList::EnforceFlagImplications() {
CHECK(!IsFrozen()); CHECK(!IsFrozen());
flag_hash = 0; flag_hash = 0;
bool changed; for (ImplicationProcessor proc; proc.EnforceImplications();) {
int iteration = 0; // Continue processing (recursive) implications. The processor has an
do { // internal limit to avoid endless recursion.
changed = false; }
#define FLAG_MODE_DEFINE_IMPLICATIONS
#include "src/flags/flag-definitions.h" // NOLINT(build/include)
#undef FLAG_MODE_DEFINE_IMPLICATIONS
// Make sure flag definitions are not touring complete. A.k.a avoid endless
// loops in case of buggy configurations.
CHECK_LT(iteration++, 1000);
} while (changed);
} }
// static // static
uint32_t FlagList::Hash() { uint32_t FlagList::Hash() {
if (flag_hash.load() == 0) ComputeFlagListHash(); if (uint32_t hash = flag_hash.load(std::memory_order_relaxed)) return hash;
return flag_hash.load(); uint32_t hash = ComputeFlagListHash();
flag_hash.store(hash, std::memory_order_relaxed);
return hash;
} }
#undef FLAG_MODE_DEFINE #undef FLAG_MODE_DEFINE
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment