Browse Source

GDScript: Add faster operator for known types

It now uses the direct operator function pointer, which increases
performance in evaluation.
George Marques 4 years ago
parent
commit
1ad5c926dc

+ 28 - 0
modules/gdscript/gdscript_byte_codegen.cpp

@@ -158,6 +158,18 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
 		function->_default_arg_ptr = nullptr;
 		function->_default_arg_ptr = nullptr;
 	}
 	}
 
 
+	if (operator_func_map.size()) {
+		function->operator_funcs.resize(operator_func_map.size());
+		function->_operator_funcs_count = function->operator_funcs.size();
+		function->_operator_funcs_ptr = function->operator_funcs.ptr();
+		for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
+			function->operator_funcs.write[E->get()] = E->key();
+		}
+	} else {
+		function->_operator_funcs_count = 0;
+		function->_operator_funcs_ptr = nullptr;
+	}
+
 	if (debug_stack) {
 	if (debug_stack) {
 		function->stack_debug = stack_debug;
 		function->stack_debug = stack_debug;
 	}
 	}
@@ -178,7 +190,23 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
 	function->_initial_line = p_line;
 	function->_initial_line = p_line;
 }
 }
 
 
+#define HAS_BUILTIN_TYPE(m_var) \
+	(m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
+
 void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
 void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
+	if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
+		// Gather specific operator.
+		Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+
+		append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+		append(p_left_operand);
+		append(p_right_operand);
+		append(p_target);
+		append(op_func);
+		return;
+	}
+
+	// No specific types, perform variant evaluation.
 	append(GDScriptFunction::OPCODE_OPERATOR, 3);
 	append(GDScriptFunction::OPCODE_OPERATOR, 3);
 	append(p_left_operand);
 	append(p_left_operand);
 	append(p_right_operand);
 	append(p_right_operand);

+ 16 - 3
modules/gdscript/gdscript_byte_codegen.h

@@ -51,15 +51,16 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 
 
 	int current_stack_size = 0;
 	int current_stack_size = 0;
 	int current_temporaries = 0;
 	int current_temporaries = 0;
+	int current_line = 0;
+	int stack_max = 0;
+	int instr_args_max = 0;
 
 
 	HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
 	HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
 	Map<StringName, int> name_map;
 	Map<StringName, int> name_map;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	Vector<StringName> named_globals;
 	Vector<StringName> named_globals;
 #endif
 #endif
-	int current_line = 0;
-	int stack_max = 0;
-	int instr_args_max = 0;
+	Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
 
 
 	List<int> if_jmp_addrs; // List since this can be nested.
 	List<int> if_jmp_addrs; // List since this can be nested.
 	List<int> for_jmp_addrs;
 	List<int> for_jmp_addrs;
@@ -136,6 +137,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		return 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];
+		int pos = operator_func_map.size();
+		operator_func_map[p_operation] = pos;
+		return pos;
+	}
+
 	void alloc_stack(int p_level) {
 	void alloc_stack(int p_level) {
 		if (p_level >= stack_max)
 		if (p_level >= stack_max)
 			stack_max = p_level + 1;
 			stack_max = p_level + 1;
@@ -191,6 +200,10 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
 		opcodes.push_back(get_name_map_pos(p_name));
 		opcodes.push_back(get_name_map_pos(p_name));
 	}
 	}
 
 
+	void append(const Variant::ValidatedOperatorEvaluator p_operation) {
+		opcodes.push_back(get_operation_pos(p_operation));
+	}
+
 	void patch_jump(int p_address) {
 	void patch_jump(int p_address) {
 		opcodes.write[p_address] = opcodes.size();
 		opcodes.write[p_address] = opcodes.size();
 	}
 	}

+ 11 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -136,6 +136,17 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 
 				incr += 5;
 				incr += 5;
 			} break;
 			} break;
+			case OPCODE_OPERATOR_VALIDATED: {
+				text += "validated operator ";
+
+				text += DADDR(3);
+				text += " = ";
+				text += DADDR(1);
+				text += " <operator function> ";
+				text += DADDR(2);
+
+				incr += 5;
+			} break;
 			case OPCODE_EXTENDS_TEST: {
 			case OPCODE_EXTENDS_TEST: {
 				text += "is object ";
 				text += "is object ";
 				text += DADDR(3);
 				text += DADDR(3);

+ 4 - 0
modules/gdscript/gdscript_function.h

@@ -159,6 +159,7 @@ class GDScriptFunction {
 public:
 public:
 	enum Opcode {
 	enum Opcode {
 		OPCODE_OPERATOR,
 		OPCODE_OPERATOR,
+		OPCODE_OPERATOR_VALIDATED,
 		OPCODE_EXTENDS_TEST,
 		OPCODE_EXTENDS_TEST,
 		OPCODE_IS_BUILTIN,
 		OPCODE_IS_BUILTIN,
 		OPCODE_SET,
 		OPCODE_SET,
@@ -241,6 +242,8 @@ private:
 	int _global_names_count = 0;
 	int _global_names_count = 0;
 	const int *_default_arg_ptr = nullptr;
 	const int *_default_arg_ptr = nullptr;
 	int _default_arg_count = 0;
 	int _default_arg_count = 0;
+	int _operator_funcs_count = 0;
+	const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
 	const int *_code_ptr = nullptr;
 	const int *_code_ptr = nullptr;
 	int _code_size = 0;
 	int _code_size = 0;
 	int _argument_count = 0;
 	int _argument_count = 0;
@@ -256,6 +259,7 @@ private:
 	Vector<Variant> constants;
 	Vector<Variant> constants;
 	Vector<StringName> global_names;
 	Vector<StringName> global_names;
 	Vector<int> default_arguments;
 	Vector<int> default_arguments;
+	Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
 	Vector<int> code;
 	Vector<int> code;
 	Vector<GDScriptDataType> argument_types;
 	Vector<GDScriptDataType> argument_types;
 	GDScriptDataType return_type;
 	GDScriptDataType return_type;

+ 18 - 0
modules/gdscript/gdscript_vm.cpp

@@ -188,6 +188,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 #define OPCODES_TABLE                         \
 #define OPCODES_TABLE                         \
 	static const void *switch_table_ops[] = { \
 	static const void *switch_table_ops[] = { \
 		&&OPCODE_OPERATOR,                    \
 		&&OPCODE_OPERATOR,                    \
+		&&OPCODE_OPERATOR_VALIDATED,          \
 		&&OPCODE_EXTENDS_TEST,                \
 		&&OPCODE_EXTENDS_TEST,                \
 		&&OPCODE_IS_BUILTIN,                  \
 		&&OPCODE_IS_BUILTIN,                  \
 		&&OPCODE_SET,                         \
 		&&OPCODE_SET,                         \
@@ -468,6 +469,23 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			}
 			DISPATCH_OPCODE;
 			DISPATCH_OPCODE;
 
 
+			OPCODE(OPCODE_OPERATOR_VALIDATED) {
+				CHECK_SPACE(5);
+
+				int operator_idx = _code_ptr[ip + 4];
+				GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
+				Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
+
+				GET_INSTRUCTION_ARG(a, 0);
+				GET_INSTRUCTION_ARG(b, 1);
+				GET_INSTRUCTION_ARG(dst, 2);
+
+				operator_func(a, b, dst);
+
+				ip += 5;
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_EXTENDS_TEST) {
 			OPCODE(OPCODE_EXTENDS_TEST) {
 				CHECK_SPACE(4);
 				CHECK_SPACE(4);