13 Symbol
A symbol is a named operation that can be referenced by name rather than through SSA dataflow. Functions, globals, aliases, and dialect-specific declarations are typical symbols. Symbols solve problems SSA values cannot: forward declarations, recursion, serialization, linking, and references across isolated regions.
13.1 Symbols Are Not Values
An SSA value is defined by an operation or block argument, has a type, and must dominate each use. A symbol has a name attribute, belongs to a symbol table, and is referenced through a SymbolRefAttr. A direct function call names its callee symbol; it does not take that function as an ordinary SSA operand.
func.func @helper(%x: i32) -> i32
%y = func.call @helper(%x) : (i32) -> i32
This allows @helper to be declared before its body, after its callers, or in a linked compilation unit. It also means changing a symbol requires updating every symbol reference in its scope, not merely replacing a textual name.
13.2 Scope, Visibility, And Paths
Symbol tables create lexical scopes. A flat reference identifies a symbol in an appropriate enclosing table; nested symbol references describe a path through nested symbol tables. Visibility controls whether a symbol is intended for external linkage, a parent scope, or only its local container.
Identical names are legal in different scopes. A transformation that outlines a function, nests a module, or merges modules must resolve references with SymbolTable APIs. String manipulation can miss nested paths, visibility rules, and references stored in attributes owned by other operations.
13.3 Symbol Uses Matter To Optimization
Inlining, dead-function elimination, renaming, and linking all need a complete symbol-use picture. A function with no direct calls may still be referenced by a global initializer, a dialect attribute, or an indirect dispatch table. A symbol-use interface or analysis must define what counts as a use before a pass deletes it.
When cloning a symbol, generate a unique name and decide whether internal references should point to the clone, the original, or both. This decision is a semantic one: cloning a recursive function without remapping recursive symbol references produces a clone that calls the original.