Skip to documentation
SLOP

tiny.smt.choir.dialect.SmtDialect

Reference tiny.smt choir dialect SmtDialect

Defined in choir.dialect.

The SMT dialect: its name, its specification, one struct per operation, and the functions that build and read its types.

API (117)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callschoir.dialectSmtDialect
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/smt/src/choir/dialect.zig:65

zig
/// The SMT dialect: its name, its specification, one struct per operation, and the functions that/// build and read its types. A caller names every operation, type getter and width reader of the/// dialect through this struct, and names the dialect by its `name` when it asks a context to load/// it. Its 42 operation structs are the declarations that end in `Op`. The namespace re-exports it/// as `SmtDialect`.pub const SmtDialect = struct {    /// The dialect's name, `smt`. A caller asks a context for the dialect by this name after    /// registering it. Every operation name and type name of the dialect starts with `smt.`.    /// `registry` records the dialect's loader under this name.    pub const name = "smt";    /// The dialect's specification: its name, its 42 operations, its three type names, and the    /// function that reads the widths of the bit-vector and array types. `loadDialect` and the type    /// getters load the dialect into a context from this value, and a test loads it directly. The    /// list of operations comes from every declaration of `SmtDialect` that carries an operation    /// specification. Loading it a second time into one context returns at once and leaves the    /// context as it was.    pub const spec = ir.dialects.DialectSpec{        .name = name,        .operations = ir.dialects.operations(@This()),        .types = &.{            ir.dialects.typeName(type_names.boolean),            ir.dialects.typeName(type_names.bv),            ir.dialects.typeName(type_names.array),        },        .type_interface_fallbacks = &.{            .{ .id = interfaces.TypeParamInterface.id, .fallback = typeParamFallback },        },    };    const same_operands_and_result_type_trait = ir.dialects.trait(ir.traits.SameOperandsAndResultType);    const same_type_operands_trait = ir.dialects.trait(ir.traits.SameTypeOperands);    const operand0_is_bool_constraint = ir.dialects.typeConstraint.exact(0, type_names.boolean);    const operand1_is_bool_constraint = ir.dialects.typeConstraint.exact(1, type_names.boolean);    const result0_is_bool_constraint = ir.dialects.typeConstraint.exact(0, type_names.boolean);    const op_specs = ir.dialects.opSpec.dialect(@This());    const op_templates = ir.dialects.operationTemplate.dialect(@This());    /// The width of a bit-vector type, parsed from the decimal text the type carries. `bitVecWidth`    /// reads a bit-vector type's width through this struct, and a caller reads the width through    /// `bitVecWidth`. The context parses it the first time a caller asks for the type's width and    /// keeps it for the type. The struct holds one field, `width`, the number of bits, at least 1.    /// A type whose text is empty, fails to parse as a decimal number or is 0 gets no payload, so    /// `bitVecWidth` returns `null` for it.    pub const BitVecTypePayload = struct {        width: u32,    };    /// The index width and the element width of an array type, parsed from the text `index:element`    /// the type carries. `arrayShape` returns an array type's two widths in this struct, and a    /// caller reads the widths from it to pick the sort of an array. The context parses it the    /// first time a caller asks for the type's widths and keeps it for the type. An array type maps    /// bit-vectors of the index width to bit-vectors of the element width. A type whose text    /// differs from two decimal numbers joined by one colon, or holds a 0, gets no payload, so    /// `arrayShape` returns `null` for it.    pub const ArrayTypePayload = struct {        /// The width in bits of the bit-vectors that index the array, at least 1.        /// `ArraySelectOp.create` and `ArrayStoreOp.create` require an index of this width. A        /// caller reads it to build the array's sort.        index_width: u32,        /// The width in bits of the bit-vectors the array holds, at least 1. `ArraySelectOp.create`        /// gives its result this width, and `ArrayStoreOp.create` requires a stored value of this        /// width. A caller reads it to build the array's sort.        element_width: u32,    };    const type_param_vtable = interfaces.TypeParamInterface.VTable{        .parse = parseTypeParams,    };    /// A named variable of a given type: the operation `smt.var`, with one result and the    /// variable's name in its `name` attribute. A checker declares each free variable of its    /// formula, such as a parameter or an input of the program it checks, as one of these    /// operations. The struct holds the operation in `op`, creates it with `create`, and reads it    /// with `getResult` and `getName`.    pub const VarOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the variable through the getters.        op: *ir.Operation,        /// The specification of `smt.var`: its full name, its one attribute key `name`, and its        /// side effects declared as unknown. The dialect's specification gathers this value, so        /// loading the dialect registers the operation with the context. `SmtDialect.spec` lists it        /// among the dialect's operations.        pub const operation_spec = op_specs.define(.{            .mnemonic = "var",            .attrs = &.{attr_names.name},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.var`. Code that walks operations compares each        /// operation's name with this constant to find the variables. `create` creates operations        /// under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.var` operation named `symbol`, with one result of type `result_type` and        /// the source location `loc`, and returns the struct holding it. A checker calls it once        /// for each parameter or input of the program it checks, with the type of that parameter or        /// input. The context keeps its own copy of `symbol`. Neither the call nor        /// `ir.verifyOperation` checks `result_type`, and a variable may have any type of the        /// context, the dialect's three types included. The new operation belongs to no block until        /// the caller adds `op` to one. The call returns the errors of creating the operation and        /// its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, symbol: []const u8, result_type: ir.Type) !VarOp {            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addTypes(&.{result_type});            const op = try builder.create(state);            try op.setAttr(attr_names.name, try ctx.getStringAttr(symbol));            return .{ .op = op };        }        /// Returns the operation's one result, the variable's value. A caller passes the variable's        /// value to the operations that use the variable.        pub fn getResult(self: *const VarOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the variable's name from its `name` attribute. Code that reads a formula back        /// turns each variable into a named constant of the solver under this name. The slice        /// points into the context's copy of the name. The call returns `null` when the operation        /// lacks a string attribute under `name`.        pub fn getName(self: VarOp) ?[]const u8 {            const attr = self.op.getAttrAs(ir.Attribute.StringAttr, attr_names.name) orelse return null;            return attr.getValue();        }    };    /// A Boolean constant: the operation `smt.bool.const`, with one Boolean result and the value in    /// its `value` attribute. A checker states a fixed truth value, for example an obligation that    /// holds for every input, as one of these operations. The struct holds the operation in `op`,    /// creates it with `create`, and reads it with `getResult` and `getValue`.    pub const BoolConstOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to inspect the        /// operation or add it to a block. A caller that finds an operation named `operation_name`        /// builds the struct around it and reads the constant through `getValue`.        op: *ir.Operation,        /// The specification of `smt.bool.const`: its full name, its one attribute key `value`, and        /// its side effects declared as unknown. The dialect's specification gathers this value, so        /// loading the dialect registers the operation with the context. `SmtDialect.spec` lists it        /// among the dialect's operations.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bool.const",            .attrs = &.{attr_names.value},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bool.const`. Code that walks operations compares        /// each operation's name with this constant to find the Boolean constants. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bool.const` operation holding `value`, with one result of the Boolean        /// type and the source location `loc`, and returns the struct holding it. A checker creates        /// the constant `true` or `false` for an obligation whose answer it knows while it builds        /// the formula. The call builds the Boolean type first, which loads the dialect into `ctx`        /// when unloaded. The new operation belongs to no block until the caller adds `op` to one.        /// The call returns the errors of `getBoolType` and of creating the operation and its        /// attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, value: bool) !BoolConstOp {            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addTypes(&.{try getBoolType(ctx)});            const op = try builder.create(state);            try op.setAttr(attr_names.value, try ctx.getBoolAttr(value));            return .{ .op = op };        }        /// Returns the operation's one result, the constant's value. A caller passes the constant's        /// value to the operations that use it.        pub fn getResult(self: *const BoolConstOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the constant from its `value` attribute. Code that reads a formula back turns        /// each Boolean constant into the solver's `true` or `false`. The call returns `null` when        /// the operation lacks a Boolean attribute under `value`.        pub fn getValue(self: BoolConstOp) ?bool {            const attr = self.op.getAttrAs(ir.Attribute.BoolAttr, attr_names.value) orelse return null;            return attr.getValue();        }    };    /// A bit-vector constant: the operation `smt.bv.const`, with one result of a bit-vector type    /// and the value in its `value` attribute as decimal text. A checker states a fixed bit-vector,    /// such as a literal of the program it checks or a limit to compare against, as one of these    /// operations. The struct holds the operation in `op`, creates it with `create`, and reads it    /// with `getResult` and `getValue`. The width lives in the result's type, and the attribute    /// holds only the value.    pub const BitVecConstOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the constant through `getValue`.        op: *ir.Operation,        /// The specification of `smt.bv.const`: its full name, its one attribute key `value`, and        /// its side effects declared as unknown. The dialect's specification gathers this value, so        /// loading the dialect registers the operation with the context. `SmtDialect.spec` lists it        /// among the dialect's operations.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bv.const",            .attrs = &.{attr_names.value},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bv.const`. Code that walks operations compares each        /// operation's name with this constant to find the bit-vector constants. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bv.const` operation holding `value`, with one result of the bit-vector        /// type of width `width` and the source location `loc`, and returns the struct holding it.        /// A checker creates one constant for each literal of the program it checks, and for limits        /// such as the width of a shifted value. The call writes `value` as decimal text into the        /// `value` attribute. The call builds the bit-vector type first, which loads the dialect        /// into `ctx` when unloaded. The call checks neither that `value` fits in `width` bits nor        /// that `width` is above 0. The new operation belongs to no block until the caller adds        /// `op` to one. The call returns the errors of building the type and of creating the        /// operation and its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, width: u32, value: u128) !BitVecConstOp {            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addTypes(&.{try getBitVecType(ctx, width)});            const op = try builder.create(state);            var buf: [40]u8 = undefined;            try op.setAttr(attr_names.value, try ctx.getStringAttr(try std.fmt.bufPrint(&buf, "{d}", .{value})));            return .{ .op = op };        }        /// Returns the operation's one result, the constant's value. A caller passes the constant's        /// value to the operations that use it.        pub fn getResult(self: *const BitVecConstOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the constant, parsed from the decimal text of its `value` attribute. Code that        /// reads a formula back turns each bit-vector constant into a solver constant of the        /// result's width. The call returns `null` when the operation lacks a string attribute        /// under `value` or its text fails to parse as a decimal number that fits in a `u128`.        pub fn getValue(self: BitVecConstOp) ?u128 {            const attr = self.op.getAttrAs(ir.Attribute.StringAttr, attr_names.value) orelse return null;            return std.fmt.parseInt(u128, attr.getValue(), 10) catch null;        }    };    /// The application of a function to the operation's operands: the operation `smt.apply`, with    /// one result and the function's name in its `name` attribute. A checker states facts about a    /// function it leaves undefined by applying the function by name to its arguments. The dialect    /// declares no function: the operand types are the argument types, and the result's type is the    /// function's result type. The struct holds the operation in `op`, creates it with `create`,    /// and reads it with `getResult` and `getName`.    pub const ApplyOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the function's name through `getName`.        op: *ir.Operation,        /// The specification of `smt.apply`: its full name, its one attribute key `name`, and its        /// side effects declared as unknown. The dialect's specification gathers this value, so        /// loading the dialect registers the operation with the context. `SmtDialect.spec` lists it        /// among the dialect's operations.        pub const operation_spec = op_specs.define(.{            .mnemonic = "apply",            .attrs = &.{attr_names.name},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.apply`. Code that walks operations compares each        /// operation's name with this constant to find the function applications. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.apply` operation that applies the function `function_name` to        /// `operands`, with one result of type `result_type` and the source location `loc`, and        /// returns the struct holding it. A checker applies a function it leaves undefined to the        /// values it passes as arguments. The operation copies `operands`, and the context keeps        /// its own copy of `function_name`. The call checks no types, and `operands` may be empty.        /// The new operation belongs to no block until the caller adds `op` to one. The call        /// returns the errors of creating the operation and its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, function_name: []const u8, operands: []const *ir.Value, result_type: ir.Type) !ApplyOp {            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(operands);            state.addTypes(&.{result_type});            const op = try builder.create(state);            try op.setAttr(attr_names.name, try ctx.getStringAttr(function_name));            return .{ .op = op };        }        /// Returns the operation's one result, the value of the application. A caller passes the        /// application's value to the operations that use it.        pub fn getResult(self: *const ApplyOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the name of the applied function from the `name` attribute. Code that reads a        /// formula back declares an uninterpreted function under this name. The slice points into        /// the context's copy of the name. The call returns `null` when the operation lacks a        /// string attribute under `name`.        pub fn getName(self: ApplyOp) ?[]const u8 {            const attr = self.op.getAttrAs(ir.Attribute.StringAttr, attr_names.name) orelse return null;            return attr.getValue();        }    };    /// A Boolean formula that must hold: the operation `smt.assert`, with one Boolean operand, no    /// result, and an optional name and group label in its `name` and `group` attributes. A checker    /// states each property it must check as one assertion, names it after the check, and gives it    /// a group label so that a report can sort the answers. The struct holds the operation in `op`,    /// creates it with `create`, `createNamed` or `createGrouped`, and reads it with    /// `getAssertion`, `getName` and `getGroup`. A caller keeps a list of the assertion operations,    /// and code that reads the formula back walks that list.    pub const AssertOp = struct {        /// The operation the struct holds. `create` sets it, and a caller appends it to its list of        /// assertions. A caller that reads its list back builds the struct around each operation        /// named `operation_name`.        op: *ir.Operation,        /// The specification of `smt.assert`: its full name, its attribute keys `name` and `group`,        /// its side effects declared as unknown, and the rule that its operand is Boolean.        /// `SmtDialect.spec` lists it among the dialect's operations, so loading the dialect        /// registers the operation with the context, and `ir.verifyOperation` checks the operand        /// rule.        pub const operation_spec = op_specs.define(.{            .mnemonic = "assert",            .attrs = &.{ attr_names.name, attr_names.group },            .interfaces = &.{unknown_effects},            .operand_types = &.{operand0_is_bool_constraint},        });        /// The full name of the operation, `smt.assert`. Code that reads a list of assertions back        /// checks each operation's name against this constant before it builds the struct. `create`        /// creates operations under this name. A caller refuses an operation of another name in its        /// list of assertions.        pub const operation_name = operation_spec.name;        /// Creates an `smt.assert` operation on `assertion` at the source location `loc`, leaves        /// its name and group label unset, and returns the struct holding it. A caller asserts a        /// background fact that its reports leave unnamed, such as an equation that fixes a        /// variable to a witness value. The call checks no type, and `ir.verifyOperation` checks        /// that `assertion` is Boolean. The new operation belongs to no block until the caller adds        /// `op` to one. The call returns the errors of creating the operation in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, assertion: *ir.Value) !AssertOp {            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(&.{assertion});            const op = try builder.create(state);            return .{ .op = op };        }        /// Creates an `smt.assert` operation on `assertion` with the name `assertion_name`, and        /// leaves its group label unset. A caller asserts a formula under a name, so a report can        /// say which assertion an answer concerns. The call calls `create` and then sets the `name`        /// attribute. The context keeps its own copy of `assertion_name`. The call returns the        /// errors of `create` and of setting the attribute in `ctx`.        pub fn createNamed(ctx: *ir.Context, loc: ir.Location, assertion_name: []const u8, assertion: *ir.Value) !AssertOp {            const op = try create(ctx, loc, assertion);            try op.op.setAttr(attr_names.name, try ctx.getStringAttr(assertion_name));            return op;        }        /// Creates an `smt.assert` operation on `assertion` with the name `assertion_name` and the        /// group label `group`. A checker asserts each property under a name and a group label such        /// as `bounds` or `overflow`, so a report can sort the answers by kind. The call calls        /// `createNamed` and then sets the `group` attribute. The context keeps its own copies of        /// `assertion_name` and `group`. The call returns the errors of `createNamed` and of        /// setting the attribute in `ctx`.        pub fn createGrouped(ctx: *ir.Context, loc: ir.Location, assertion_name: []const u8, group: []const u8, assertion: *ir.Value) !AssertOp {            const op = try createNamed(ctx, loc, assertion_name, assertion);            try op.op.setAttr(attr_names.group, try ctx.getStringAttr(group));            return op;        }        /// Returns the operation's one operand, the asserted formula, so code that reads the        /// assertions back turns each asserted formula into a solver term. On an operation with no        /// operand, the call panics in Debug and ReleaseSafe builds, and its behavior is undefined        /// in ReleaseFast and ReleaseSmall builds.        pub fn getAssertion(self: AssertOp) *ir.Value {            return self.op.getOperand(0).?;        }        /// Returns the assertion's name from its `name` attribute, so code that reports an answer        /// names the assertion it concerns. The call returns `null` whenever the operation lacks a        /// string attribute under `name`, such as for an assertion made by `create`.        pub fn getName(self: AssertOp) ?[]const u8 {            const attr = self.op.getAttrAs(ir.Attribute.StringAttr, attr_names.name) orelse return null;            return attr.getValue();        }        /// Returns the assertion's group label from its `group` attribute, so code that reports        /// answers sorts them by this label. The call returns `null` whenever the operation lacks a        /// string attribute under `group`, such as for an assertion made by `create` or        /// `createNamed`.        pub fn getGroup(self: AssertOp) ?[]const u8 {            const attr = self.op.getAttrAs(ir.Attribute.StringAttr, attr_names.group) orelse return null;            return attr.getValue();        }    };    /// The negation of a Boolean formula, as the operation `smt.not`. A checker negates a formula,    /// for example to turn a condition that must hold into a search for an input that breaks it.    /// `create(ctx, loc, input)` creates the operation with one result of the Boolean type.    /// `getResult` returns the result, and `getInput` returns the operand. `operation_name` holds    /// `smt.not`, and the struct holds the operation in `op`. `create` checks no type, and    /// `ir.verifyOperation` checks the rule the dialect registered: the operand and the result are    /// Boolean. `create` returns the errors of building the Boolean type and of creating the    /// operation in `ctx`.    pub const NotOp: type = op_templates.unaryFixedResult(        "not",        boolUnaryOptions(.{}),        getBoolType,    );    /// The conjunction of two Boolean formulas, as the operation `smt.and`. A checker joins two    /// conditions that must both hold. `create(ctx, loc, lhs, rhs)` creates the operation with one    /// result of the Boolean type. `getResult` returns the result, and `getLhs` and `getRhs` return    /// the operands. `operation_name` holds `smt.and`, and the struct holds the operation in `op`.    /// `create` checks no types, and `ir.verifyOperation` checks the rule the dialect registered:    /// both operands and the result are Boolean. The context records the operation as commutative.    /// `create` returns the errors of building the Boolean type and of creating the operation in    /// `ctx`.    pub const AndOp: type = op_templates.binaryFixedResult(        "and",        boolBinaryOptions(commutative_op_traits),        getBoolType,    );    /// The disjunction of two Boolean formulas, as the operation `smt.or`. A checker joins two    /// conditions of which one must hold. `create(ctx, loc, lhs, rhs)` creates the operation with    /// one result of the Boolean type. `getResult` returns the result, and `getLhs` and `getRhs`    /// return the operands. `operation_name` holds `smt.or`, and the struct holds the operation in    /// `op`. `create` checks no types, and `ir.verifyOperation` checks the rule the dialect    /// registered: both operands and the result are Boolean. The context records the operation as    /// commutative. `create` returns the errors of building the Boolean type and of creating the    /// operation in `ctx`.    pub const OrOp: type = op_templates.binaryFixedResult(        "or",        boolBinaryOptions(commutative_op_traits),        getBoolType,    );    /// The implication from the first Boolean formula to the second, as the operation    /// `smt.implies`. A checker states that one condition implies another, for example that a    /// precondition implies a bound. `create(ctx, loc, lhs, rhs)` creates the operation with one    /// result of the Boolean type. `getResult` returns the result, and `getLhs` and `getRhs` return    /// the operands. `operation_name` holds `smt.implies`, and the struct holds the operation in    /// `op`. `create` checks no types, and `ir.verifyOperation` checks the rule the dialect    /// registered: both operands and the result are Boolean. `create` returns the errors of    /// building the Boolean type and of creating the operation in `ctx`.    pub const ImpliesOp: type = op_templates.binaryFixedResult(        "implies",        boolBinaryOptions(.{}),        getBoolType,    );    /// The equality of two values of one type, as the operation `smt.eq`. A checker states that two    /// values are equal, for example that a variable holds a given constant or that a result is    /// zero. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the Boolean    /// type. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.eq`, and the struct holds the operation in `op`. The operands    /// may be Boolean values, bit-vectors or arrays. `create` checks no types, and    /// `ir.verifyOperation` checks the rule the dialect registered: the two operands share one type    /// and the result is Boolean. The context records the operation as commutative. `create`    /// returns the errors of building the Boolean type and of creating the operation in `ctx`.    pub const EqOp: type = op_templates.binaryFixedResult(        "eq",        sameOperandBoolResultOptions(commutative_op_traits),        getBoolType,    );    /// The element of an array at an index, as the operation `smt.array.select`, with the array and    /// the index as operands and one result of the element type. A checker reads a memory modeled    /// as an array at an index. The struct holds the operation in `op`, creates it with `create`,    /// and returns the result with `getResult`.    pub const ArraySelectOp = struct {        op: *ir.Operation,        /// The specification of `smt.array.select`: its full name and its side effects declared as        /// unknown. `SmtDialect.spec` lists it among the dialect's operations, so loading the        /// dialect registers the operation with the context. The specification registers no type        /// rule, so `create` alone checks the operand types.        pub const operation_spec = op_specs.define(.{            .mnemonic = "array.select",            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.array.select`. Code that walks operations compares        /// each operation's name with this constant to find the array reads. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.array.select` operation that reads `array` at `index` at the source        /// location `loc`, and returns the struct holding it. A checker reads an element of a        /// modeled memory and gets a value of the element's width. The result is a bit-vector of        /// the array's element width. The call returns `error.UnsupportedArrayType` when        /// `arrayShape` returns `null` for the type of `array`, `error.UnsupportedBitVectorType`        /// when `bitVecWidth` returns `null` for the type of `index`, and        /// `error.InvalidArrayIndexType` when the index width differs from the array's index width.        /// The new operation belongs to no block until the caller adds `op` to one. The call also        /// returns the errors of building the element type and of creating the operation in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, array: *ir.Value, index: *ir.Value) !ArraySelectOp {            const shape = arrayShape(ctx, array.type) orelse return error.UnsupportedArrayType;            const index_width = bitVecWidth(ctx, index.type) orelse return error.UnsupportedBitVectorType;            if (index_width != shape.index_width) return error.InvalidArrayIndexType;            return .{ .op = try binary(ctx, loc, operation_name, array, index, try getBitVecType(ctx, shape.element_width)) };        }        /// Returns the operation's one result, the element read, so a caller passes it to the        /// operations that use it.        pub fn getResult(self: *const ArraySelectOp) *ir.Value {            return self.op.getResult(0).?;        }    };    /// The array equal to a given array except that one index holds a new value, as the operation    /// `smt.array.store`, with the array, the index and the value as operands and one result of the    /// array's type. A checker writes an element into a memory modeled as an array and gets the new    /// array. The struct holds the operation in `op`, creates it with `create`, and returns the    /// result with `getResult`.    pub const ArrayStoreOp = struct {        op: *ir.Operation,        /// The specification of `smt.array.store`: its full name and its side effects declared as        /// unknown. `SmtDialect.spec` lists it among the dialect's operations, so loading the        /// dialect registers the operation with the context. The specification registers no type        /// rule, so `create` alone checks the operand types.        pub const operation_spec = op_specs.define(.{            .mnemonic = "array.store",            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.array.store`. Code that walks operations compares        /// each operation's name with this constant to find the array writes. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.array.store` operation that writes `value` into `array` at `index` at        /// the source location `loc`, and returns the struct holding it. A checker records a write        /// into a modeled memory as a new array value. The result has the type of `array`. The call        /// returns `error.UnsupportedArrayType` when `arrayShape` returns `null` for the type of        /// `array`, and `error.UnsupportedBitVectorType` when `bitVecWidth` returns `null` for the        /// type of `index` or of `value`. The call returns `error.InvalidArrayIndexType` when the        /// index width differs from the array's index width, and `error.InvalidArrayElementType`        /// when the value's width differs from the array's element width. The new operation belongs        /// to no block until the caller adds `op` to one. The call also returns the errors of        /// creating the operation in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, array: *ir.Value, index: *ir.Value, value: *ir.Value) !ArrayStoreOp {            const shape = arrayShape(ctx, array.type) orelse return error.UnsupportedArrayType;            const index_width = bitVecWidth(ctx, index.type) orelse return error.UnsupportedBitVectorType;            const element_width = bitVecWidth(ctx, value.type) orelse return error.UnsupportedBitVectorType;            if (index_width != shape.index_width) return error.InvalidArrayIndexType;            if (element_width != shape.element_width) return error.InvalidArrayElementType;            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(&.{ array, index, value });            state.addTypes(&.{array.type});            const op = try builder.create(state);            return .{ .op = op };        }        /// Returns the operation's one result, the array after the write, so a caller passes it to        /// the operations that read or write it next.        pub fn getResult(self: *const ArrayStoreOp) *ir.Value {            return self.op.getResult(0).?;        }    };    /// The sum of two bit-vectors of one width, modulo 2 to the power of the width, as the    /// operation `smt.bvadd`. A checker lowers a program's addition into this operation.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of `lhs`.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvadd`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. The context records the operation as commutative.    /// `create` returns the errors of creating the operation in `ctx`.    pub const BvAddOp: type = op_templates.binarySameType(        "bvadd",        sameOperandsResultOptions(commutative_op_traits),    );    /// The difference of two bit-vectors of one width, the first minus the second, modulo 2 to the    /// power of the width, as the operation `smt.bvsub`. A checker lowers a program's subtraction    /// into this operation. `create(ctx, loc, lhs, rhs)` creates the operation with one result of    /// the type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the    /// operands. `operation_name` holds `smt.bvsub`, and the struct holds the operation in `op`.    /// `create` checks no types, and `ir.verifyOperation` checks the rule the dialect registered:    /// both operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvSubOp: type = op_templates.binarySameType("bvsub", sameOperandsResultOptions(.{}));    /// The product of two bit-vectors of one width, modulo 2 to the power of the width, as the    /// operation `smt.bvmul`. A checker lowers a program's multiplication into this operation.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of `lhs`.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvmul`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. The context records the operation as commutative.    /// `create` returns the errors of creating the operation in `ctx`.    pub const BvMulOp: type = op_templates.binarySameType(        "bvmul",        sameOperandsResultOptions(commutative_op_traits),    );    /// The bitwise complement of a bit-vector, as the operation `smt.bvnot`. A checker complements    /// every bit of a bit-vector. `create(ctx, loc, input)` creates the operation with one result    /// of the type of `input`. `getResult` returns the result, and `getInput` returns the operand.    /// `operation_name` holds `smt.bvnot`, and the struct holds the operation in `op`. `create`    /// checks no type, and `ir.verifyOperation` checks the rule the dialect registered: the operand    /// and the result share one type. `create` returns the errors of creating the operation in    /// `ctx`.    pub const BvNotOp: type = op_templates.unarySameType("bvnot", sameOperandsResultOptions(.{}));    /// The bitwise and of two bit-vectors of one width, as the operation `smt.bvand`. A checker    /// masks the bits of a bit-vector. `create(ctx, loc, lhs, rhs)` creates the operation with one    /// result of the type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs`    /// return the operands. `operation_name` holds `smt.bvand`, and the struct holds the operation    /// in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule the dialect    /// registered: both operands and the result share one type. The context records the operation    /// as commutative. `create` returns the errors of creating the operation in `ctx`.    pub const BvAndOp: type = op_templates.binarySameType(        "bvand",        sameOperandsResultOptions(commutative_op_traits),    );    /// The bitwise or of two bit-vectors of one width, as the operation `smt.bvor`. A checker sets    /// bits of a bit-vector. `create(ctx, loc, lhs, rhs)` creates the operation with one result of    /// the type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the    /// operands. `operation_name` holds `smt.bvor`, and the struct holds the operation in `op`.    /// `create` checks no types, and `ir.verifyOperation` checks the rule the dialect registered:    /// both operands and the result share one type. The context records the operation as    /// commutative. `create` returns the errors of creating the operation in `ctx`.    pub const BvOrOp: type = op_templates.binarySameType(        "bvor",        sameOperandsResultOptions(commutative_op_traits),    );    /// The bitwise exclusive or of two bit-vectors of one width, as the operation `smt.bvxor`. A    /// checker flips bits of a bit-vector. `create(ctx, loc, lhs, rhs)` creates the operation with    /// one result of the type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs`    /// return the operands. `operation_name` holds `smt.bvxor`, and the struct holds the operation    /// in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule the dialect    /// registered: both operands and the result share one type. The context records the operation    /// as commutative. `create` returns the errors of creating the operation in `ctx`.    pub const BvXorOp: type = op_templates.binarySameType(        "bvxor",        sameOperandsResultOptions(commutative_op_traits),    );    /// The first bit-vector shifted toward its most significant bit by the value of the second, as    /// the operation `smt.bvshl`. A checker lowers a program's left shift into this operation. The    /// shift amount is a bit-vector of the shifted value's width. `create(ctx, loc, lhs, rhs)`    /// creates the operation with one result of the type of `lhs`. `getResult` returns the result,    /// and `getLhs` and `getRhs` return the operands. `operation_name` holds `smt.bvshl`, and the    /// struct holds the operation in `op`. `create` checks no types, and `ir.verifyOperation`    /// checks the rule the dialect registered: both operands and the result share one type.    /// `create` returns the errors of creating the operation in `ctx`.    pub const BvShlOp: type = op_templates.binarySameType("bvshl", sameOperandsResultOptions(.{}));    /// The first bit-vector shifted toward its least significant bit by the value of the second,    /// with zeros shifted in, as the operation `smt.bvlshr`. A checker lowers a program's unsigned    /// right shift into this operation. The shift amount is a bit-vector of the shifted value's    /// width. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of    /// `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvlshr`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvLshrOp: type = op_templates.binarySameType(        "bvlshr",        sameOperandsResultOptions(.{}),    );    /// The first bit-vector shifted toward its least significant bit by the value of the second,    /// with copies of its sign bit shifted in, as the operation `smt.bvashr`. A checker lowers a    /// program's signed right shift into this operation. The shift amount is a bit-vector of the    /// shifted value's width. `create(ctx, loc, lhs, rhs)` creates the operation with one result of    /// the type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the    /// operands. `operation_name` holds `smt.bvashr`, and the struct holds the operation in `op`.    /// `create` checks no types, and `ir.verifyOperation` checks the rule the dialect registered:    /// both operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvAshrOp: type = op_templates.binarySameType(        "bvashr",        sameOperandsResultOptions(.{}),    );    /// The rotation of a bit-vector toward its most significant bit by a fixed number of positions,    /// as the operation `smt.bvrotl`, with the number of positions in its `amount` attribute, for a    /// checker that rotates a bit-vector left. The result has the operand's type. The struct holds    /// the operation in `op`, creates it with `create`, and reads it with `getResult` and    /// `getAmount`.    pub const BvRotlOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the rotation amount through `getAmount`.        op: *ir.Operation,        /// The specification of `smt.bvrotl`: its full name, its one attribute key `amount`, its        /// side effects declared as unknown, and the rule that the operand and the result share one        /// type. `SmtDialect.spec` lists it among the dialect's operations, so loading the dialect        /// registers the operation with the context, and `ir.verifyOperation` checks the type rule.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvrotl",            .attrs = &.{attr_names.amount},            .interfaces = &.{unknown_effects},            .dynamic_traits = &.{same_operands_and_result_type_trait},        });        /// The full name of the operation, `smt.bvrotl`, so code that walks operations can compare        /// each operation's name with this constant to find the left rotations. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvrotl` operation that rotates `operand` by `amount` positions at the        /// source location `loc`, and returns the struct holding it, so a checker can rotate a        /// value left by the number of positions it knows when it builds the formula. The call        /// returns `error.UnsupportedBitVectorType` when `bitVecWidth` returns `null` for the type        /// of `operand`. The call accepts an `amount` at or above the operand's width. The new        /// operation belongs to no block until the caller adds `op` to one. The call also returns        /// the errors of creating the operation and its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, operand: *ir.Value, amount: u32) !BvRotlOp {            return .{ .op = try rotate(ctx, loc, operation_name, operand, amount) };        }        /// Returns the operation's one result, the rotated value, so a caller can pass it to the        /// operations that use it.        pub fn getResult(self: *const BvRotlOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the rotation amount from the `amount` attribute, so code that reads a formula        /// back can build the solver's rotation term. The call returns `null` when the operation        /// lacks an integer attribute under `amount` or its value lies outside the range of a        /// `u32`.        pub fn getAmount(self: BvRotlOp) ?u32 {            return getU32Attr(self.op, attr_names.amount);        }    };    /// The rotation of a bit-vector toward its least significant bit by a fixed number of    /// positions, as the operation `smt.bvrotr`, with the number of positions in its `amount`    /// attribute, for a checker that rotates a bit-vector right. The result has the operand's type.    /// The struct holds the operation in `op`, creates it with `create`, and reads it with    /// `getResult` and `getAmount`.    pub const BvRotrOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the rotation amount through `getAmount`.        op: *ir.Operation,        /// The specification of `smt.bvrotr`: its full name, its one attribute key `amount`, its        /// side effects declared as unknown, and the rule that the operand and the result share one        /// type. `SmtDialect.spec` lists it among the dialect's operations, so loading the dialect        /// registers the operation with the context, and `ir.verifyOperation` checks the type rule.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvrotr",            .attrs = &.{attr_names.amount},            .interfaces = &.{unknown_effects},            .dynamic_traits = &.{same_operands_and_result_type_trait},        });        /// The full name of the operation, `smt.bvrotr`, so code that walks operations can compare        /// each operation's name with this constant to find the right rotations. `create` creates        /// operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvrotr` operation that rotates `operand` by `amount` positions at the        /// source location `loc`, and returns the struct holding it, so a checker can rotate a        /// value right by the number of positions it knows when it builds the formula. The call        /// returns `error.UnsupportedBitVectorType` when `bitVecWidth` returns `null` for the type        /// of `operand`. The call accepts an `amount` at or above the operand's width. The new        /// operation belongs to no block until the caller adds `op` to one. The call also returns        /// the errors of creating the operation and its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, operand: *ir.Value, amount: u32) !BvRotrOp {            return .{ .op = try rotate(ctx, loc, operation_name, operand, amount) };        }        /// Returns the operation's one result, the rotated value, so a caller can pass it to the        /// operations that use it.        pub fn getResult(self: *const BvRotrOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the rotation amount from the `amount` attribute, so code that reads a formula        /// back can build the solver's rotation term. The call returns `null` when the operation        /// lacks an integer attribute under `amount` or its value lies outside the range of a        /// `u32`.        pub fn getAmount(self: BvRotrOp) ?u32 {            return getU32Attr(self.op, attr_names.amount);        }    };    /// The unsigned quotient of the first bit-vector by the second, as the operation `smt.bvudiv`,    /// for a checker lowering a program's unsigned division into this operation.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of `lhs`.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvudiv`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvUdivOp: type = op_templates.binarySameType(        "bvudiv",        sameOperandsResultOptions(.{}),    );    /// The unsigned remainder of the first bit-vector by the second, as the operation `smt.bvurem`,    /// for a checker taking the unsigned remainder of a program's division.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of `lhs`.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvurem`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvUremOp: type = op_templates.binarySameType(        "bvurem",        sameOperandsResultOptions(.{}),    );    /// The two's-complement quotient of the first bit-vector by the second, rounded toward zero, as    /// the operation `smt.bvsdiv`, for a checker lowering a program's signed division into this    /// operation. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of    /// `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvsdiv`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvSdivOp: type = op_templates.binarySameType(        "bvsdiv",        sameOperandsResultOptions(.{}),    );    /// The remainder of the first bit-vector by the second with the sign of the first, as the    /// operation `smt.bvsrem`, for a checker taking the signed remainder of a program's division.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the type of `lhs`.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvsrem`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: both    /// operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvSremOp: type = op_templates.binarySameType(        "bvsrem",        sameOperandsResultOptions(.{}),    );    /// The modulo of the first bit-vector by the second with the sign of the second, as the    /// operation `smt.bvsmod`, for a checker taking a signed modulo whose result follows the    /// divisor's sign. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the    /// type of `lhs`. `getResult` returns the result, and `getLhs` and `getRhs` return the    /// operands. `operation_name` holds `smt.bvsmod`, and the struct holds the operation in `op`.    /// `create` checks no types, and `ir.verifyOperation` checks the rule the dialect registered:    /// both operands and the result share one type. `create` returns the errors of creating the    /// operation in `ctx`.    pub const BvSmodOp: type = op_templates.binarySameType(        "bvsmod",        sameOperandsResultOptions(.{}),    );    /// The bits of the first bit-vector above the bits of the second, as the operation    /// `smt.bvconcat`, with one result whose width is the sum of the two widths, for a checker    /// joining two bit-vectors into a wider one, for example two halves of a word. The struct holds    /// the operation in `op`, creates it with `create`, and returns the result with `getResult`.    pub const BvConcatOp = struct {        op: *ir.Operation,        /// The specification of `smt.bvconcat`: its full name and its side effects declared as        /// unknown. `SmtDialect.spec` lists it among the dialect's operations, so loading the        /// dialect registers the operation with the context. The specification registers no type        /// rule, so `create` alone checks the operand types.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvconcat",            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bvconcat`, so code that walks operations can        /// compare each operation's name with this constant to find the concatenations. `create`        /// creates operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvconcat` operation that joins `lhs` above `rhs` at the source location        /// `loc`, and returns the struct holding it, for a checker building a wider value from two        /// parts with `lhs` as the high part. The result is a bit-vector whose width is the width        /// of `lhs` plus the width of `rhs`. The call returns `error.UnsupportedBitVectorType` when        /// `bitVecWidth` returns `null` for the type of either operand, and        /// `error.InvalidBitVectorWidth` when the sum of the widths overflows a `u32`. The new        /// operation belongs to no block until the caller adds `op` to one. The call also returns        /// the errors of building the result type and of creating the operation in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value) !BvConcatOp {            const lhs_width = bitVecWidth(ctx, lhs.type) orelse return error.UnsupportedBitVectorType;            const rhs_width = bitVecWidth(ctx, rhs.type) orelse return error.UnsupportedBitVectorType;            const result_width = std.math.add(u32, lhs_width, rhs_width) catch return error.InvalidBitVectorWidth;            return .{ .op = try binary(ctx, loc, operation_name, lhs, rhs, try getBitVecType(ctx, result_width)) };        }        /// Returns the operation's one result, the joined value, so a caller can pass it to the        /// operations that use it.        pub fn getResult(self: *const BvConcatOp) *ir.Value {            return self.op.getResult(0).?;        }    };    /// The bits of a bit-vector from a high position down to a low position, both included, as the    /// operation `smt.bvextract`, with the positions in its `high` and `low` attributes, for a    /// checker taking a range of bits from a bit-vector, for example to narrow a value to a smaller    /// width. The result is a bit-vector of `high - low + 1` bits. The struct holds the operation    /// in `op`, creates it with `create`, and reads it with `getResult`, `getHigh` and `getLow`.    pub const BvExtractOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the positions through `getHigh` and `getLow`.        op: *ir.Operation,        /// The specification of `smt.bvextract`: its full name, its attribute keys `high` and        /// `low`, and its side effects declared as unknown. `SmtDialect.spec` lists it among the        /// dialect's operations, so loading the dialect registers the operation with the context.        /// The specification registers no type rule, so `create` alone checks the operand type and        /// the range.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvextract",            .attrs = &.{ attr_names.high, attr_names.low },            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bvextract`, so code that walks operations can        /// compare each operation's name with this constant to find the extractions. `create`        /// creates operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvextract` operation that keeps the bits of `operand` from position        /// `high` down to position `low` at the source location `loc`, and returns the struct        /// holding it, so a checker can narrow a value to fewer bits by keeping its low bits, such        /// as positions `7` down to `0`. Position 0 is the least significant bit. The result is a        /// bit-vector of `high - low + 1` bits. The call returns `error.UnsupportedBitVectorType`        /// when `bitVecWidth` returns `null` for the type of `operand`, and        /// `error.InvalidBitVectorRange` when `low` is above `high` or `high` is at or above the        /// operand's width. The new operation belongs to no block until the caller adds `op` to        /// one. The call also returns the errors of building the result type and of creating the        /// operation and its attributes in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, operand: *ir.Value, high: u32, low: u32) !BvExtractOp {            const source_width = bitVecWidth(ctx, operand.type) orelse return error.UnsupportedBitVectorType;            if (low > high or high >= source_width) return error.InvalidBitVectorRange;            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(&.{operand});            state.addTypes(&.{try getBitVecType(ctx, high - low + 1)});            const op = try builder.create(state);            try op.setAttr(attr_names.high, try ctx.getI64Attr(high));            try op.setAttr(attr_names.low, try ctx.getI64Attr(low));            return .{ .op = op };        }        /// Returns the operation's one result, the extracted bits, so a caller can pass them to the        /// operations that use them.        pub fn getResult(self: *const BvExtractOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the highest bit position the extraction keeps, from the `high` attribute, so        /// code that reads a formula back can build the solver's extraction term. The call returns        /// `null` when the operation lacks an integer attribute under `high` or its value lies        /// outside the range of a `u32`.        pub fn getHigh(self: BvExtractOp) ?u32 {            return getU32Attr(self.op, attr_names.high);        }        /// Returns the lowest bit position the extraction keeps, from the `low` attribute, so code        /// that reads a formula back can build the solver's extraction term. The call returns        /// `null` when the operation lacks an integer attribute under `low` or its value lies        /// outside the range of a `u32`.        pub fn getLow(self: BvExtractOp) ?u32 {            return getU32Attr(self.op, attr_names.low);        }    };    /// A bit-vector widened by zero bits above its most significant bit, as the operation    /// `smt.bvzeroext`, with the number of added bits in its `extra` attribute, for a checker    /// widening an unsigned value to a larger width, for example before comparing it with a wider    /// value. The result is a bit-vector of the operand's width plus `extra` bits. The struct holds    /// the operation in `op`, creates it with `create`, and reads it with `getResult` and    /// `getExtra`.    pub const BvZeroExtOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the added width through `getExtra`.        op: *ir.Operation,        /// The specification of `smt.bvzeroext`: its full name, its one attribute key `extra`, and        /// its side effects declared as unknown. `SmtDialect.spec` lists it among the dialect's        /// operations, so loading the dialect registers the operation with the context. The        /// specification registers no type rule, so `create` alone checks the operand type.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvzeroext",            .attrs = &.{attr_names.extra},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bvzeroext`, so code that walks operations can        /// compare each operation's name with this constant to find the zero extensions. `create`        /// creates operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvzeroext` operation that adds `extra` zero bits above `operand` at the        /// source location `loc`, and returns the struct holding it, so a checker can widen an        /// unsigned value by the number of bits the target width needs. The result is a bit-vector        /// of the operand's width plus `extra` bits, and an `extra` of 0 keeps the width. The call        /// returns `error.UnsupportedBitVectorType` when `bitVecWidth` returns `null` for the type        /// of `operand`, and `error.InvalidBitVectorWidth` when the new width overflows a `u32`.        /// The new operation belongs to no block until the caller adds `op` to one. The call also        /// returns the errors of building the result type and of creating the operation and its        /// attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, operand: *ir.Value, extra: u32) !BvZeroExtOp {            const source_width = bitVecWidth(ctx, operand.type) orelse return error.UnsupportedBitVectorType;            const result_width = std.math.add(u32, source_width, extra) catch return error.InvalidBitVectorWidth;            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(&.{operand});            state.addTypes(&.{try getBitVecType(ctx, result_width)});            const op = try builder.create(state);            try op.setAttr(attr_names.extra, try ctx.getI64Attr(extra));            return .{ .op = op };        }        /// Returns the operation's one result, the widened value, so a caller can pass it to the        /// operations that use it.        pub fn getResult(self: *const BvZeroExtOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the number of added bits from the `extra` attribute, so code that reads a        /// formula back can build the solver's extension term. The call returns `null` when the        /// operation lacks an integer attribute under `extra` or its value lies outside the range        /// of a `u32`.        pub fn getExtra(self: BvZeroExtOp) ?u32 {            return getU32Attr(self.op, attr_names.extra);        }    };    /// A bit-vector widened by copies of its sign bit, as the operation `smt.bvsignext`, with the    /// number of added bits in its `extra` attribute. A checker widens a signed value to a larger    /// width and keeps its sign. The result is a bit-vector of the operand's width plus `extra`    /// bits. The struct holds the operation in `op`, creates it with `create`, and reads it with    /// `getResult` and `getExtra`.    pub const BvSignExtOp = struct {        /// The operation the struct holds. `create` sets it, and a caller reads it to add the        /// operation to a block. A caller that finds an operation named `operation_name` builds the        /// struct around it and reads the added width through `getExtra`.        op: *ir.Operation,        /// The specification of `smt.bvsignext`: its full name, its one attribute key `extra`, and        /// its side effects declared as unknown. `SmtDialect.spec` lists it among the dialect's        /// operations, so loading the dialect registers the operation with the context. The        /// specification registers no type rule, so `create` alone checks the operand type.        pub const operation_spec = op_specs.define(.{            .mnemonic = "bvsignext",            .attrs = &.{attr_names.extra},            .interfaces = &.{unknown_effects},        });        /// The full name of the operation, `smt.bvsignext`. Code that walks operations compares        /// each operation's name with this constant to find the sign extensions, and `create`        /// creates operations under this name.        pub const operation_name = operation_spec.name;        /// Creates an `smt.bvsignext` operation that adds `extra` copies of the sign bit of        /// `operand` above it at the source location `loc`, and returns the struct holding it. A        /// checker widens a signed value by the number of bits the target width needs. The result        /// is a bit-vector of the operand's width plus `extra` bits, and an `extra` of 0 keeps the        /// width. The new operation belongs to no block until the caller adds `op` to one. The call        /// returns `error.UnsupportedBitVectorType` when `bitVecWidth` returns `null` for the type        /// of `operand`, and `error.InvalidBitVectorWidth` when the new width overflows a `u32`.        /// The call also returns the errors of building the result type and of creating the        /// operation and its attribute in `ctx`.        pub fn create(ctx: *ir.Context, loc: ir.Location, operand: *ir.Value, extra: u32) !BvSignExtOp {            const source_width = bitVecWidth(ctx, operand.type) orelse return error.UnsupportedBitVectorType;            const result_width = std.math.add(u32, source_width, extra) catch return error.InvalidBitVectorWidth;            var builder = ir.OperationBuilder.init(ctx);            var state = ir.Operation.State.init(operation_name, loc);            state.addOperands(&.{operand});            state.addTypes(&.{try getBitVecType(ctx, result_width)});            const op = try builder.create(state);            try op.setAttr(attr_names.extra, try ctx.getI64Attr(extra));            return .{ .op = op };        }        /// Returns the operation's one result, the widened value, so a caller can pass the value to        /// the operations that use it.        pub fn getResult(self: *const BvSignExtOp) *ir.Value {            return self.op.getResult(0).?;        }        /// Returns the number of added bits from the `extra` attribute, so code that reads a        /// formula back can build the solver's extension term. The call returns `null` when the        /// operation lacks an integer attribute under `extra` or its value lies outside the range        /// of a `u32`.        pub fn getExtra(self: BvSignExtOp) ?u32 {            return getU32Attr(self.op, attr_names.extra);        }    };    /// Whether the first bit-vector is less than the second as unsigned numbers, as the operation    /// `smt.bvult`, for a checker that states an unsigned bound, for example that an index is below    /// a length. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the Boolean    /// type. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvult`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: the two    /// operands share one type and the result is Boolean. `create` returns the errors of building    /// the Boolean type and of creating the operation in `ctx`.    pub const BvUltOp: type = op_templates.binaryFixedResult(        "bvult",        sameOperandBoolResultOptions(.{}),        getBoolType,    );    /// Whether the first bit-vector is at most the second as unsigned numbers, as the operation    /// `smt.bvule`, for a checker that states an unsigned bound that includes its limit, for    /// example that an end offset is at most a length. `create(ctx, loc, lhs, rhs)` creates the    /// operation with one result of the Boolean type. `getResult` returns the result, and `getLhs`    /// and `getRhs` return the operands. `operation_name` holds `smt.bvule`, and the struct holds    /// the operation in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule    /// the dialect registered: the two operands share one type and the result is Boolean. `create`    /// returns the errors of building the Boolean type and of creating the operation in `ctx`.    pub const BvUleOp: type = op_templates.binaryFixedResult(        "bvule",        sameOperandBoolResultOptions(.{}),        getBoolType,    );    /// Whether the first bit-vector is less than the second as two's-complement numbers, as the    /// operation `smt.bvslt`, for a checker that states a signed bound, for example that a value is    /// below a limit as a two's-complement number. `create(ctx, loc, lhs, rhs)` creates the    /// operation with one result of the Boolean type. `getResult` returns the result, and `getLhs`    /// and `getRhs` return the operands. `operation_name` holds `smt.bvslt`, and the struct holds    /// the operation in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule    /// the dialect registered: the two operands share one type and the result is Boolean. `create`    /// returns the errors of building the Boolean type and of creating the operation in `ctx`.    pub const BvSltOp: type = op_templates.binaryFixedResult(        "bvslt",        sameOperandBoolResultOptions(.{}),        getBoolType,    );    /// Whether the first bit-vector is at most the second as two's-complement numbers, as the    /// operation `smt.bvsle`, for a checker that states a signed bound that includes its limit, for    /// example that an offset is at least zero. `create(ctx, loc, lhs, rhs)` creates the operation    /// with one result of the Boolean type. `getResult` returns the result, and `getLhs` and    /// `getRhs` return the operands. `operation_name` holds `smt.bvsle`, and the struct holds the    /// operation in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule the    /// dialect registered: the two operands share one type and the result is Boolean. `create`    /// returns the errors of building the Boolean type and of creating the operation in `ctx`.    pub const BvSleOp: type = op_templates.binaryFixedResult(        "bvsle",        sameOperandBoolResultOptions(.{}),        getBoolType,    );    /// Whether the unsigned sum of two bit-vectors overflows their width, as the operation    /// `smt.bvuaddo`, for a checker that asks whether a program's unsigned addition can overflow.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the Boolean type.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvuaddo`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: the two    /// operands share one type and the result is Boolean. The context records the operation as    /// commutative. Common subexpression elimination keeps two of these operations with swapped    /// operands as two, because every SMT operation declares its side effects as unknown. `create`    /// returns the errors of building the Boolean type and of creating the operation in `ctx`.    pub const BvUaddoOp: type = op_templates.binaryFixedResult(        "bvuaddo",        sameOperandBoolResultOptions(commutative_op_traits),        getBoolType,    );    /// Whether the two's-complement sum of two bit-vectors overflows their width, as the operation    /// `smt.bvsaddo`, for a checker that asks whether a program's signed addition can overflow.    /// `create(ctx, loc, lhs, rhs)` creates the operation with one result of the Boolean type.    /// `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvsaddo`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: the two    /// operands share one type and the result is Boolean. The context records the operation as    /// commutative. `create` returns the errors of building the Boolean type and of creating the    /// operation in `ctx`.    pub const BvSaddoOp: type = op_templates.binaryFixedResult(        "bvsaddo",        sameOperandBoolResultOptions(commutative_op_traits),        getBoolType,    );    /// Whether the two's-complement difference of two bit-vectors, the first minus the second,    /// overflows their width, as the operation `smt.bvssubo`, for a checker that asks whether a    /// program's signed subtraction can overflow. `create(ctx, loc, lhs, rhs)` creates the    /// operation with one result of the Boolean type. `getResult` returns the result, and `getLhs`    /// and `getRhs` return the operands. `operation_name` holds `smt.bvssubo`, and the struct holds    /// the operation in `op`. `create` checks no types, and `ir.verifyOperation` checks the rule    /// the dialect registered: the two operands share one type and the result is Boolean. The    /// context records the operation as not commutative. `create` returns the errors of building    /// the Boolean type and of creating the operation in `ctx`.    pub const BvSsuboOp: type = op_templates.binaryFixedResult(        "bvssubo",        sameOperandBoolResultOptions(.{}),        getBoolType,    );    /// Whether the unsigned product of two bit-vectors overflows their width, as the operation    /// `smt.bvumulo`, for a checker that asks whether a program's unsigned multiplication can    /// overflow. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the Boolean    /// type. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvumulo`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: the two    /// operands share one type and the result is Boolean. The context records the operation as    /// commutative. `create` returns the errors of building the Boolean type and of creating the    /// operation in `ctx`.    pub const BvUmuloOp: type = op_templates.binaryFixedResult(        "bvumulo",        sameOperandBoolResultOptions(commutative_op_traits),        getBoolType,    );    /// Whether the two's-complement product of two bit-vectors overflows their width, as the    /// operation `smt.bvsmulo`, for a checker that asks whether a program's signed multiplication    /// can overflow. `create(ctx, loc, lhs, rhs)` creates the operation with one result of the    /// Boolean type. `getResult` returns the result, and `getLhs` and `getRhs` return the operands.    /// `operation_name` holds `smt.bvsmulo`, and the struct holds the operation in `op`. `create`    /// checks no types, and `ir.verifyOperation` checks the rule the dialect registered: the two    /// operands share one type and the result is Boolean. The context records the operation as    /// commutative. `create` returns the errors of building the Boolean type and of creating the    /// operation in `ctx`.    pub const BvSmuloOp: type = op_templates.binaryFixedResult(        "bvsmulo",        sameOperandBoolResultOptions(commutative_op_traits),        getBoolType,    );    fn sameOperandsResultOptions(comptime traits: ir.OperationTraits) ir.dialects.opSpec.Options {        return .{            .traits = traits,            .interfaces = &.{unknown_effects},            .dynamic_traits = &.{same_operands_and_result_type_trait},        };    }    fn boolUnaryOptions(comptime traits: ir.OperationTraits) ir.dialects.opSpec.Options {        return .{            .traits = traits,            .interfaces = &.{unknown_effects},            .operand_types = &.{operand0_is_bool_constraint},            .result_types = &.{result0_is_bool_constraint},        };    }    fn boolBinaryOptions(comptime traits: ir.OperationTraits) ir.dialects.opSpec.Options {        return .{            .traits = traits,            .interfaces = &.{unknown_effects},            .operand_types = &.{ operand0_is_bool_constraint, operand1_is_bool_constraint },            .result_types = &.{result0_is_bool_constraint},        };    }    fn sameOperandBoolResultOptions(comptime traits: ir.OperationTraits) ir.dialects.opSpec.Options {        return .{            .traits = traits,            .interfaces = &.{unknown_effects},            .result_types = &.{result0_is_bool_constraint},            .dynamic_traits = &.{same_type_operands_trait},        };    }    fn deinitBitVecPayload(allocator: std.mem.Allocator, ptr: *anyopaque) void {        const payload: *BitVecTypePayload = @ptrCast(@alignCast(ptr));        allocator.destroy(payload);    }    fn deinitArrayPayload(allocator: std.mem.Allocator, ptr: *anyopaque) void {        const payload: *ArrayTypePayload = @ptrCast(@alignCast(ptr));        allocator.destroy(payload);    }    fn typeParamFallback(ctx: *const ir.Context, typ: ir.Type) ?*const anyopaque {        _ = ctx;        const type_name = typ.getDialectTypeName() orelse return null;        if (!std.mem.eql(u8, type_name, type_names.bv) and !std.mem.eql(u8, type_name, type_names.array)) return null;        return &type_param_vtable;    }    fn parseTypeParams(type_ptr: *const anyopaque, ctx_opaque: *const interfaces.ContextOpaque) anyerror!?interfaces.TypeParamPayload {        const ctx = interfaces.castContext(ir.Context, ctx_opaque);        const storage: *const ir.Type.DialectTypeStorage = @ptrCast(@alignCast(type_ptr));        if (std.mem.eql(u8, storage.name, type_names.bv)) {            if (storage.param_key.len == 0) return null;            const width = std.fmt.parseInt(u32, storage.param_key, 10) catch return null;            if (width == 0) return null;            const payload = try ir.context.typePayloadAllocator(ctx).create(BitVecTypePayload);            payload.* = .{ .width = width };            return .{ .ptr = payload, .deinit = deinitBitVecPayload };        }        if (std.mem.eql(u8, storage.name, type_names.array)) {            const shape = parseArrayParamKey(storage.param_key) orelse return null;            const payload = try ir.context.typePayloadAllocator(ctx).create(ArrayTypePayload);            payload.* = shape;            return .{ .ptr = payload, .deinit = deinitArrayPayload };        }        return null;    }    fn loadSpec(ctx: *ir.Context) !void {        ir.dialects.loadDialectSpec(ctx, spec) catch |err| switch (err) {            error.ContextFrozen => {},            else => return err,        };    }    /// Returns the dialect's Boolean type, `smt.bool`, from `ctx`. A caller needs the Boolean type    /// to compare a value's type against it, and the Boolean operations' `create` functions build    /// it themselves. The call loads the dialect into `ctx` first when the dialect is unloaded.    /// Every call on one context returns the same type. On a frozen context that has not loaded the    /// dialect and refuses unregistered types (the default), the call returns `error.UnknownType`,    /// and the context keeps the dialect marked as loaded with no operations registered. The call    /// returns the errors of loading the dialect other than `error.ContextFrozen`, and the errors    /// of looking up the type in `ctx`.    pub fn getBoolType(ctx: *ir.Context) !ir.Type {        try loadSpec(ctx);        return ctx.getDialectTypeFromName(type_names.boolean);    }    /// Returns the bit-vector type of `width` bits from `ctx`: the type `smt.bv` with `width` as    /// decimal text. A checker builds the type of each variable and constant from the width of the    /// program value it stands for. The call loads the dialect into `ctx` first when the dialect is    /// unloaded. Every call with one width on one context returns the same type. The call accepts a    /// `width` of 0 and returns a type whose width `bitVecWidth` reads as `null`. On a frozen    /// context that has not loaded the dialect and refuses unregistered types (the default), the    /// call returns `error.UnknownType`, and the context keeps the dialect marked as loaded with no    /// operations registered. The call returns the errors of loading the dialect other than    /// `error.ContextFrozen`, and the errors of looking up or creating the type in `ctx`.    pub fn getBitVecType(ctx: *ir.Context, width: u32) !ir.Type {        try loadSpec(ctx);        var buf: [16]u8 = undefined;        const key = try std.fmt.bufPrint(&buf, "{d}", .{width});        return ctx.getDialectTypeFromNameWithKey(type_names.bv, key);    }    /// Returns the array type from `ctx` whose index is a bit-vector of `index_width` bits and    /// whose element is a bit-vector of `element_width` bits: the type `smt.array` with the text    /// `index_width:element_width`. A checker builds the type of a memory it models as an array    /// from the widths of its addresses and of its cells. The call loads the dialect into `ctx`    /// first when the dialect is unloaded. Every call with one pair of widths on one context    /// returns the same type. The call accepts a width of 0 and returns a type whose widths    /// `arrayShape` reads as `null`. On a frozen context that has not loaded the dialect and    /// refuses unregistered types (the default), the call returns `error.UnknownType`, and the    /// context keeps the dialect marked as loaded with no operations registered. The call returns    /// the errors of loading the dialect other than `error.ContextFrozen`, and the errors of    /// looking up or creating the type in `ctx`.    pub fn getArrayType(ctx: *ir.Context, index_width: u32, element_width: u32) !ir.Type {        try loadSpec(ctx);        var buf: [32]u8 = undefined;        const key = try std.fmt.bufPrint(&buf, "{d}:{d}", .{ index_width, element_width });        return ctx.getDialectTypeFromNameWithKey(type_names.array, key);    }    /// Returns the width of the bit-vector type `typ`, so code that reads a formula back can pick a    /// solver sort and a constant's width from a value's type. The call returns `null` for a type    /// of another kind, for a bit-vector type whose width text fails to parse as a `u32` above 0,    /// and when the context fails to parse the width. The context parses the width once per type    /// and keeps it. The constructors of the rotation, width-changing and array operations call it    /// to check their operands.    pub fn bitVecWidth(ctx: *ir.Context, typ: ir.Type) ?u32 {        const payload = ctx.getTypeParamPayload(typ, BitVecTypePayload) catch return null;        return if (payload) |value| value.width else null;    }    /// Returns the index width and the element width of the array type `typ`, as a copy of its    /// `ArrayTypePayload`, so code that reads a formula back can pick an array sort from a value's    /// type. The call returns `null` for a type of another kind, for an array type whose text fails    /// to parse as two `u32` values above 0 joined by one colon, and when the context fails to    /// parse the widths. The context parses the widths once per type and keeps them.    /// `ArraySelectOp.create` and `ArrayStoreOp.create` call it to check their array operand.    pub fn arrayShape(ctx: *ir.Context, typ: ir.Type) ?ArrayTypePayload {        const payload = ctx.getTypeParamPayload(typ, ArrayTypePayload) catch return null;        return if (payload) |value| value.* else null;    }    fn parseArrayParamKey(param_key: []const u8) ?ArrayTypePayload {        var parts = std.mem.splitScalar(u8, param_key, ':');        const index_text = parts.next() orelse return null;        const element_text = parts.next() orelse return null;        if (parts.next() != null) return null;        const index_width = std.fmt.parseInt(u32, index_text, 10) catch return null;        const element_width = std.fmt.parseInt(u32, element_text, 10) catch return null;        if (index_width == 0 or element_width == 0) return null;        return .{ .index_width = index_width, .element_width = element_width };    }    fn getU32Attr(op: *const ir.Operation, attr_name: []const u8) ?u32 {        const attr = op.getAttrAs(ir.Attribute.IntegerAttr, attr_name) orelse return null;        const value = attr.getValue();        if (value < 0 or value > std.math.maxInt(u32)) return null;        return @intCast(value);    }    fn unary(ctx: *ir.Context, loc: ir.Location, operation_name: []const u8, operand: *ir.Value, result_type: ir.Type) !*ir.Operation {        var builder = ir.OperationBuilder.init(ctx);        var state = ir.Operation.State.init(operation_name, loc);        state.addOperands(&.{operand});        state.addTypes(&.{result_type});        return builder.create(state);    }    fn rotate(ctx: *ir.Context, loc: ir.Location, operation_name: []const u8, operand: *ir.Value, amount: u32) !*ir.Operation {        _ = bitVecWidth(ctx, operand.type) orelse return error.UnsupportedBitVectorType;        const op = try unary(ctx, loc, operation_name, operand, operand.type);        try op.setAttr(attr_names.amount, try ctx.getI64Attr(amount));        return op;    }    fn binary(ctx: *ir.Context, loc: ir.Location, operation_name: []const u8, lhs: *ir.Value, rhs: *ir.Value, result_type: ir.Type) !*ir.Operation {        var builder = ir.OperationBuilder.init(ctx);        var state = ir.Operation.State.init(operation_name, loc);        state.addOperands(&.{ lhs, rhs });        state.addTypes(&.{result_type});        return builder.create(state);    }};
Called byCallsNo direct callstest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...choir.SmtDialect.ApplyOpcreate
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...choir.SmtDialectarrayShapeprivate sourcelib.smt.src.choir.dialect.SmtDialectbinarychoir.SmtDialectbitVecWidthchoir.SmtDialectgetBitVecTypechoir.SmtDialect.ArraySelectOpcreate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...choir.SmtDialectarrayShapechoir.SmtDialectbitVecWidthchoir.SmtDialect.ArrayStoreOpcreate
Static calls · unresolved targets: 0 · external targets: 5.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...choir.SmtDialect.AssertOpcreateNamedchoir.SmtDialect.AssertOpcreateGrouped
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callschoir.SmtDialect.AssertOpcreateGroupedchoir.SmtDialect.AssertOpcreateNamed
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...choir.SmtDialectgetBitVecTypechoir.SmtDialect.BitVecConstOpcreate
Static calls · unresolved targets: 0 · external targets: 6.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT effect declarations remain ...choir.SmtDialectgetBoolTypechoir.SmtDialect.BoolConstOpcreate
Static calls · unresolved targets: 0 · external targets: 6.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...private sourcelib.smt.src.choir.dialect.SmtDialectbinarychoir.SmtDialectbitVecWidthchoir.SmtDialectgetBitVecTypechoir.SmtDialect.BvConcatOpcreate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...choir.SmtDialectbitVecWidthchoir.SmtDialectgetBitVecTypechoir.SmtDialect.BvExtractOpcreate
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvExtractOpgetHigh
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvExtractOpgetLow
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...test sourcelib.smt.src.choir.testtest: SMT fixed rotates remain unqual...private sourcelib.smt.src.choir.dialect.SmtDialectrotatechoir.SmtDialect.BvRotlOpcreate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvRotlOpgetAmount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...private sourcelib.smt.src.choir.dialect.SmtDialectrotatechoir.SmtDialect.BvRotrOpcreate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvRotrOpgetAmount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...choir.SmtDialectbitVecWidthchoir.SmtDialectgetBitVecTypechoir.SmtDialect.BvSignExtOpcreate
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvSignExtOpgetExtra
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...choir.SmtDialectbitVecWidthchoir.SmtDialectgetBitVecTypechoir.SmtDialect.BvZeroExtOpcreate
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallsNo direct callersprivate sourcelib.smt.src.choir.dialect.SmtDialectgetU32Attrchoir.SmtDialect.BvZeroExtOpgetExtra
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.smt.src.choir.testtest: SMT commutative traits grant no...test sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...test sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...test sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...test sourcelib.smt.src.choir.testtest: SMT fixed rotates remain unqual...choir.SmtDialect.VarOpcreate
Static calls · unresolved targets: 0 · external targets: 6.
Called byCallsNo direct callschoir.SmtDialect.ArraySelectOpcreatechoir.SmtDialect.ArrayStoreOpcreatetest sourcelib.smt.src.choir.testtest: SMT dialect registers parameter...choir.SmtDialectarrayShape
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callschoir.SmtDialect.ArraySelectOpcreatechoir.SmtDialect.ArrayStoreOpcreatechoir.SmtDialect.BvConcatOpcreatechoir.SmtDialect.BvExtractOpcreatechoir.SmtDialect.BvSignExtOpcreate+3 morechoir.SmtDialectbitVecWidth
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.smt.src.choir.testtest: SMT dialect creates bit-vector ...test sourcelib.smt.src.choir.testtest: SMT dialect registers parameter...private sourcelib.smt.src.choir.dialect.SmtDialectloadSpecchoir.SmtDialectgetArrayType
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallschoir.SmtDialect.ArraySelectOpcreatechoir.SmtDialect.BitVecConstOpcreatechoir.SmtDialect.BvConcatOpcreatechoir.SmtDialect.BvExtractOpcreatechoir.SmtDialect.BvSignExtOpcreate+7 moreprivate sourcelib.smt.src.choir.dialect.SmtDialectloadSpecchoir.SmtDialectgetBitVecType
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallschoir.SmtDialect.BoolConstOpcreatetest sourcelib.smt.src.choir.testtest: SMT dialect creates symbolic bi...test sourcelib.smt.src.choir.testtest: SMT dialect registers parameter...private sourcelib.smt.src.choir.dialect.SmtDialectloadSpecchoir.SmtDialectgetBoolType
Static calls · unresolved targets: 0 · external targets: 1.

Also reachable as

choir.SmtDialect.

Complete caller list for choir.SmtDialect.bitVecWidth

8 direct callers.

Complete caller list for choir.SmtDialect.getBitVecType

12 direct callers.

Audit

Definitions118
Public names236
Members16
Version26.7.0
Revisiondaab053ee433