You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Merge pull request #79990 from vnen/gdscript-assume-op-types
GDScript: Optimize operators by assuming the types
This commit is contained in:
@@ -226,7 +226,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
|
|||||||
|
|
||||||
if (opcodes.size()) {
|
if (opcodes.size()) {
|
||||||
function->code = opcodes;
|
function->code = opcodes;
|
||||||
function->_code_ptr = &function->code[0];
|
function->_code_ptr = &function->code.write[0];
|
||||||
function->_code_size = opcodes.size();
|
function->_code_size = opcodes.size();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -577,6 +577,12 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
|
|||||||
append(Address());
|
append(Address());
|
||||||
append(p_target);
|
append(p_target);
|
||||||
append(p_operator);
|
append(p_operator);
|
||||||
|
append(0); // Signature storage.
|
||||||
|
append(0); // Return type storage.
|
||||||
|
constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
|
||||||
|
for (int i = 0; i < _pointer_size; i++) {
|
||||||
|
append(0); // Space for function pointer.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
|
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
|
||||||
@@ -610,6 +616,12 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
|
|||||||
append(p_right_operand);
|
append(p_right_operand);
|
||||||
append(p_target);
|
append(p_target);
|
||||||
append(p_operator);
|
append(p_operator);
|
||||||
|
append(0); // Signature storage.
|
||||||
|
append(0); // Return type storage.
|
||||||
|
constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
|
||||||
|
for (int i = 0; i < _pointer_size; i++) {
|
||||||
|
append(0); // Space for function pointer.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
|
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case OPCODE_OPERATOR: {
|
case OPCODE_OPERATOR: {
|
||||||
|
constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
|
||||||
int operation = _code_ptr[ip + 4];
|
int operation = _code_ptr[ip + 4];
|
||||||
|
|
||||||
text += "operator ";
|
text += "operator ";
|
||||||
@@ -125,7 +126,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
text += " ";
|
text += " ";
|
||||||
text += DADDR(2);
|
text += DADDR(2);
|
||||||
|
|
||||||
incr += 5;
|
incr += 7 + _pointer_size;
|
||||||
} break;
|
} break;
|
||||||
case OPCODE_OPERATOR_VALIDATED: {
|
case OPCODE_OPERATOR_VALIDATED: {
|
||||||
text += "validated operator ";
|
text += "validated operator ";
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ private:
|
|||||||
MethodBind **_methods_ptr = nullptr;
|
MethodBind **_methods_ptr = nullptr;
|
||||||
int _lambdas_count = 0;
|
int _lambdas_count = 0;
|
||||||
GDScriptFunction **_lambdas_ptr = nullptr;
|
GDScriptFunction **_lambdas_ptr = nullptr;
|
||||||
const int *_code_ptr = nullptr;
|
int *_code_ptr = nullptr;
|
||||||
int _code_size = 0;
|
int _code_size = 0;
|
||||||
int _argument_count = 0;
|
int _argument_count = 0;
|
||||||
int _stack_size = 0;
|
int _stack_size = 0;
|
||||||
|
|||||||
@@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
|
|
||||||
OPCODE_SWITCH(_code_ptr[ip]) {
|
OPCODE_SWITCH(_code_ptr[ip]) {
|
||||||
OPCODE(OPCODE_OPERATOR) {
|
OPCODE(OPCODE_OPERATOR) {
|
||||||
CHECK_SPACE(5);
|
constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
|
||||||
|
CHECK_SPACE(7 + _pointer_size);
|
||||||
|
|
||||||
bool valid;
|
bool valid;
|
||||||
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
|
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
|
||||||
@@ -694,7 +695,49 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
GET_VARIANT_PTR(a, 0);
|
GET_VARIANT_PTR(a, 0);
|
||||||
GET_VARIANT_PTR(b, 1);
|
GET_VARIANT_PTR(b, 1);
|
||||||
GET_VARIANT_PTR(dst, 2);
|
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
|
#ifdef DEBUG_ENABLED
|
||||||
|
|
||||||
Variant ret;
|
Variant ret;
|
||||||
@@ -715,7 +758,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
}
|
}
|
||||||
*dst = ret;
|
*dst = ret;
|
||||||
#endif
|
#endif
|
||||||
ip += 5;
|
}
|
||||||
|
ip += 7 + _pointer_size;
|
||||||
}
|
}
|
||||||
DISPATCH_OPCODE;
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user