LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Dialect.cpp
Go to the documentation of this file.
1//===-- Dialect.cpp - Verif dialect implementation --------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
11
14
15#include <mlir/Dialect/SCF/IR/SCF.h>
16#include <mlir/IR/DialectImplementation.h>
17#include <mlir/IR/DialectRegistry.h>
18#include <mlir/IR/MLIRContext.h>
19#include <mlir/Support/LLVM.h>
20#include <mlir/Support/TypeID.h>
21
22#include <llvm/ADT/SmallVector.h>
23#include <llvm/ADT/SmallVectorExtras.h>
24#include <llvm/ADT/TypeSwitch.h>
25
26// TableGen'd implementation files
28
29using namespace mlir;
30
31//===------------------------------------------------------------------===//
32// InvariantTarget implementations for upstream dialects
33//===------------------------------------------------------------------===//
34
35namespace {
36
38static FailureOr<StringRef> getLabelImpl(Operation *op) {
39 auto attr = dyn_cast_or_null<StringAttr>(op->getDiscardableAttr("loop_label"));
40 if (!attr) {
41 return failure();
42 }
43 return attr.getValue();
44}
45
46struct ScfWhileExternalModel : public llzk::verif::InvariantTargetOpInterface::ExternalModel<
47 ScfWhileExternalModel, scf::WhileOp> {
48 FailureOr<StringRef> getLabel(Operation *op) const { return getLabelImpl(op); }
49
50 SmallVector<Type> getArgumentTypes(Operation *op) const {
51 auto whileOp = cast<scf::WhileOp>(op);
52 // In the case of `scf.while` we return the 'before' arguments.
53 // Depending on how the loop is constructed it may not be the most ergonomic
54 // when it comes to binding the loop arguments in an invariant. The limitation of using these
55 // arguments is that invariants will have trouble expressing properties of a loop that rely
56 // on intermediate values passed via the 'after' arguments. The downside of using both
57 // 'before' and 'after' arguments is that any 'before' argument that is passed to the
58 // 'after' arguments will require a duplicate binding in the invariant, which is probably
59 // not very user-friendly and may lead to confusion.
60 return llvm::map_to_vector(whileOp.getBeforeArguments(), [](auto arg) {
61 return arg.getType();
62 });
63 }
64};
65
66struct ScfForExternalModel : public llzk::verif::InvariantTargetOpInterface::ExternalModel<
67 ScfForExternalModel, scf::ForOp> {
68 FailureOr<StringRef> getLabel(Operation *op) const { return getLabelImpl(op); }
69
70 SmallVector<Type> getArgumentTypes(Operation *op) const {
71 auto forOp = cast<scf::ForOp>(op);
72 // In the case of `scf.for` we return the control values in a fixed order defined by the
73 // spec language semantics followed by any loop carried values. The order for the control values
74 // is: lower bound, induction variable, upper bound, step.
75 SmallVector<Type, 4> types;
76 types.reserve(forOp.getNumRegionIterArgs() + 4);
77 types.append(
78 {forOp.getLowerBound().getType(), forOp.getInductionVar().getType(),
79 forOp.getUpperBound().getType(), forOp.getStep().getType()}
80 );
81 types.append(llvm::map_to_vector(forOp.getInitArgs(), [](auto arg) { return arg.getType(); }));
82 return types;
83 }
84};
85
87struct InterfacesExtension
88 : public DialectExtension<InterfacesExtension, llzk::verif::VerifDialect, scf::SCFDialect> {
89 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(InterfacesExtension)
90
91 void apply(MLIRContext *context, llzk::verif::VerifDialect *, scf::SCFDialect *) const final {
93 }
94};
95
96} // namespace
97
98//===------------------------------------------------------------------===//
99// VerifDialect
100//===------------------------------------------------------------------===//
101
102void llzk::verif::attachInterfaces(MLIRContext &context) {
103 scf::WhileOp::attachInterface<ScfWhileExternalModel>(context);
104 scf::ForOp::attachInterface<ScfForExternalModel>(context);
105}
106
107void llzk::verif::registerExtensions(mlir::DialectRegistry &registry) {
108 registry.addExtension(
109 TypeID::get<InterfacesExtension>(), std::make_unique<InterfacesExtension>()
110 );
111}
112
113auto llzk::verif::VerifDialect::initialize() -> void {
114 // clang-format off
115 addOperations<
116 #define GET_OP_LIST
118 >();
119 // clang-format on
120 addInterfaces<LLZKDialectBytecodeInterface<VerifDialect>>();
121 declarePromisedInterface<llzk::verif::InvariantTargetOpInterface, scf::WhileOp>();
122 declarePromisedInterface<llzk::verif::InvariantTargetOpInterface, scf::ForOp>();
123}
void registerExtensions(mlir::DialectRegistry &registry)
Registers dialect extensions for the verif dialect.
Definition Dialect.cpp:107
void attachInterfaces(mlir::MLIRContext &context)
Attaches the interfaces defined by the verif dialect to upstream IR elements.