LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
llzk::polymorphic::detail::StructSpecializationDiscovery Class Reference

Determine which concrete struct definitions are needed when instantiating a struct or free function with the supplied template arguments. More...

#include <StructSpecializationDiscovery.h>

Public Types

using Bindings = llvm::DenseMap<mlir::Attribute, mlir::Attribute>
using Requests = llvm::SmallVector<StructSpecializationRequest>
using CallTargets = llvm::SmallVector<RolledCallTargets>

Public Member Functions

mlir::FailureOr< Bindings > evaluateBindings (mlir::Operation *source, const Bindings &bindings) const
 Evaluate enclosing template expressions in declaration order and return an extended copy of bindings for substitution into a concrete clone.
mlir::FailureOr< Requests > discover (mlir::Operation *source, const Bindings &bindings, llvm::SmallVectorImpl< mlir::Operation * > *visitedOperations=nullptr, CallTargets *callTargets=nullptr) const
 Return ordered, deduplicated requests for a struct or free-function source, or diagnose an unresolved dependency.

Detailed Description

Determine which concrete struct definitions are needed when instantiating a struct or free function with the supplied template arguments.

For example, Parent<N> may contain an array of Child instances for 0 <= i < N. Evaluating that range with N = 3 tells the caller to instantiate Child<0>, Child<1>, and Child<2> while leaving the original loop intact.

Template parameters and expressions supply constants to the discovered methods or free-function body. Function arguments without known values remain unknown; intermediate SSA values are computed separately for each function and loop iteration because their values depend on that execution's inputs. Template-expression results are stored in a private copy of bindings so the caller's inputs stay unchanged.

Supported input structure (assuming verified LLZK IR):

  • Every method must have a defined, single-block body. Discovery visits every method, even if no call to that method appears in the source.
  • Template expressions are evaluated in declaration order. A known yielded value becomes a binding available to subsequent expressions and methods.
  • Visited operations may contain regions only for scf.if, scf.for and scf.while. Other region operations and unstructured branches on explored paths are unsupported.
  • scf.if selects the branch when its condition is known. Otherwise both branches must be supported, and a result stays known only if both branches yield the same constant. Discovery does not retain alternative values or infer facts from the branch condition.
  • Every visited scf.for requires known signed bounds and a positive step, representable as int64_t, with no induction overflow or unsignedCmp. Iterations propagate known scalar iter_args; zero iterations return the initial values. These requirements apply even when the loop contains no struct uses.
  • Every visited scf.while requires a known i1 condition at each check. The condition region runs before the first iteration and after every body yield. Its forwarded values become the body arguments or, when false, the loop results.

Values available for specialization:

  • poly.read_const reads the supplied bindings and known template-expression results. Method and free-function arguments start unknown.
  • A region-free, memory-effect-free operation produces known values when all operands are known and its fold hook returns attributes or known SSA values. This includes foldable constants and arithmetic; purity alone is insufficient.
  • Calls are inspected for result types but their bodies are not evaluated. Call results, global reads (including constant globals), and struct-member reads remain unknown. Local POD and array reads snapshot known contents; bound result types and allocation affine operands determine aggregate shape. Unknown control flow and writes through untracked aliases invalidate storage facts. Unknown values cannot supply a required specialization argument or loop bound.

Supported type dependencies:

  • Member types, method signatures and visited operation result types are inspected for struct uses, including uses nested in other types. Template bindings substitute type variables, struct arguments and array dimensions. Every requested struct argument must then be concrete.
  • Affine arguments directly on a call's struct result type are evaluated from the corresponding map-operand groups, whose values must be known integers.
  • An affine struct nested in an array requires concrete nonnegative dimensions. A bare affine operation result may remain rolled, as on array.read or pod.read; a bare affine member is unsupported. POD records between the array and struct preserve that array's indices. Each affine argument must accept one input per dimension and fold to one non-poison result. An empty dimension produces no requests but establishes a known empty set.
  • Affine calls also contribute concrete specializations. A call with a rolled struct argument receives the union from all arrays and affine calls with the same rolled type in this source instance. A call with no known source is diagnosed. Affine maps nested in struct type arguments are unsupported.

An unresolved required argument or unsupported explored structure is diagnosed. Success describes dependencies of this source instance; the caller must repeat discovery for newly instantiated definitions to obtain the transitive set of specializations.

Results retain source parameterized types, including nested concrete type arguments. Identity, caching, cloning, call retargeting and metadata belong to the caller.

Definition at line 128 of file StructSpecializationDiscovery.h.

Member Typedef Documentation

◆ Bindings

using llzk::polymorphic::detail::StructSpecializationDiscovery::Bindings = llvm::DenseMap<mlir::Attribute, mlir::Attribute>

Definition at line 130 of file StructSpecializationDiscovery.h.

◆ CallTargets

◆ Requests

Member Function Documentation

◆ discover()

FailureOr< StructSpecializationDiscovery::Requests > StructSpecializationDiscovery::discover ( mlir::Operation * source,
const Bindings & bindings,
llvm::SmallVectorImpl< mlir::Operation * > * visitedOperations = nullptr,
CallTargets * callTargets = nullptr ) const

Return ordered, deduplicated requests for a struct or free-function source, or diagnose an unresolved dependency.

Self references are omitted; the caller already owns the source instance. When supplied, visitedOperations receives explored operations so callers can retarget uses without inspecting branches excluded by constant conditions. callTargets receives the candidate sets for rolled method arguments.

Definition at line 999 of file StructSpecializationDiscovery.cpp.

◆ evaluateBindings()

FailureOr< StructSpecializationDiscovery::Bindings > StructSpecializationDiscovery::evaluateBindings ( mlir::Operation * source,
const Bindings & bindings ) const

Evaluate enclosing template expressions in declaration order and return an extended copy of bindings for substitution into a concrete clone.

Source must be a struct definition or free-function definition.

Definition at line 995 of file StructSpecializationDiscovery.cpp.


The documentation for this class was generated from the following files: