rocq/glibc/StrlenBits.v
The bit facts glibc's word-at-a-time strlen rests on (StrlenLogic.v, docs/glibc-ir.md).
glibc's __strlen loads whole 64-bit words and finds a zero byte in each with two masks, then the zero byte's index with a population count. Each fact is stated over byte_of, the model's little-endian byte i of a word:
byte_of_land,byte_of_lor,byte_of_lxor: bitwise ops work on each byte apart;byte_of_mod64andbyte_of_div: the model's wrap to 64 bits keeps the low eight bytes, and a shift right by whole bytes moves them down;byte_of_add: a sum whose bytes do not carry adds byte by byte;byte_of_sub: a difference with no borrow below byte j subtracts byte by byte through byte j;first_word_byte: the first word's mask marks exactly the zero bytes from the string's start, with 128 each;loop_word_low: the loop's mask is 0 below the word's first zero byte and 128 at it;tail_first: the population count of a mask whose bytes are 0 or 128 finds its first 128, each such mask computed.
Every claim about all bytes is reduced to one about a byte, and the byte claims are checked on all 256 bytes by computation.
Bytes of a word
Bit n of byte i of x is bit 8i + n of x, for n below 8.
Proof 7 steps Qed
A byte of a bitwise and is the and of the bytes.
Proof 5 steps Qed
A byte of a bitwise or is the or of the bytes.
Proof 5 steps Qed
A byte of a bitwise xor is the xor of the bytes.
Proof 5 steps Qed
The wrap to 64 bits keeps the low eight bytes.
Proof 8 steps Qed
A shift right by k whole bytes moves byte i + k to byte i.
Proof 6 steps Qed
A 64-bit word has no bytes above its eighth.
Proof 6 steps Qed
A byte is in [0, 256).
Proof 3 steps Qed
The low n + 1 bytes are the low n bytes and byte n above them.
Proof 4 steps Qed
Sums and differences byte by byte
Where no byte pair of u and v sums past 255, no low part of them carries.
Proof 15 steps Qed
A byte of a sum with no carries is the sum of the bytes.
Proof 21 steps Qed
Where c's bytes below j are at most x's, no low part of x - c through byte j borrows.
Proof 14 steps Qed
A byte i at or below j of x - c, where c's bytes below j are at most x's, is the difference of the bytes, wrapped to a byte.
Proof 16 steps Qed
A word from its bytes
The little-endian integer of the bytes cs.
The eight byte indices of a word.
A 64-bit word is the integer of its eight bytes.
Proof 20 steps Qed
Each of the eight byte indices is one of idx8's.
Proof 3 steps Qed
Byte i of a constant, for each i below 8, by computing it.
A property of bytes, checked by computing it on all 256.
Proof 8 steps Qed
The masks
0x7f in every byte.
0x80 in every byte.
0x01 in every byte.
All 64 bits.
The bytes of 0x7f in every byte.
Proof 2 steps Qed
The bytes of 0x80 in every byte.
Proof 2 steps Qed
The bytes of 0x01 in every byte.
Proof 2 steps Qed
The bytes of all 64 bits.
Proof 2 steps Qed
glibc's find_zero_all (string-fza.h) on the first word x, as nanofil's IR computes it, every read wrapped to 64 bits: 128 in each zero byte of x, 0 in each other.
The first mask's byte, on one byte.
Proof 3 steps Qed
A byte's low seven bits are at most 127.
Proof 4 steps Qed
Byte i of the first mask is 128 if byte i of x is zero, else 0.
Proof 6 steps Qed
glibc's find_zero_low (string-fzb.h) on a loop word x, as nanofil's IR computes it: (x - 0x01...) & ~x & 0x80..., every read wrapped. Exact below x's first zero byte, and 128 at it.
The loop mask's byte, on one nonzero byte: 0.
Proof 4 steps Qed
Byte i at or below j of the loop mask, where x's bytes below j are nonzero: the loop mask's byte of x's byte i, with no borrow.
Proof 11 steps Qed
Below x's first zero byte j, the loop mask's bytes are 0.
Proof 6 steps Qed
At x's first zero byte j, the loop mask's byte is 128.
Proof 4 steps Qed
Every byte of the loop mask is 0 or 128.
Proof 5 steps Qed
Words in range
A word in range has no bits from 64 up.
Proof 4 steps Qed
An and of words in range is in range.
Proof 12 steps Qed
A word in range whose bytes are all 0 is 0.
Proof 5 steps Qed
The population count
glibc's index_first (string-fzi.h) as nanofil inlines it on the mask r: the population count of the bits below r's lowest set bit, (r & -r) - 1, every read wrapped.
On a mask in range whose bytes are 0 or 128, with the first 128 at byte j: the count is 8j + 7, so its eighth is j. Each of the 255 masks with a first 128 at some byte is computed.
Proof 4 steps Qed
The first word, from the string's start
The first word's mask shifted right by the string's offset k in its word, k * 8 bits, as nanofil's IR computes it from the address a: the zero bytes of x from byte k on, moved down to byte 0.
The shift is the offset of a in its word, in bytes.
Proof 8 steps Qed
Byte i of the shifted mask: 128 where byte i + k of x is zero, for i + k inside the word, else 0.
Proof 10 steps Qed
The shifted mask is in range.
Proof 6 steps Qed
The loop mask is in range.
Proof 2 steps Qed
Rounding a 64-bit address down to its word, as nanofil's IR does it: an and with ~7.