Module: Optimize::Codec::LocalTable
- Defined in:
- lib/optimize/codec/local_table.rb
Overview
Parse and emit the local_table section of an iseq body.
On-disk shape (research/cruby/ibf-format.md §4.1):
local_table_size entries, each an ID-sized (uintptr_t) object-table
index for the Symbol that names the local. ID is 8 bytes on a
64-bit build (little-endian), which is the only configuration we
target for this talk.
The raw blob may include trailing alignment zeros that belong to
the section after this one in the enclosing iseq layout. Decode
reads exactly size entries; encode returns exactly the content
bytes (no padding). The iseq_list encoder re-adds trailing pad.
Constant Summary collapse
- ID_SIZE =
uintptr_t on 64-bit
8
Class Method Summary collapse
-
.decode(bytes, size) ⇒ Array<Integer>
Object-table indices, one per local.
-
.encode(entries) ⇒ String
ASCII-8BIT byte string.
-
.grow!(fn, object_table_index) ⇒ Integer
Append a new local slot to
fn's local table.
Class Method Details
.decode(bytes, size) ⇒ Array<Integer>
Returns object-table indices, one per local.
26 27 28 29 30 31 32 33 34 35 |
# File 'lib/optimize/codec/local_table.rb', line 26 def decode(bytes, size) return [] if size.nil? || size.zero? || bytes.nil? || bytes.empty? required = size * ID_SIZE if bytes.bytesize < required raise ArgumentError, "local_table buffer too short: got #{bytes.bytesize} bytes, need #{required} (size=#{size})" end reader = BinaryReader.new(bytes) Array.new(size) { reader.read_u64 } end |
.encode(entries) ⇒ String
Returns ASCII-8BIT byte string.
39 40 41 42 43 |
# File 'lib/optimize/codec/local_table.rb', line 39 def encode(entries) writer = BinaryWriter.new entries.each { |idx| writer.write_u64(idx) } writer.buffer end |
.grow!(fn, object_table_index) ⇒ Integer
Append a new local slot to fn's local table.
Mutates fn.misc[:local_table_size] (+1) and fn.misc[:local_table_raw]
(re-encoded content followed by the same number of trailing alignment
pad bytes that the original raw carried — the iseq_list encoder uses
raw.bytesize for downstream section positioning).
Side effect: on the first call for a given iseq, stashes the
pre-growth size in fn.misc[:local_table_size_pre_growth] (set-once;
subsequent grow! calls leave it alone). The encoder's body-identity
guard reads this to detect legitimate growth.
Does NOT mutate getlocal/setlocal LINDEX operands in fn.instructions;
that rewrite is the inlining pass's responsibility.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/optimize/codec/local_table.rb', line 66 def grow!(fn, object_table_index) unless object_table_index.is_a?(Integer) && object_table_index >= 0 && object_table_index < (1 << 64) raise ArgumentError, "object_table_index must be a non-negative Integer < 2**64, got #{object_table_index.inspect}" end misc = fn.misc old_size = (misc[:local_table_size] || 0) old_raw = (misc[:local_table_raw] || "".b) entries = decode(old_raw, old_size) entries << object_table_index new_content = encode(entries) old_content_size = old_size * ID_SIZE pad_bytes = [old_raw.bytesize - old_content_size, 0].max # Preserve the pre-growth size once, so the iseq_list encoder can detect # that the body record's local_table_size / relative offsets have # legitimately changed and skip byte-identity assertions. misc[:local_table_size_pre_growth] ||= old_size misc[:local_table_raw] = new_content + ("\x00".b * pad_bytes) misc[:local_table_size] = old_size + 1 old_size end |