diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 27c7d41268..14b9a85884 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -6365,8 +6365,8 @@ static void RecursiveDisallowDSEForProcedure(const SpriteGroup *group) if (group->type != SGT_DETERMINISTIC) return; const DeterministicSpriteGroup *sub = static_cast(group); - if (sub->dsg_flags & DSGF_NO_DSE) return; - const_cast(sub)->dsg_flags |= DSGF_NO_DSE; + if (sub->dsg_flags & DSGF_DSE_RECURSIVE_DISABLE) return; + const_cast(sub)->dsg_flags |= (DSGF_NO_DSE | DSGF_DSE_RECURSIVE_DISABLE); for (const DeterministicSpriteGroupAdjust &adjust : sub->adjusts) { if (adjust.variable == 0x7E) RecursiveDisallowDSEForProcedure(adjust.subroutine); } @@ -6661,11 +6661,19 @@ static void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState & } std::bitset<256> bits; + std::bitset<256> pending_bits; + bool seen_pending = false; if (!group->calculated_result) { auto handle_group = y_combinator([&](auto handle_group, const SpriteGroup *sg) -> void { if (sg != nullptr && sg->type == SGT_DETERMINISTIC) { VarAction2GroupVariableTracking *var_tracking = _cur.GetVarAction2GroupVariableTracking(sg, false); - if (var_tracking != nullptr) bits |= var_tracking->in; + const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)sg; + if (dsg->dsg_flags & DSGF_VAR_TRACKING_PENDING) { + seen_pending = true; + if (var_tracking != nullptr) pending_bits |= var_tracking->in; + } else { + if (var_tracking != nullptr) bits |= var_tracking->in; + } } if (sg != nullptr && sg->type == SGT_RANDOMIZED) { const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg; @@ -6714,25 +6722,40 @@ static void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState & } if (bits.any()) { state.GetVarTracking(group)->out = bits; - std::bitset<256> in_bits = bits; + std::bitset<256> in_bits = bits | pending_bits; for (auto &it : state.temp_stores) { in_bits.set(it.first, false); } state.GetVarTracking(group)->in |= in_bits; } } - bool dse_allowed = IsFeatureUsableForDSE(feature); + bool dse_allowed = IsFeatureUsableForDSE(feature) && !HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_DSE); bool dse_eligible = state.enable_dse; - if ((dse_allowed && !dse_eligible) || state.seen_procedure_call) { - dse_eligible |= CheckDeterministicSpriteGroupOutputVarBits(group, bits, !state.seen_procedure_call); + if (dse_allowed && !dse_eligible) { + dse_eligible |= CheckDeterministicSpriteGroupOutputVarBits(group, bits, true); + } + if (state.seen_procedure_call) { + /* Be more pessimistic with procedures as the ordering is different. + * Later groups can require variables set in earlier procedures instead of the usual + * where earlier groups can require variables set in later groups. + * DSE on the procedure runs before the groups which use it, so set the procedure + * output bits not using values from call site groups before DSE. */ + CheckDeterministicSpriteGroupOutputVarBits(group, bits | pending_bits, false); } bool dse_candidate = (dse_allowed && dse_eligible); - if (dse_candidate) _cur.dead_store_elimination_candidates.push_back(group); + if (seen_pending && !dse_candidate) { + group->dsg_flags |= DSGF_NO_DSE; + dse_candidate = true; + } + if (dse_candidate) { + _cur.dead_store_elimination_candidates.push_back(group); + group->dsg_flags |= DSGF_VAR_TRACKING_PENDING; + } if (!dse_candidate) OptimiseVarAction2DeterministicSpriteGroupSimplifyStores(group); if (state.check_expensive_vars && !HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_EXPENSIVE_VARS)) { - if (dse_candidate && !HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_DSE)) { + if (dse_candidate) { _cur.pending_expensive_var_checks.push_back({ feature, group }); } else { OptimiseVarAction2DeterministicSpriteGroupExpensiveVars(feature, group); @@ -6740,11 +6763,10 @@ static void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState & } } -static void HandleVarAction2DeadStoreElimination(DeterministicSpriteGroup *group) +static std::bitset<256> HandleVarAction2DeadStoreElimination(DeterministicSpriteGroup *group, VarAction2GroupVariableTracking *var_tracking, bool no_changes) { std::bitset<256> bits; std::vector substitution_candidates; - VarAction2GroupVariableTracking *var_tracking = _cur.GetVarAction2GroupVariableTracking(group, false); if (var_tracking != nullptr) bits = var_tracking->out; auto abandon_substitution_candidates = [&]() { @@ -6830,7 +6852,7 @@ static void HandleVarAction2DeadStoreElimination(DeterministicSpriteGroup *group } } - if (!bits[idx]) { + if (!bits[idx] && !no_changes) { /* Redundant store */ erase_adjust(i); i--; @@ -6886,7 +6908,7 @@ static void HandleVarAction2DeadStoreElimination(DeterministicSpriteGroup *group abandon_substitution_candidates(); } if (adjust.variable == 0x7D && adjust.parameter < 0x100) { - if (i > 0 && !bits[adjust.parameter]) { + if (i > 0 && !bits[adjust.parameter] && !no_changes) { /* See if this can be made a substitution candidate */ bool add = true; for (size_t j = 0; j < substitution_candidates.size(); j++) { @@ -6924,14 +6946,49 @@ static void HandleVarAction2DeadStoreElimination(DeterministicSpriteGroup *group } i--; } + abandon_substitution_candidates(); + return bits; } static void HandleVarAction2OptimisationPasses() { - if (unlikely(HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2) || HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_DSE))) return; + if (unlikely(HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2))) return; for (DeterministicSpriteGroup *group : _cur.dead_store_elimination_candidates) { - if (!(group->dsg_flags & DSGF_NO_DSE)) HandleVarAction2DeadStoreElimination(group); + VarAction2GroupVariableTracking *var_tracking = _cur.GetVarAction2GroupVariableTracking(group, false); + if (!group->calculated_result) { + /* Add bits from any groups previously marked with DSGF_VAR_TRACKING_PENDING which should now be correctly updated after DSE */ + auto handle_group = y_combinator([&](auto handle_group, const SpriteGroup *sg) -> void { + if (sg != nullptr && sg->type == SGT_DETERMINISTIC) { + VarAction2GroupVariableTracking *targ_var_tracking = _cur.GetVarAction2GroupVariableTracking(sg, false); + if (targ_var_tracking != nullptr) { + if (var_tracking == nullptr) var_tracking = _cur.GetVarAction2GroupVariableTracking(group, true); + var_tracking->out |= targ_var_tracking->in; + } + } + if (sg != nullptr && sg->type == SGT_RANDOMIZED) { + const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg; + for (const auto &group : rsg->groups) { + handle_group(group); + } + } + }); + handle_group(group->default_group); + for (const auto &range : group->ranges) { + handle_group(range.group); + } + } + + /* Always run this even DSGF_NO_DSE is set because the load/store tracking is needed to re-calculate the input bits, + * even if no stores are actually eliminated */ + std::bitset<256> in_bits = HandleVarAction2DeadStoreElimination(group, var_tracking, group->dsg_flags & DSGF_NO_DSE); + if (var_tracking == nullptr && in_bits.any()) { + var_tracking = _cur.GetVarAction2GroupVariableTracking(group, true); + var_tracking->in = in_bits; + } else if (var_tracking != nullptr) { + var_tracking->in = in_bits; + } + OptimiseVarAction2DeterministicSpriteGroupSimplifyStores(group); } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 5397c707d4..170bfc17f2 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -357,8 +357,10 @@ struct DeterministicSpriteGroupRange { }; enum DeterministicSpriteGroupFlags : uint8 { - DSGF_NONE = 0, - DSGF_NO_DSE = 1 << 0, + DSGF_NONE = 0, + DSGF_NO_DSE = 1 << 0, + DSGF_DSE_RECURSIVE_DISABLE = 1 << 1, + DSGF_VAR_TRACKING_PENDING = 1 << 2, }; DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags)