Secret

‘secret’ Dialect

Secret is a dialect for computations that operate on encrypted data.

Secret is intended to serve as a scheme-agnostic front-end for the HEIR ecosystem of dialects. It is supposed to be fully interoperable with the rest of MLIR via secret.generic, while lower-level HEIR dialects would have custom types for arithmetic on secret integers of various bit widths.

Secret types

SecretType

A secret value

Syntax:

!secret.secret<
  Type   # valueType
>

A generic wrapper around another MLIR type, representing an encrypted value but not specifying the manner of encryption. This is useful in HEIR because the compiler may choose various details of the FHE scheme based on the properties of the input program, the backend target hardware, and cost models of the various passes.

Parameters:

ParameterC++ typeDescription
valueTypeType

Secret ops

secret.cast (heir::secret::CastOp)

A placeholder cast from one secret type to another

Syntax:

operation ::= `secret.cast` $input attr-dict `:` qualified(type($input)) `to` qualified(type($output))

A cast operation represents a type cast from one secret type to another, that is used to enable the intermixing of various equivalent secret types before a lower-level FHE scheme has been chosen.

For example, secret.cast can be used to convert a secret<i8> to a secret<tensor<8xi1>> as a compatibility layer between boolean and non-boolean parts of a program. The pass that later lowers the IR to specific FHE schemes would need to replace these casts with appropriate scheme-specific operations, and it is left to those later passes to determine which casts are considered valid.

Example:

%result = secret.cast %0 : !secret.secret<i8> to !secret.secret<tensor<8xi1>>
%result2 = secret.cast %0 : !secret.secret<i8> to !secret.secret<tensor<2xi4>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
inputA secret value

Results:

ResultDescription
outputA secret value

secret.conceal (heir::secret::ConcealOp)

Convert a non-secret value into a secret

Syntax:

operation ::= `secret.conceal` $cleartext attr-dict `:` type($cleartext) `->` type($output)

Convert a value to a secret containing the same value.

This op represents a scheme-agnostic encryption operation, as well as a “trivial encryption” operation which is needed for some FHE schemes. This op is also useful for type materialization in the dialect conversion framework.

Examples:

%Y = secret.conceal %value : i32 -> !secret.secret<i32>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
cleartextany type

Results:

ResultDescription
outputA secret value

secret.generic (heir::secret::GenericOp)

Lift a plaintext computation to operate on secrets.

secret.generic lifts a plaintext computation to operate on one or more secrets. The lifted computation is represented as a region containing a single block terminated by secret.yield. The arguments of the secret.generic may include one or more !secret.secret types. The arguments of the block in the op’s body correspond to the underlying plaintext types of the secrets.

secret.generic is not isolated from above, so you may directly reference values in the enclosing scope. This is required to support using secret.generic inside of ops with AffineScope, while having the body of the generic use the induction variables defined by the affine scope.

Basic examples:

Add two secret integers together

%Z = secret.generic ins(%X, %Y : !secret.secret<i32>, !secret.secret<i32>) {
  ^bb0(%x: i32, %y: i32):
    %z = arith.addi %x, %y: i32
    secret.yield %z : i32
  } -> (!secret.secret<i32>)

Add a secret value with a plaintext value. I.e., not all arguments to the op need be secret.

%Z = secret.generic ins(%X, %Y : i32, !secret.secret<i32>) {
  ^bb0(%x: i32, %y: i32):
    %z = arith.addi %x, %y: i32
    secret.yield %z : i32
  } -> (!secret.secret<i32>)

The same as above, but the plaintext op is not passed through the basic block.

%y = arith.constant 7: i32
%Z = secret.generic ins(%X : !secret.secret<i32>) {
  ^bb0(%x: i32):
    %z = arith.addi %x, %y: i32
    secret.yield %z : i32
  } -> (!secret.secret<i32>)

Traits: SingleBlockImplicitTerminator<YieldOp>, SingleBlock

Operands:

OperandDescription
inputsvariadic of any type

Results:

ResultDescription
resultsvariadic of any type

secret.reveal (heir::secret::RevealOp)

Convert a secret value into a non-secret

Syntax:

operation ::= `secret.reveal` $input attr-dict `:` type($input) `->` type($cleartext)

Convert a secret into a non-secret containing the same value.

This op represents a scheme-agnostic decryption operation. This op is also useful for target materialization in the dialect conversion framework.

Examples:

%Y = secret.reveal %secret_value : !secret.secret<i32> -> i32

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
inputA secret value

Results:

ResultDescription
cleartextany type

secret.separator (heir::secret::SeparatorOp)

Convert a non-secret value into a secret

Syntax:

operation ::= `secret.separator` attr-dict ($inputs^ `:` type($inputs))?

This operation is used as a separation boundary between logical subunits of the module. This is used in conjunction with --secret-distribute-generic=distribute-through=secret.separator to break a generic around these separators and allow for optimization passses to analyze and optimize the sub-units locally.

In order to allow bufferization of modules with this operation, we must register a (bogus) memory effect that also prevents this operation from being trivially dead during operation folding.

This operation also accepts operands, which act as boundaries between the logical units. This enforces separation of memref and affine optimizations between the subunits, preventing optimizations from removing the operand and combining the two separated regions. The operand can be thought of as an return value of the logical subunit.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands:

OperandDescription
inputsvariadic of any type

secret.yield (heir::secret::YieldOp)

Secret yield operation

secret.yield is a special terminator operation for blocks inside regions in secret generic ops. It returns the cleartext value of the corresponding private computation to the immediately enclosing secret generic op.

Traits: AlwaysSpeculatableImplTrait, HasParent<GenericOp>, ReturnLike, Terminator

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
valuesvariadic of any type