GDScript: Optimize non-constant for-range
This commit is contained in:
@ -2195,101 +2195,39 @@ void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
|
void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
|
||||||
bool list_resolved = false;
|
|
||||||
|
|
||||||
// Optimize constant range() call to not allocate an array.
|
|
||||||
// Use int, Vector2i, Vector3i instead, which also can be used as range iterators.
|
|
||||||
if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) {
|
|
||||||
GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list);
|
|
||||||
GDScriptParser::Node::Type callee_type = call->get_callee_type();
|
|
||||||
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
|
|
||||||
GDScriptParser::IdentifierNode *callee = static_cast<GDScriptParser::IdentifierNode *>(call->callee);
|
|
||||||
if (callee->name == "range") {
|
|
||||||
list_resolved = true;
|
|
||||||
if (call->arguments.is_empty()) {
|
|
||||||
push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call->callee);
|
|
||||||
} else if (call->arguments.size() > 3) {
|
|
||||||
push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee);
|
|
||||||
} else {
|
|
||||||
// Now we can optimize it.
|
|
||||||
bool can_reduce = true;
|
|
||||||
Vector<Variant> args;
|
|
||||||
args.resize(call->arguments.size());
|
|
||||||
for (int i = 0; i < call->arguments.size(); i++) {
|
|
||||||
GDScriptParser::ExpressionNode *argument = call->arguments[i];
|
|
||||||
reduce_expression(argument);
|
|
||||||
|
|
||||||
if (argument->is_constant) {
|
|
||||||
if (argument->reduced_value.get_type() != Variant::INT && argument->reduced_value.get_type() != Variant::FLOAT) {
|
|
||||||
can_reduce = false;
|
|
||||||
push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, Variant::get_type_name(argument->reduced_value.get_type())), argument);
|
|
||||||
}
|
|
||||||
if (can_reduce) {
|
|
||||||
args.write[i] = argument->reduced_value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
can_reduce = false;
|
|
||||||
GDScriptParser::DataType argument_type = argument->get_datatype();
|
|
||||||
if (argument_type.is_variant() || !argument_type.is_hard_type()) {
|
|
||||||
mark_node_unsafe(argument);
|
|
||||||
}
|
|
||||||
if (!argument_type.is_variant() && (argument_type.builtin_type != Variant::INT && argument_type.builtin_type != Variant::FLOAT)) {
|
|
||||||
if (!argument_type.is_hard_type()) {
|
|
||||||
downgrade_node_type_source(argument);
|
|
||||||
} else {
|
|
||||||
push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, argument_type.to_string()), argument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Variant reduced;
|
|
||||||
|
|
||||||
if (can_reduce) {
|
|
||||||
switch (args.size()) {
|
|
||||||
case 1:
|
|
||||||
reduced = (int32_t)args[0];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
reduced = Vector2i(args[0], args[1]);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
reduced = Vector3i(args[0], args[1], args[2]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p_for->list->is_constant = true;
|
|
||||||
p_for->list->reduced_value = reduced;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_for->list->is_constant) {
|
|
||||||
p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list));
|
|
||||||
} else {
|
|
||||||
GDScriptParser::DataType list_type;
|
|
||||||
list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
|
||||||
list_type.kind = GDScriptParser::DataType::BUILTIN;
|
|
||||||
list_type.builtin_type = Variant::ARRAY;
|
|
||||||
p_for->list->set_datatype(list_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GDScriptParser::DataType variable_type;
|
GDScriptParser::DataType variable_type;
|
||||||
String list_visible_type = "<unresolved type>";
|
GDScriptParser::DataType list_type;
|
||||||
if (list_resolved) {
|
|
||||||
|
if (p_for->list) {
|
||||||
|
resolve_node(p_for->list, false);
|
||||||
|
|
||||||
|
bool is_range = false;
|
||||||
|
if (p_for->list->type == GDScriptParser::Node::CALL) {
|
||||||
|
GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list);
|
||||||
|
if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {
|
||||||
|
if (static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name == "range") {
|
||||||
|
if (call->arguments.is_empty()) {
|
||||||
|
push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call);
|
||||||
|
} else if (call->arguments.size() > 3) {
|
||||||
|
push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call);
|
||||||
|
}
|
||||||
|
is_range = true;
|
||||||
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
||||||
variable_type.kind = GDScriptParser::DataType::BUILTIN;
|
variable_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||||
variable_type.builtin_type = Variant::INT;
|
variable_type.builtin_type = Variant::INT;
|
||||||
list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type.
|
}
|
||||||
} else if (p_for->list) {
|
}
|
||||||
resolve_node(p_for->list, false);
|
}
|
||||||
GDScriptParser::DataType list_type = p_for->list->get_datatype();
|
|
||||||
list_visible_type = list_type.to_string();
|
list_type = p_for->list->get_datatype();
|
||||||
|
|
||||||
if (!list_type.is_hard_type()) {
|
if (!list_type.is_hard_type()) {
|
||||||
mark_node_unsafe(p_for->list);
|
mark_node_unsafe(p_for->list);
|
||||||
}
|
}
|
||||||
if (list_type.is_variant()) {
|
|
||||||
|
if (is_range) {
|
||||||
|
// Already solved.
|
||||||
|
} else if (list_type.is_variant()) {
|
||||||
variable_type.kind = GDScriptParser::DataType::VARIANT;
|
variable_type.kind = GDScriptParser::DataType::VARIANT;
|
||||||
mark_node_unsafe(p_for->list);
|
mark_node_unsafe(p_for->list);
|
||||||
} else if (list_type.has_container_element_type(0)) {
|
} else if (list_type.has_container_element_type(0)) {
|
||||||
@ -2342,7 +2280,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
|
|||||||
mark_node_unsafe(p_for->variable);
|
mark_node_unsafe(p_for->variable);
|
||||||
p_for->use_conversion_assign = true;
|
p_for->use_conversion_assign = true;
|
||||||
} else {
|
} else {
|
||||||
push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier);
|
push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_type.to_string(), specified_type.to_string()), p_for->datatype_specifier);
|
||||||
}
|
}
|
||||||
} else if (!is_type_compatible(specified_type, variable_type)) {
|
} else if (!is_type_compatible(specified_type, variable_type)) {
|
||||||
p_for->use_conversion_assign = true;
|
p_for->use_conversion_assign = true;
|
||||||
|
|||||||
@ -1542,16 +1542,35 @@ void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
|
|||||||
if_jmp_addrs.pop_back();
|
if_jmp_addrs.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
|
void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) {
|
||||||
Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
|
Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
|
||||||
Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
|
|
||||||
|
|
||||||
// Store state.
|
// Store state.
|
||||||
for_counter_variables.push_back(counter);
|
for_counter_variables.push_back(counter);
|
||||||
|
|
||||||
|
if (p_is_range) {
|
||||||
|
GDScriptDataType int_type;
|
||||||
|
int_type.has_type = true;
|
||||||
|
int_type.kind = GDScriptDataType::BUILTIN;
|
||||||
|
int_type.builtin_type = Variant::INT;
|
||||||
|
|
||||||
|
Address range_from(Address::LOCAL_VARIABLE, add_local("@range_from", int_type), int_type);
|
||||||
|
Address range_to(Address::LOCAL_VARIABLE, add_local("@range_to", int_type), int_type);
|
||||||
|
Address range_step(Address::LOCAL_VARIABLE, add_local("@range_step", int_type), int_type);
|
||||||
|
|
||||||
|
// Store state.
|
||||||
|
for_range_from_variables.push_back(range_from);
|
||||||
|
for_range_to_variables.push_back(range_to);
|
||||||
|
for_range_step_variables.push_back(range_step);
|
||||||
|
} else {
|
||||||
|
Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
|
||||||
|
|
||||||
|
// Store state.
|
||||||
for_container_variables.push_back(container);
|
for_container_variables.push_back(container);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) {
|
void GDScriptByteCodeGenerator::write_for_list_assignment(const Address &p_list) {
|
||||||
const Address &container = for_container_variables.back()->get();
|
const Address &container = for_container_variables.back()->get();
|
||||||
|
|
||||||
// Assign container.
|
// Assign container.
|
||||||
@ -1560,16 +1579,33 @@ void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) {
|
|||||||
append(p_list);
|
append(p_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion) {
|
void GDScriptByteCodeGenerator::write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) {
|
||||||
|
const Address &range_from = for_range_from_variables.back()->get();
|
||||||
|
const Address &range_to = for_range_to_variables.back()->get();
|
||||||
|
const Address &range_step = for_range_step_variables.back()->get();
|
||||||
|
|
||||||
|
// Assign range args.
|
||||||
|
write_assign(range_from, p_from);
|
||||||
|
write_assign(range_to, p_to);
|
||||||
|
write_assign(range_step, p_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) {
|
||||||
const Address &counter = for_counter_variables.back()->get();
|
const Address &counter = for_counter_variables.back()->get();
|
||||||
const Address &container = for_container_variables.back()->get();
|
const Address &container = p_is_range ? Address() : for_container_variables.back()->get();
|
||||||
|
const Address &range_from = p_is_range ? for_range_from_variables.back()->get() : Address();
|
||||||
|
const Address &range_to = p_is_range ? for_range_to_variables.back()->get() : Address();
|
||||||
|
const Address &range_step = p_is_range ? for_range_step_variables.back()->get() : Address();
|
||||||
|
|
||||||
current_breaks_to_patch.push_back(List<int>());
|
current_breaks_to_patch.push_back(List<int>());
|
||||||
|
|
||||||
GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
|
GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
|
||||||
GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
|
GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
|
||||||
|
|
||||||
if (container.type.has_type) {
|
if (p_is_range) {
|
||||||
|
begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_RANGE;
|
||||||
|
iterate_opcode = GDScriptFunction::OPCODE_ITERATE_RANGE;
|
||||||
|
} else if (container.type.has_type) {
|
||||||
if (container.type.kind == GDScriptDataType::BUILTIN) {
|
if (container.type.kind == GDScriptDataType::BUILTIN) {
|
||||||
switch (container.type.builtin_type) {
|
switch (container.type.builtin_type) {
|
||||||
case Variant::INT:
|
case Variant::INT:
|
||||||
@ -1665,19 +1701,30 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
|
|||||||
// Begin loop.
|
// Begin loop.
|
||||||
append_opcode(begin_opcode);
|
append_opcode(begin_opcode);
|
||||||
append(counter);
|
append(counter);
|
||||||
|
if (p_is_range) {
|
||||||
|
append(range_from);
|
||||||
|
append(range_to);
|
||||||
|
append(range_step);
|
||||||
|
} else {
|
||||||
append(container);
|
append(container);
|
||||||
|
}
|
||||||
append(p_use_conversion ? temp : p_variable);
|
append(p_use_conversion ? temp : p_variable);
|
||||||
for_jmp_addrs.push_back(opcodes.size());
|
for_jmp_addrs.push_back(opcodes.size());
|
||||||
append(0); // End of loop address, will be patched.
|
append(0); // End of loop address, will be patched.
|
||||||
append_opcode(GDScriptFunction::OPCODE_JUMP);
|
append_opcode(GDScriptFunction::OPCODE_JUMP);
|
||||||
append(opcodes.size() + 6); // Skip over 'continue' code.
|
append(opcodes.size() + (p_is_range ? 7 : 6)); // Skip over 'continue' code.
|
||||||
|
|
||||||
// Next iteration.
|
// Next iteration.
|
||||||
int continue_addr = opcodes.size();
|
int continue_addr = opcodes.size();
|
||||||
continue_addrs.push_back(continue_addr);
|
continue_addrs.push_back(continue_addr);
|
||||||
append_opcode(iterate_opcode);
|
append_opcode(iterate_opcode);
|
||||||
append(counter);
|
append(counter);
|
||||||
|
if (p_is_range) {
|
||||||
|
append(range_to);
|
||||||
|
append(range_step);
|
||||||
|
} else {
|
||||||
append(container);
|
append(container);
|
||||||
|
}
|
||||||
append(p_use_conversion ? temp : p_variable);
|
append(p_use_conversion ? temp : p_variable);
|
||||||
for_jmp_addrs.push_back(opcodes.size());
|
for_jmp_addrs.push_back(opcodes.size());
|
||||||
append(0); // Jump destination, will be patched.
|
append(0); // Jump destination, will be patched.
|
||||||
@ -1690,7 +1737,7 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_endfor() {
|
void GDScriptByteCodeGenerator::write_endfor(bool p_is_range) {
|
||||||
// Jump back to loop check.
|
// Jump back to loop check.
|
||||||
append_opcode(GDScriptFunction::OPCODE_JUMP);
|
append_opcode(GDScriptFunction::OPCODE_JUMP);
|
||||||
append(continue_addrs.back()->get());
|
append(continue_addrs.back()->get());
|
||||||
@ -1710,7 +1757,13 @@ void GDScriptByteCodeGenerator::write_endfor() {
|
|||||||
|
|
||||||
// Pop state.
|
// Pop state.
|
||||||
for_counter_variables.pop_back();
|
for_counter_variables.pop_back();
|
||||||
|
if (p_is_range) {
|
||||||
|
for_range_from_variables.pop_back();
|
||||||
|
for_range_to_variables.pop_back();
|
||||||
|
for_range_step_variables.pop_back();
|
||||||
|
} else {
|
||||||
for_container_variables.pop_back();
|
for_container_variables.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::start_while_condition() {
|
void GDScriptByteCodeGenerator::start_while_condition() {
|
||||||
|
|||||||
@ -144,6 +144,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
|||||||
List<int> for_jmp_addrs;
|
List<int> for_jmp_addrs;
|
||||||
List<Address> for_counter_variables;
|
List<Address> for_counter_variables;
|
||||||
List<Address> for_container_variables;
|
List<Address> for_container_variables;
|
||||||
|
List<Address> for_range_from_variables;
|
||||||
|
List<Address> for_range_to_variables;
|
||||||
|
List<Address> for_range_step_variables;
|
||||||
List<int> while_jmp_addrs;
|
List<int> while_jmp_addrs;
|
||||||
List<int> continue_addrs;
|
List<int> continue_addrs;
|
||||||
|
|
||||||
@ -535,10 +538,11 @@ public:
|
|||||||
virtual void write_endif() override;
|
virtual void write_endif() override;
|
||||||
virtual void write_jump_if_shared(const Address &p_value) override;
|
virtual void write_jump_if_shared(const Address &p_value) override;
|
||||||
virtual void write_end_jump_if_shared() override;
|
virtual void write_end_jump_if_shared() override;
|
||||||
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
|
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) override;
|
||||||
virtual void write_for_assignment(const Address &p_list) override;
|
virtual void write_for_list_assignment(const Address &p_list) override;
|
||||||
virtual void write_for(const Address &p_variable, bool p_use_conversion) override;
|
virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) override;
|
||||||
virtual void write_endfor() override;
|
virtual void write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) override;
|
||||||
|
virtual void write_endfor(bool p_is_range) override;
|
||||||
virtual void start_while_condition() override;
|
virtual void start_while_condition() override;
|
||||||
virtual void write_while(const Address &p_condition) override;
|
virtual void write_while(const Address &p_condition) override;
|
||||||
virtual void write_endwhile() override;
|
virtual void write_endwhile() override;
|
||||||
|
|||||||
@ -148,10 +148,11 @@ public:
|
|||||||
virtual void write_endif() = 0;
|
virtual void write_endif() = 0;
|
||||||
virtual void write_jump_if_shared(const Address &p_value) = 0;
|
virtual void write_jump_if_shared(const Address &p_value) = 0;
|
||||||
virtual void write_end_jump_if_shared() = 0;
|
virtual void write_end_jump_if_shared() = 0;
|
||||||
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
|
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) = 0;
|
||||||
virtual void write_for_assignment(const Address &p_list) = 0;
|
virtual void write_for_list_assignment(const Address &p_list) = 0;
|
||||||
virtual void write_for(const Address &p_variable, bool p_use_conversion) = 0;
|
virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) = 0;
|
||||||
virtual void write_endfor() = 0;
|
virtual void write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) = 0;
|
||||||
|
virtual void write_endfor(bool p_is_range) = 0;
|
||||||
virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
|
virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
|
||||||
virtual void write_while(const Address &p_condition) = 0;
|
virtual void write_while(const Address &p_condition) = 0;
|
||||||
virtual void write_endwhile() = 0;
|
virtual void write_endwhile() = 0;
|
||||||
|
|||||||
@ -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));
|
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));
|
// 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->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script), range_call != nullptr);
|
||||||
|
|
||||||
|
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);
|
GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gen->write_for_assignment(list);
|
gen->write_for_list_assignment(list);
|
||||||
|
|
||||||
if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
||||||
codegen.generator->pop_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.
|
// Loop variables must be cleared even when `break`/`continue` is used.
|
||||||
List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop);
|
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;
|
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.
|
_clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit.
|
||||||
|
|
||||||
|
|||||||
@ -553,7 +553,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
case OPCODE_CONSTRUCT_ARRAY: {
|
case OPCODE_CONSTRUCT_ARRAY: {
|
||||||
int instr_var_args = _code_ptr[++ip];
|
int instr_var_args = _code_ptr[++ip];
|
||||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||||
text += " make_array ";
|
text += "make_array ";
|
||||||
text += DADDR(1 + argc);
|
text += DADDR(1 + argc);
|
||||||
text += " = [";
|
text += " = [";
|
||||||
|
|
||||||
@ -585,7 +585,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
type_name = Variant::get_type_name(builtin_type);
|
type_name = Variant::get_type_name(builtin_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
text += " make_typed_array (";
|
text += "make_typed_array (";
|
||||||
text += type_name;
|
text += type_name;
|
||||||
text += ") ";
|
text += ") ";
|
||||||
|
|
||||||
@ -1181,9 +1181,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
incr += 5;
|
incr += 5;
|
||||||
} break;
|
} break;
|
||||||
DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
|
DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
|
||||||
|
case OPCODE_ITERATE_BEGIN_RANGE: {
|
||||||
|
text += "for-init ";
|
||||||
|
text += DADDR(5);
|
||||||
|
text += " in range from ";
|
||||||
|
text += DADDR(2);
|
||||||
|
text += " to ";
|
||||||
|
text += DADDR(3);
|
||||||
|
text += " step ";
|
||||||
|
text += DADDR(4);
|
||||||
|
text += " counter ";
|
||||||
|
text += DADDR(1);
|
||||||
|
text += " end ";
|
||||||
|
text += itos(_code_ptr[ip + 6]);
|
||||||
|
|
||||||
|
incr += 7;
|
||||||
|
} break;
|
||||||
case OPCODE_ITERATE: {
|
case OPCODE_ITERATE: {
|
||||||
text += "for-loop ";
|
text += "for-loop ";
|
||||||
text += DADDR(2);
|
text += DADDR(3);
|
||||||
text += " in ";
|
text += " in ";
|
||||||
text += DADDR(2);
|
text += DADDR(2);
|
||||||
text += " counter ";
|
text += " counter ";
|
||||||
@ -1194,6 +1210,20 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
incr += 5;
|
incr += 5;
|
||||||
} break;
|
} break;
|
||||||
DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
|
DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
|
||||||
|
case OPCODE_ITERATE_RANGE: {
|
||||||
|
text += "for-loop ";
|
||||||
|
text += DADDR(4);
|
||||||
|
text += " in range to ";
|
||||||
|
text += DADDR(2);
|
||||||
|
text += " step ";
|
||||||
|
text += DADDR(3);
|
||||||
|
text += " counter ";
|
||||||
|
text += DADDR(1);
|
||||||
|
text += " end ";
|
||||||
|
text += itos(_code_ptr[ip + 5]);
|
||||||
|
|
||||||
|
incr += 6;
|
||||||
|
} break;
|
||||||
case OPCODE_STORE_GLOBAL: {
|
case OPCODE_STORE_GLOBAL: {
|
||||||
text += "store global ";
|
text += "store global ";
|
||||||
text += DADDR(1);
|
text += DADDR(1);
|
||||||
|
|||||||
@ -345,6 +345,7 @@ public:
|
|||||||
OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
|
OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
|
||||||
OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,
|
OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,
|
||||||
OPCODE_ITERATE_BEGIN_OBJECT,
|
OPCODE_ITERATE_BEGIN_OBJECT,
|
||||||
|
OPCODE_ITERATE_BEGIN_RANGE,
|
||||||
OPCODE_ITERATE,
|
OPCODE_ITERATE,
|
||||||
OPCODE_ITERATE_INT,
|
OPCODE_ITERATE_INT,
|
||||||
OPCODE_ITERATE_FLOAT,
|
OPCODE_ITERATE_FLOAT,
|
||||||
@ -366,6 +367,7 @@ public:
|
|||||||
OPCODE_ITERATE_PACKED_COLOR_ARRAY,
|
OPCODE_ITERATE_PACKED_COLOR_ARRAY,
|
||||||
OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,
|
OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,
|
||||||
OPCODE_ITERATE_OBJECT,
|
OPCODE_ITERATE_OBJECT,
|
||||||
|
OPCODE_ITERATE_RANGE,
|
||||||
OPCODE_STORE_GLOBAL,
|
OPCODE_STORE_GLOBAL,
|
||||||
OPCODE_STORE_NAMED_GLOBAL,
|
OPCODE_STORE_NAMED_GLOBAL,
|
||||||
OPCODE_TYPE_ADJUST_BOOL,
|
OPCODE_TYPE_ADJUST_BOOL,
|
||||||
|
|||||||
@ -343,6 +343,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||||||
&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
|
&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
|
||||||
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
|
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
|
||||||
&&OPCODE_ITERATE_BEGIN_OBJECT, \
|
&&OPCODE_ITERATE_BEGIN_OBJECT, \
|
||||||
|
&&OPCODE_ITERATE_BEGIN_RANGE, \
|
||||||
&&OPCODE_ITERATE, \
|
&&OPCODE_ITERATE, \
|
||||||
&&OPCODE_ITERATE_INT, \
|
&&OPCODE_ITERATE_INT, \
|
||||||
&&OPCODE_ITERATE_FLOAT, \
|
&&OPCODE_ITERATE_FLOAT, \
|
||||||
@ -364,6 +365,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||||||
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
|
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
|
||||||
&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
|
&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
|
||||||
&&OPCODE_ITERATE_OBJECT, \
|
&&OPCODE_ITERATE_OBJECT, \
|
||||||
|
&&OPCODE_ITERATE_RANGE, \
|
||||||
&&OPCODE_STORE_GLOBAL, \
|
&&OPCODE_STORE_GLOBAL, \
|
||||||
&&OPCODE_STORE_NAMED_GLOBAL, \
|
&&OPCODE_STORE_NAMED_GLOBAL, \
|
||||||
&&OPCODE_TYPE_ADJUST_BOOL, \
|
&&OPCODE_TYPE_ADJUST_BOOL, \
|
||||||
@ -3345,6 +3347,39 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
}
|
}
|
||||||
DISPATCH_OPCODE;
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
|
OPCODE(OPCODE_ITERATE_BEGIN_RANGE) {
|
||||||
|
CHECK_SPACE(6);
|
||||||
|
|
||||||
|
GET_VARIANT_PTR(counter, 0);
|
||||||
|
GET_VARIANT_PTR(from_ptr, 1);
|
||||||
|
GET_VARIANT_PTR(to_ptr, 2);
|
||||||
|
GET_VARIANT_PTR(step_ptr, 3);
|
||||||
|
|
||||||
|
int64_t from = *VariantInternal::get_int(from_ptr);
|
||||||
|
int64_t to = *VariantInternal::get_int(to_ptr);
|
||||||
|
int64_t step = *VariantInternal::get_int(step_ptr);
|
||||||
|
|
||||||
|
VariantInternal::initialize(counter, Variant::INT);
|
||||||
|
*VariantInternal::get_int(counter) = from;
|
||||||
|
|
||||||
|
bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
|
||||||
|
|
||||||
|
if (do_continue) {
|
||||||
|
GET_VARIANT_PTR(iterator, 4);
|
||||||
|
VariantInternal::initialize(iterator, Variant::INT);
|
||||||
|
*VariantInternal::get_int(iterator) = from;
|
||||||
|
|
||||||
|
// Skip regular iterate.
|
||||||
|
ip += 7;
|
||||||
|
} else {
|
||||||
|
// Jump to end of loop.
|
||||||
|
int jumpto = _code_ptr[ip + 6];
|
||||||
|
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
|
||||||
|
ip = jumpto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
OPCODE(OPCODE_ITERATE) {
|
OPCODE(OPCODE_ITERATE) {
|
||||||
CHECK_SPACE(4);
|
CHECK_SPACE(4);
|
||||||
|
|
||||||
@ -3678,6 +3713,33 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
}
|
}
|
||||||
DISPATCH_OPCODE;
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
|
OPCODE(OPCODE_ITERATE_RANGE) {
|
||||||
|
CHECK_SPACE(5);
|
||||||
|
|
||||||
|
GET_VARIANT_PTR(counter, 0);
|
||||||
|
GET_VARIANT_PTR(to_ptr, 1);
|
||||||
|
GET_VARIANT_PTR(step_ptr, 2);
|
||||||
|
|
||||||
|
int64_t to = *VariantInternal::get_int(to_ptr);
|
||||||
|
int64_t step = *VariantInternal::get_int(step_ptr);
|
||||||
|
|
||||||
|
int64_t *count = VariantInternal::get_int(counter);
|
||||||
|
|
||||||
|
*count += step;
|
||||||
|
|
||||||
|
if ((step < 0 && *count <= to) || (step > 0 && *count >= to)) {
|
||||||
|
int jumpto = _code_ptr[ip + 5];
|
||||||
|
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
|
||||||
|
ip = jumpto;
|
||||||
|
} else {
|
||||||
|
GET_VARIANT_PTR(iterator, 3);
|
||||||
|
*VariantInternal::get_int(iterator) = *count;
|
||||||
|
|
||||||
|
ip += 6; // Loop again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
OPCODE(OPCODE_STORE_GLOBAL) {
|
OPCODE(OPCODE_STORE_GLOBAL) {
|
||||||
CHECK_SPACE(3);
|
CHECK_SPACE(3);
|
||||||
int global_idx = _code_ptr[ip + 2];
|
int global_idx = _code_ptr[ip + 2];
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
# GH-83293
|
||||||
|
|
||||||
|
func test():
|
||||||
|
for x in range(1 << 31, (1 << 31) + 3):
|
||||||
|
print(x)
|
||||||
|
for x in range(1 << 62, (1 << 62) + 3):
|
||||||
|
print(x)
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
2147483648
|
||||||
|
2147483649
|
||||||
|
2147483650
|
||||||
|
4611686018427387904
|
||||||
|
4611686018427387905
|
||||||
|
4611686018427387906
|
||||||
Reference in New Issue
Block a user