VarAction2: Add quick exit case to last-level callback groups if possible
This commit is contained in:
@@ -35,6 +35,7 @@ enum NewGRFOptimiserFlags {
|
|||||||
NGOF_NO_OPT_VARACT2_SIMPLIFY_STORES = 4,
|
NGOF_NO_OPT_VARACT2_SIMPLIFY_STORES = 4,
|
||||||
NGOF_NO_OPT_VARACT2_ADJUST_ORDERING = 5,
|
NGOF_NO_OPT_VARACT2_ADJUST_ORDERING = 5,
|
||||||
NGOF_NO_OPT_VARACT2_INSERT_JUMPS = 6,
|
NGOF_NO_OPT_VARACT2_INSERT_JUMPS = 6,
|
||||||
|
NGOF_NO_OPT_VARACT2_CB_QUICK_EXIT = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool HasGrfOptimiserFlag(NewGRFOptimiserFlags flag)
|
inline bool HasGrfOptimiserFlag(NewGRFOptimiserFlags flag)
|
||||||
|
@@ -5539,10 +5539,8 @@ static void SkipAct1(ByteReader *buf)
|
|||||||
grfmsg(3, "SkipAct1: Skipping %d sprites", _cur.skip_sprites);
|
grfmsg(3, "SkipAct1: Skipping %d sprites", _cur.skip_sprites);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16 groupid)
|
const CallbackResultSpriteGroup *NewCallbackResultSpriteGroupNoTransform(uint16 result)
|
||||||
{
|
{
|
||||||
uint16 result = CallbackResultSpriteGroup::TransformResultValue(groupid, _cur.grffile->grf_version >= 8);
|
|
||||||
|
|
||||||
const CallbackResultSpriteGroup *&ptr = _callback_result_cache[result];
|
const CallbackResultSpriteGroup *&ptr = _callback_result_cache[result];
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
assert(CallbackResultSpriteGroup::CanAllocateItem());
|
assert(CallbackResultSpriteGroup::CanAllocateItem());
|
||||||
@@ -5551,6 +5549,12 @@ static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16 grou
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16 groupid)
|
||||||
|
{
|
||||||
|
uint16 result = CallbackResultSpriteGroup::TransformResultValue(groupid, _cur.grffile->grf_version >= 8);
|
||||||
|
return NewCallbackResultSpriteGroupNoTransform(result);
|
||||||
|
}
|
||||||
|
|
||||||
/* Helper function to either create a callback or link to a previously
|
/* Helper function to either create a callback or link to a previously
|
||||||
* defined spritegroup. */
|
* defined spritegroup. */
|
||||||
static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
|
static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
|
||||||
|
@@ -94,6 +94,11 @@ static bool IsFeatureUsableForDSE(GrfSpecFeature feature)
|
|||||||
return (feature != GSF_STATIONS);
|
return (feature != GSF_STATIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsFeatureUsableForCBQuickExit(GrfSpecFeature feature)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsIdenticalValueLoad(const DeterministicSpriteGroupAdjust *a, const DeterministicSpriteGroupAdjust *b)
|
static bool IsIdenticalValueLoad(const DeterministicSpriteGroupAdjust *a, const DeterministicSpriteGroupAdjust *b)
|
||||||
{
|
{
|
||||||
if (a == nullptr && b == nullptr) return true;
|
if (a == nullptr && b == nullptr) return true;
|
||||||
@@ -955,6 +960,9 @@ void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const GrfSpecFeatu
|
|||||||
if (!state.seen_procedure_call && ((const DeterministicSpriteGroup*)sg)->dsg_flags & DSGF_REQUIRES_VAR1C) {
|
if (!state.seen_procedure_call && ((const DeterministicSpriteGroup*)sg)->dsg_flags & DSGF_REQUIRES_VAR1C) {
|
||||||
group->dsg_flags |= DSGF_REQUIRES_VAR1C;
|
group->dsg_flags |= DSGF_REQUIRES_VAR1C;
|
||||||
}
|
}
|
||||||
|
if (((const DeterministicSpriteGroup*)sg)->dsg_flags & DSGF_CB_HANDLER) {
|
||||||
|
group->dsg_flags |= DSGF_CB_HANDLER;
|
||||||
|
}
|
||||||
handle_proc_stores(sg);
|
handle_proc_stores(sg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2217,8 +2225,11 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
|
|||||||
{
|
{
|
||||||
if (unlikely(HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2))) return;
|
if (unlikely(HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2))) return;
|
||||||
|
|
||||||
|
bool possible_callback_handler = false;
|
||||||
for (DeterministicSpriteGroupAdjust &adjust : group->adjusts) {
|
for (DeterministicSpriteGroupAdjust &adjust : group->adjusts) {
|
||||||
if (adjust.variable == 0x7D) adjust.parameter &= 0xFF; // Clear temporary version tags
|
if (adjust.variable == 0x7D) adjust.parameter &= 0xFF; // Clear temporary version tags
|
||||||
|
if (adjust.variable == 0xC) possible_callback_handler = true;
|
||||||
|
if (adjust.operation == DSGA_OP_STOP) possible_callback_handler = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE) && (state.inference & VA2AIF_HAVE_CONSTANT) && !group->calculated_result) {
|
if (!HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE) && (state.inference & VA2AIF_HAVE_CONSTANT) && !group->calculated_result) {
|
||||||
@@ -2247,7 +2258,21 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
|
|||||||
bool seen_pending = false;
|
bool seen_pending = false;
|
||||||
bool seen_req_var1C = false;
|
bool seen_req_var1C = false;
|
||||||
if (!group->calculated_result) {
|
if (!group->calculated_result) {
|
||||||
auto handle_group = y_combinator([&](auto handle_group, const SpriteGroup *sg) -> void {
|
bool is_cb_switch = false;
|
||||||
|
if (possible_callback_handler && group->adjusts.size() == 1 && !group->calculated_result &&
|
||||||
|
IsFeatureUsableForCBQuickExit(group->feature) && !HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_CB_QUICK_EXIT)) {
|
||||||
|
const auto &adjust = group->adjusts[0];
|
||||||
|
if (adjust.variable == 0xC && (adjust.operation == DSGA_OP_ADD || adjust.operation == DSGA_OP_RST) &&
|
||||||
|
adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) {
|
||||||
|
is_cb_switch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HandleGroupState {
|
||||||
|
bool ignore_cb_handler = false;
|
||||||
|
bool have_cb_handler = false;
|
||||||
|
};
|
||||||
|
auto handle_group = y_combinator([&](auto handle_group, const SpriteGroup *sg, HandleGroupState &state) -> void {
|
||||||
if (sg != nullptr && sg->type == SGT_DETERMINISTIC) {
|
if (sg != nullptr && sg->type == SGT_DETERMINISTIC) {
|
||||||
VarAction2GroupVariableTracking *var_tracking = _cur.GetVarAction2GroupVariableTracking(sg, false);
|
VarAction2GroupVariableTracking *var_tracking = _cur.GetVarAction2GroupVariableTracking(sg, false);
|
||||||
const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)sg;
|
const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)sg;
|
||||||
@@ -2258,11 +2283,15 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
|
|||||||
if (var_tracking != nullptr) bits |= var_tracking->in;
|
if (var_tracking != nullptr) bits |= var_tracking->in;
|
||||||
}
|
}
|
||||||
if (dsg->dsg_flags & DSGF_REQUIRES_VAR1C) seen_req_var1C = true;
|
if (dsg->dsg_flags & DSGF_REQUIRES_VAR1C) seen_req_var1C = true;
|
||||||
|
if ((dsg->dsg_flags & DSGF_CB_HANDLER) && !state.ignore_cb_handler) {
|
||||||
|
group->dsg_flags |= DSGF_CB_HANDLER;
|
||||||
|
state.have_cb_handler = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sg != nullptr && sg->type == SGT_RANDOMIZED) {
|
if (sg != nullptr && sg->type == SGT_RANDOMIZED) {
|
||||||
const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg;
|
const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg;
|
||||||
for (const auto &group : rsg->groups) {
|
for (const auto &group : rsg->groups) {
|
||||||
handle_group(group);
|
handle_group(group, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sg != nullptr && sg->type == SGT_TILELAYOUT) {
|
if (sg != nullptr && sg->type == SGT_TILELAYOUT) {
|
||||||
@@ -2300,10 +2329,36 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
handle_group(group->default_group);
|
|
||||||
|
HandleGroupState default_group_state;
|
||||||
|
handle_group(group->default_group, default_group_state);
|
||||||
|
|
||||||
|
HandleGroupState ranges_state;
|
||||||
for (const auto &range : group->ranges) {
|
for (const auto &range : group->ranges) {
|
||||||
handle_group(range.group);
|
ranges_state.ignore_cb_handler = is_cb_switch && range.low == 0 && range.high == 0;
|
||||||
|
handle_group(range.group, ranges_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!default_group_state.have_cb_handler && is_cb_switch) {
|
||||||
|
bool found_zero_value = false;
|
||||||
|
bool found_non_zero_value = false;
|
||||||
|
for (const auto &range : group->ranges) {
|
||||||
|
if (range.low == 0) found_zero_value = true;
|
||||||
|
if (range.high > 0) found_non_zero_value = true;
|
||||||
|
}
|
||||||
|
if (!found_non_zero_value) {
|
||||||
|
/* Group looks at var C but has no branches for non-zero cases, so don't consider it a callback handler.
|
||||||
|
* This pattern is generally only used to implement an "always fail" group.
|
||||||
|
*/
|
||||||
|
possible_callback_handler = false;
|
||||||
|
}
|
||||||
|
if (!found_zero_value) {
|
||||||
|
group->ranges.insert(group->ranges.begin(), { group->default_group, 0, 0 });
|
||||||
|
extern const CallbackResultSpriteGroup *NewCallbackResultSpriteGroupNoTransform(uint16 result);
|
||||||
|
group->default_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (bits.any()) {
|
if (bits.any()) {
|
||||||
state.GetVarTracking(group)->out = bits;
|
state.GetVarTracking(group)->out = bits;
|
||||||
std::bitset<256> in_bits = bits | pending_bits;
|
std::bitset<256> in_bits = bits | pending_bits;
|
||||||
@@ -2313,6 +2368,7 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
|
|||||||
state.GetVarTracking(group)->in |= in_bits;
|
state.GetVarTracking(group)->in |= in_bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (possible_callback_handler) group->dsg_flags |= DSGF_CB_HANDLER;
|
||||||
|
|
||||||
if (!HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE) && group->ranges.empty() && !group->calculated_result && !seen_req_var1C) {
|
if (!HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE) && group->ranges.empty() && !group->calculated_result && !seen_req_var1C) {
|
||||||
/* There is only one option, remove any redundant adjustments when the result will be ignored anyway */
|
/* There is only one option, remove any redundant adjustments when the result will be ignored anyway */
|
||||||
|
@@ -605,6 +605,12 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
|
|||||||
if (adjust.variable == 0xC && (adjust.operation == DSGA_OP_ADD || adjust.operation == DSGA_OP_RST)
|
if (adjust.variable == 0xC && (adjust.operation == DSGA_OP_ADD || adjust.operation == DSGA_OP_RST)
|
||||||
&& adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) {
|
&& adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) {
|
||||||
is_callback_group = true;
|
is_callback_group = true;
|
||||||
|
if (*padding == 0 && !dsg->calculated_result && ranges->size() > 0) {
|
||||||
|
const DeterministicSpriteGroupRange &first_range = (*ranges)[0];
|
||||||
|
if (first_range.low == 0 && first_range.high == 0 && first_range.group != nullptr) {
|
||||||
|
this->top_graphics_group = first_range.group;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +623,12 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
|
|||||||
print();
|
print();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (dsg == this->top_graphics_group && !((flags & SGDF_RANGE) && strlen(padding) == 2)) {
|
||||||
|
seprintf(this->buffer, lastof(this->buffer), "%sTOP LEVEL GRAPHICS GROUP: Deterministic (%s, %s), [%u]",
|
||||||
|
padding, _sg_scope_names[dsg->var_scope], _sg_size_names[dsg->size], dsg->nfo_line);
|
||||||
|
print();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto res = this->seen_dsgs.insert(dsg);
|
auto res = this->seen_dsgs.insert(dsg);
|
||||||
if (!res.second) {
|
if (!res.second) {
|
||||||
seprintf(this->buffer, lastof(this->buffer), "%sGROUP SEEN ABOVE: Deterministic (%s, %s), [%u]",
|
seprintf(this->buffer, lastof(this->buffer), "%sGROUP SEEN ABOVE: Deterministic (%s, %s), [%u]",
|
||||||
@@ -634,6 +646,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
|
|||||||
if (dsg->dsg_flags & DSGF_REQUIRES_VAR1C) p += seprintf(p, lastof(this->buffer), ", REQ_1C");
|
if (dsg->dsg_flags & DSGF_REQUIRES_VAR1C) p += seprintf(p, lastof(this->buffer), ", REQ_1C");
|
||||||
if (dsg->dsg_flags & DSGF_CHECK_EXPENSIVE_VARS) p += seprintf(p, lastof(this->buffer), ", CHECK_EXP_VAR");
|
if (dsg->dsg_flags & DSGF_CHECK_EXPENSIVE_VARS) p += seprintf(p, lastof(this->buffer), ", CHECK_EXP_VAR");
|
||||||
if (dsg->dsg_flags & DSGF_CHECK_INSERT_JUMP) p += seprintf(p, lastof(this->buffer), ", CHECK_INS_JMP");
|
if (dsg->dsg_flags & DSGF_CHECK_INSERT_JUMP) p += seprintf(p, lastof(this->buffer), ", CHECK_INS_JMP");
|
||||||
|
if (dsg->dsg_flags & DSGF_CB_HANDLER) p += seprintf(p, lastof(this->buffer), ", CB_HANDLER");
|
||||||
}
|
}
|
||||||
print();
|
print();
|
||||||
emit_start();
|
emit_start();
|
||||||
@@ -668,7 +681,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
print();
|
print();
|
||||||
this->DumpSpriteGroup(range.group, subgroup_padding.c_str(), 0);
|
this->DumpSpriteGroup(range.group, subgroup_padding.c_str(), SGDF_RANGE);
|
||||||
}
|
}
|
||||||
if (default_group != nullptr) {
|
if (default_group != nullptr) {
|
||||||
seprintf(this->buffer, lastof(this->buffer), "%sdefault", padding);
|
seprintf(this->buffer, lastof(this->buffer), "%sdefault", padding);
|
||||||
|
@@ -432,6 +432,7 @@ enum DeterministicSpriteGroupFlags : uint8 {
|
|||||||
DSGF_REQUIRES_VAR1C = 1 << 3,
|
DSGF_REQUIRES_VAR1C = 1 << 3,
|
||||||
DSGF_CHECK_EXPENSIVE_VARS = 1 << 4,
|
DSGF_CHECK_EXPENSIVE_VARS = 1 << 4,
|
||||||
DSGF_CHECK_INSERT_JUMP = 1 << 5,
|
DSGF_CHECK_INSERT_JUMP = 1 << 5,
|
||||||
|
DSGF_CB_HANDLER = 1 << 6,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags)
|
DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags)
|
||||||
|
|
||||||
@@ -727,10 +728,12 @@ private:
|
|||||||
DumpSpriteGroupPrinter print_fn;
|
DumpSpriteGroupPrinter print_fn;
|
||||||
|
|
||||||
const SpriteGroup *top_default_group = nullptr;
|
const SpriteGroup *top_default_group = nullptr;
|
||||||
|
const SpriteGroup *top_graphics_group = nullptr;
|
||||||
btree::btree_set<const DeterministicSpriteGroup *> seen_dsgs;
|
btree::btree_set<const DeterministicSpriteGroup *> seen_dsgs;
|
||||||
|
|
||||||
enum SpriteGroupDumperFlags {
|
enum SpriteGroupDumperFlags {
|
||||||
SGDF_DEFAULT = 1 << 0,
|
SGDF_DEFAULT = 1 << 0,
|
||||||
|
SGDF_RANGE = 1 << 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
void DumpSpriteGroup(const SpriteGroup *sg, const char *prefix, uint flags);
|
void DumpSpriteGroup(const SpriteGroup *sg, const char *prefix, uint flags);
|
||||||
|
Reference in New Issue
Block a user