LibJS: Share declarative binding flags in shapes
Let shaped declarative environments read static binding flags from their EnvironmentShape instead of retaining a per-environment copy. The local flag vector now mirrors the local name vector and only stores dynamic bindings created after the shared shape. Split EnvironmentShape binding descriptors into separate name and flag vectors so the shared metadata is compact as well.
This commit is contained in:
parent
b0372ca84c
commit
da65105cd6
6 changed files with 47 additions and 21 deletions
|
|
@ -994,9 +994,27 @@ macro store_binding_value(env, idx, binding_values, value)
|
|||
end
|
||||
|
||||
macro load_binding_flags(env, idx, flags, flag)
|
||||
temp shape, shape_size, local_index
|
||||
load64 shape, [env, DECLARATIVE_ENVIRONMENT_SHAPE]
|
||||
branch_zero shape, .load_environment_flags
|
||||
load64 shape_size, [shape, ENVIRONMENT_SHAPE_BINDING_FLAGS]
|
||||
branch_ge_unsigned idx, shape_size, .load_environment_local_flags
|
||||
load64 flags, [shape, ENVIRONMENT_SHAPE_BINDING_FLAGS_DATA_PTR]
|
||||
assert_nonzero flags
|
||||
load8 flag, [flags, idx]
|
||||
jmp .done
|
||||
.load_environment_local_flags:
|
||||
load64 flags, [env, BINDING_FLAGS_DATA_PTR]
|
||||
assert_nonzero flags
|
||||
mov local_index, idx
|
||||
sub local_index, shape_size
|
||||
load8 flag, [flags, local_index]
|
||||
jmp .done
|
||||
.load_environment_flags:
|
||||
load64 flags, [env, BINDING_FLAGS_DATA_PTR]
|
||||
assert_nonzero flags
|
||||
load8 flag, [flags, idx]
|
||||
.done:
|
||||
end
|
||||
|
||||
# Inline environment chain walk + binding value load with TDZ check.
|
||||
|
|
|
|||
|
|
@ -326,13 +326,16 @@ int main()
|
|||
|
||||
// DeclarativeEnvironment binding storage layout
|
||||
outln("\n# DeclarativeEnvironment binding storage layout");
|
||||
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_SHAPE, DeclarativeEnvironment, m_shape);
|
||||
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_BINDING_VALUES, DeclarativeEnvironment, m_binding_values);
|
||||
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_BINDING_FLAGS, DeclarativeEnvironment, m_binding_flags);
|
||||
EMIT_OFFSET(ENVIRONMENT_SHAPE_BINDING_FLAGS, EnvironmentShape, m_binding_flags);
|
||||
outln("const BINDING_FLAG_MUTABLE = {}", 1 << 1);
|
||||
|
||||
// Vector<T> layout: m_size(0), m_capacity(8), m_metadata.outline_buffer(16)
|
||||
outln("const BINDING_VALUES_DATA_PTR = {}", offsetof(DeclarativeEnvironment, m_binding_values) + sizeof(size_t) * 2);
|
||||
outln("const BINDING_FLAGS_DATA_PTR = {}", offsetof(DeclarativeEnvironment, m_binding_flags) + sizeof(size_t) * 2);
|
||||
outln("const ENVIRONMENT_SHAPE_BINDING_FLAGS_DATA_PTR = {}", offsetof(EnvironmentShape, m_binding_flags) + sizeof(size_t) * 2);
|
||||
|
||||
// EnvironmentCoordinate layout
|
||||
outln("\n# EnvironmentCoordinate layout");
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ void DeclarativeEnvironment::append_binding(Binding binding)
|
|||
} else {
|
||||
m_bindings_assoc.set(binding.name, index);
|
||||
m_binding_names.append(move(binding.name));
|
||||
m_binding_flags.append(flags);
|
||||
}
|
||||
|
||||
m_binding_values.append(binding.initialized ? binding.value : js_special_empty_value());
|
||||
m_binding_flags.append(flags);
|
||||
}
|
||||
|
||||
void DeclarativeEnvironment::clear_binding(Utf16FlyString const& name, size_t index)
|
||||
|
|
@ -94,7 +94,7 @@ void DeclarativeEnvironment::clear_binding(Utf16FlyString const& name, size_t in
|
|||
auto local_index = local_binding_index(index);
|
||||
m_binding_names[local_index] = Utf16FlyString {};
|
||||
m_binding_values[index] = js_special_empty_value();
|
||||
m_binding_flags[index] = 0;
|
||||
m_binding_flags[local_index] = 0;
|
||||
}
|
||||
|
||||
DeclarativeEnvironment::Binding DeclarativeEnvironment::binding_at(size_t index) const
|
||||
|
|
@ -127,6 +127,7 @@ void DeclarativeEnvironment::set_environment_shape(GC::Ref<EnvironmentShape> sha
|
|||
|
||||
m_shape = shape;
|
||||
m_binding_names.clear();
|
||||
m_binding_flags.clear();
|
||||
m_bindings_assoc.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ public:
|
|||
void ensure_capacity(size_t needed_capacity)
|
||||
{
|
||||
m_binding_values.ensure_capacity(needed_capacity);
|
||||
m_binding_flags.ensure_capacity(needed_capacity);
|
||||
if (m_shape)
|
||||
return;
|
||||
|
||||
m_binding_names.ensure_capacity(needed_capacity);
|
||||
m_binding_flags.ensure_capacity(needed_capacity);
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 environment_serial_number() const { return m_environment_serial_number; }
|
||||
|
|
@ -123,7 +123,7 @@ private:
|
|||
{
|
||||
if (index < shape_binding_count())
|
||||
return m_shape->binding_flags(index);
|
||||
return m_binding_flags[index];
|
||||
return m_binding_flags[local_binding_index(index)];
|
||||
}
|
||||
bool binding_is_strict(size_t index) const { return (binding_flags(index) & BindingFlagStrict) != 0; }
|
||||
bool binding_is_mutable(size_t index) const { return (binding_flags(index) & BindingFlagMutable) != 0; }
|
||||
|
|
@ -191,6 +191,8 @@ private:
|
|||
GC::Ptr<EnvironmentShape> m_shape;
|
||||
Vector<Utf16FlyString> m_binding_names;
|
||||
Vector<Value> m_binding_values;
|
||||
// When m_shape is set, this only stores flags for local bindings after
|
||||
// the shared shape bindings.
|
||||
Vector<u8> m_binding_flags;
|
||||
Bitmap m_deleted_bindings;
|
||||
HashMap<Utf16FlyString, size_t> m_bindings_assoc;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ namespace JS {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(EnvironmentShape);
|
||||
|
||||
EnvironmentShape::EnvironmentShape(Vector<BindingDescriptor> bindings, HashMap<Utf16FlyString, size_t> binding_indices)
|
||||
: m_bindings(move(bindings))
|
||||
EnvironmentShape::EnvironmentShape(Vector<Utf16FlyString> binding_names, Vector<u8> binding_flags, HashMap<Utf16FlyString, size_t> binding_indices)
|
||||
: m_binding_names(move(binding_names))
|
||||
, m_binding_flags(move(binding_flags))
|
||||
, m_binding_indices(move(binding_indices))
|
||||
{
|
||||
}
|
||||
|
|
@ -22,20 +23,24 @@ GC::Ref<EnvironmentShape> EnvironmentShape::create(VM& vm, ReadonlySpan<Utf16Fly
|
|||
{
|
||||
VERIFY(names.size() == flags.size());
|
||||
|
||||
Vector<BindingDescriptor> bindings;
|
||||
bindings.ensure_capacity(names.size());
|
||||
Vector<Utf16FlyString> binding_names;
|
||||
binding_names.ensure_capacity(names.size());
|
||||
|
||||
Vector<u8> binding_flags;
|
||||
binding_flags.ensure_capacity(flags.size());
|
||||
|
||||
HashMap<Utf16FlyString, size_t> binding_indices;
|
||||
binding_indices.ensure_capacity(names.size());
|
||||
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
bindings.unchecked_append({ names[i], flags[i] });
|
||||
binding_names.unchecked_append(names[i]);
|
||||
binding_flags.unchecked_append(flags[i]);
|
||||
|
||||
if (!names[i].is_empty())
|
||||
binding_indices.set(names[i], i);
|
||||
}
|
||||
|
||||
return vm.heap().allocate<EnvironmentShape>(move(bindings), move(binding_indices));
|
||||
return vm.heap().allocate<EnvironmentShape>(move(binding_names), move(binding_flags), move(binding_indices));
|
||||
}
|
||||
|
||||
Optional<size_t> EnvironmentShape::find_binding(Utf16FlyString const& name) const
|
||||
|
|
@ -48,7 +53,8 @@ Optional<size_t> EnvironmentShape::find_binding(Utf16FlyString const& name) cons
|
|||
|
||||
size_t EnvironmentShape::external_memory_size() const
|
||||
{
|
||||
auto size = vector_external_memory_size(m_bindings);
|
||||
auto size = vector_external_memory_size(m_binding_names);
|
||||
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_binding_flags));
|
||||
size = saturating_add_external_memory_size(size, hash_map_external_memory_size(m_binding_indices));
|
||||
return size;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,25 +30,21 @@ public:
|
|||
BindingFlagCanBeDeleted = 1 << 2,
|
||||
};
|
||||
|
||||
struct BindingDescriptor {
|
||||
Utf16FlyString name;
|
||||
u8 flags { 0 };
|
||||
};
|
||||
|
||||
EnvironmentShape(Vector<BindingDescriptor>, HashMap<Utf16FlyString, size_t>);
|
||||
EnvironmentShape(Vector<Utf16FlyString>, Vector<u8>, HashMap<Utf16FlyString, size_t>);
|
||||
virtual ~EnvironmentShape() override = default;
|
||||
|
||||
[[nodiscard]] static GC::Ref<EnvironmentShape> create(VM&, ReadonlySpan<Utf16FlyString> names, ReadonlySpan<u8> flags);
|
||||
|
||||
[[nodiscard]] size_t size() const { return m_bindings.size(); }
|
||||
[[nodiscard]] Utf16FlyString const& binding_name(size_t index) const { return m_bindings[index].name; }
|
||||
[[nodiscard]] u8 binding_flags(size_t index) const { return m_bindings[index].flags; }
|
||||
[[nodiscard]] size_t size() const { return m_binding_names.size(); }
|
||||
[[nodiscard]] Utf16FlyString const& binding_name(size_t index) const { return m_binding_names[index]; }
|
||||
[[nodiscard]] u8 binding_flags(size_t index) const { return m_binding_flags[index]; }
|
||||
[[nodiscard]] Optional<size_t> find_binding(Utf16FlyString const&) const;
|
||||
|
||||
private:
|
||||
virtual size_t external_memory_size() const override;
|
||||
|
||||
Vector<BindingDescriptor> m_bindings;
|
||||
Vector<Utf16FlyString> m_binding_names;
|
||||
Vector<u8> m_binding_flags;
|
||||
HashMap<Utf16FlyString, size_t> m_binding_indices;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue