From 52ab64db691e1359ae64ef0d61a8359a2c2a38be Mon Sep 17 00:00:00 2001 From: George Marques Date: Wed, 18 Nov 2020 10:32:28 -0300 Subject: GDScript: Add faster call instructions for builtin methods Methods from builtin types can be called by using the function pointer when the argument and base types are known at compile time. --- modules/gdscript/gdscript_byte_codegen.cpp | 47 ++++++++++++++++++++++++++++++ modules/gdscript/gdscript_byte_codegen.h | 15 ++++++++++ modules/gdscript/gdscript_codegen.h | 1 + modules/gdscript/gdscript_compiler.cpp | 2 ++ modules/gdscript/gdscript_disassembler.cpp | 21 +++++++++++++ modules/gdscript/gdscript_function.h | 4 +++ modules/gdscript/gdscript_vm.cpp | 35 ++++++++++++++++++++++ 7 files changed, 125 insertions(+) (limited to 'modules') diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 186ca1dfa2..2ee8e1a2fa 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -242,6 +242,18 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { function->_indexed_getters_ptr = nullptr; } + if (builtin_method_map.size()) { + function->builtin_methods.resize(builtin_method_map.size()); + function->_builtin_methods_ptr = function->builtin_methods.ptrw(); + function->_builtin_methods_count = builtin_method_map.size(); + for (const Map::Element *E = builtin_method_map.front(); E; E = E->next()) { + function->builtin_methods.write[E->get()] = E->key(); + } + } else { + function->_builtin_methods_ptr = nullptr; + function->_builtin_methods_count = 0; + } + if (method_bind_map.size()) { function->methods.resize(method_bind_map.size()); function->_methods_ptr = function->methods.ptrw(); @@ -647,6 +659,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc append(p_function); } +void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector
&p_arguments) { + bool is_validated = false; + + // Check if all types are correct. + if (Variant::is_builtin_method_vararg(p_type, p_method)) { + is_validated = true; // Vararg works fine with any argument, since they can be any type. + } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) { + bool all_types_exact = true; + for (int i = 0; i < p_arguments.size(); i++) { + if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) { + all_types_exact = false; + break; + } + } + + is_validated = all_types_exact; + } + + if (!is_validated) { + // Perform regular call. + write_call(p_target, p_base, p_method, p_arguments); + return; + } + + append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size()); + + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(p_base); + append(p_target); + append(p_arguments.size()); + append(Variant::get_validated_builtin_method(p_type, p_method)); +} + void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector
&p_arguments) { append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size()); for (int i = 0; i < p_arguments.size(); i++) { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 9b43adcd73..cced0ecabe 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -68,6 +68,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { Map keyed_getters_map; Map indexed_setters_map; Map indexed_getters_map; + Map builtin_method_map; Map method_bind_map; List if_jmp_addrs; // List since this can be nested. @@ -201,6 +202,15 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { return pos; } + int get_builtin_method_pos(const Variant::ValidatedBuiltInMethod p_method) { + if (builtin_method_map.has(p_method)) { + return builtin_method_map[p_method]; + } + int pos = builtin_method_map.size(); + builtin_method_map[p_method] = pos; + return pos; + } + int get_method_bind_pos(MethodBind *p_method) { if (method_bind_map.has(p_method)) { return method_bind_map[p_method]; @@ -298,6 +308,10 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { opcodes.push_back(get_indexed_getter_pos(p_indexed_getter)); } + void append(const Variant::ValidatedBuiltInMethod p_method) { + opcodes.push_back(get_builtin_method_pos(p_method)); + } + void append(MethodBind *p_method) { opcodes.push_back(get_method_bind_pos(p_method)); } @@ -357,6 +371,7 @@ public: virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector
&p_arguments) override; virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector
&p_arguments) override; virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector
&p_arguments) override; + virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector
&p_arguments) override; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector
&p_arguments) override; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector
&p_arguments) override; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector
&p_arguments) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index ff9bdb5f9e..2616a34719 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -126,6 +126,7 @@ public: virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector
&p_arguments) = 0; virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector
&p_arguments) = 0; virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector
&p_arguments) = 0; + virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector
&p_arguments) = 0; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector
&p_arguments) = 0; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector
&p_arguments) = 0; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector
&p_arguments) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 8383e47dec..69fe6d27cc 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -522,6 +522,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else { gen->write_call(result, base, call->function_name, arguments); } + } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { + gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); } else { gen->write_call(result, base, call->function_name, arguments); } diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index bb76a14cfe..b6f9a6f647 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -572,6 +572,27 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { DISASSEMBLE_PTRCALL(PACKED_VECTOR3_ARRAY); DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY); + case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: { + int argc = _code_ptr[ip + 1 + instr_var_args]; + + text += "call-builtin-method validated "; + + text += DADDR(2 + argc) + " = "; + + text += DADDR(1) + "."; + text += ""; + + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(1 + i); + } + text += ")"; + + incr = 5 + argc; + } break; case OPCODE_CALL_BUILT_IN: { text += "call-built-in "; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 1bc5ab6f64..794da38f8c 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -190,6 +190,7 @@ public: OPCODE_CALL_RETURN, OPCODE_CALL_ASYNC, OPCODE_CALL_BUILT_IN, + OPCODE_CALL_BUILTIN_TYPE_VALIDATED, OPCODE_CALL_SELF_BASE, OPCODE_CALL_METHOD_BIND, OPCODE_CALL_METHOD_BIND_RET, @@ -300,6 +301,8 @@ private: const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr; int _indexed_getters_count = 0; const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr; + int _builtin_methods_count = 0; + const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr; int _methods_count = 0; MethodBind **_methods_ptr = nullptr; const int *_code_ptr = nullptr; @@ -326,6 +329,7 @@ private: Vector keyed_getters; Vector indexed_setters; Vector indexed_getters; + Vector builtin_methods; Vector methods; Vector code; Vector argument_types; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 4772163b29..3a7d422f1d 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -219,6 +219,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&OPCODE_CALL_RETURN, \ &&OPCODE_CALL_ASYNC, \ &&OPCODE_CALL_BUILT_IN, \ + &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \ &&OPCODE_CALL_SELF_BASE, \ &&OPCODE_CALL_METHOD_BIND, \ &&OPCODE_CALL_METHOD_BIND_RET, \ @@ -1653,6 +1654,40 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) { + CHECK_SPACE(3 + instr_arg_count); + + ip += instr_arg_count; + + int argc = _code_ptr[ip + 1]; + GD_ERR_BREAK(argc < 0); + + GET_INSTRUCTION_ARG(base, argc); + + GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _builtin_methods_count); + Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]]; + Variant **argptrs = instruction_args; + +#ifdef DEBUG_ENABLED + uint64_t call_time = 0; + if (GDScriptLanguage::get_singleton()->profiling) { + call_time = OS::get_singleton()->get_ticks_usec(); + } +#endif + + GET_INSTRUCTION_ARG(ret, argc + 1); + method(base, (const Variant **)argptrs, argc, ret); + +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->profiling) { + function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + } +#endif + + ip += 3; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_CALL_BUILT_IN) { CHECK_SPACE(3 + instr_arg_count); -- cgit v1.2.3