Skip to main content

Module params

Module params 

Source
Expand description

How big the object behind a pointer parameter is, worked out from the calls that pass it.

Design: spec/safe-memory/07-check-elimination.md section 7.5, which asks for a summary per function recording “which pointer parameters are dereferenced and over what range, which are freed, which escape, and whether the function can free memory at all”. crate::nofree is the last of those four. This is the first, read the other way round.

Section 7.5 writes the dereferenced range as something the callee tells its callers, which is what makes a call site cheaper. What is here is the callers telling the callee, which is what makes the callee’s own checks cheaper, and the callee is where the checks are. On the SQLite amalgamation 13284 of the bounds checks the discharge pass keeps are on a pointer that arrived as a parameter, which is more than the next two sources put together, and a parameter is exactly the value a function-at-a-time pass can say nothing about.

§What is claimed

A function only this module can call, every call to which passes an object with at least so many bytes left in it, has a parameter with at least so many bytes wherever it is used. The objects believed are the two whose extent is already written down: a frame slot of the caller, read off a fixed size alloca, and a global this module defines and vouches for, which is crate::extents’ table. Both are alive for as long as the call runs, so the answer says a lifetime as well as an extent and Flags::HANDED licenses both, in the way Flags::STATIC does.

Only this module can call it means internal linkage and an address this module never takes. An address is taken by a global_addr naming it anywhere in any body, by a relocation in any global’s initial image, and by an alias resolving to it. Any of those and the function is left alone, because a call through an address is a call site this cannot see and the argument it passes is one nobody counted.

§Where the answer goes

Onto the check, as Flags::HANDED, before the pipeline starts. The reason is crate::extents’ reason: what is being said is worked out across functions and a pass is given one. Writing it on the instruction is also what keeps the claim in one place. A pass reading a flag cannot accidentally believe half of it.

§Which way the fixed point goes

Every parameter starts unknown and becomes known only when every call site has an answer, and the round is repeated until nothing changes. That is the least fixed point, and it is the one that has to be taken here, because the opposite start would let a fact hold itself up: two functions that pass each other the parameter they were given would agree on any number at all, and a self-recursive function would agree with itself. Starting from unknown, neither of them ever gets an answer, which is a check that stays rather than a check that should not have gone.

One argument reaching an answer through the caller’s own parameter is the case that makes this worth iterating rather than reading once. A static helper is usually passed what its caller was passed, and the chain only bottoms out at a frame slot several calls up.

§What is not here

Nothing is said about a pointer that arrived from a load, from an allocator or from a call, and nothing is said about a function this module does not define or that anything can reach. Those are the other rows of the measurement and they need their own work.

The summary is spent on the checks and thrown away, in the way crate::nofree’s is, and for the same reason: a record that survives the file it was worked out in is what link time optimization will want and there is no link time optimization yet.

Functions§

annotate
Writes Flags::HANDED onto every check whose bytes are inside an object its callers hand in.