Emitter

struct Emitter : Sendable

Hylo’s IR emitter.

The emitter transforms well-formed, typed ASTs to a representation suitable for flow-sensitive analysis. IR generated from the emitter may be incomplete and must go through mandatory passes before it can be fed to code generation.

You create an instance by calling Emitter.withInstance, providing the module in which IR should be incorporated and a diagnostic log. Then, the two main entry points are:

  • incorporateTopLevelDeclarations: incorporates the top-level declarations of a module’s AST into its corresponding IR form.
  • incorporateSyntheticDeclarations: generates the implementations of the synthesized declarations that are notionally part of a module. This method is called after definite deinitialization.

Other entry points may be used during IR passes (e.g., emitDeinit).

Note

Unless documented otherwise, the methods of Emitter type insert IR in self.module at self.insertionPoint, anchoring new instructions at the given source range, named site in their parameter lists.
  • The diagnostics of lowering errors.

    Declaration

    Swift

    private var diagnostics: DiagnosticSet
  • The module into which new IR is inserted.

    Declaration

    Swift

    private var module: Module!
  • A stack of frames describing the variables and allocations of each traversed lexical scope.

    Declaration

    Swift

    private var frames: Emitter.Stack
  • The loops in which control flow has currently entered.

    Declaration

    Swift

    private var loops: Emitter.LoopIDs
  • For each block, the state of frames where the block was first entered.

    Declaration

    Swift

    private var stackOnEntry: [Block.ID : Stack]
  • Where new instructions are inserted.

    Declaration

    Swift

    var insertionPoint: InsertionPoint?
  • The source code associated with instructions to be inserted.

    Declaration

    Swift

    var currentSource: SourceRange
  • The program being lowered.

    Declaration

    Swift

    private var program: TypedProgram { get }
  • ast

    The AST of the program being lowered.

    Declaration

    Swift

    private var ast: AST { get }
  • The basic block in which new instructions are currently inserted.

    Declaration

    Swift

    private var insertionBlock: Block.ID? { get }
  • The function containing the current insertion block.

    Declaration

    Swift

    private var insertionFunction: Function.ID? { get }
  • The scope corresponding to the current insertion block.

    Declaration

    Swift

    private var insertionScope: AnyScopeID? { get }
  • The address of the return value in the current function, if any.

    Declaration

    Swift

    private var returnValue: Operand? { get }
  • Reports the given diagnostic.

    Declaration

    Swift

    private mutating func report(_ d: Diagnostic)
  • Appends a new basic block at the end of self.insertionFunction, defined in s.

    Declaration

    Swift

    private mutating func appendBlock<T>(in s: T) -> Block.ID where T : ScopeID
  • Appends a new basic block at the end of self.insertionFunction, defined in the same scope as self.insertionBlock.

    Declaration

    Swift

    private mutating func appendBlock() -> Block.ID
  • Inserts newInstruction into self.module at the end of self.insertionPoint.

    Requires

    self.insertionPoint refers to the end of a block.

    Declaration

    Swift

    @discardableResult
    private mutating func insert<I>(_ newInstruction: I) -> Operand? where I : Instruction

Top-level entry points

Declarations

Synthetic declarations

Statements

Values

  • Inserts the IR for initializing storage by storing value to it.

    Declaration

    Swift

    private mutating func _emitInitialize(storage: Operand, to value: Operand)
  • Inserts the IR for storing the value of e to a fresh stack allocation, returning the address of this allocation.

    Declaration

    Swift

    @discardableResult
    private mutating func emitStore<T>(value e: T) -> Operand where T : ExprID
  • Inserts the IR for storing the value of e to storage.

    Requires

    storage is the address of some uninitialized memory block capable of storing the value of e.

    Declaration

    Swift

    private mutating func emitStore<T>(value e: T, to storage: Operand) where T : ExprID
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: BufferLiteralExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: CaptureExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: CastExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(upcast e: CastExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(downcast e: CastExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(pointerConversion e: CastExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: ConditionalExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: FloatLiteralExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: FunctionCallExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: IntegerLiteralExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: LambdaExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: NameExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(
      _ e: PragmaLiteralExpr.ID, to storage: Operand
    )
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: RemoteTypeExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: SequenceExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: FoldedSequenceExpr, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func emitStore(_ e: SubscriptCallExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: StringLiteralExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: TupleExpr.ID, to storage: Operand)
  • Inserts the IR for storing the value of e to storage.

    Declaration

    Swift

    private mutating func _emitStore(_ e: TupleMemberExpr.ID, to storage: Operand)
  • Inserts the IR to store the value of e to storage, converting it to the type of storage if necessary.

    The type comparison is performed in the scope of e.

    Declaration

    Swift

    private mutating func emitStore<T: ExprID>(
      convertingIfNecessary e: T,
      to storage: Operand
    )
  • Writes the value of literal to storage.

    Declaration

    Swift

    private mutating func emitStore<T: NumericLiteralExpr>(
      numericLiteral literal: T.ID, to storage: Operand
    )
  • Writes the value of literal to storage, knowing it is a core floating-point instance evaluated by evaluate.

    Declaration

    Swift

    private mutating func _emitStore<T: NumericLiteralExpr>(
      floatingPoint literal: T.ID, to storage: Operand,
      evaluatedBy evaluate: (String) -> FloatingPointConstant
    )
  • Writes the value of literal to storage, knowing it is a core integer instance with given sign and width.

    Declaration

    Swift

    private mutating func _emitStore<T: NumericLiteralExpr>(
      integer literal: T.ID, signed: Bool, bitWidth: Int, to storage: Operand
    )
  • Writes an instance of Hylo.Bool with value v to storage.

    Requires

    storage is the address of uninitialized memory of type Hylo.Int.

    Declaration

    Swift

    private mutating func _emitStore(boolean v: Bool, to storage: Operand)
  • Writes an instance of Hylo.Int with value v to storage.

    Requires

    storage is the address of uninitialized memory of type Hylo.Int.

    Declaration

    Swift

    mutating func _emitStore(int v: Int, to storage: Operand)
  • Writes an instance of Hylo.String with value v to storage.

    Requires

    storage is the address of uninitialized memory of type Hylo.String.

    Declaration

    Swift

    private mutating func _emitStore(utf8 v: String.UTF8View, to storage: Operand)
  • Inserts the IR for storing a, which is an access, to storage.

    Declaration

    Swift

    private mutating func _emitStore(
      access a: Operand, to storage: Operand
    )

    Parameters

    storage

    an address derived from an alloc_stack that is outlived by the provenances of a.

  • Inserts the IR for calling callee on arguments, storing the result to storage.

    Declaration

    Swift

    private mutating func _emitApply(
      _ callee: Callee, to arguments: [Operand],
      writingResultTo storage: Operand
    )
  • Inserts the IR for calling callee on arguments, storing the result to storage.

    Declaration

    Swift

    private mutating func _emitApply(
      _ callee: Operand, to arguments: [Operand],
      writingResultTo storage: Operand
    )
  • Inserts the IR for calling callee on arguments, storing the result to storage.

    Declaration

    Swift

    private mutating func _emitApply(
      _ callee: BundleReference<MethodDecl>, to arguments: [Operand],
      writingResultTo storage: Operand
    )
  • Inserts the IR for given constructor call, which initializes storage r by applying initializer d parameterized by a.

    Declaration

    Swift

    private mutating func _emit(constructorCall call: FunctionCallExpr.ID, initializing s: Operand)

    Parameters

    call

    The syntax of the call.

    s

    The address of uninitialized storage typed by the receiver of call. This storage is borrowed for initialization after evaluating call‘s arguments and before the call.

  • Inserts the IR for given memberwise constructor call, which initializes receiver.

    Declaration

    Swift

    private mutating func _emit(
      memberwiseInitializerCall call: FunctionCallExpr.ID, initializing receiver: Operand
    )

    Parameters

    call

    The syntax of the call.

    receiver

    The address of uninitialized storage typed by the receiver of d. This storage is borrowed for initialization after evaluating call‘s arguments.

  • Inserts the IR preparing the run-time arguments passed to callee in call, lowering arguments and synthesizing default values at syntheticSite.

    Information about argument resolution is read from program.callOperands. Arguments passed explicitly have a corresponding expression in arguments. If default arguments are used, callee is a name expression referring to a callable declaration.

    Declaration

    Swift

    private mutating func _emitArguments(
      to callee: AnyExprID, in call: CallID,
      usingExplicit arguments: [LabeledArgument]
    ) -> [Operand]
  • Inserts the IR for the argument e passed to a parameter of type p

    Declaration

    Swift

    private mutating func _emitArgument(
      _ e: AnyExprID, to p: ParameterType
    ) -> Operand
  • Inserts the IR for argument e passed to an autoclosure parameter of type p.

    Declaration

    Swift

    private mutating func _emitAutoclosureArgument(_ e: AnyExprID, to p: ParameterType) -> Operand
  • Inserts the IR for argument e passed to a parameter of type p

    Declaration

    Swift

    private mutating func _emitPragmaLiteralArgument(
      _ e: PragmaLiteralExpr.ID, to p: ParameterType
    ) -> Operand
  • Inserts the IR generating the operands of the subscript call e.

    Declaration

    Swift

    private mutating func emitOperands(
      _ e: SubscriptCallExpr.ID
    ) -> (callee: BundleReference<SubscriptDecl>, arguments: [Operand])
  • Inserts the IR for infix operand e passed to a parameter of type p.

    Declaration

    Swift

    private mutating func emit(
      infixOperand e: FoldedSequenceExpr, passedTo p: ParameterType
    ) -> Operand
  • Emits the IR of a call to f with given arguments.

    Declaration

    Swift

    private mutating func _emit(apply f: BuiltinFunction, to arguments: [LabeledArgument]) -> Operand
  • Inserts the IR for given callee, which is marked for mutation iff isMutating is true, and returns the callee’s value along with its lifted arguments.

    Lifted arguments correspond to the captures of the callee, which are additional parameters of the lowered function. Bound member functions have a single lifted argument denoting their receiver. Local functions with non-empty environments may have many. Lifted arguments are returned in the same order as the additional parameters in the lowered function.

    Requires

    callee has a lambda type.

    Declaration

    Swift

    private mutating func emitFunctionCallee(
      _ callee: AnyExprID, markedForMutation isMutating: Bool
    ) -> (callee: Callee, captures: [Operand])
  • Inserts the IR for given callee, which is marked for mutation iff isMutating is true, and returns the callee’s value along with its lifted arguments.

    Declaration

    Swift

    private mutating func emitNamedFunctionCallee(
      _ callee: NameExpr.ID, markedForMutation isMutating: Bool
    ) -> (callee: Callee, captures: [Operand])
  • Inserts the IR evaluating callee, which refers to a member function, returning the callee’s value along with the call receiver.

    The callee is marked for mutation iff isMutating is true, in which case the receiver is accessed with a set or inout capability.

    Declaration

    Swift

    private mutating func emitMemberFunctionCallee(
      _ callee: NameExpr.ID, markedForMutation isMutating: Bool
    ) -> (callee: Callee, captures: [Operand])
  • Inserts the IR constructing the callee of a call referring to d, which is a member function of r, returning the callee’s value along with the call receiver.

    The callee is marked for mutation iff isMutating is true, in which case the receiver is accessed with a set or inout capability.

    Declaration

    Swift

    private mutating func _emitMemberFunctionCallee(
      referringTo d: AnyDeclID, memberOf r: Operand, markedForMutation isMutating: Bool,
      specializedBy a: GenericArguments, in scopeOfUse: AnyScopeID
    ) -> (callee: Callee, captures: [Operand])
  • Returns the value of a reference to b, which is bound to receiver.

    If b requests only one capability, the returned callee is a direct function reference to the corresponding variant. Otherwise, it is b unchanged. The returned capture is an access on receiver taking the weakest capability that b requests. This access may be modified later to take a stronger capability if last use analysis allows it.

    Declaration

    Swift

    private mutating func _emitMethodBundleCallee(
      referringTo b: BundleReference<MethodDecl>, on receiver: Operand
    ) -> (callee: Callee, captures: [Operand])
  • Inserts the IR for given callee and returns its value.

    Requires

    callee has a lambda type.

    Declaration

    Swift

    private mutating func emitLambdaCallee(_ callee: AnyExprID) -> Operand
  • Inserts the IR for given callee and returns its value along with its lifted arguments.

    Declaration

    Swift

    private mutating func emitSubscriptCallee(
      _ callee: AnyExprID
    ) -> (callee: BundleReference<SubscriptDecl>, captures: [Operand])
  • Inserts the IR for given callee and returns its value along with its lifted arguments.

    Declaration

    Swift

    private mutating func emitNamedSubscriptCallee(
      _ callee: NameExpr.ID
    ) -> (callee: BundleReference<SubscriptDecl>, captures: [Operand])
  • Inserts the IR evaluating callee, which refers to a member subscript, returning the callee’s value along with the call receiver.

    Declaration

    Swift

    private mutating func emitMemberSubscriptCallee(
      _ callee: NameExpr.ID
    ) -> (callee: BundleReference<SubscriptDecl>, captures: [Operand])
  • Returns (success: a, failure: b) where a is the basic block reached if all items in condition hold and b is the basic block reached otherwise, creating new basic blocks in scope.

    Declaration

    Swift

    private mutating func emitTest(
      condition: [ConditionItem], in scope: AnyScopeID
    ) -> (success: Block.ID, failure: Block.ID)
  • If d declares stored bindings, inserts the IR for allocating their storage and returns a the address of that storage. Otherwise, returns nil.

    Declaration

    Swift

    private mutating func emitAllocation(binding d: BindingDecl.ID) -> Operand?
  • Returns a basic block in which the names in d have been declared and initialized.

    This method emits IR to:

    • evaluate the d‘s initializer as value v,
    • check whether the value in v is an instance of d’s type;
    • if it isn’t, jump to failure;
    • if it is, jump to a new basic block and define and initialize the bindings declared in d.

    If d has a consuming introducer (e.g., var), the value of d’s initializer is moved to storage, which denotes a memory location with d’s type. Otherwise, storage is nil and the bindings in d are defined as new projections. In either case, the emitter’s context is is updated to associate each binding to its value.

    The return value of the method is a basic block, defined in scope. If v has the same type as d, the narrowing is irrefutable and failure is unreachable in the generated IR.

    Declaration

    Swift

    private mutating func emitConditionalNarrowing(
      _ d: BindingDecl.ID,
      movingConsumedValuesTo storage: Operand?,
      branchingOnFailureTo failure: Block.ID,
      in scope: AnyScopeID
    ) -> Block.ID
  • Returns a basic block in which the names in lhs have been declared and initialized.

    Declaration

    Swift

    private mutating func emitUnionNarrowing(
      from rhs: Operand, to lhs: BindingPattern.ID, typed lhsType: AnyType,
      movingConsumedValuesTo storage: Operand?,
      branchingOnFailureTo failure: Block.ID,
      in scope: AnyScopeID
    ) -> Block.ID

    Parameters

    rhs

    A union container of a type that includes lhsType.

    storage

    For a consuming narrowing, the storage of the bindings declared in lhs.

    failure

    The basic block to which control flow jumps if the narrowing fails.

    scope

    The scope in which the new basic block is introducked.

  • Inserts the IR for branch condition e.

    Requires

    e.type is Hylo.Bool

    Declaration

    Swift

    private mutating func emit(branchCondition e: AnyExprID) -> Operand
  • Inserts the IR for extracting the built-in value stored in an instance of Hylo.Bool.

    Declaration

    Swift

    private mutating func _emitLoadBuiltinBool(_ wrapper: Operand) -> Operand
  • If s has a remote type, returns the result of an instruction exposing the captured access; otherwise, returns s.

    Declaration

    Swift

    private mutating func _unwrapCapture(_ s: Operand) -> Operand
  • Inserts the IR for coercing source to an address of type target.

    source is returned unchanged if it stores an instance of target. Otherwise, the IR producing an address of type target is inserted, consuming source if necessary.

    Declaration

    Swift

    private mutating func _emitCoerce(_ source: Operand, to target: AnyType) -> Operand
  • Inserts the IR for coercing source to an address of type target.

    Requires

    target is canonical.

    Declaration

    Swift

    private mutating func _emitCoerce(
      _ source: Operand, to target: ExistentialType
    ) -> Operand
  • Inserts the IR for coercing source to an address of type target.

    Requires

    target is canonical.

    Declaration

    Swift

    private mutating func _emitCoerce(
      _ source: Operand, to target: ArrowType
    ) -> Operand
  • Inserts the IR for coercing source to an address of type target.

    Requires

    target is canonical.

    Declaration

    Swift

    private mutating func _emitCoerce(
      _ source: Operand, to target: UnionType
    ) -> Operand
  • Traps on this execution path because of un unexpected coercion from lhs to rhs.

    Declaration

    Swift

    private func unexpectedCoercion(
      from lhs: AnyType, to rhs: AnyType, file: StaticString = #filePath, line: UInt = #line
    ) -> Never
  • Inserts the IR for converting foreign to a value of type ir.

    Declaration

    Swift

    private mutating func _emitConvert(foreign: Operand, to ir: AnyType) -> Operand
  • Appends the IR to convert o to a FFI argument.

    The returned operand is the result of a load instruction.

    Declaration

    Swift

    private mutating func _emitConvertToForeign(_ o: Operand) -> Operand
  • Returns an existential container of type t wrappring witness.

    Declaration

    Swift

    private mutating func _emitExistential(
      _ t: ExistentialType, wrapping witness: Operand
    ) -> Operand

l-values

Move

  • Replaces i, which is a move instruction with move-assignment of semantics == .inout or move-initialization if semantics == .set, returning the identity of the first instruction taking the place of i.

    After the call, insertionPoint set to nil.

    Declaration

    Swift

    mutating func replaceMove(_ i: InstructionID, with semantics: AccessEffect) -> InstructionID
  • Inserts IR for move-initializing/assigning storage with value.

    The value of semantics defines the type of move to emit:

    • .set emits move-initialization.
    • .inout emits move-assignment.
    • [.inout, .set] emits a move instruction that will is later replaced during definite initialization analysis by either move-assignment if storage is found initialized or by move-initialization otherwise.

    Declaration

    Swift

    private mutating func _emitMove(
      _ semantics: AccessEffectSet, _ value: Operand, to storage: Operand
    )
  • Inserts IR for move-initializing/assigning storage with built-in value.

    Declaration

    Swift

    private mutating func _emitMoveBuiltIn(_ value: Operand, to storage: Operand)
  • Inserts IR for move-initializing/assigning storage with value using movable to locate the implementations of these operations.

    The value of semantics defines the type of move to emit:

    • .set emits move-initialization.
    • .inout emits move-assignment.

    Requires

    storage does not have a built-in type.

    Declaration

    Swift

    private mutating func _emitMove(
      _ semantics: AccessEffect, _ value: Operand, to storage: Operand,
      withMovableConformance movable: FrontEnd.Conformance
    )

Copy

Deinitialization

  • If storage is deinitializable in self.insertionScope, inserts the IR for deinitializing it, or reports a diagnostic otherwise.

    Let T be the type of storage, storage is deinitializable iff T has a deinitializer exposed to self.insertionScope.

    Declaration

    Swift

    mutating func _emitDeinit(_ storage: Operand)
  • Inserts the IR for deinitializing storage using the Deinitializable conformance c.

    Declaration

    Swift

    private mutating func _emitDeinit(
      _ storage: Operand, via c: FrontEnd.Conformance
    )
  • If storage is deinitializable in self.insertionScope, inserts the IR for deinitializing it; reports a diagnostic for each part that isn’t deinitializable otherwise.

    Declaration

    Swift

    mutating func _emitDeinitParts(of storage: Operand)
  • If storage, which stores a record, is deinitializable in self.insertionScope, inserts the IR for deinitializing it; reports a diagnostic for each part that isn’t deinitializable otherwise.

    Requires

    the type of storage has a record layout.

    Declaration

    Swift

    private mutating func _emitDeinitRecordParts(of storage: Operand)
  • If storage, which stores a union, is deinitializable in self.insertionScope, inserts the IR for deinitializing it; reports a diagnostic for each part that isn’t deinitializable otherwise.

    Requires

    the type of storage is a union.

    Declaration

    Swift

    private mutating func _emitDeinitUnionPayload(of storage: Operand)
  • If storage, which stores a union container holding a payload, is deinitializable in self.insertionScope, inserts the IR for deinitializing it; reports a diagnostic for each part that isn’t deinitializable otherwise.

    Declaration

    Swift

    private mutating func _emitDeinitUnionPayload(of storage: Operand, containing payload: AnyType)

Equality

Helpers