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:
Parameter | C++ type | Description |
---|---|---|
valueType | Type |
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:
Operand | Description |
---|---|
input | A secret value |
Results:
Result | Description |
---|---|
output | A 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:
Operand | Description |
---|---|
cleartext | any type |
Results:
Result | Description |
---|---|
output | A 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:
Operand | Description |
---|---|
inputs | variadic of any type |
Results:
Result | Description |
---|---|
results | variadic 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:
Operand | Description |
---|---|
input | A secret value |
Results:
Result | Description |
---|---|
cleartext | any 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:
Operand | Description |
---|---|
inputs | variadic 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:
Operand | Description |
---|---|
values | variadic of any type |