From e9c89bcac0f542db7550c864102dff39df01a882 Mon Sep 17 00:00:00 2001 From: Gerwin Klein Date: Wed, 7 Jan 2026 15:53:44 +1100 Subject: [PATCH] bfgen: add field_ptr(align) name size Add the new block command field_ptr(align) name size as an abbreviation for padding size - canonical + align field_high name canonical - align The (align) part is optional. If left out, align is 0. This is useful for removing #ifdefs for pointer storage that are trying to achieve constant size over different canonical bit representations. Signed-off-by: Gerwin Klein --- tools/bitfield_gen.md | 99 ++++++++++++++++++++++++++++++++++++++++++- tools/bitfield_gen.py | 27 +++++++++++- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/tools/bitfield_gen.md b/tools/bitfield_gen.md index f60f760d0..815360c88 100644 --- a/tools/bitfield_gen.md +++ b/tools/bitfield_gen.md @@ -252,6 +252,100 @@ that stores 36 bits (on a 64 bit base platform with 48 significant bits). [Architecture Parameters]: #architecture-parameters-and-canonical-pointers +### Field Ptr + +Often we want to store a pointer with some known alignment (i.e. not all bits +need to be stored), and pad out to a specific overall field size. +The `field_ptr` abbreviation makes this convenient. + +```bf_gen +block some_block { + ... + field_ptr(align) some_name size + ... +} +``` + +stands for "a field `some_name` for a pointer with alignment `align` padded out +to `size`". It is an abbreviation for + +```bf_gen +block some_block { + ... + padding size - canonical_size + align + field_high some_name canonical_size - align + ... +} +``` + +The `(align)` parameter can be left out and is equivalent to `field_ptr(0)`. The +constant `canonical_size` in the expression above stands for the number of +significant bits (the canonical size) of a pointer according to the most recent +`base` definition. For example the long form + +```bf_gen +base 64(48,1) + +block mdb_node { + padding 16 + field_high mdbNext 46 + field mdbRevocable 1 + field mdbFirstBadged 1 + + field mdbPrev 64 +} +``` + +can be written more concisely as + +```bf_gen +base 64(48,1) + +block mdb_node { + field_ptr(2) mdbNext 62 + field mdbRevocable 1 + field mdbFirstBadged 1 + + field mdbPrev 64 +} +``` + +The `field_ptr` abbreviation is especially useful for sharing the same block +definition under different `base` declarations, e.g. for different +architectures. For example, with a `base 64(39,1)` the `field_ptr` definition +text remains the same, but expands to a different `field_high`/`padding` +combination that achieves the same overall width: + +```bf_gen +base 64(39,1) + +block mdb_node { + padding 25 + field_high mdbNext 37 + field mdbRevocable 1 + field mdbFirstBadged 1 + + field mdbPrev 64 +} +``` + +can be also be written as + +```bf_gen +base 64(39,1) + +block mdb_node { + field_ptr(2) mdbNext 62 + field mdbRevocable 1 + field mdbFirstBadged 1 + + field mdbPrev 64 +} +``` + +This means the same block definition can produce a constant field size while +correctly accommodating multiple different `base` configurations. + ### Visible Field Order Sometimes it is useful for the constructor to take its arguments in a particular @@ -624,7 +718,10 @@ canonical_spec ::= "(" expr "," ("0"|"1") ")" block ::= "block" IDENT visible_order_spec_opt? "{" fields "}" visible_order_spec_opt ::= "(" visible_order_spec? ")" visible_order_spec ::= IDENT ("," IDENT)* -fields ::= ( "field" IDENT expr | "field_high" IDENT expr | "padding" expr )* +fields ::= ( "field" IDENT expr | + "field_high" IDENT expr | + "field_ptr" ("(" expr ")")? IDENT expr | + "padding" expr )* tagged_union ::= "tagged_union" IDENT IDENT tag_slices? "{" masks tags "}" diff --git a/tools/bitfield_gen.py b/tools/bitfield_gen.py index e6edb16bc..dadfa4e13 100755 --- a/tools/bitfield_gen.py +++ b/tools/bitfield_gen.py @@ -87,8 +87,8 @@ TYPES = { # Parser -reserved = ('BLOCK', 'BASE', 'FIELD', 'FIELD_HIGH', 'MASK', 'PADDING', - 'TAGGED_UNION', 'TAG') +reserved = ('BLOCK', 'BASE', 'FIELD', 'FIELD_HIGH', 'FIELD_PTR', 'MASK', + 'PADDING', 'TAGGED_UNION', 'TAG') tokens = reserved + ('IDENTIFIER', 'INTLIT', 'LBRACE', 'RBRACE', 'LPAREN', 'RPAREN', 'COMMA', 'PLUS', 'MINUS', @@ -235,6 +235,21 @@ def p_fields_field_high(t): t[0] = t[1] + [(t[3], t[4], True)] +def p_fields_field_ptr(t): + """fields : fields FIELD_PTR IDENTIFIER int_expr""" + padding_size = ('-', t[4], "canonical_size") + field_size = "canonical_size" + t[0] = t[1] + [(None, padding_size, False), (t[3], field_size, True)] + + +def p_fields_field_ptr_align(t): + """fields : fields FIELD_PTR LPAREN int_expr RPAREN IDENTIFIER int_expr""" + align = t[4] + padding_size = ('+', ('-', t[7], "canonical_size"), align) # size - canonical_size + align + field_size = ('-', "canonical_size", align) + t[0] = t[1] + [(None, padding_size, False), (t[6], field_size, True)] + + def p_fields_padding(t): """fields : fields PADDING int_expr""" t[0] = t[1] + [(None, t[3], False)] @@ -2445,6 +2460,14 @@ class Block: offset = self.size for _name, _size, _high in self.fields: + if _name: + if _size <= 0: + raise ValueError(f"Fields must have positive size, but {_name} " + f"in block {self.name} has size {_size}") + else: + if _size < 0: + raise ValueError(f"Padding must be non-negative, but padding " + f"in block {self.name} has size {_size}") offset -= _size if _name is not None: if visible_order is None: