1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

GDScript: Optimize non-constant for-range

This commit is contained in:
Danil Alexeev
2025-05-02 18:43:08 +03:00
parent 3b963ab8b6
commit a13fbc6e3e
10 changed files with 267 additions and 119 deletions

View File

@@ -2045,20 +2045,64 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script));
gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);
if (err) {
return err;
// Optimize `range()` call to not allocate an array.
GDScriptParser::CallNode *range_call = nullptr;
if (for_n->list && for_n->list->type == GDScriptParser::Node::CALL) {
GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(for_n->list);
if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {
if (static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name == "range") {
range_call = call;
}
}
}
gen->write_for_assignment(list);
gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script), range_call != nullptr);
if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
if (range_call != nullptr) {
Vector<GDScriptCodeGenerator::Address> args;
args.resize(range_call->arguments.size());
for (int j = 0; j < args.size(); j++) {
args.write[j] = _parse_expression(codegen, err, range_call->arguments[j]);
if (err) {
return err;
}
}
switch (args.size()) {
case 1:
gen->write_for_range_assignment(codegen.add_constant(0), args[0], codegen.add_constant(1));
break;
case 2:
gen->write_for_range_assignment(args[0], args[1], codegen.add_constant(1));
break;
case 3:
gen->write_for_range_assignment(args[0], args[1], args[2]);
break;
default:
_set_error(R"*(Analyzer bug: Wrong "range()" argument count.)*", range_call);
return ERR_BUG;
}
for (int j = 0; j < args.size(); j++) {
if (args[j].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
}
} else {
GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);
if (err) {
return err;
}
gen->write_for_list_assignment(list);
if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
}
gen->write_for(iterator, for_n->use_conversion_assign);
gen->write_for(iterator, for_n->use_conversion_assign, range_call != nullptr);
// Loop variables must be cleared even when `break`/`continue` is used.
List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop);
@@ -2070,7 +2114,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
return err;
}
gen->write_endfor();
gen->write_endfor(range_call != nullptr);
_clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit.