Skip to content

Commit

Permalink
Merge pull request #79990 from vnen/gdscript-assume-op-types
Browse files Browse the repository at this point in the history
GDScript: Optimize operators by assuming the types
  • Loading branch information
YuriSizov committed Aug 1, 2023
2 parents f4b6bc0 + c1bca65 commit f6e02dc
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 18 deletions.
14 changes: 13 additions & 1 deletion modules/gdscript/gdscript_byte_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {

if (opcodes.size()) {
function->code = opcodes;
function->_code_ptr = &function->code[0];
function->_code_ptr = &function->code.write[0];
function->_code_size = opcodes.size();

} else {
Expand Down Expand Up @@ -577,6 +577,12 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
append(Address());
append(p_target);
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) {
Expand Down Expand Up @@ -610,6 +616,12 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
append(p_right_operand);
append(p_target);
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) {
Expand Down
3 changes: 2 additions & 1 deletion modules/gdscript/gdscript_disassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {

switch (opcode) {
case OPCODE_OPERATOR: {
constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
int operation = _code_ptr[ip + 4];

text += "operator ";
Expand All @@ -125,7 +126,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += " ";
text += DADDR(2);

incr += 5;
incr += 7 + _pointer_size;
} break;
case OPCODE_OPERATOR_VALIDATED: {
text += "validated operator ";
Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/gdscript_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ class GDScriptFunction {
MethodBind **_methods_ptr = nullptr;
int _lambdas_count = 0;
GDScriptFunction **_lambdas_ptr = nullptr;
const int *_code_ptr = nullptr;
int *_code_ptr = nullptr;
int _code_size = 0;
int _argument_count = 0;
int _stack_size = 0;
Expand Down
74 changes: 59 additions & 15 deletions modules/gdscript/gdscript_vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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;

Expand Down

0 comments on commit f6e02dc

Please sign in to comment.