VarAction2: Allow inlining trivial procedures

pull/436/head
Jonathan G Rennison 2 years ago
parent 9e24414033
commit c2ede2af54

@ -36,6 +36,7 @@ enum NewGRFOptimiserFlags {
NGOF_NO_OPT_VARACT2_ADJUST_ORDERING = 5,
NGOF_NO_OPT_VARACT2_INSERT_JUMPS = 6,
NGOF_NO_OPT_VARACT2_CB_QUICK_EXIT = 7,
NGOF_NO_OPT_VARACT2_PROC_INLINE = 8,
};
inline bool HasGrfOptimiserFlag(NewGRFOptimiserFlags flag)

@ -5722,6 +5722,8 @@ static void NewSpriteGroup(ByteReader *buf)
if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) {
shadow = &(_deterministic_sg_shadows[group]);
}
static std::vector<DeterministicSpriteGroupAdjust> current_adjusts;
current_adjusts.clear();
VarAction2OptimiseState va2_opt_state;
/* The initial value is always the constant 0 */
@ -5731,7 +5733,7 @@ static void NewSpriteGroup(ByteReader *buf)
/* Loop through the var adjusts. Unfortunately we don't know how many we have
* from the outset, so we shall have to keep reallocing. */
do {
DeterministicSpriteGroupAdjust &adjust = group->adjusts.emplace_back();
DeterministicSpriteGroupAdjust &adjust = current_adjusts.emplace_back();
/* The first var adjust doesn't have an operation specified, so we set it to add. */
adjust.operation = first_adjust ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte();
@ -5784,11 +5786,16 @@ static void NewSpriteGroup(ByteReader *buf)
if (adjust.subroutine != nullptr) adjust.subroutine = PruneTargetSpriteGroup(adjust.subroutine);
}
OptimiseVarAction2Adjust(va2_opt_state, feature, varsize, group, adjust);
OptimiseVarAction2PreCheckAdjust(va2_opt_state, adjust);
/* Continue reading var adjusts while bit 5 is set. */
} while (HasBit(varadjust, 5));
for (const DeterministicSpriteGroupAdjust &adjust : current_adjusts) {
group->adjusts.push_back(adjust);
OptimiseVarAction2Adjust(va2_opt_state, feature, varsize, group, group->adjusts.back());
}
std::vector<DeterministicSpriteGroupRange> ranges;
ranges.resize(buf->ReadByte());
for (uint i = 0; i < ranges.size(); i++) {
@ -5817,7 +5824,7 @@ static void NewSpriteGroup(ByteReader *buf)
ProcessDeterministicSpriteGroupRanges(ranges, group->ranges, group->default_group);
OptimiseVarAction2DeterministicSpriteGroup(va2_opt_state, feature, varsize, group);
OptimiseVarAction2DeterministicSpriteGroup(va2_opt_state, feature, varsize, group, current_adjusts);
break;
}

@ -72,6 +72,8 @@ public:
UniformArenaAllocator<sizeof(VarAction2GroupVariableTracking), 1024> group_temp_store_variable_tracking_storage;
btree::btree_map<const SpriteGroup *, VarAction2ProcedureAnnotation *> procedure_annotations;
UniformArenaAllocator<sizeof(VarAction2ProcedureAnnotation), 1024> procedure_annotations_storage;
btree::btree_map<const DeterministicSpriteGroup *, std::vector<DeterministicSpriteGroupAdjust> *> inlinable_adjust_groups;
UniformArenaAllocator<sizeof(std::vector<DeterministicSpriteGroupAdjust>), 1024> inlinable_adjust_groups_storage;
std::vector<DeterministicSpriteGroup *> dead_store_elimination_candidates;
VarAction2GroupVariableTracking *GetVarAction2GroupVariableTracking(const SpriteGroup *group, bool make_new)
@ -98,6 +100,19 @@ public:
}
}
std::vector<DeterministicSpriteGroupAdjust> *GetInlinableGroupAdjusts(const DeterministicSpriteGroup *group, bool make_new)
{
if (make_new) {
std::vector<DeterministicSpriteGroupAdjust> *&ptr = this->inlinable_adjust_groups[group];
if (!ptr) ptr = new (this->inlinable_adjust_groups_storage.Allocate()) std::vector<DeterministicSpriteGroupAdjust>();
return ptr;
} else {
auto iter = this->inlinable_adjust_groups.find(group);
if (iter != this->inlinable_adjust_groups.end()) return iter->second;
return nullptr;
}
}
/** Clear temporary data before processing the next file in the current loading stage */
void ClearDataForNextFile()
{
@ -114,6 +129,8 @@ public:
this->group_temp_store_variable_tracking_storage.EmptyArena();
this->procedure_annotations.clear();
this->procedure_annotations_storage.EmptyArena();
this->inlinable_adjust_groups.clear();
this->inlinable_adjust_groups_storage.EmptyArena();
this->dead_store_elimination_candidates.clear();
}
@ -234,6 +251,7 @@ struct VarAction2OptimiseState {
VarAction2InferenceBackup inference_backup;
VarAction2GroupVariableTracking *var_tracking = nullptr;
bool seen_procedure_call = false;
bool var_1C_present = false;
bool check_expensive_vars = false;
bool enable_dse = false;
uint default_variable_version = 0;
@ -249,9 +267,16 @@ struct VarAction2OptimiseState {
}
};
inline void OptimiseVarAction2PreCheckAdjust(VarAction2OptimiseState &state, const DeterministicSpriteGroupAdjust &adjust)
{
uint16 variable = adjust.variable;
if (variable == 0x7B) variable = adjust.parameter;
if (variable == 0x1C) state.var_1C_present = true;
}
const SpriteGroup *PruneTargetSpriteGroup(const SpriteGroup *result);
void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const GrfSpecFeature feature, const byte varsize, DeterministicSpriteGroup *group, DeterministicSpriteGroupAdjust &adjust);
void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state, const GrfSpecFeature feature, const byte varsize, DeterministicSpriteGroup *group);
void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state, const GrfSpecFeature feature, const byte varsize, DeterministicSpriteGroup *group, std::vector<DeterministicSpriteGroupAdjust> &saved_adjusts);
void HandleVarAction2OptimisationPasses();
#endif /* NEWGRF_INTERNAL_H */

@ -742,6 +742,59 @@ void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const GrfSpecFeatu
}
};
auto try_inline_procedure = [&]() -> bool {
if (adjust.operation != DSGA_OP_RST || adjust.type != DSGA_TYPE_NONE || state.var_1C_present) return false;
const SpriteGroup *subroutine = adjust.subroutine;
if (subroutine == nullptr || subroutine->type != SGT_DETERMINISTIC || subroutine->feature != group->feature) {
return false;
}
const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)subroutine;
if (!(dsg->dsg_flags & DSGF_INLINE_CANDIDATE) || dsg->var_scope != group->var_scope || dsg->size != group->size) return false;
std::vector<DeterministicSpriteGroupAdjust> *proc = _cur.GetInlinableGroupAdjusts(dsg, false);
if (proc == nullptr) return false;
byte shift_num = adjust.shift_num;
uint32 and_mask = adjust.and_mask;
// Initial value state is 0
replace_with_constant_load(0);
for (const DeterministicSpriteGroupAdjust &proc_adjust : *proc) {
group->adjusts.push_back(proc_adjust);
OptimiseVarAction2Adjust(state, feature, varsize, group, group->adjusts.back());
}
if (shift_num != 0) {
DeterministicSpriteGroupAdjust &adj = group->adjusts.emplace_back();
adj.operation = DSGA_OP_SHR;
adj.variable = 0x1A;
adj.shift_num = 0;
adj.type = DSGA_TYPE_NONE;
adj.and_mask = shift_num;
adj.add_val = 0;
adj.divmod_val = 0;
OptimiseVarAction2Adjust(state, feature, varsize, group, group->adjusts.back());
}
if (and_mask != 0xFFFFFFFF) {
DeterministicSpriteGroupAdjust &adj = group->adjusts.emplace_back();
adj.operation = DSGA_OP_AND;
adj.variable = 0x1A;
adj.shift_num = 0;
adj.type = DSGA_TYPE_NONE;
adj.and_mask = and_mask;
adj.add_val = 0;
adj.divmod_val = 0;
OptimiseVarAction2Adjust(state, feature, varsize, group, group->adjusts.back());
}
group->sg_flags |= SGF_INLINING;
return true;
};
/* Special handling of variable 7B, this uses the parameter as the variable number, and the last value as the variable's parameter.
* If the last value is a known constant, it can be substituted immediately. */
if (adjust.variable == 0x7B) {
@ -772,6 +825,7 @@ void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const GrfSpecFeatu
const VarAction2TempStoreInference &store = iter->second;
if (store.inference & VA2AIF_HAVE_CONSTANT) {
adjust.variable = 0x1A;
adjust.parameter = 0;
adjust.and_mask &= (store.store_constant >> adjust.shift_num);
} else if ((store.inference & VA2AIF_SINGLE_LOAD) && (store.var_source.variable == 0x7D || IsVariableVeryCheap(store.var_source.variable, feature))) {
if (adjust.type == DSGA_TYPE_NONE && adjust.shift_num == 0 && (adjust.and_mask == 0xFFFFFFFF || ((store.inference & VA2AIF_ONE_OR_ZERO) && (adjust.and_mask & 1)))) {
@ -904,6 +958,8 @@ void OptimiseVarAction2Adjust(VarAction2OptimiseState &state, const GrfSpecFeatu
/* Procedure call or complex adjustment */
if (adjust.operation == DSGA_OP_STO) handle_unpredictable_temp_store();
if (adjust.variable == 0x7E) {
if (try_inline_procedure()) return;
std::bitset<256> seen_stores;
bool seen_unpredictable_store = false;
bool seen_special_store = false;
@ -2311,7 +2367,30 @@ static void OptimiseVarAction2DeterministicSpriteResolveJumps(DeterministicSprit
}
}
void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state, const GrfSpecFeature feature, const byte varsize, DeterministicSpriteGroup *group)
static const size_t MAX_PROC_INLINE_ADJUST_COUNT = 5;
static void OptimiseVarAction2CheckInliningCandidate(DeterministicSpriteGroup *group, std::vector<DeterministicSpriteGroupAdjust> &saved_adjusts)
{
if (HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_PROC_INLINE)) return;
if (group->adjusts.size() > MAX_PROC_INLINE_ADJUST_COUNT || !group->calculated_result || group->var_scope != VSG_SCOPE_SELF) return;
for (const DeterministicSpriteGroupAdjust &adjust : group->adjusts) {
uint variable = adjust.variable;
if (variable == 0x7B) variable = adjust.parameter;
if (variable == 0xC || variable == 0x10 || variable == 0x18 || variable == 0x1A || (variable >= 0x7D && variable <= 0x7F)) {
// OK
} else if (variable == 0x7C) {
if (group->feature != GSF_AIRPORTS && group->feature != GSF_INDUSTRIES) return;
} else {
return;
}
}
group->dsg_flags |= DSGF_INLINE_CANDIDATE;
*(_cur.GetInlinableGroupAdjusts(group, true)) = std::move(saved_adjusts);
}
void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state, const GrfSpecFeature feature, const byte varsize, DeterministicSpriteGroup *group, std::vector<DeterministicSpriteGroupAdjust> &saved_adjusts)
{
if (unlikely(HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2))) return;
@ -2506,6 +2585,8 @@ void OptimiseVarAction2DeterministicSpriteGroup(VarAction2OptimiseState &state,
OptimiseVarAction2DeterministicSpriteGroupAdjustOrdering(group);
}
OptimiseVarAction2CheckInliningCandidate(group, saved_adjusts);
if (state.check_expensive_vars && !HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_EXPENSIVE_VARS)) {
if (dse_candidate) {
group->dsg_flags |= DSGF_CHECK_EXPENSIVE_VARS;

@ -561,6 +561,9 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
char extra_info[64] = "";
if (sg->sg_flags & SGF_ACTION6) strecat(extra_info, " (action 6 modified)", lastof(extra_info));
if (HasBit(_misc_debug_flags, MDF_NEWGRF_SG_DUMP_MORE_DETAIL)) {
if (sg->sg_flags & SGF_INLINING) strecat(extra_info, " (inlining)", lastof(extra_info));
}
switch (sg->type) {
case SGT_REAL: {
@ -648,6 +651,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
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_CB_HANDLER) p += seprintf(p, lastof(this->buffer), ", CB_HANDLER");
if (dsg->dsg_flags & DSGF_INLINE_CANDIDATE) p += seprintf(p, lastof(this->buffer), ", INLINE_CANDIDATE");
}
print();
emit_start();

@ -61,6 +61,7 @@ extern SpriteGroupPool _spritegroup_pool;
enum SpriteGroupFlags : uint8 {
SGF_NONE = 0,
SGF_ACTION6 = 1 << 0,
SGF_INLINING = 1 << 1,
};
DECLARE_ENUM_AS_BIT_SET(SpriteGroupFlags)
@ -432,6 +433,7 @@ enum DeterministicSpriteGroupFlags : uint8 {
DSGF_CHECK_EXPENSIVE_VARS = 1 << 4,
DSGF_CHECK_INSERT_JUMP = 1 << 5,
DSGF_CB_HANDLER = 1 << 6,
DSGF_INLINE_CANDIDATE = 1 << 7,
};
DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags)

Loading…
Cancel
Save