summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_vm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_vm.cpp')
-rw-r--r--modules/gdscript/gdscript_vm.cpp94
1 files changed, 70 insertions, 24 deletions
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index a8c9cfa579..1ddd54b323 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -663,8 +663,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (GDScriptLanguage::get_singleton()->profiling) {
function_start_time = OS::get_singleton()->get_ticks_usec();
function_call_time = 0;
- profile.call_count++;
- profile.frame_call_count++;
+ profile.call_count.increment();
+ profile.frame_call_count.increment();
}
bool exit_ok = false;
bool awaited = false;
@@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_SWITCH(_code_ptr[ip]) {
OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
+ CHECK_SPACE(7 + _pointer_size);
bool valid;
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
@@ -694,28 +695,71 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(a, 0);
GET_VARIANT_PTR(b, 1);
GET_VARIANT_PTR(dst, 2);
+ // Compute signatures (types of operands) so it can be optimized when matching.
+ uint32_t op_signature = _code_ptr[ip + 5];
+ uint32_t actual_signature = (a->get_type() << 8) | (b->get_type());
+
+ // Check if this is the first run. If so, store the current signature for the optimized path.
+ if (unlikely(op_signature == 0)) {
+ static Mutex initializer_mutex;
+ initializer_mutex.lock();
+ Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF);
+ Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF);
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type);
+
+ if (unlikely(!op_func)) {
+#ifdef DEBUG_ENABLED
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+#endif
+ initializer_mutex.unlock();
+ OPCODE_BREAK;
+ } else {
+ Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type);
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+
+ // Check again in case another thread already set it.
+ if (_code_ptr[ip + 5] == 0) {
+ _code_ptr[ip + 5] = actual_signature;
+ _code_ptr[ip + 6] = static_cast<int>(ret_type);
+ Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+ *tmp = op_func;
+ }
+ }
+ initializer_mutex.unlock();
+ } else if (likely(op_signature == actual_signature)) {
+ // If the signature matches, we can use the optimized path.
+ Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]);
+ Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+
+ // Make sure the return value has the correct type.
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+ } else {
+ // If the signature doesn't match, we have to use the slow path.
#ifdef DEBUG_ENABLED
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
#else
- Variant::evaluate(op, *a, *b, *dst, valid);
+ Variant::evaluate(op, *a, *b, *dst, valid);
#endif
#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
}
- OPCODE_BREAK;
- }
- *dst = ret;
+ *dst = ret;
#endif
- ip += 5;
+ }
+ ip += 7 + _pointer_size;
}
DISPATCH_OPCODE;
@@ -3550,7 +3594,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// line
bool do_break = false;
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+ if (unlikely(EngineDebugger::get_script_debugger()->get_lines_left() > 0)) {
if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
}
@@ -3563,7 +3607,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
do_break = true;
}
- if (do_break) {
+ if (unlikely(do_break)) {
GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
}
@@ -3630,11 +3674,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time += time_taken;
- profile.self_time += time_taken - function_call_time;
- profile.frame_total_time += time_taken;
- profile.frame_self_time += time_taken - function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ profile.total_time.add(time_taken);
+ profile.self_time.add(time_taken - function_call_time);
+ profile.frame_total_time.add(time_taken);
+ profile.frame_self_time.add(time_taken - function_call_time);
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ }
}
// Check if this is not the last time it was interrupted by `await` or if it's the first time executing.