Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDScript: Optimize match #78742

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@
<member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await.
</member>
<member name="debug/gdscript/warnings/redundant_pattern" type="int" setter="" getter="" default="1">
</member>
<member name="debug/gdscript/warnings/redundant_static_unload" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables.
</member>
Expand Down
17 changes: 15 additions & 2 deletions modules/gdscript/gdscript_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,9 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
resolve_match_pattern(p_match_pattern->array[i], nullptr);
decide_suite_type(p_match_pattern, p_match_pattern->array[i]);
}
result = p_match_pattern->get_datatype();
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::ARRAY;
break;
case GDScriptParser::PatternNode::PT_DICTIONARY:
for (int i = 0; i < p_match_pattern->dictionary.size(); i++) {
Expand All @@ -2282,14 +2284,25 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
decide_suite_type(p_match_pattern, p_match_pattern->dictionary[i].value_pattern);
}
}
result = p_match_pattern->get_datatype();
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::DICTIONARY;
break;
case GDScriptParser::PatternNode::PT_WILDCARD:
case GDScriptParser::PatternNode::PT_REST:
result.kind = GDScriptParser::DataType::VARIANT;
break;
}

if (p_match_test != nullptr) {
GDScriptParser::DataType test_type = p_match_test->get_datatype();
if (!result.is_variant() && result.is_hard_type() && !test_type.is_variant() && test_type.is_hard_type() && !is_type_compatible(result, test_type) && !is_type_compatible(test_type, result)) {
if (!(result.builtin_type == Variant::STRING && test_type.builtin_type == Variant::STRING_NAME) && !(result.builtin_type == Variant::STRING_NAME && test_type.builtin_type == Variant::STRING)) {
push_error(vformat(R"(Expression of type "%s" cannot match a pattern of type "%s".)", test_type.to_string(), result.to_string()), p_match_pattern);
}
}
}

p_match_pattern->set_datatype(result);
}

Expand Down
85 changes: 85 additions & 0 deletions modules/gdscript/gdscript_byte_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ uint32_t GDScriptByteCodeGenerator::add_or_get_constant(const Variant &p_constan
return get_constant_pos(p_constant);
}

uint32_t GDScriptByteCodeGenerator::add_or_get_variant_vector_constant(const Vector<Variant> &p_constant) {
return get_variant_vector_constant_pos(p_constant);
}

uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) {
return get_name_map_pos(p_name);
}
Expand Down Expand Up @@ -208,6 +212,11 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_constant_count = 0;
}

function->variant_vector_constants.resize(variant_vector_constant_map.size());
for (const KeyValue<Vector<Variant>, int> &K : variant_vector_constant_map) {
function->variant_vector_constants.write[K.value] = K.key;
}

if (name_map.size()) {
function->global_names.resize(name_map.size());
function->_global_names_ptr = &function->global_names[0];
Expand Down Expand Up @@ -1482,6 +1491,82 @@ void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
if_jmp_addrs.pop_back();
}

void GDScriptByteCodeGenerator::write_jump_table_range(const Address &p_value, int p_offset, int p_size, int p_branch_count) {
ERR_FAIL_COND(p_size < 0);

append_opcode(GDScriptFunction::OPCODE_JUMP_TABLE_RANGE);
append(p_value);
append(p_offset);
append(p_size);

int start_addr = opcodes.size();
for (int i = 0; i < p_size; i++) {
append(-1); // Jump destination, will be patched.
}
append(-1); // Jump destination, will be patched.

jmp_tables.push_back(JumpTable(start_addr, p_size + 1, p_branch_count));
}

void GDScriptByteCodeGenerator::write_jump_table_bsearch(const Address &p_value, const Vector<Variant> &p_array, int p_branch_count) {
append_opcode(GDScriptFunction::OPCODE_JUMP_TABLE_BSEARCH);
append(p_value);
append(add_or_get_variant_vector_constant(p_array));

int start_addr = opcodes.size();
for (int i = 0; i < p_array.size(); i++) {
append(-1); // Jump destination, will be patched.
}
append(-1); // Jump destination, will be patched.

jmp_tables.push_back(JumpTable(start_addr, p_array.size() + 1, p_branch_count));
}

void GDScriptByteCodeGenerator::write_jump_table_branch() {
JumpTable &jmp_table = jmp_tables.back()->get();
jmp_table.current_branch++;

ERR_FAIL_COND(jmp_table.current_branch >= jmp_table.branch_count);

if (jmp_table.current_branch > 0) {
// End previous branch.
append_opcode(GDScriptFunction::OPCODE_JUMP); // Jump to end.
jmp_table.jmp_to_end_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
}

jmp_table.branch_start_addrs.write[jmp_table.current_branch] = opcodes.size();
}

// Unset cases jump to the end.
void GDScriptByteCodeGenerator::write_jump_table_set_case_branch(int p_case, int p_branch) {
JumpTable &jmp_table = jmp_tables.back()->get();

ERR_FAIL_INDEX(p_case, jmp_table.case_count);
ERR_FAIL_INDEX(p_branch, jmp_table.branch_count);
ERR_FAIL_COND(p_branch > jmp_table.current_branch);

opcodes.write[jmp_table.start_addr + p_case] = jmp_table.branch_start_addrs[p_branch];
}

void GDScriptByteCodeGenerator::write_end_jump_table() {
JumpTable &jmp_table = jmp_tables.back()->get();

ERR_FAIL_COND(jmp_table.current_branch != jmp_table.branch_count - 1);

for (int i = 0; i < jmp_table.case_count; i++) {
if (opcodes[jmp_table.start_addr + i] < 0) {
patch_jump(jmp_table.start_addr + i);
}
}

for (List<int>::Element *E = jmp_table.jmp_to_end_addrs.front(); E; E = E->next()) {
patch_jump(E->get());
}

jmp_tables.pop_back();
}

void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_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);
Expand Down
37 changes: 37 additions & 0 deletions modules/gdscript/gdscript_byte_codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
CallTarget &operator=(CallTarget &) = delete;
};

struct JumpTable {
int start_addr = 0;
int case_count = 0; // Including the default case.
int branch_count = 0; // Including the default branch (if any).
int current_branch = -1;
Vector<int> branch_start_addrs;
List<int> jmp_to_end_addrs;

JumpTable() {}
JumpTable(int p_start_addr, int p_case_count, int p_branch_count) {
ERR_FAIL_COND(p_case_count < 0);
ERR_FAIL_COND(p_branch_count < 0 || p_branch_count > p_case_count);

start_addr = p_start_addr;
case_count = p_case_count;
branch_count = p_branch_count;
branch_start_addrs.resize(p_branch_count);
}
};

bool ended = false;
GDScriptFunction *function = nullptr;
bool debug_stack = false;
Expand Down Expand Up @@ -104,6 +124,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
#endif

HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
HashMap<Vector<Variant>, int, VariantHasher, VariantComparator> variant_vector_constant_map;
RBMap<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
Expand Down Expand Up @@ -147,6 +168,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
List<Address> for_container_variables;
List<int> while_jmp_addrs;
List<int> continue_addrs;
List<JumpTable> jmp_tables;

// Used to patch jumps with `and` and `or` operators with short-circuit.
List<int> logic_op_jump_pos1;
Expand Down Expand Up @@ -229,6 +251,15 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return pos;
}

int get_variant_vector_constant_pos(const Vector<Variant> &p_constant) {
if (variant_vector_constant_map.has(p_constant)) {
return variant_vector_constant_map[p_constant];
}
int pos = variant_vector_constant_map.size();
variant_vector_constant_map[p_constant] = pos;
return pos;
}

int get_operation_pos(const Variant::ValidatedOperatorEvaluator p_operation) {
if (operator_func_map.has(p_operation)) {
return operator_func_map[p_operation];
Expand Down Expand Up @@ -459,6 +490,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) override;
virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) override;
virtual uint32_t add_or_get_constant(const Variant &p_constant) override;
virtual uint32_t add_or_get_variant_vector_constant(const Vector<Variant> &p_constant) override;
virtual uint32_t add_or_get_name(const StringName &p_name) override;
virtual uint32_t add_temporary(const GDScriptDataType &p_type) override;
virtual void pop_temporary() override;
Expand Down Expand Up @@ -534,6 +566,11 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
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 write_jump_table_range(const Address &p_value, int p_offset, int p_size, int p_branch_count) override;
virtual void write_jump_table_bsearch(const Address &p_value, const Vector<Variant> &p_array, int p_branch_count) override;
virtual void write_jump_table_branch() override;
virtual void write_jump_table_set_case_branch(int p_case, int p_branch) override;
virtual void write_end_jump_table() 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;
Expand Down
6 changes: 6 additions & 0 deletions modules/gdscript/gdscript_codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class GDScriptCodeGenerator {
virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) = 0;
virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) = 0;
virtual uint32_t add_or_get_constant(const Variant &p_constant) = 0;
virtual uint32_t add_or_get_variant_vector_constant(const Vector<Variant> &p_constant) = 0;
virtual uint32_t add_or_get_name(const StringName &p_name) = 0;
virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0;
virtual void pop_temporary() = 0;
Expand Down Expand Up @@ -144,6 +145,11 @@ class GDScriptCodeGenerator {
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 write_jump_table_range(const Address &p_value, int p_offset, int p_size, int p_branch_count) = 0;
virtual void write_jump_table_bsearch(const Address &p_value, const Vector<Variant> &p_array, int p_branch_count) = 0;
virtual void write_jump_table_branch() = 0;
virtual void write_jump_table_set_case_branch(int p_case, int p_branch) = 0;
virtual void write_end_jump_table() = 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;
Expand Down
Loading