You've already forked godot
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:
@@ -2195,101 +2195,39 @@ void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
|
||||
bool list_resolved = false;
|
||||
GDScriptParser::DataType variable_type;
|
||||
GDScriptParser::DataType list_type;
|
||||
|
||||
// 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 (p_for->list) {
|
||||
resolve_node(p_for->list, false);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
is_range = true;
|
||||
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
||||
variable_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
variable_type.builtin_type = Variant::INT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GDScriptParser::DataType variable_type;
|
||||
String list_visible_type = "<unresolved type>";
|
||||
if (list_resolved) {
|
||||
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
||||
variable_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
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()) {
|
||||
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;
|
||||
mark_node_unsafe(p_for->list);
|
||||
} 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);
|
||||
p_for->use_conversion_assign = true;
|
||||
} 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)) {
|
||||
p_for->use_conversion_assign = true;
|
||||
|
||||
@@ -1542,16 +1542,35 @@ void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
|
||||
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 container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
|
||||
|
||||
// Store state.
|
||||
for_counter_variables.push_back(counter);
|
||||
for_container_variables.push_back(container);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Assign container.
|
||||
@@ -1560,16 +1579,33 @@ void GDScriptByteCodeGenerator::write_for_assignment(const Address &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 &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>());
|
||||
|
||||
GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
|
||||
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) {
|
||||
switch (container.type.builtin_type) {
|
||||
case Variant::INT:
|
||||
@@ -1665,19 +1701,30 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
|
||||
// Begin loop.
|
||||
append_opcode(begin_opcode);
|
||||
append(counter);
|
||||
append(container);
|
||||
if (p_is_range) {
|
||||
append(range_from);
|
||||
append(range_to);
|
||||
append(range_step);
|
||||
} else {
|
||||
append(container);
|
||||
}
|
||||
append(p_use_conversion ? temp : p_variable);
|
||||
for_jmp_addrs.push_back(opcodes.size());
|
||||
append(0); // End of loop address, will be patched.
|
||||
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.
|
||||
int continue_addr = opcodes.size();
|
||||
continue_addrs.push_back(continue_addr);
|
||||
append_opcode(iterate_opcode);
|
||||
append(counter);
|
||||
append(container);
|
||||
if (p_is_range) {
|
||||
append(range_to);
|
||||
append(range_step);
|
||||
} else {
|
||||
append(container);
|
||||
}
|
||||
append(p_use_conversion ? temp : p_variable);
|
||||
for_jmp_addrs.push_back(opcodes.size());
|
||||
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.
|
||||
append_opcode(GDScriptFunction::OPCODE_JUMP);
|
||||
append(continue_addrs.back()->get());
|
||||
@@ -1710,7 +1757,13 @@ void GDScriptByteCodeGenerator::write_endfor() {
|
||||
|
||||
// Pop state.
|
||||
for_counter_variables.pop_back();
|
||||
for_container_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();
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::start_while_condition() {
|
||||
|
||||
@@ -144,6 +144,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
||||
List<int> for_jmp_addrs;
|
||||
List<Address> for_counter_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> continue_addrs;
|
||||
|
||||
@@ -535,10 +538,11 @@ public:
|
||||
virtual void write_endif() override;
|
||||
virtual void write_jump_if_shared(const Address &p_value) 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 write_for_assignment(const Address &p_list) override;
|
||||
virtual void write_for(const Address &p_variable, bool p_use_conversion) override;
|
||||
virtual void write_endfor() override;
|
||||
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) override;
|
||||
virtual void write_for_list_assignment(const Address &p_list) override;
|
||||
virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) 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 write_while(const Address &p_condition) override;
|
||||
virtual void write_endwhile() override;
|
||||
|
||||
@@ -148,10 +148,11 @@ public:
|
||||
virtual void write_endif() = 0;
|
||||
virtual void write_jump_if_shared(const Address &p_value) = 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 write_for_assignment(const Address &p_list) = 0;
|
||||
virtual void write_for(const Address &p_variable, bool p_use_conversion) = 0;
|
||||
virtual void write_endfor() = 0;
|
||||
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) = 0;
|
||||
virtual void write_for_list_assignment(const Address &p_list) = 0;
|
||||
virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) = 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 write_while(const Address &p_condition) = 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));
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -553,7 +553,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
case OPCODE_CONSTRUCT_ARRAY: {
|
||||
int instr_var_args = _code_ptr[++ip];
|
||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||
text += " make_array ";
|
||||
text += "make_array ";
|
||||
text += DADDR(1 + argc);
|
||||
text += " = [";
|
||||
|
||||
@@ -585,7 +585,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
type_name = Variant::get_type_name(builtin_type);
|
||||
}
|
||||
|
||||
text += " make_typed_array (";
|
||||
text += "make_typed_array (";
|
||||
text += type_name;
|
||||
text += ") ";
|
||||
|
||||
@@ -1181,9 +1181,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
incr += 5;
|
||||
} break;
|
||||
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: {
|
||||
text += "for-loop ";
|
||||
text += DADDR(2);
|
||||
text += DADDR(3);
|
||||
text += " in ";
|
||||
text += DADDR(2);
|
||||
text += " counter ";
|
||||
@@ -1194,6 +1210,20 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
incr += 5;
|
||||
} break;
|
||||
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: {
|
||||
text += "store global ";
|
||||
text += DADDR(1);
|
||||
|
||||
@@ -345,6 +345,7 @@ public:
|
||||
OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
|
||||
OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,
|
||||
OPCODE_ITERATE_BEGIN_OBJECT,
|
||||
OPCODE_ITERATE_BEGIN_RANGE,
|
||||
OPCODE_ITERATE,
|
||||
OPCODE_ITERATE_INT,
|
||||
OPCODE_ITERATE_FLOAT,
|
||||
@@ -366,6 +367,7 @@ public:
|
||||
OPCODE_ITERATE_PACKED_COLOR_ARRAY,
|
||||
OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,
|
||||
OPCODE_ITERATE_OBJECT,
|
||||
OPCODE_ITERATE_RANGE,
|
||||
OPCODE_STORE_GLOBAL,
|
||||
OPCODE_STORE_NAMED_GLOBAL,
|
||||
OPCODE_TYPE_ADJUST_BOOL,
|
||||
|
||||
@@ -343,6 +343,7 @@ void (*type_init_function_table[])(Variant *) = {
|
||||
&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
|
||||
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
|
||||
&&OPCODE_ITERATE_BEGIN_OBJECT, \
|
||||
&&OPCODE_ITERATE_BEGIN_RANGE, \
|
||||
&&OPCODE_ITERATE, \
|
||||
&&OPCODE_ITERATE_INT, \
|
||||
&&OPCODE_ITERATE_FLOAT, \
|
||||
@@ -364,6 +365,7 @@ void (*type_init_function_table[])(Variant *) = {
|
||||
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
|
||||
&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
|
||||
&&OPCODE_ITERATE_OBJECT, \
|
||||
&&OPCODE_ITERATE_RANGE, \
|
||||
&&OPCODE_STORE_GLOBAL, \
|
||||
&&OPCODE_STORE_NAMED_GLOBAL, \
|
||||
&&OPCODE_TYPE_ADJUST_BOOL, \
|
||||
@@ -3345,6 +3347,39 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||
}
|
||||
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) {
|
||||
CHECK_SPACE(4);
|
||||
|
||||
@@ -3678,6 +3713,33 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||
}
|
||||
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) {
|
||||
CHECK_SPACE(3);
|
||||
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