Reduce number of addressing modes in GDScript VM

There's now only 3 addressing modes: stack, constant, and member.

Self, class, and nil are now present respectively in the first 3 stack
slots. Global and class constants are moved to local constants when
compiling. Named globals is only present on editor to use on tool
singletons, so its use now emits a new instruction to copy the global to
the stack.

This allow us to further optimize the VM later by embedding the
addressing modes in the instructions themselves, which is better done
with less permutations.
This commit is contained in:
George Marques 2021-04-08 11:55:24 -03:00
parent 084b882c0a
commit cf4079cb5f
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
8 changed files with 129 additions and 194 deletions

View file

@ -34,22 +34,22 @@
#include "core/os/os.h"
#include "gdscript.h"
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
int address = p_address & ADDR_MASK;
//sequential table (jump table generated by compiler)
switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
case ADDR_TYPE_SELF: {
case ADDR_TYPE_STACK: {
#ifdef DEBUG_ENABLED
if (unlikely(!p_instance)) {
r_error = "Cannot access self without instance.";
return nullptr;
}
ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
#endif
return &self;
return &p_stack[address];
} break;
case ADDR_TYPE_CLASS: {
return &static_ref;
case ADDR_TYPE_CONSTANT: {
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
#endif
return &_constants_ptr[address];
} break;
case ADDR_TYPE_MEMBER: {
#ifdef DEBUG_ENABLED
@ -61,65 +61,6 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
//member indexing is O(1)
return &p_instance->members.write[address];
} break;
case ADDR_TYPE_CLASS_CONSTANT: {
//todo change to index!
GDScript *s = p_script;
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
#endif
const StringName *sn = &_global_names_ptr[address];
while (s) {
GDScript *o = s;
while (o) {
Map<StringName, Variant>::Element *E = o->constants.find(*sn);
if (E) {
return &E->get();
}
o = o->_owner;
}
s = s->_base;
}
ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
} break;
case ADDR_TYPE_LOCAL_CONSTANT: {
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
#endif
return &_constants_ptr[address];
} break;
case ADDR_TYPE_STACK:
case ADDR_TYPE_STACK_VARIABLE: {
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
#endif
return &p_stack[address];
} break;
case ADDR_TYPE_GLOBAL: {
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
#endif
return &GDScriptLanguage::get_singleton()->get_global_array()[address];
} break;
#ifdef TOOLS_ENABLED
case ADDR_TYPE_NAMED_GLOBAL: {
#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
#endif
StringName id = _global_names_ptr[address];
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
} else {
r_error = "Autoload singleton '" + String(id) + "' has been removed.";
return nullptr;
}
} break;
#endif
case ADDR_TYPE_NIL: {
return &nil;
} break;
}
ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
@ -340,6 +281,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_OBJECT, \
&&OPCODE_STORE_NAMED_GLOBAL, \
&&OPCODE_ASSERT, \
&&OPCODE_BREAKPOINT, \
&&OPCODE_LINE, \
@ -415,11 +357,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
r_err.error = Callable::CallError::CALL_OK;
Variant self;
Variant static_ref;
Variant retvalue;
Variant *stack = nullptr;
Variant **instruction_args;
Variant **instruction_args = nullptr;
const void **call_args_ptr = nullptr;
int defarg = 0;
@ -444,7 +384,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
script = p_state->script;
p_instance = p_state->instance;
defarg = p_state->defarg;
self = p_state->self;
} else {
if (p_argcount != _argument_count) {
@ -462,55 +401,49 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
}
alloca_size = sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
// Add 3 here for self, class, and nil.
alloca_size = sizeof(Variant *) * 3 + sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
if (alloca_size) {
uint8_t *aptr = (uint8_t *)alloca(alloca_size);
uint8_t *aptr = (uint8_t *)alloca(alloca_size);
stack = (Variant *)aptr;
if (_stack_size) {
stack = (Variant *)aptr;
for (int i = 0; i < p_argcount; i++) {
if (!argument_types[i].has_type) {
memnew_placement(&stack[i], Variant(*p_args[i]));
continue;
}
if (!argument_types[i].is_type(*p_args[i], true)) {
r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_err.argument = i;
r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
return Variant();
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
Variant arg;
Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
memnew_placement(&stack[i], Variant(arg));
} else {
memnew_placement(&stack[i], Variant(*p_args[i]));
}
}
for (int i = p_argcount; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
}
} else {
stack = nullptr;
for (int i = 0; i < p_argcount; i++) {
if (!argument_types[i].has_type) {
memnew_placement(&stack[i + 3], Variant(*p_args[i]));
continue;
}
if (_instruction_args_size) {
instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
} else {
instruction_args = nullptr;
if (!argument_types[i].is_type(*p_args[i], true)) {
r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_err.argument = i;
r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
return Variant();
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
Variant arg;
Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
memnew_placement(&stack[i + 3], Variant(arg));
} else {
memnew_placement(&stack[i + 3], Variant(*p_args[i]));
}
}
for (int i = p_argcount + 3; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
}
memnew_placement(&stack[ADDR_STACK_NIL], Variant);
if (_instruction_args_size) {
instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
} else {
stack = nullptr;
instruction_args = nullptr;
}
if (p_instance) {
self = p_instance->owner;
memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
script = p_instance->script.ptr();
} else {
memnew_placement(&stack[ADDR_STACK_SELF], Variant);
script = _script;
}
}
@ -520,7 +453,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
call_args_ptr = nullptr;
}
static_ref = script;
memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
String err_text;
@ -541,10 +474,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space) \
GD_ERR_BREAK((ip + m_space) > _code_size)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
if (unlikely(!m_v)) \
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text); \
if (unlikely(!m_v)) \
OPCODE_BREAK;
#else
@ -552,7 +485,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text);
#endif
@ -2038,7 +1971,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
}
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
gdfs->state.alloca_size = alloca_size;
gdfs->state.ip = ip + 2;
gdfs->state.line = line;
@ -3028,6 +2960,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_STORE_NAMED_GLOBAL) {
CHECK_SPACE(3);
int globalname_idx = _code_ptr[ip + 2];
GD_ERR_BREAK(globalname_idx < 0 || globalname_idx >= _global_names_count);
const StringName *globalname = &_global_names_ptr[globalname_idx];
GET_INSTRUCTION_ARG(dst, 0);
*dst = GDScriptLanguage::get_singleton()->get_named_globals_map()[*globalname];
ip += 3;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_ASSERT) {
CHECK_SPACE(3);