pub struct Func {
pub name: Symbol,
pub spelled: Option<Symbol>,
pub linkage: Linkage,
pub visibility: Visibility,
pub section: Option<Symbol>,
pub align: Option<u32>,
pub attrs: Attrs,
pub declared: Span,
/* private fields */
}Expand description
One function.
Fields§
§name: SymbolThe name it is called by, which is what a direct call to it names.
spelled: Option<Symbol>The name the source spelled, where an assembler name means the symbol is not that name.
extern char *strstr (const char *, const char *) __asm ("my_strstr"); declares the
standard strstr and says the symbol is my_strstr, and both of those are facts a later
pass needs: the symbol is what a call names and what the linker resolves, and the spelling
is what says this is the function the standard describes. Keeping only the symbol is how a
rename hides a library call from every fold that knows what that library call does, which
is what gcc.c-torture/execute/builtins/strstr-asm.c is written to catch.
None where the two are the same, which is every function that renamed nothing, so this
costs a word on a function and appears in the printed form only where a program asked for
it.
linkage: LinkageHow the linker sees it. Internal for a static function.
visibility: VisibilityHow the dynamic linker sees it.
section: Option<Symbol>The section to put it in, from __attribute__((section(...))), or None to let the
object writer choose.
align: Option<u32>What its first instruction has to be aligned to, from __attribute__((aligned(...))), or
None for the alignment the target gives every function anyway.
A raise and never a lower, the way the attribute is everywhere: a function asked to be at a multiple of two hundred and fifty six is at one, and one asked for less than the target’s own alignment keeps the target’s.
attrs: AttrsWhat is true of the whole function, which is what a caller reads when it wants to know what a call to it does without looking inside.
declared: SpanWhere it was declared, which is what a debugger says the prologue is.
Not any instruction’s span, and that is the point of it. The pushes, the frame and the
moves that put the arguments where the body expects them come from no expression in the
source, so every one of them carries Span::DUMMY, and the front of every function would
otherwise be the one part of it the line table says nothing about. A program counter in
there would get no answer rather than a slightly early one, which is the worse of the two
for whoever is reading a backtrace.
Span::DUMMY in a function built by something that is not a C source, which is what the
tests and the IR parser build.
Spelled declared rather than span because Func::span is already the span of an
instruction, and a field and a method of the same name on the same type is a reading
hazard for no gain.
Implementations§
Source§impl Func
impl Func
Sourcepub fn new(name: Symbol, signature: Signature) -> Self
pub fn new(name: Symbol, signature: Signature) -> Self
A function with that name and that signature, and nothing in it.
The signature becomes signature zero, which is what Func::signature gives back. The
entry block is not created here, because the caller is about to create it and give it
the parameters, and a half-built entry block is worse than no entry block. So a
function fresh from here is a declaration, and stops being one when it gets a block.
Sourcepub fn set_signature(&mut self, signature: Signature)
pub fn set_signature(&mut self, signature: Signature)
Gives the function a different signature of its own.
Two callers. One is the back end pass that puts an integer the machine has no register for
into the pair of registers it travels in, where one parameter becomes two. The other is the
interprocedural pass that takes out a parameter nothing reads, where one parameter becomes
none, and that one rewrites every call in the unit in the same breath. A parameter list
that is not the one the function was created with is a list the entry block’s parameters
have to say the same thing about, which is why this is next to Func::retain_params in
what a pass has to keep straight rather than something the middle end reaches for. Nothing
else changes a function’s own signature, because a signature is what its callers were
compiled against.
Sourcepub fn signatures(&self) -> impl Iterator<Item = &Signature>
pub fn signatures(&self) -> impl Iterator<Item = &Signature>
Every signature the function holds, its own first and then the ones its calls name.
Sourcepub fn add_signature(&mut self, signature: Signature) -> Sig
pub fn add_signature(&mut self, signature: Signature) -> Sig
Records a signature a call_indirect is made with, and gives back its index.
Sourcepub fn entry(&self) -> Option<Block>
pub fn entry(&self) -> Option<Block>
The entry block, which is the first one in layout order.
None only before one has been created. The verifier is what insists a finished
function has one.
Sourcepub fn is_declaration(&self) -> bool
pub fn is_declaration(&self) -> bool
Whether this only says the function exists somewhere, which is a function with no blocks in it.
extern int puts(const char *); and every other declaration of something defined in
another object is one of these, and it is here rather than left out of the module
because a call needs its signature and its linkage.
Sourcepub fn create_block(&mut self) -> Block
pub fn create_block(&mut self) -> Block
Creates a block with no parameters and no instructions, at the end of the layout.
Sourcepub fn remove_block(&mut self, block: Block)
pub fn remove_block(&mut self, block: Block)
Takes a block out of the layout, along with everything in it.
The block keeps its number, the way a removed instruction keeps its own, because renumbering would move every block after it and invalidate every index anybody was holding. What it stops being is a block of this function: nothing walks it, nothing prints it, and the values defined in it are as gone as the instructions that defined them. Deleting one whose branches something still reaches is how a function ends up branching to nowhere, so the caller is the one that has to know nothing reaches it.
§Panics
Panics if the block is the entry block, which is the one block a function has to have.
Sourcepub fn append_param(&mut self, block: Block, ty: Type) -> Value
pub fn append_param(&mut self, block: Block, ty: Type) -> Value
Adds a parameter of that type to a block, and gives back the value it arrives as.
Every predecessor’s branch has to grow an argument to match, which is
Func::append_arg, and the verifier is what notices if one of them did not.
§Panics
Panics if the block already has four billion parameters, which no block does.
Sourcepub fn retain_params(&mut self, block: Block, keep: impl FnMut(Value) -> bool)
pub fn retain_params(&mut self, block: Block, keep: impl FnMut(Value) -> bool)
Drops the parameters of a block that a predicate turns down, and renumbers the rest.
The predicate is asked about each parameter in the order the block takes them. A parameter that goes has to take the argument in the same position out of every branch to the block, which is the caller’s work rather than this method’s, because only the caller knows which branches there are. This is what removing a redundant block parameter is, and SSA construction is the thing that makes them.
§Panics
Panics if the block has four billion parameters, which no block does.
Sourcepub fn retype(&mut self, value: Value, ty: Type)
pub fn retype(&mut self, value: Value, ty: Type)
Gives a value a different type, leaving where it comes from alone.
There is one caller and it is the back end pass that puts an integer of a width the machine has no register for into the width it does have one for. Nothing in the middle end changes a value’s type, because a value’s type is what the instruction that made it produces and changing one without changing the other is how an IR stops meaning anything. That pass changes both, which is why this is a method and not a field.
§Panics
Panics if the value is not one of this function’s.
Sourcepub fn values(&self) -> impl Iterator<Item = Value> + use<'_>
pub fn values(&self) -> impl Iterator<Item = Value> + use<'_>
Every value the function has, including ones whose defining instruction has gone.
In the order they were created, which is the order a pass that walks all of them wants: a value is defined before it is used, so a walk in this order sees a definition first.
Sourcepub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_>
pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_>
Every instruction in a block, in order.
Sourcepub fn insts_backwards(
&self,
block: Block,
) -> impl Iterator<Item = Inst> + use<'_>
pub fn insts_backwards( &self, block: Block, ) -> impl Iterator<Item = Inst> + use<'_>
Every instruction in a block, last first.
Which is the order a liveness walk needs, and it is here rather than at the caller because the layout links are private and collecting the block into a vector to reverse it is an allocation per block per round of a fixpoint.
Sourcepub fn terminator(&self, block: Block) -> Option<Inst>
pub fn terminator(&self, block: Block) -> Option<Inst>
The last instruction of a block, which is its terminator once it is finished.
Sourcepub fn is_terminator(&self, inst: Inst) -> bool
pub fn is_terminator(&self, inst: Inst) -> bool
Whether control leaves the block at this instruction.
A question for the function rather than for the instruction, because inline assembly is
the one case where the opcode is not enough: asm goto has labels and everything else
does not, and the labels are in the function’s table rather than on the instruction.
Sourcepub fn create_inst(
&mut self,
data: InstData,
results: &[Type],
span: Span,
) -> Inst
pub fn create_inst( &mut self, data: InstData, results: &[Type], span: Span, ) -> Inst
Creates an instruction and its result values, without putting it in a block.
The results are allocated here and are contiguous, which is what lets an instruction hold the first of them and a count rather than a list.
§Panics
Panics if results has more than 255 types, which no instruction in the set does.
Sourcepub fn drop_results(&mut self, inst: Inst, drop: u8)
pub fn drop_results(&mut self, inst: Inst, drop: u8)
Takes the results in front of an instruction away, leaving the ones behind them.
One caller, the interprocedural pass that stops a function handing a value back. The results of an instruction are consecutive values with memory last, so dropping the ones in front is moving the first one along and shortening the count, and the values that went stay in the table with nothing referring to them, because nothing here ever takes a value out.
The ones that stay are renumbered, so that a value still says which of its instruction’s results it is. A pass that asks that question of a call result is asking whether it is the pointer an allocator handed back, and an answer left over from before the drop is an answer about a result that is no longer there.
§Panics
Panics if the instruction does not produce that many results.
Sourcepub fn append_inst(&mut self, block: Block, inst: Inst)
pub fn append_inst(&mut self, block: Block, inst: Inst)
Puts an instruction at the end of a block.
§Panics
Panics if the instruction is already in a block. Moving one is removing it and appending it, and doing it by accident is how a linked list ends up in two pieces.
Sourcepub fn insert_before(&mut self, inst: Inst, before: Inst)
pub fn insert_before(&mut self, inst: Inst, before: Inst)
Puts an instruction immediately before another one, in the block that one is in.
§Panics
Panics if inst is already in a block, or if before is not in one.
Sourcepub fn insert_after(&mut self, inst: Inst, after: Inst)
pub fn insert_after(&mut self, inst: Inst, after: Inst)
Puts an instruction immediately after another one, in the block that one is in.
The mirror of Func::insert_before, and it exists because a pass that has to talk about
a value an instruction produced has nowhere else to put what it is adding. Check insertion
is the caller: check_deriv is handed the pointer the derivation produced, so it goes
after the derivation and no amount of rearranging moves it earlier.
§Panics
Panics if inst is already in a block, if after is not in one, or if after is the
block’s terminator, since nothing may come between a terminator and the branch it is.
Sourcepub fn remove_inst(&mut self, inst: Inst)
pub fn remove_inst(&mut self, inst: Inst)
Takes an instruction out of its block, leaving it and its results in the tables.
The instruction is not deleted, because deleting it would move every instruction after it. A removed instruction is unreachable from any block and is dropped when the whole function is.
§Panics
Panics if the instruction is not in a block.
Sourcepub fn block_of(&self, inst: Inst) -> Option<Block>
pub fn block_of(&self, inst: Inst) -> Option<Block>
The block an instruction is in, or None if it has been removed from one.
Sourcepub fn mem_in(&self, inst: Inst) -> Option<Value>
pub fn mem_in(&self, inst: Inst) -> Option<Value>
The version of memory an instruction reads, when the function carries memory SSA.
Document 09 of spec/optimizer. Memory is a value of type mem, it is the last operand
of every instruction that touches memory, and it is absent in a function that does not
carry it, which is what -O0 and -O1 produce. Absent means unordered with respect to
everything, so a reader that gets None asks the alias analysis directly.
The operand is last rather than first on purpose. Every other operand keeps the position
it had, so a pass that reads the address of a load as args[0] goes on working whether
or not memory has been threaded, and the only code that has to know about the extra
operand is this accessor and the verifier.
Sourcepub fn mem_out(&self, inst: Inst) -> Option<Value>
pub fn mem_out(&self, inst: Inst) -> Option<Value>
The version of memory an instruction produces, when it writes memory and the function carries memory SSA.
Last among the results, for the reason Func::mem_in is last among the operands. A
load never has one, because it reads memory without changing it.
Nothing reads the last version in a function, and that means nothing. A store whose memory result has no reader is not dead, and what decides whether it is dead is dead store elimination, which is document 17’s.
Sourcepub fn bulk(&self, inst: Inst) -> Option<Bulk>
pub fn bulk(&self, inst: Inst) -> Option<Bulk>
A bulk copy or fill taken apart, or nothing where the instruction is not one.
The length is an operand on a bulk operation over an object whose length the program works
out and is MemInfo::size on every other one. Both shapes are here so that a pass which
asks this cannot read the payload’s number on the one where it is not the count: what it
gets is the operand or nothing, and nothing is the only answer that means the payload.
Memory is the last operand where the function carries it, which is why the length is found by position from the front rather than from the back.
Sourcepub fn carries_mem(&self, inst: Inst) -> bool
pub fn carries_mem(&self, inst: Inst) -> bool
Whether an instruction has been threaded onto the memory chain.
Sourcepub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst
pub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst
The same instruction with a version of memory threaded through it.
A result cannot be added to an instruction that already exists, because the results of one are values next to each other and there is no room after them. So threading memory makes a new instruction and the caller puts it where the old one was, forwards the old results to the new ones, which are at the same positions, and deletes the old one. That is what memory SSA construction does in one pass over the function.
The new instruction is not in any block. Its results are what the old one produced, in the same order, and then the new version of memory where the opcode writes memory.
§Panics
Panics if incoming is not memory, if the instruction does not touch memory, or if it is
already on the chain. All three are a construction bug rather than bad input.
Sourcepub fn without_mem(&mut self, inst: Inst) -> Inst
pub fn without_mem(&mut self, inst: Inst) -> Inst
The same instruction with the version of memory taken back off.
The inverse of Func::with_mem and the same shape for the same reason: a result cannot be
taken off an instruction that already exists, so this makes a new one and the caller puts it
where the old one was, forwards the results it kept, which are at the same positions, and
deletes the old one. The memory result has no forwarding to do, because taking the chain off
is only ever done when nothing reads it any more.
The new instruction is not in any block. Its results are what the old one produced without the version of memory at the end of them.
§Panics
Panics if the instruction is not on the chain, which is a caller that did not look first.
Sourcepub fn successors(
&self,
inst: Inst,
) -> impl Iterator<Item = BlockCall> + use<'_>
pub fn successors( &self, inst: Inst, ) -> impl Iterator<Item = BlockCall> + use<'_>
Where an instruction branches to, which is empty when it does not branch.
This is the one place that knows a switch keeps its targets in a side table and
asm goto in another one, so nothing walking the CFG has to.
Sourcepub fn target_list(&self, inst: Inst) -> BlockCallList
pub fn target_list(&self, inst: Inst) -> BlockCallList
Where a terminator keeps its targets, for something that edits them rather than reads them.
Func::successors is what walking the CFG wants. This is what recording an edge
wants, because an edge that will grow an argument later has to be named by its place in
the table rather than by the block it went to.
Sourcepub fn push_values(&mut self, values: &[Value]) -> ValueList
pub fn push_values(&mut self, values: &[Value]) -> ValueList
Records a run of value operands.
Sourcepub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList
pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList
Adds one value to the end of a run, giving back the run it became.
The run grows in place when nothing has been put after it, which is the case while a list is being built. Otherwise it is copied to the end and the old space is left behind, which is what makes adding a parameter to a loop header possible at all. That happens once per value carried around a loop, so the copying is not what costs.
Sourcepub fn rewrite(&mut self, list: ValueList, with: impl FnMut(Value) -> Value)
pub fn rewrite(&mut self, list: ValueList, with: impl FnMut(Value) -> Value)
Replaces the values in a run, which is what substituting one definition for another is.
A run is a run whether it is an instruction’s operands or a branch’s arguments, so this is the whole of the rewriting a substitution has to do.
Sourcepub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList
pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList
Records a run of branch targets.
Sourcepub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall)
pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall)
Replaces one branch target, which is what redirecting an edge is.
Sourcepub fn push_slots(&mut self, slots: &[Slot]) -> SlotList
pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList
Records where each eightbyte of an object travelled.
Sourcepub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo>
pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo>
Records an object read off a variable argument list.
Sourcepub fn push_abis(&mut self, abis: &[Abi]) -> AbiList
pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList
Records what the ABI asks of the arguments a call’s signature does not name.
Sourcepub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo>
pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo>
Records a call’s callee and signature.
Sourcepub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo>
pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo>
Records a switch’s targets and case values.
Sourcepub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo>
pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo>
Records an inline assembly instruction’s template and constraints.
Sourcepub fn counts(&self) -> Counts
pub fn counts(&self) -> Counts
How many values, instructions and blocks there are, for a reader that wants to size something by them.
Sourcepub fn facts(&self, value: Value) -> Facts
pub fn facts(&self, value: Value) -> Facts
What is known about a value, which is nothing at all unless somebody said otherwise.
Section 6.2.3 of spec/safe-memory/06-instrumentation.md. Facts are in a side table and
not in the value, so a function nobody has said anything about carries no facts and is
the same size it was before facts existed.
Sourcepub fn set_facts(&mut self, value: Value, facts: Facts)
pub fn set_facts(&mut self, value: Value, facts: Facts)
Says what is known about a value, replacing whatever was known before.
Setting Facts::NONE takes the value back out of the table, which is what keeps the
table empty in a function that has had facts put on and then taken off again.
Sourcepub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_
pub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_
Every value something is known about, in value order.
Sourcepub fn name_block(&mut self, block: Block, name: Symbol)
pub fn name_block(&mut self, block: Block, name: Symbol)
Gives a block a name of its own, which is how an image written before the program runs says it holds the address of a place inside this function.
What asks for this is GNU’s address of a label in the initializer of an object with static
storage duration, which is how every threaded interpreter builds its dispatch table. The
address a lea produces needs none of this, because both ends of that distance are in the
same section and the object writer works it out for itself. An image is the other case: it
is in another section, so what it holds is a relocation, and a relocation names a symbol.
One name per block. Two labels on the same statement are two labels and one block, and the image asks for a name rather than for a particular one, so the second ask keeps the first answer. Nothing outside this table ever sees the name, which is why it may be anything the object format lets a local symbol be called.
Sourcepub fn block_name(&self, block: Block) -> Option<Symbol>
pub fn block_name(&self, block: Block) -> Option<Symbol>
The name a block was given, or None for a block nothing took the address of.
Sourcepub fn named_blocks(&self) -> impl Iterator<Item = (Block, Symbol)> + '_
pub fn named_blocks(&self) -> impl Iterator<Item = (Block, Symbol)> + '_
Every block that has a name, in block order.
Sourcepub fn declare_mem(&mut self, mem: Idx<MemInfo>, decl: u32)
pub fn declare_mem(&mut self, mem: Idx<MemInfo>, decl: u32)
Says which declaration in the source a piece of memory was made for, which is how a build that was asked for debugging information ends up able to print a local by its name.
The number is whatever the front end counts declarations by and means nothing here. This crate sits below the one that has a type for it, and inventing a second name for the same thing so that it could be spelled out here would buy nothing: nothing between the front end that writes the number and the back end that hands it back reads it.
On the memory rather than on the alloca that asks for it because the memory is what
survives. An instruction is rewritten, moved and renumbered by every pass it goes through,
and MemInfo is appended to and never reordered, so the number an access carries today is
the number it carries at the end.
One declaration per piece of memory, and the first ask wins. Nothing asks twice.
Sourcepub fn mem_decl(&self, mem: Idx<MemInfo>) -> Option<u32>
pub fn mem_decl(&self, mem: Idx<MemInfo>) -> Option<u32>
The declaration a piece of memory was made for, or None for memory no declaration in the
source asked for, which is every temporary and every spill.
Sourcepub fn declare_value(&mut self, value: Value, decl: u32)
pub fn declare_value(&mut self, value: Value, decl: u32)
Says which declaration in the source a value is a value of, which is the other half of
Func::declare_mem.
A local whose address is never taken has no memory to put the number on, because nothing asked for any, and what holds it is a value the SSA construction worked out.
One declaration is many values. Every assignment to it makes one, and so does every block parameter that collects two of them where control joins. One value can be more than one declaration as well, because a pass that finds two values equal points the readers of one at the other, and both names then mean the one that is left. Neither of those is a mistake to be ruled out here, so this is a list of pairs rather than a map in either direction.
What the pairs do not say is which of a declaration’s values it holds at a given address, and nothing in this crate can say it. That is a question about where the definitions ended up in the code that came out and how long each of them survived there, which the back end knows and the IR does not.
Sourcepub fn value_decls(&self, value: Value) -> impl Iterator<Item = u32> + '_
pub fn value_decls(&self, value: Value) -> impl Iterator<Item = u32> + '_
Every declaration a value is a value of, in the order the front end numbered them.
Empty for a value no declaration in the source is behind, which is most of them: every temporary an expression needed, every address computed on the way to a member, and every result of a rule the peephole applied.
Sourcepub fn rename_value(&mut self, from: Value, to: Value)
pub fn rename_value(&mut self, from: Value, to: Value)
Moves every declaration one value is a value of onto another value.
What a pass that found two values equal does about the names. It points the readers of one at the other and the one it pointed away from is about to be nobody’s, so the names go with the readers: the declaration still holds the value it held, and the value is now spelled the other way. A pass that deletes a value without giving its readers somewhere else to look is a pass that deleted something nothing reads, and a declaration whose value went that way is one the back end will have nothing to say about over those addresses, which is the right answer rather than a lost one.
Trait Implementations§
Source§impl Index<Idx<SwitchInfo>> for Func
impl Index<Idx<SwitchInfo>> for Func
Source§type Output = SwitchInfo
type Output = SwitchInfo
Source§fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo
fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo
container[index]) operation. Read more