Skip to content

[circt-bmc] handle 'verif.formal' / 'verif.contract' op in module#10772

Open
Clo91eaf wants to merge 3 commits into
llvm:mainfrom
xinpian-tech:circt-bmc-verif-formal
Open

[circt-bmc] handle 'verif.formal' / 'verif.contract' op in module#10772
Clo91eaf wants to merge 3 commits into
llvm:mainfrom
xinpian-tech:circt-bmc-verif-formal

Conversation

@Clo91eaf

@Clo91eaf Clo91eaf commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

This fixes circt-bmc on verif.contract lowering results.

circt-bmc already supports verif.formal: its pipeline runs verif-lower-tests, which lowers a selected formal test into an hw.module before BMC lowering. The issue is specific to the IR shape produced by lower-contracts.

lower-contracts emits a generated verif.formal proof op, but also keeps the original hw.module in the surrounding builtin.module with the contract applied to it. In that apply-mode module, contract results are replaced by verif.symbolic_value, and contract ensures become verif.assume.

When invoking circt-bmc --module <generated-formal>, the intended target is only the generated verif.formal. However, the BMC pipeline runs over the whole builtin.module, so unrelated top-level tests and modules may still be processed. This patch detects when --module resolves to a verif.formal, keeps that formal and its top-level symbol dependencies, and removes unrelated top-level hw.module, verif.formal, and verif.simulation ops before running the existing BMC pipeline.

This also handles generated formals that depend on callee contract abstractions. After contract lowering, a caller proof may instantiate a callee whose contract has been applied as an abstraction. During BMC lowering, hw-flatten-modules inlines that callee abstraction into the selected target, which can leave verif.symbolic_value ops constrained by verif.assume inside the flattened module. The patch materializes those symbolic values as additional module inputs after flattening, preserving their nondeterministic semantics in a form the BMC pipeline can lower.

For example:

hw.module @Callee(in %a : i8, out y : i8) {
  %y = verif.contract %a : i8 {
    %ok = comb.icmp eq %y, %a : i8
    verif.ensure %ok : i1
  }
  hw.output %y : i8
}

hw.module @Caller(in %a : i8, out y : i8) {
  %callee.y = hw.instance "callee" @Callee(a: %a: i8) -> (y: i8)

  %y = verif.contract %callee.y : i8 {
    %ok = comb.icmp eq %y, %a : i8
    verif.ensure %ok : i1
  }

  hw.output %y : i8
}

would be lowered after --lower-contracts to

hw.module @Callee(in %a : i8, out y : i8) {
  %sym = verif.symbolic_value : i8
  %ok = comb.icmp eq %sym, %a : i8
  verif.assume %ok : i1
  hw.output %sym : i8
}

verif.formal @Caller_CheckContract_0 {
  %a = verif.symbolic_value : i8
  %callee.y = hw.instance "callee" @Callee(a: %a: i8) -> (y: i8)
  %ok = comb.icmp eq %callee.y, %a : i8
  verif.assert %ok : i1
}

Here the @Caller_CheckContract_0 is depending the @Callee and the hw-flatten-modules pass will flatten the @Callee in the @Caller_CheckContract_0 to

hw.module @Caller_CheckContract_0(...) {
  %a = ...  // caller proof input

  // Inlined from Callee abstraction:
  %callee.sym = verif.symbolic_value : i8
  %callee.ok = comb.icmp eq %callee.sym, %a : i8
  verif.assume %callee.ok : i1

  // Caller contract check:
  %caller.ok = comb.icmp eq %callee.sym, %a : i8
  verif.assert %caller.ok : i1
}

So that the @Callee can become an abstract module rather than a specified implement during the formal verification solving. But this can not be processed by circt-bmc because

'verif.symbolic_value' op expects parent op to be one of 'verif.formal, hw.module'

And this pr move it to the input so we can see the symbolic value of the children module as the symbolic value of the parent module.

hw.module @Caller_CheckContract_0(
  in %a : i8,
  in %symbolic_value : i8
) {
  %callee.ok = comb.icmp eq %symbolic_value, %a : i8
  verif.assume %callee.ok : i1

  %caller.ok = comb.icmp eq %symbolic_value, %a : i8
  verif.assert %caller.ok : i1
}

Testing

This PR adds regression coverage that starts from real verif.contract IR, runs --lower-contracts, and then invokes circt-bmc on the generated verif.formal, including a caller/callee case where the selected proof depends on an applied callee contract abstraction.

Assisted-by: Codex:GPT-5.5

@Clo91eaf Clo91eaf changed the title Circt bmc verif formal [circt-bmc] handle 'verif.formal' op in module Jul 5, 2026
@circt-bot

circt-bot Bot commented Jul 5, 2026

Copy link
Copy Markdown

Results of circt-tests run for 722bcb1 compared to results for 0228f99: no change to test results.

@Clo91eaf Clo91eaf changed the title [circt-bmc] handle 'verif.formal' op in module [circt-bmc] handle 'verif.formal' / 'verif.contract' op in module Jul 6, 2026
@Clo91eaf

Clo91eaf commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@TaoBi22 wdyt? In this way we can handle the verif.contract in circt-bmc!

@circt-bot

circt-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

Results of circt-tests run for 425108f compared to results for 0228f99: no change to test results.

@TaoBi22 TaoBi22 self-requested a review July 6, 2026 07:53
@TaoBi22

TaoBi22 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for looking into this @Clo91eaf! I wonder if it would make sense to make this hw.module input technique a new lowering strategy in LowerSymbolicValues rather than creating a new pass - WDYT?

@Clo91eaf

Clo91eaf commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

That's really make sense! I will try this new lowering strategy!

@Clo91eaf Clo91eaf force-pushed the circt-bmc-verif-formal branch 2 times, most recently from 29ecaf5 to 7eea901 Compare July 7, 2026 08:28

@TaoBi22 TaoBi22 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

auto result = module.walk([&](SymbolicValueOp op) -> WalkResult {
if (!op->getParentOfType<HWModuleOp>())
return op.emitError()
<< "cannot lower symbolic value to HW module input outside of an "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
<< "cannot lower symbolic value to HW module input outside of an "
<< "cannot lower symbolic value to hw.module input outside of an "


if (referencedModules.contains(hwModule.getModuleNameAttr())) {
hwModule.emitError()
<< "cannot lower symbolic values in instantiated module '"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we clarify that this is only the case for this particular lowering strategy?

@@ -0,0 +1,22 @@
// RUN: circt-opt %s --verif-lower-symbolic-values=mode=hw-input --split-input-file --verify-diagnostics | FileCheck %s

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these be moved into the existing test files for this pass?

@@ -0,0 +1,22 @@
// RUN: circt-opt %s --verif-lower-symbolic-values=mode=hw-input --split-input-file --verify-diagnostics | FileCheck %s

// CHECK-LABEL: hw.module @M(in %symbolic_value : i8, out y : i8)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these be regex rather than hardcoded names?

Comment thread test/Tools/circt-bmc/contract.mlir Outdated

// CHECK-NOT: verif.symbolic_value
// CHECK-LABEL: func.func @Callee(
// CHECK-SAME: %arg0: !smt.bv<8>, %arg1: !smt.bv<8>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above re: regex

Comment thread test/Tools/circt-bmc/contract.mlir Outdated
// CHECK-NOT: verif.symbolic_value
// CHECK-LABEL: func.func @Callee(
// CHECK-SAME: %arg0: !smt.bv<8>, %arg1: !smt.bv<8>
// CHECK: smt.assert

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe check what we're asserting too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. Thanks!

@circt-bot

circt-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

Results of circt-tests run for 7eea901 compared to results for 0228f99: no change to test results.

@Clo91eaf Clo91eaf force-pushed the circt-bmc-verif-formal branch from 7eea901 to da63082 Compare July 7, 2026 10:08
@circt-bot

circt-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

Results of circt-tests run for da63082 compared to results for 0228f99: no change to test results.

@Clo91eaf Clo91eaf requested a review from TaoBi22 July 7, 2026 17:31

@TaoBi22 TaoBi22 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Clo91eaf! A couple of remaining nits, let me know when those are addressed and you're happy for merge!

hw.output %0 : i8
}

// expected-error @+1 {{cannot lower symbolic values in instantiated module}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we include the full error message?

auto result = module.walk([&](SymbolicValueOp op) -> WalkResult {
if (!op->getParentOfType<HWModuleOp>())
return op.emitError()
<< "cannot lower symbolic value to hw.module input outside of an "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is missing a test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants