Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,24 @@ jobs:
if: ${{ !cancelled() }}
run: python3 scripts/class_id_collisions.py

# #8047/#8067. `ObjectHeader.object_type`, `field_count` and `keys_array`
# are being retired in favour of the authoritative ShapeId descriptor.
# #8086 migrated the guards AND built this exact-callsite census to keep
# them retired -- then wired it into nothing, so it has never been able to
# fail a build. A gate that no job invokes is documentation.
#
# The census is exact rather than a grep count: it strips comments and
# string literals, classifies each site declaration-vs-access, and diffs
# the full multiset against a reviewed baseline, so it can tell an
# ObjectHeader read from another struct's identically-named field. It
# also asserts the authority surfaces directly (no guard may compare a
# keys pointer, no emitter may bake header offsets 0/12/16) and carries
# its own lexer self-test plus sabotage self-tests, which run
# unconditionally on every invocation.
- name: Object-header shape-descriptor census
if: ${{ !cancelled() }}
run: python3 scripts/shape_descriptor_census.py

# #7645. The copying minor skips its eligibility preflight — the walk that
# proves nothing reachable is pinned — whenever the young-pin latch is
# clear. That is sound only while EVERY creation of GC_FLAG_PINNED goes
Expand Down
12 changes: 12 additions & 0 deletions changelog.d/8110-shape-census-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
### Fixed

- Wire the `ObjectHeader` shape-descriptor census into `lint`. #8086 built the
exact-callsite census #8067 required — the instrument that keeps
`object_type`, `field_count` and `keys_array` retired while `ShapeId` takes
over their facts — but referenced it from no workflow, so it had never been
able to fail a build. Wiring it up exposed that its emitted-guard arm was
vacuous: it rejected `add(..., "0"|"12"|"16")`, while all four functions it
names build their header address with `blk.gep(I8, &p, &[(I64, "N")])`, so a
planted read of the removed `keys_array` offset in
`emit_element_shape_field_load` left the census green. The check now matches
the gep form, and each of the four emitters is sabotage-verified red.
17 changes: 15 additions & 2 deletions scripts/shape_descriptor_census.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ def assert_authority_surfaces(sources: dict[str, str]) -> None:
raw_write_pics = sources["crates/perry-codegen/src/expr/proxy_reflect.rs"]

for pattern, label in (
(r"descriptors\s*:\s*HashMap\s*<\s*u32\s*,\s*ShapeDescriptor", "by-id descriptor table"),
# `PtrHashMap` since #8157 (SipHash on a bare u32 was 25% of self time in
# `shapes`). The fact this asserts is that a by-id table EXISTS, not which
# hasher backs it, so accept either spelling.
(r"descriptors\s*:\s*(?:[\w:]+::)?(?:Ptr)?HashMap\s*<\s*u32\s*,\s*ShapeDescriptor", "by-id descriptor table"),
(r"logical_key_count\s*:\s*u32", "exact logical-key fact"),
(r"live_inline_slot_count\s*:\s*u32", "exact live-slot fact"),
(r"semantic_generation\s*:\s*u64", "semantic transition fact"),
Expand Down Expand Up @@ -463,7 +466,17 @@ def assert_authority_surfaces(sources: dict[str, str]) -> None:
):
for name in names:
body = function_body(source, name)
if re.search(r"expected_keys|add\s*\([^\n]*\"(?:0|12|16)\"", body):
# Match BOTH emission forms. These four guards build their header
# address with `blk.gep(I8, &p, &[(I64, "N")])`, not `add(..)`, so
# an `add`-only pattern was vacuous for every function in this
# list -- planting `gep(I8, &elem_ptr, &[(I64, "16")])` in
# `emit_element_shape_field_load` left the census green.
if re.search(
r"expected_keys"
r"|add\s*\([^\n]*\"(?:0|12|16)\""
r"|gep\s*\([^\n]*\(\s*I64\s*,\s*\"(?:0|12|16)\"\s*\)",
body,
):
raise CensusError(f"{name} emits a removed ObjectHeader fact")

generic_body = function_body(raw_generic_pic, "lower_generic_property_get")
Expand Down
18 changes: 12 additions & 6 deletions scripts/shape_descriptor_census_baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"crates/perry-codegen/src/expr/property_set.rs|crate::target_layout::object_header_size_bytes(": 3,
"crates/perry-codegen/src/expr/property_set.rs|crate::target_layout::object_header_size_bytes(ctx.target_triple)": 1,
"crates/perry-codegen/src/expr/property_set.rs|crate::target_layout::object_header_size_bytes(ctx.target_triple).to_string();": 2,
"crates/perry-codegen/src/expr/proxy_reflect.rs|(crate::target_layout::object_header_size_bytes(ctx.target_triple) / 8).to_string();": 1,
"crates/perry-codegen/src/expr/proxy_reflect.rs|crate::target_layout::object_header_size_bytes(ctx.target_triple).to_string();": 1,
"crates/perry-codegen/src/expr/proxy_reflect.rs|let header_bytes = crate::target_layout::object_header_size_bytes(ctx.target_triple);": 1,
"crates/perry-codegen/src/lower_call/new.rs|crate::target_layout::object_header_size_bytes(ctx.target_triple).to_string();": 1,
"crates/perry-codegen/src/lower_call/new_alloc.rs|crate::target_layout::object_header_size_bytes(ctx.target_triple);": 1,
"crates/perry-codegen/src/lower_call/scalar_method.rs|let header_skip = crate::target_layout::object_header_size_bytes(ctx.target_triple).to_string();": 1,
Expand Down Expand Up @@ -274,9 +274,15 @@
"crates/perry-runtime/src/object/spill.rs|field_count|access|std::cmp::max((*obj).field_count, crate::object::INLINE_SLOT_FLOOR as u32);": 1,
"crates/perry-runtime/src/object/spill.rs|field_count|declaration|pub(crate) fn reserve_object_spill(obj_ptr: usize, field_count: u32) {": 1,
"crates/perry-runtime/src/os.rs|field_count|declaration|let field_count: u32 = 14;": 1,
"crates/perry-runtime/src/param_type_guard.rs|field_count|access|for _ in 0..field_count {": 1,
"crates/perry-runtime/src/param_type_guard.rs|field_count|access|let inline_fields = ((*object).field_count as usize).max(crate::object::INLINE_SLOT_FLOOR);": 1,
"crates/perry-runtime/src/param_type_guard.rs|field_count|access||| (*object).field_count as usize > MAX_CONTAINER_LEN": 1,
"crates/perry-runtime/src/param_type_guard.rs|keys_array|access|let keys = (*object).keys_array;": 1,
"crates/perry-runtime/src/param_type_guard.rs|object_type|access|if (*object).object_type != crate::error::OBJECT_TYPE_REGULAR": 1,
"crates/perry-runtime/src/perf_hooks.rs|keys_array|access|let keys_ptr = (*obj).keys_array as usize;": 1,
"crates/perry-runtime/src/perf_hooks.rs|keys_array|access|recorded != 0 && (*obj).keys_array as usize == recorded": 1,
"crates/perry-runtime/src/pointer_event.rs|field_count|declaration|let field_count: u32 = 4;": 1,
"crates/perry-runtime/src/process/node_module/source_map.rs|keys_array|access|(*obj).keys_array = std::ptr::null_mut();": 1,
"crates/perry-runtime/src/promise/then_probe.rs|keys_array|access|let keys = (*obj).keys_array;": 2,
"crates/perry-runtime/src/proxy.rs|object_type|access|&& (*(addr as *const crate::ObjectHeader)).object_type": 1,
"crates/perry-runtime/src/proxy/put_value.rs|field_count|access|object_array_numeric_write_slots(array, &keys[..field_count as usize], receiver_count)": 1,
Expand All @@ -290,9 +296,9 @@
"crates/perry-runtime/src/typed_feedback/tests.rs|field_count|access|(*obj).field_count = 0;": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|field_count|access|(*obj).field_count = original_field_count;": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|field_count|access|let original_field_count = unsafe { (*obj).field_count };": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|&(*obj).keys_array as *const _ as usize,": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|(*obj).keys_array = original_keys;": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|(*obj).keys_array = std::ptr::null_mut();": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|&(*obj).keys_array as *const _ as usize,": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|assert_ne!(unsafe { (*obj).keys_array }, original_keys);": 1,
"crates/perry-runtime/src/typed_feedback/tests.rs|keys_array|access|let keys = unsafe { (*obj).keys_array };": 1,
"crates/perry-runtime/src/url/search_params.rs|keys_array|access|let keys_arr = (*obj).keys_array;": 2,
Expand Down Expand Up @@ -328,11 +334,11 @@
},
"summary": {
"codegen_object_header_size_sites": 32,
"raw_member_files": 99,
"raw_member_files": 101,
"raw_member_sites": {
"field_count": 159,
"keys_array": 181,
"object_type": 31
"field_count": 162,
"keys_array": 183,
"object_type": 32
}
}
}
Loading