Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SMT] Add bv2int op #8049

Merged
merged 5 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/circt/Dialect/SMT/SMTBitVectorOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,19 @@ def RepeatOp : SMTBVOp<"repeat", [Pure]> {
}];
}

def BV2IntOp : SMTOp<"bv2int", [Pure]> {
let summary = "Convert an SMT bit-vector to an SMT integer.";
let description = [{
Adapted from the Z3 documentation:
Create an integer from the bit-vector argument `input`.
If `is_signed` is false, then the bit-vector `input` is treated as
unsigned. So the result is non-negative and in the range [0..2^N-1], where
N are the number of bits in `input`. If `is_signed` is true, `input` is
treated as a signed bit-vector.
}];
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
let arguments = (ins BitVectorType:$input, UnitAttr:$is_signed);
let results = (outs IntType:$result);
let assemblyFormat = [{$input (`signed` $is_signed^)? attr-dict `:` qualified(type($input))}];
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
}

#endif // CIRCT_DIALECT_SMT_SMTBITVECTOROPS_TD
3 changes: 2 additions & 1 deletion include/circt/Dialect/SMT/SMTVisitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class SMTOpVisitor {
// Bit-vector bitwise
BVNotOp, BVAndOp, BVOrOp, BVXOrOp,
// Other bit-vector ops
ConcatOp, ExtractOp, RepeatOp, BVCmpOp,
ConcatOp, ExtractOp, RepeatOp, BVCmpOp, BV2IntOp,
// Int arithmetic
IntAddOp, IntMulOp, IntSubOp, IntDivOp, IntModOp, IntCmpOp,
Int2BVOp,
Expand Down Expand Up @@ -106,6 +106,7 @@ class SMTOpVisitor {
HANDLE(ExtractOp, Unhandled);
HANDLE(RepeatOp, Unhandled);
HANDLE(BVCmpOp, Unhandled);
HANDLE(BV2IntOp, Unhandled);

// Int arithmetic
HANDLE(IntAddOp, Unhandled);
Expand Down
34 changes: 34 additions & 0 deletions integration_test/Dialect/SMT/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,40 @@ func.func @entry() {
smt.yield
}

// CHECK: unsat
// CHECK: Res: -1
smt.solver () : () -> () {
%c4 = smt.int.constant 4
%c4_bv16 = smt.bv.constant #smt.bv<4> : !smt.bv<16>
%bv2int = smt.bv2int %c4_bv16 : !smt.bv<16>
%eq = smt.distinct %c4, %bv2int : !smt.int
func.call @check(%eq) : (!smt.bool) -> ()
smt.yield
}

// CHECK: unsat
// CHECK: Res: -1
smt.solver () : () -> () {
%c-4 = smt.int.constant -4
%c-4_bv16 = smt.bv.constant #smt.bv<-4> : !smt.bv<16>
%bv2int = smt.bv2int %c-4_bv16 signed : !smt.bv<16>
%eq = smt.distinct %c-4, %bv2int : !smt.int
func.call @check(%eq) : (!smt.bool) -> ()
smt.yield
}

// Check that unsigned conversion actually produces unsigned result
// CHECK: unsat
// CHECK: Res: -1
smt.solver () : () -> () {
%c-4 = smt.int.constant -4
%c-4_bv16 = smt.bv.constant #smt.bv<-4> : !smt.bv<16>
%bv2int = smt.bv2int %c-4_bv16 : !smt.bv<16>
%eq = smt.eq %c-4, %bv2int : !smt.int
func.call @check(%eq) : (!smt.bool) -> ()
smt.yield
}

return
}

Expand Down
26 changes: 24 additions & 2 deletions lib/Conversion/SMTToZ3LLVM/LowerSMTToZ3LLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,27 @@ struct Int2BVOpLowering : public SMTLoweringPattern<Int2BVOp> {
}
};

/// Lower `smt.bv2int` operations to the following Z3 API function call.
/// ```
/// Z3_ast Z3_API Z3_mk_bv2int(Z3_context c, Z3_ast t1, bool is_signed)
/// ```
struct BV2IntOpLowering : public SMTLoweringPattern<BV2IntOp> {
using SMTLoweringPattern::SMTLoweringPattern;

LogicalResult
matchAndRewrite(BV2IntOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const final {
// FIXME: ideally we don't want to use i1 here, since bools can sometimes be
// compiled to wider widths in LLVM
Value isSignedConst = rewriter.create<LLVM::ConstantOp>(
op->getLoc(), rewriter.getI1Type(), op.getIsSigned());
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
rewriter.replaceOp(op,
buildPtrAPICall(rewriter, op.getLoc(), "Z3_mk_bv2int",
{adaptor.getInput(), isSignedConst}));
return success();
}
};

/// Lower `smt.bv.cmp` operations to one of the following Z3 API function calls,
/// performing two's complement comparison, depending on the predicate
/// attribute.
Expand Down Expand Up @@ -1482,8 +1503,9 @@ void circt::populateSMTToZ3LLVMConversionPatterns(
RepeatOpLowering, ExtractOpLowering, BoolConstantOpLowering,
IntConstantOpLowering, ArrayBroadcastOpLowering, BVCmpOpLowering,
IntCmpOpLowering, IntAbsOpLowering, Int2BVOpLowering,
QuantifierLowering<ForallOp>, QuantifierLowering<ExistsOp>>(
converter, patterns.getContext(), globals, options);
BV2IntOpLowering, QuantifierLowering<ForallOp>,
QuantifierLowering<ExistsOp>>(converter, patterns.getContext(),
globals, options);
}

void LowerSMTToZ3LLVMPass::runOnOperation() {
Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/SMT/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_circt_dialect_library(CIRCTSMT
Support

LINK_LIBS PUBLIC
CIRCTSupport
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
MLIRIR
MLIRInferTypeOpInterface
MLIRSideEffectInterfaces
Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/SMT/SMTOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "circt/Dialect/SMT/SMTOps.h"
#include "circt/Support/CustomDirectiveImpl.h"
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
#include "mlir/IR/Builders.h"
#include "mlir/IR/OpImplementation.h"
#include "llvm/ADT/APSInt.h"
Expand Down
5 changes: 2 additions & 3 deletions lib/Target/ExportSMTLIB/ExportSMTLIB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,8 @@ struct StatementVisitor
mlir::raw_indented_ostream &stream,
ValueMap &valueMap) {
// Ignore operations which are handled in the Expression Visitor.
if (isa<smt::Int2BVOp>(op))
return op->emitError(
"int2bv operations are not supported for SMTLIB emission");
if (isa<smt::Int2BVOp, BV2IntOp>(op))
return op->emitError("operation not supported for SMTLIB emission");

return success();
Comment on lines +605 to 608
Copy link
Member

Choose a reason for hiding this comment

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

I think just

  return op->emitError("operation not supported for SMTLIB emission");

should work instead of

    if (isa<smt::Int2BVOp, BV2IntOp>(op))
      return op->emitError("operation not supported for SMTLIB emission");

    return success();

to generically emit this error if we add new operations that we don't explicitly add support for in ExportSMTLIB (i.e., should be more robust)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah I see, sorry for the misunderstanding before!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just checked and this actually breaks all the ExportSMTLIB tests - I'm not super clear on the Visitor infrastructure so I'm not sure why yet, but from a brief scan it looks like getting rid of the condition causes the error to throw on the first non-ConstantLike op the export pass encounters.

}
Expand Down
7 changes: 7 additions & 0 deletions test/Conversion/SMTToZ3LLVM/smt-to-z3-llvm.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,13 @@ func.func @test(%arg0: i32) {
// CHECK: llvm.call @Z3_mk_int2bv({{%[0-9a-zA-Z_]+}}, [[WIDTHCONST]], [[C123]]) : (!llvm.ptr, i32, !llvm.ptr) -> !llvm.ptr
smt.int2bv %10 : !smt.bv<4>

// CHECK: [[UNSIGNEDCONST:%.+]] = llvm.mlir.constant(false) : i1
// CHECK: llvm.call @Z3_mk_bv2int({{%[0-9a-zA-Z_]+}}, [[BV0]], [[UNSIGNEDCONST]]) : (!llvm.ptr, !llvm.ptr, i1) -> !llvm.ptr
smt.bv2int %c0_bv4 : !smt.bv<4>
// CHECK: [[SIGNEDCONST:%.+]] = llvm.mlir.constant(true) : i1
// CHECK: llvm.call @Z3_mk_bv2int({{%[0-9a-zA-Z_]+}}, [[BV0]], [[SIGNEDCONST]]) : (!llvm.ptr, !llvm.ptr, i1) -> !llvm.ptr
smt.bv2int %c0_bv4 signed : !smt.bv<4>

// CHECK: [[C0:%.+]] = llvm.mlir.constant(0 : i32)
// CHECK: [[C2:%.+]] = llvm.mlir.constant(2 : i32)
// CHECK: [[ZERO:%.+]] = llvm.mlir.zero : !llvm.ptr
Expand Down
9 changes: 9 additions & 0 deletions test/Dialect/SMT/bitvector-errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,12 @@ func.func @repeat_result_type_bitwidth_too_large(%arg0: !smt.bv<1844674407370955
smt.bv.repeat 2 times %arg0 : !smt.bv<18446744073709551612>
return
}

// -----

func.func @invalid_bv2int_signedness() {
%c5_bv32 = smt.bv.constant #smt.bv<5> : !smt.bv<32>
// expected-error @below {{expected ':'}}
%bv2int = smt.bv2int %c5_bv32 unsigned : !smt.bv<32>
return
}
5 changes: 5 additions & 0 deletions test/Dialect/SMT/bitvectors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,10 @@ func.func @bitvectors() {
// CHECK: %{{.*}} = smt.bv.repeat 2 times [[C0]] {smt.some_attr} : !smt.bv<32>
%27 = smt.bv.repeat 2 times %c {smt.some_attr} : !smt.bv<32>

// CHECK: %{{.*}} = smt.bv2int [[C0]] {smt.some_attr} : !smt.bv<32>
%29 = smt.bv2int %c {smt.some_attr} : !smt.bv<32>
// CHECK: %{{.*}} = smt.bv2int [[C0]] signed {smt.some_attr} : !smt.bv<32>
%28 = smt.bv2int %c signed {smt.some_attr} : !smt.bv<32>

return
}
7 changes: 7 additions & 0 deletions test/Target/ExportSMTLIB/bitvector-errors.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: circt-translate --export-smtlib %s --split-input-file --verify-diagnostics

smt.solver () : () -> () {
%0 = smt.bv.constant #smt.bv<5> : !smt.bv<16>
// expected-error @below {{operation not supported for SMTLIB emission}}
%1 = smt.bv2int %0 signed : !smt.bv<16>
}
2 changes: 1 addition & 1 deletion test/Target/ExportSMTLIB/integer-errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

smt.solver () : () -> () {
%0 = smt.int.constant 5
// expected-error @below {{int2bv operations are not supported for SMTLIB emission}}
// expected-error @below {{operation not supported for SMTLIB emission}}
%1 = smt.int2bv %0 : !smt.bv<4>
}
Loading