[LEC] Clean up LogicExporter and add missing namespace; NFC

Remove some code duplication in `LogicExporter.cpp` and add missing
`circt` namespaces around the library pieces.
This commit is contained in:
Fabian Schuiki 2023-05-27 10:53:22 -07:00
parent eb5090b4ba
commit e2b602dec7
No known key found for this signature in database
GPG Key ID: C42F5825FC5275E6
7 changed files with 289 additions and 611 deletions

View File

@ -25,6 +25,8 @@
#include <string>
#include <z3++.h>
namespace circt {
/// The representation of a circuit within a logical engine.
///
/// This class defines a circuit as an abstraction of its underlying
@ -113,4 +115,6 @@ private:
llvm::DenseMap<mlir::Value, z3::expr> exprTable;
};
} // namespace circt
#endif // TOOLS_CIRCT_LEC_CIRCUIT_H

View File

@ -24,6 +24,8 @@
#include "llvm/ADT/StringRef.h"
#include <string>
namespace circt {
/// A class traversing MLIR IR to extrapolate the logic of a given circuit.
///
/// This class implements a MLIR exporter which searches the IR for the
@ -37,103 +39,9 @@ public:
/// Initializes the exporting by visiting the builtin module.
mlir::LogicalResult run(mlir::ModuleOp &module);
mlir::LogicalResult run(hw::HWModuleOp &module);
private:
/// This class provides logic-exporting functions for the implemented
/// operations, along with a dispatcher to visit the correct handler.
struct Visitor
: public circt::hw::StmtVisitor<Visitor, mlir::LogicalResult,
Solver::Circuit *>,
public circt::hw::TypeOpVisitor<Visitor, mlir::LogicalResult,
Solver::Circuit *>,
public circt::comb::CombinationalVisitor<Visitor, mlir::LogicalResult,
Solver::Circuit *> {
// StmtVisitor definitions
// Handle implemented `hw` statement operations.
static mlir::LogicalResult visitStmt(circt::hw::InstanceOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitStmt(circt::hw::OutputOp &op,
Solver::Circuit *circuit);
/// Collects unhandled `hw` statement operations.
static mlir::LogicalResult visitStmt(mlir::Operation *op,
Solver::Circuit *circuit);
/// Handles invalid `hw` statement operations.
mlir::LogicalResult visitInvalidStmt(mlir::Operation *op,
Solver::Circuit *circuit);
// TypeOpVisitor definitions
// Handle implemented `hw` type operations.
static mlir::LogicalResult visitTypeOp(circt::hw::ConstantOp &op,
Solver::Circuit *circuit);
/// Collects unhandled `hw` type operations.
static mlir::LogicalResult visitTypeOp(mlir::Operation *op,
Solver::Circuit *circuit);
/// Handles invalid `hw` type operations.
mlir::LogicalResult visitInvalidTypeOp(mlir::Operation *op,
Solver::Circuit *circuit);
// CombinationalVisitor definitions
// Handle implemented `comb` operations.
static mlir::LogicalResult visitComb(circt::comb::AddOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::AndOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ConcatOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::DivSOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::DivUOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ExtractOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ICmpOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ModSOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ModUOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::MulOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::MuxOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::OrOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ParityOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ReplicateOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ShlOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ShrSOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::ShrUOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::SubOp &op,
Solver::Circuit *circuit);
static mlir::LogicalResult visitComb(circt::comb::XorOp &op,
Solver::Circuit *circuit);
// Additional definitions
/// Handles `builtin.module` logic exporting.
static mlir::LogicalResult visitBuiltin(mlir::ModuleOp &op,
Solver::Circuit *circuit,
llvm::StringRef targetModule);
/// Handles `hw.module` logic exporting.
static mlir::LogicalResult visitHW(circt::hw::HWModuleOp &op,
Solver::Circuit *circuit);
/// Reports a failure whenever an unhandled operation is visited.
static mlir::LogicalResult visitUnhandledOp(mlir::Operation *op);
/// Dispatches an operation to the appropriate visit function.
mlir::LogicalResult dispatch(mlir::Operation *op, Solver::Circuit *circuit);
};
// For Solver::Circuit::addInstance to access Visitor::visitHW.
friend Solver::Circuit;
@ -144,4 +52,6 @@ private:
Solver::Circuit *circuit;
};
} // namespace circt
#endif // TOOLS_CIRCT_LEC_LOGICEXPORTER_H

View File

@ -19,6 +19,8 @@
#include "mlir/IR/Value.h"
#include <z3++.h>
namespace circt {
/// A satisfiability checker for circuit equivalence
///
/// This class interfaces with an external SMT solver acting as a logical
@ -72,4 +74,6 @@ private:
bool statisticsOpt;
};
} // namespace circt
#endif // TOOLS_CIRCT_LEC_SOLVER_H

View File

@ -19,8 +19,11 @@
#define DEBUG_TYPE "lec-circuit"
using namespace mlir;
using namespace circt;
/// Add an input to the circuit; internally a new value gets allocated.
void Solver::Circuit::addInput(mlir::Value value) {
void Solver::Circuit::addInput(Value value) {
LLVM_DEBUG(lec::dbgs() << name << " addInput\n");
lec::Scope indent;
z3::expr input = allocateValue(value);
@ -28,7 +31,7 @@ void Solver::Circuit::addInput(mlir::Value value) {
}
/// Add an output to the circuit.
void Solver::Circuit::addOutput(mlir::Value value) {
void Solver::Circuit::addOutput(Value value) {
LLVM_DEBUG(lec::dbgs() << name << " addOutput\n");
// Referenced value already assigned, fetching from expression table.
z3::expr output = fetchExpr(value);
@ -45,8 +48,7 @@ llvm::ArrayRef<z3::expr> Solver::Circuit::getOutputs() { return outputs; }
// `hw` dialect operations
//===----------------------------------------------------------------------===//
void Solver::Circuit::addConstant(mlir::Value opResult,
const mlir::APInt &opValue) {
void Solver::Circuit::addConstant(Value opResult, const APInt &opValue) {
LLVM_DEBUG(lec::dbgs() << name << " addConstant\n");
lec::Scope indent;
allocateConstant(opResult, opValue);
@ -54,8 +56,7 @@ void Solver::Circuit::addConstant(mlir::Value opResult,
void Solver::Circuit::addInstance(llvm::StringRef instanceName,
circt::hw::HWModuleOp op,
mlir::OperandRange arguments,
mlir::ResultRange results) {
OperandRange arguments, ResultRange results) {
LLVM_DEBUG(lec::dbgs() << name << " addInstance\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "instance name: " << instanceName << "\n");
@ -66,7 +67,7 @@ void Solver::Circuit::addInstance(llvm::StringRef instanceName,
Circuit instance(name + "@" + instanceName + suffix, solver);
// Export logic to the instance's circuit by visiting the IR of the
// instanced module.
auto res = LogicExporter::Visitor::visitHW(op, &instance);
auto res = LogicExporter(op.getModuleName(), &instance).run(op);
assert(res.succeeded() && "Instance visit failed");
// Constrain the inputs and outputs of the instanced circuit to, respectively,
@ -75,7 +76,7 @@ void Solver::Circuit::addInstance(llvm::StringRef instanceName,
LLVM_DEBUG(lec::dbgs() << "instance inputs:\n");
lec::Scope indent;
auto *input = instance.inputs.begin();
for (mlir::Value argument : arguments) {
for (Value argument : arguments) {
LLVM_DEBUG(lec::dbgs() << "input\n");
z3::expr argExpr = fetchExpr(argument);
solver.solver.add(argExpr == *input++);
@ -96,32 +97,28 @@ void Solver::Circuit::addInstance(llvm::StringRef instanceName,
// `comb` dialect operations
//===----------------------------------------------------------------------===//
void Solver::Circuit::performAdd(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performAdd(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Add\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return op1 + op2; });
}
void Solver::Circuit::performAnd(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performAnd(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform And\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return z3::operator&(op1, op2); });
}
void Solver::Circuit::performConcat(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performConcat(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Concat\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return z3::concat(op1, op2); });
}
void Solver::Circuit::performDivS(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performDivS(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform DivS\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -132,8 +129,7 @@ void Solver::Circuit::performDivS(mlir::Value result, mlir::Value lhs,
constrainResult(result, op);
}
void Solver::Circuit::performDivU(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performDivU(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform DivU\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -144,7 +140,7 @@ void Solver::Circuit::performDivU(mlir::Value result, mlir::Value lhs,
constrainResult(result, op);
}
void Solver::Circuit::performExtract(mlir::Value result, mlir::Value input,
void Solver::Circuit::performExtract(Value result, Value input,
uint32_t lowBit) {
LLVM_DEBUG(lec::dbgs() << name << " performExtract\n");
lec::Scope indent;
@ -156,10 +152,9 @@ void Solver::Circuit::performExtract(mlir::Value result, mlir::Value input,
constrainResult(result, extract);
}
mlir::LogicalResult
Solver::Circuit::performICmp(mlir::Value result,
circt::comb::ICmpPredicate predicate,
mlir::Value lhs, mlir::Value rhs) {
LogicalResult Solver::Circuit::performICmp(Value result,
circt::comb::ICmpPredicate predicate,
Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " performICmp\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -206,15 +201,14 @@ Solver::Circuit::performICmp(mlir::Value result,
case circt::comb::ICmpPredicate::wne:
result.getDefiningOp()->emitError(
"n-state logic predicates are not supported");
return mlir::failure();
return failure();
};
constrainResult(result, icmp);
return mlir::success();
return success();
}
void Solver::Circuit::performModS(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performModS(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform ModS\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -225,8 +219,7 @@ void Solver::Circuit::performModS(mlir::Value result, mlir::Value lhs,
constrainResult(result, op);
}
void Solver::Circuit::performModU(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performModU(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform ModU\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -237,17 +230,15 @@ void Solver::Circuit::performModU(mlir::Value result, mlir::Value lhs,
constrainResult(result, op);
}
void Solver::Circuit::performMul(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performMul(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Mul\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return op1 * op2; });
}
void Solver::Circuit::performMux(mlir::Value result, mlir::Value cond,
mlir::Value trueValue,
mlir::Value falseValue) {
void Solver::Circuit::performMux(Value result, Value cond, Value trueValue,
Value falseValue) {
LLVM_DEBUG(lec::dbgs() << name << " performMux\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "cond:\n");
@ -261,15 +252,14 @@ void Solver::Circuit::performMux(mlir::Value result, mlir::Value cond,
constrainResult(result, mux);
}
void Solver::Circuit::performOr(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performOr(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Or\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return op1 | op2; });
}
void Solver::Circuit::performParity(mlir::Value result, mlir::Value input) {
void Solver::Circuit::performParity(Value result, Value input) {
LLVM_DEBUG(lec::dbgs() << name << " performParity\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "input:\n");
@ -287,7 +277,7 @@ void Solver::Circuit::performParity(mlir::Value result, mlir::Value input) {
constrainResult(result, parity);
}
void Solver::Circuit::performReplicate(mlir::Value result, mlir::Value input) {
void Solver::Circuit::performReplicate(Value result, Value input) {
LLVM_DEBUG(lec::dbgs() << name << " performReplicate\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "input:\n");
@ -306,8 +296,7 @@ void Solver::Circuit::performReplicate(mlir::Value result, mlir::Value input) {
constrainResult(result, replicate);
}
void Solver::Circuit::performShl(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performShl(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform Shl\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -319,8 +308,7 @@ void Solver::Circuit::performShl(mlir::Value result, mlir::Value lhs,
}
// Arithmetic shift right.
void Solver::Circuit::performShrS(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performShrS(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform ShrS\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -332,8 +320,7 @@ void Solver::Circuit::performShrS(mlir::Value result, mlir::Value lhs,
}
// Logical shift right.
void Solver::Circuit::performShrU(mlir::Value result, mlir::Value lhs,
mlir::Value rhs) {
void Solver::Circuit::performShrU(Value result, Value lhs, Value rhs) {
LLVM_DEBUG(lec::dbgs() << name << " perform ShrU\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "lhs:\n");
@ -344,16 +331,14 @@ void Solver::Circuit::performShrU(mlir::Value result, mlir::Value lhs,
constrainResult(result, op);
}
void Solver::Circuit::performSub(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performSub(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Sub\n");
lec::Scope indent;
variadicOperation(result, operands,
[](auto op1, auto op2) { return op1 - op2; });
}
void Solver::Circuit::performXor(mlir::Value result,
mlir::OperandRange operands) {
void Solver::Circuit::performXor(Value result, OperandRange operands) {
LLVM_DEBUG(lec::dbgs() << name << " perform Xor\n");
lec::Scope indent;
variadicOperation(result, operands,
@ -363,14 +348,14 @@ void Solver::Circuit::performXor(mlir::Value result,
/// Helper function for performing a variadic operation: it executes a lambda
/// over a range of operands.
void Solver::Circuit::variadicOperation(
mlir::Value result, mlir::OperandRange operands,
Value result, OperandRange operands,
llvm::function_ref<z3::expr(const z3::expr &, const z3::expr &)>
operation) {
LLVM_DEBUG(lec::dbgs() << "variadic operation\n");
lec::Scope indent;
// Vacuous base case.
auto it = operands.begin();
mlir::Value operand = *it;
Value operand = *it;
z3::expr varOp = exprTable.find(operand)->second;
{
LLVM_DEBUG(lec::dbgs() << "first operand:\n");
@ -394,11 +379,11 @@ void Solver::Circuit::variadicOperation(
/// Allocates an IR value in the logical backend and returns its representing
/// expression.
z3::expr Solver::Circuit::allocateValue(mlir::Value value) {
z3::expr Solver::Circuit::allocateValue(Value value) {
std::string valueName = name + "%" + std::to_string(assignments++);
LLVM_DEBUG(lec::dbgs() << "allocating value:\n");
lec::Scope indent;
mlir::Type type = value.getType();
Type type = value.getType();
assert(type.isSignlessInteger() && "Unsupported type");
unsigned int width = type.getIntOrFloatBitWidth();
// Technically allowed for the `hw` dialect but
@ -409,8 +394,8 @@ z3::expr Solver::Circuit::allocateValue(mlir::Value value) {
LLVM_DEBUG(lec::printValue(value));
auto exprInsertion = exprTable.insert(std::pair(value, expr));
assert(exprInsertion.second && "Value not inserted in expression table");
mlir::Builder builder(solver.mlirCtx);
mlir::StringAttr symbol = builder.getStringAttr(valueName);
Builder builder(solver.mlirCtx);
StringAttr symbol = builder.getStringAttr(valueName);
auto symInsertion = solver.symbolTable.insert(std::pair(symbol, value));
assert(symInsertion.second && "Value not inserted in symbol table");
return expr;
@ -418,8 +403,7 @@ z3::expr Solver::Circuit::allocateValue(mlir::Value value) {
/// Allocates a constant value in the logical backend and returns its
/// representing expression.
void Solver::Circuit::allocateConstant(mlir::Value result,
const mlir::APInt &value) {
void Solver::Circuit::allocateConstant(Value result, const APInt &value) {
// `The constant operation produces a constant value
// of standard integer type without a sign`
const z3::expr constant =
@ -431,7 +415,7 @@ void Solver::Circuit::allocateConstant(mlir::Value result,
}
/// Fetches the corresponding logical expression for a given IR value.
z3::expr Solver::Circuit::fetchExpr(mlir::Value &value) {
z3::expr Solver::Circuit::fetchExpr(Value &value) {
z3::expr expr = exprTable.find(value)->second;
lec::Scope indent;
LLVM_DEBUG(lec::printExpr(expr));
@ -441,7 +425,7 @@ z3::expr Solver::Circuit::fetchExpr(mlir::Value &value) {
/// Constrains the result of a MLIR operation to be equal a given logical
/// express, simulating an assignment.
void Solver::Circuit::constrainResult(mlir::Value &result, z3::expr &expr) {
void Solver::Circuit::constrainResult(Value &result, z3::expr &expr) {
LLVM_DEBUG(lec::dbgs() << "constraining result:\n");
lec::Scope indent;
{

View File

@ -18,440 +18,211 @@
#define DEBUG_TYPE "lec-exporter"
using namespace circt;
using namespace mlir;
namespace {
/// Helper function to provide a common debug formatting for
/// an operation's list of operands.
template <class ConcreteOp>
static void debugOperands(ConcreteOp op) {
for (const mlir::OpOperand &operand : op->getOpOperands()) {
mlir::Value value = operand.get();
lec::dbgs() << "Operand:\n";
lec::Scope indent;
lec::printValue(value);
}
}
/// Helper function to provide a common debug formatting for
/// an operation's result.
static void debugOpResult(mlir::Value result) {
lec::dbgs() << "Result:\n";
lec::Scope indent;
lec::printValue(result);
}
/// This class provides logic-exporting functions for the implemented
/// operations, along with a dispatcher to visit the correct handler.
struct Visitor : public hw::StmtVisitor<Visitor, LogicalResult>,
public hw::TypeOpVisitor<Visitor, LogicalResult>,
public comb::CombinationalVisitor<Visitor, LogicalResult> {
using hw::StmtVisitor<Visitor, LogicalResult>::visitStmt;
using hw::TypeOpVisitor<Visitor, LogicalResult>::visitTypeOp;
using comb::CombinationalVisitor<Visitor, LogicalResult>::visitComb;
friend class hw::StmtVisitor<Visitor, LogicalResult>;
friend class hw::TypeOpVisitor<Visitor, LogicalResult>;
friend class comb::CombinationalVisitor<Visitor, LogicalResult>;
/// Helper function to provide a common debug formatting for
/// an operation's list of results.
template <class ConcreteOp>
static void debugOpResults(mlir::OpTrait::VariadicResults<ConcreteOp> *op) {
lec::dbgs() << "Results:\n";
for (mlir::OpResult result : op->getResults()) {
lec::Scope indent;
lec::dbgs() << "#" << result.getResultNumber() << " ";
debugOpResult(result);
}
}
Visitor(Solver::Circuit *circuit) : circuit(circuit) {}
Solver::Circuit *circuit;
/// Helper function to provide a common debug formatting for
/// an operation's list of attributes.
static void debugAttributes(llvm::ArrayRef<mlir::NamedAttribute> attributes) {
lec::dbgs() << "Attributes:\n";
lec::Scope indent;
for (mlir::NamedAttribute attr : attributes) {
lec::dbgs() << attr.getName().getValue() << ": " << attr.getValue() << "\n";
}
}
} // anonymous namespace
/// Initializes the exporter by visiting the builtin module.
mlir::LogicalResult LogicExporter::run(mlir::ModuleOp &builtinModule) {
mlir::LogicalResult outcome =
Visitor::visitBuiltin(builtinModule, circuit, moduleName);
return outcome;
}
//===----------------------------------------------------------------------===//
// Visitor implementation
//===----------------------------------------------------------------------===//
// StmtVisitor implementation
//===----------------------------------------------------------------------===//
mlir::LogicalResult
LogicExporter::Visitor::visitStmt(circt::hw::InstanceOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting hw.instance\n");
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << op->getName() << "\n");
LLVM_DEBUG(debugAttributes(op->getAttrs()));
LLVM_DEBUG(debugOperands(op));
LLVM_DEBUG(debugOpResults(&op));
llvm::StringRef instanceName = op.getInstanceName();
LLVM_DEBUG(lec::dbgs() << "Instance name: " << instanceName << "\n");
llvm::StringRef targetModule = op.getModuleName();
LLVM_DEBUG(lec::dbgs() << "Target module name: " << targetModule << "\n");
llvm::Optional<llvm::StringRef> innerSym = op.getInnerSym();
LLVM_DEBUG(lec::dbgs() << "Inner symbol: " << innerSym << "\n");
auto hwModule = llvm::dyn_cast_if_present<circt::hw::HWModuleOp>(
op.getReferencedModule());
if (hwModule) {
circuit->addInstance(instanceName.str(), hwModule, op->getOperands(),
op->getResults());
return mlir::success();
}
op.emitError("expected referenced module `" + targetModule + "` not found");
return mlir::failure();
}
mlir::LogicalResult
LogicExporter::Visitor::visitStmt(circt::hw::OutputOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting hw.output\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
for (auto operand : op.getOperands())
circuit->addOutput(operand);
return mlir::success();
}
/// Collects unhandled `hw` statement operations.
mlir::LogicalResult
LogicExporter::Visitor::visitStmt(mlir::Operation *op,
Solver::Circuit *circuit) {
return visitUnhandledOp(op);
}
/// Handles invalid `hw` statement operations.
mlir::LogicalResult
LogicExporter::Visitor::visitInvalidStmt(mlir::Operation *op,
Solver::Circuit *circuit) {
// op is not valid for StmtVisitor.
// Attempt dispatching it to TypeOpVisitor next.
return dispatchTypeOpVisitor(op, circuit);
}
//===----------------------------------------------------------------------===//
// TypeOpVisitor implementation
//===----------------------------------------------------------------------===//
mlir::LogicalResult
LogicExporter::Visitor::visitTypeOp(circt::hw::ConstantOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting hw.constant\n");
lec::Scope indent;
mlir::Value result = op.getResult();
LLVM_DEBUG(lec::printValue(result));
mlir::APInt value = op.getValue();
LLVM_DEBUG(lec::printAPInt(value));
circuit->addConstant(result, value);
return mlir::success();
}
/// Collects unhandled `hw` type operations.
mlir::LogicalResult
LogicExporter::Visitor::visitTypeOp(mlir::Operation *op,
Solver::Circuit *circuit) {
return visitUnhandledOp(op);
}
/// Handles invalid `hw` type operations.
mlir::LogicalResult
LogicExporter::Visitor::visitInvalidTypeOp(mlir::Operation *op,
Solver::Circuit *circuit) {
// op is neither valid for StmtVisitor nor TypeOpVisitor.
// Attempt dispatching it to CombinationalVisitor next.
return dispatchCombinationalVisitor(op, circuit);
}
//===----------------------------------------------------------------------===//
// CombinationalVisitor implementation
//===----------------------------------------------------------------------===//
// This macro is used to reject the visited operation when n-state logic is
// not supported.
#define REJECT_N_STATE_LOGIC() \
if (!twoState) { \
op.emitError("`bin` attribute unset, but n-state logic is not supported"); \
return mlir::failure(); \
}
// This macro implements the visiting function for a `comb` operation accepting
// a variadic number of operands.
template <typename OpTy, typename FnTy>
static mlir::LogicalResult visitVariadicCombOp(Solver::Circuit *circuit,
OpTy op, FnTy fn) {
LLVM_DEBUG(lec::dbgs() << "Visiting " << op->getName() << "\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
bool twoState = op.getTwoState();
REJECT_N_STATE_LOGIC();
mlir::Value result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
(circuit->*fn)(result, op.getOperands());
return mlir::success();
}
// This macro implements the visiting function for a `comb` operation accepting
// two operands.
template <typename OpTy, typename FnTy>
static mlir::LogicalResult visitBinaryCombOp(Solver::Circuit *circuit, OpTy op,
FnTy fn) {
LLVM_DEBUG(lec::dbgs() << "Visiting " << op->getName() << "\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
bool twoState = op.getTwoState();
REJECT_N_STATE_LOGIC();
auto lhs = op.getLhs();
auto rhs = op.getRhs();
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
(circuit->*fn)(result, lhs, rhs);
return mlir::success();
}
// This macro implements the visiting function for a `comb` operation accepting
// one operand.
template <typename OpTy, typename FnTy>
static mlir::LogicalResult visitUnaryCombOp(Solver::Circuit *circuit, OpTy op,
FnTy fn) {
LLVM_DEBUG(lec::dbgs() << "Visiting " << op->getName() << "\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
bool twoState = op.getTwoState();
REJECT_N_STATE_LOGIC();
auto input = op.getInput();
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
(circuit->*fn)(result, input);
return mlir::success();
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::AddOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performAdd);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::AndOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performAnd);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ConcatOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting comb.concat\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
mlir::Value result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
circuit->performConcat(result, op.getOperands());
return mlir::success();
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::DivSOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performDivS);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::DivUOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performDivU);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ExtractOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting comb.extract\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
auto input = op.getInput();
uint32_t lowBit = op.getLowBit();
LLVM_DEBUG(lec::dbgs() << "lowBit: " << lowBit << "\n");
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
circuit->performExtract(result, input, lowBit);
return mlir::success();
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ICmpOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting comb.icmp\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
bool twoState = op.getTwoState();
REJECT_N_STATE_LOGIC();
circt::comb::ICmpPredicate predicate = op.getPredicate();
auto lhs = op.getLhs();
auto rhs = op.getRhs();
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
mlir::LogicalResult comparisonResult =
circuit->performICmp(result, predicate, lhs, rhs);
return comparisonResult;
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ModSOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performModS);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ModUOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performModU);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::MulOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performMul);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::MuxOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting comb.mux\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
bool twoState = op.getTwoState();
REJECT_N_STATE_LOGIC();
auto cond = op.getCond();
auto trueValue = op.getTrueValue();
auto falseValue = op.getFalseValue();
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
circuit->performMux(result, cond, trueValue, falseValue);
return mlir::success();
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::OrOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performOr);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ParityOp &op,
Solver::Circuit *circuit) {
return visitUnaryCombOp(circuit, op, &Solver::Circuit::performParity);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ReplicateOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting comb.replicate\n");
lec::Scope indent;
LLVM_DEBUG(debugOperands(op));
auto input = op.getInput();
auto result = op.getResult();
LLVM_DEBUG(debugOpResult(result));
circuit->performReplicate(result, input);
return mlir::success();
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ShlOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performShl);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ShrSOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performShrS);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::ShrUOp &op,
Solver::Circuit *circuit) {
return visitBinaryCombOp(circuit, op, &Solver::Circuit::performShrU);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::SubOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performSub);
}
mlir::LogicalResult
LogicExporter::Visitor::visitComb(circt::comb::XorOp &op,
Solver::Circuit *circuit) {
return visitVariadicCombOp(circuit, op, &Solver::Circuit::performXor);
}
//===----------------------------------------------------------------------===//
// Additional Visitor implementations
//===----------------------------------------------------------------------===//
/// Handles `builtin.module` logic exporting.
mlir::LogicalResult
LogicExporter::Visitor::visitBuiltin(mlir::ModuleOp &op,
Solver::Circuit *circuit,
llvm::StringRef targetModule) {
LLVM_DEBUG(lec::dbgs() << "Visiting `builtin.module`\n");
lec::Scope indent;
// Currently only `hw.module` handling is implemented.
for (auto hwModule : op.getOps<circt::hw::HWModuleOp>()) {
llvm::StringRef moduleName = hwModule.getName();
LLVM_DEBUG(lec::dbgs() << "found `hw.module@" << moduleName << "`\n");
// When no module name is specified the first module encountered is
// selected.
if (targetModule.empty() || moduleName == targetModule) {
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "proceeding with this module\n");
return visitHW(hwModule, circuit);
/// Handles `builtin.module` logic exporting.
LogicalResult visit(ModuleOp op, llvm::StringRef targetModule) {
for (auto hwModule : op.getOps<hw::HWModuleOp>()) {
if (targetModule.empty() || hwModule.getName() == targetModule) {
LLVM_DEBUG(llvm::dbgs()
<< "Using module `" << hwModule.getName() << "`\n");
return visit(hwModule);
}
}
op.emitError("module not found");
return failure();
}
op.emitError("expected `" + targetModule + "` module not found");
return mlir::failure();
}
/// Handles `hw.module` logic exporting.
mlir::LogicalResult LogicExporter::Visitor::visitHW(circt::hw::HWModuleOp &op,
Solver::Circuit *circuit) {
LLVM_DEBUG(lec::dbgs() << "Visiting `hw.module@" << op.getName() << "`\n");
lec::Scope indent;
LLVM_DEBUG(debugAttributes(op->getAttrs()));
LLVM_DEBUG(lec::dbgs() << "Arguments:\n");
for (mlir::BlockArgument argument : op.getArguments()) {
lec::Scope indent;
LLVM_DEBUG(lec::dbgs() << "Argument\n");
{
lec::Scope indent;
LLVM_DEBUG(lec::printValue(argument));
/// Handles `hw.module` logic exporting.
LogicalResult visit(hw::HWModuleOp op) {
for (auto argument : op.getArguments())
circuit->addInput(argument);
for (auto &op : op.getOps())
if (failed(dispatch(&op)))
return failure();
return success();
}
LogicalResult visitUnhandledOp(Operation *op) {
op->emitOpError("not supported");
return failure();
}
/// Dispatches an operation to the appropriate visit function.
LogicalResult dispatch(Operation *op) { return dispatchStmtVisitor(op); }
//===--------------------------------------------------------------------===//
// hw::StmtVisitor
//===--------------------------------------------------------------------===//
LogicalResult visitStmt(hw::InstanceOp op) {
if (auto hwModule =
llvm::dyn_cast<hw::HWModuleOp>(op.getReferencedModule())) {
circuit->addInstance(op.getInstanceName(), hwModule, op->getOperands(),
op->getResults());
return success();
}
circuit->addInput(argument);
op.emitError("instantiated module `" + op.getModuleName() +
"` is not an HW module");
return failure();
}
// Traverse the module's IR, dispatching the appropriate visiting function.
Visitor visitor;
for (mlir::Operation &op : op.getOps()) {
mlir::LogicalResult outcome = visitor.dispatch(&op, circuit);
if (outcome.failed())
return outcome;
LogicalResult visitStmt(hw::OutputOp op) {
for (auto operand : op.getOperands())
circuit->addOutput(operand);
return success();
}
return mlir::success();
LogicalResult visitInvalidStmt(Operation *op) {
return dispatchTypeOpVisitor(op);
}
LogicalResult visitUnhandledStmt(Operation *op) {
return visitUnhandledOp(op);
}
//===--------------------------------------------------------------------===//
// hw::TypeOpVisitor
//===--------------------------------------------------------------------===//
LogicalResult visitTypeOp(hw::ConstantOp op) {
circuit->addConstant(op.getResult(), op.getValue());
return success();
}
LogicalResult visitInvalidTypeOp(Operation *op) {
return dispatchCombinationalVisitor(op);
}
LogicalResult visitUnhandledTypeOp(Operation *op) {
return visitUnhandledOp(op);
}
//===--------------------------------------------------------------------===//
// comb::CombinationalVisitor
//===--------------------------------------------------------------------===//
// Visit a comb operation with a variadic number of operands.
template <typename OpTy, typename FnTy>
LogicalResult visitVariadicCombOp(OpTy op, FnTy fn) {
if (!op.getTwoState())
return op.emitOpError("without 'bin' unsupported");
(circuit->*fn)(op.getResult(), op.getOperands());
return success();
}
// Visit a comb operation with two operands.
template <typename OpTy, typename FnTy>
LogicalResult visitBinaryCombOp(OpTy op, FnTy fn) {
if (!op.getTwoState())
return op.emitOpError("without 'bin' unsupported");
(circuit->*fn)(op.getResult(), op.getLhs(), op.getRhs());
return success();
}
// Visit a comb operation with one operand.
template <typename OpTy, typename FnTy>
LogicalResult visitUnaryCombOp(OpTy op, FnTy fn) {
if (!op.getTwoState())
return op.emitOpError("without 'bin' unsupported");
(circuit->*fn)(op.getResult(), op.getInput());
return success();
}
LogicalResult visitComb(comb::AddOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performAdd);
}
LogicalResult visitComb(comb::AndOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performAnd);
}
LogicalResult visitComb(comb::ConcatOp op) {
circuit->performConcat(op.getResult(), op.getOperands());
return success();
}
LogicalResult visitComb(comb::DivSOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performDivS);
}
LogicalResult visitComb(comb::DivUOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performDivU);
}
LogicalResult visitComb(comb::ExtractOp op) {
circuit->performExtract(op.getResult(), op.getInput(), op.getLowBit());
return success();
}
LogicalResult visitComb(comb::ICmpOp op) {
if (!op.getTwoState())
return op.emitOpError("without 'bin' unsupported");
return circuit->performICmp(op.getResult(), op.getPredicate(), op.getLhs(),
op.getRhs());
}
LogicalResult visitComb(comb::ModSOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performModS);
}
LogicalResult visitComb(comb::ModUOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performModU);
}
LogicalResult visitComb(comb::MulOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performMul);
}
LogicalResult visitComb(comb::MuxOp op) {
if (!op.getTwoState())
return op.emitOpError("without 'bin' unsupported");
circuit->performMux(op.getResult(), op.getCond(), op.getTrueValue(),
op.getFalseValue());
return success();
}
LogicalResult visitComb(comb::OrOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performOr);
}
LogicalResult visitComb(comb::ParityOp op) {
return visitUnaryCombOp(op, &Solver::Circuit::performParity);
}
LogicalResult visitComb(comb::ReplicateOp op) {
circuit->performReplicate(op.getResult(), op.getInput());
return success();
}
LogicalResult visitComb(comb::ShlOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performShl);
}
LogicalResult visitComb(comb::ShrSOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performShrS);
}
LogicalResult visitComb(comb::ShrUOp op) {
return visitBinaryCombOp(op, &Solver::Circuit::performShrU);
}
LogicalResult visitComb(comb::SubOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performSub);
}
LogicalResult visitComb(comb::XorOp op) {
return visitVariadicCombOp(op, &Solver::Circuit::performXor);
}
LogicalResult visitUnhandledComb(Operation *op) {
return visitUnhandledOp(op);
}
};
} // namespace
LogicalResult LogicExporter::run(ModuleOp &builtinModule) {
return Visitor(circuit).visit(builtinModule, moduleName);
}
/// Reports a failure whenever an unhandled operation is visited.
mlir::LogicalResult
LogicExporter::Visitor::visitUnhandledOp(mlir::Operation *op) {
return mlir::failure();
}
/// Dispatches an operation to the appropriate visit function.
mlir::LogicalResult LogicExporter::Visitor::dispatch(mlir::Operation *op,
Solver::Circuit *circuit) {
// Attempt dispatching the operation to the StmtVisitor; if it is an invalid
// `hw` statement operation, the StmtVisitor will dispatch it to another
// visitor, and so on in a chain until it gets dispatched to the appropriate
// visitor.
return dispatchStmtVisitor(op, circuit);
LogicalResult LogicExporter::run(hw::HWModuleOp &module) {
return Visitor(circuit).visit(module);
}

View File

@ -20,22 +20,25 @@
#define DEBUG_TYPE "lec-solver"
Solver::Solver(mlir::MLIRContext *mlirCtx, bool statisticsOpt)
using namespace circt;
using namespace mlir;
Solver::Solver(MLIRContext *mlirCtx, bool statisticsOpt)
: circuits{}, mlirCtx(mlirCtx), context(), solver(context),
statisticsOpt(statisticsOpt) {}
/// Solve the equivalence problem between the two circuits, then present the
/// results to the user.
mlir::LogicalResult Solver::solve() {
LogicalResult Solver::solve() {
// Constrain the circuits for equivalence checking to be made:
// require them to produce different outputs starting from the same inputs.
if (constrainCircuits().failed())
return mlir::failure();
return failure();
// Instruct the logical engine to solve the constraints:
// if they can't be satisfied it must mean the two circuits are functionally
// equivalent. Otherwise, print a model to act as a counterexample.
mlir::LogicalResult outcome = mlir::success();
LogicalResult outcome = success();
switch (solver.check()) {
case z3::unsat:
lec::outs() << "c1 == c2\n";
@ -43,10 +46,10 @@ mlir::LogicalResult Solver::solve() {
case z3::sat:
lec::outs() << "c1 != c2\n";
printModel();
outcome = mlir::failure();
outcome = failure();
break;
case z3::unknown:
outcome = mlir::failure();
outcome = failure();
lec::errs() << "circt-lec error: solver ran out of time\n";
}
@ -78,20 +81,20 @@ void Solver::printModel() {
lec::Scope indent;
z3::model model = solver.get_model();
for (unsigned int i = 0; i < model.size(); i++) {
// Recover the corresponding mlir::Value for the z3::expression
// Recover the corresponding Value for the z3::expression
// then emit a remark for its location.
z3::func_decl f = model.get_const_decl(i);
mlir::Builder builder(mlirCtx);
Builder builder(mlirCtx);
std::string symbolStr = f.name().str();
mlir::StringAttr symbol = builder.getStringAttr(symbolStr);
mlir::Value value = symbolTable.find(symbol)->second;
StringAttr symbol = builder.getStringAttr(symbolStr);
Value value = symbolTable.find(symbol)->second;
z3::expr e = model.get_const_interp(f);
mlir::emitRemark(value.getLoc(), "");
// Explicitly unfolded asm printing for `mlir::Value`.
if (auto arg = value.dyn_cast<mlir::BlockArgument>()) {
emitRemark(value.getLoc(), "");
// Explicitly unfolded asm printing for `Value`.
if (auto arg = value.dyn_cast<BlockArgument>()) {
// Value is an argument rather than a SSA'ed value of an operation.
mlir::Operation *parentOp = value.getParentRegion()->getParentOp();
if (auto op = llvm::dyn_cast<circt::hw::HWModuleOp>(parentOp)) {
Operation *parentOp = value.getParentRegion()->getParentOp();
if (auto op = llvm::dyn_cast<hw::HWModuleOp>(parentOp)) {
// Argument of a `hw.module`.
lec::outs() << "argument name: " << op.getArgNames()[arg.getArgNumber()]
<< "\n";
@ -133,7 +136,7 @@ void Solver::printStatistics() {
/// there would be a model acting as a counterexample.
/// The procedure fails when detecting a mismatch of arity or type between
/// the inputs and outputs of the circuits.
mlir::LogicalResult Solver::constrainCircuits() {
LogicalResult Solver::constrainCircuits() {
// TODO: Perform these failure checks before nalyzing the whole IR of the
// modules during the pass.
auto c1Inputs = circuits[0]->getInputs();
@ -144,7 +147,7 @@ mlir::LogicalResult Solver::constrainCircuits() {
// Can't compare two circuits with different number of inputs.
if (nc1Inputs != nc2Inputs) {
lec::errs() << "circt-lec error: different input arity\n";
return mlir::failure();
return failure();
}
const auto *c1inIt = c1Inputs.begin();
@ -153,7 +156,7 @@ mlir::LogicalResult Solver::constrainCircuits() {
// Can't compare two circuits when their ith inputs differ in type.
if (c1inIt->get_sort().bv_size() != c2inIt->get_sort().bv_size()) {
lec::errs() << "circt-lec error: input #" << i + 1 << " type mismatch\n";
return mlir::failure();
return failure();
}
// Their ith inputs have to be equivalent.
solver.add(*c1inIt++ == *c2inIt++);
@ -167,7 +170,7 @@ mlir::LogicalResult Solver::constrainCircuits() {
// Can't compare two circuits with different number of outputs.
if (nc1Outputs != nc2Outputs) {
lec::errs() << "circt-lec error: different output arity\n";
return mlir::failure();
return failure();
}
const auto *c1outIt = c1Outputs.begin();
@ -176,11 +179,11 @@ mlir::LogicalResult Solver::constrainCircuits() {
// Can't compare two circuits when their ith outputs differ in type.
if (c1outIt->get_sort().bv_size() != c2outIt->get_sort().bv_size()) {
lec::errs() << "circt-lec error: output #" << i + 1 << " type mismatch\n";
return mlir::failure();
return failure();
}
// Their ith outputs have to be equivalent.
solver.add(*c1outIt++ != *c2outIt++);
}
return mlir::success();
return success();
}

View File

@ -27,6 +27,9 @@
namespace cl = llvm::cl;
using namespace mlir;
using namespace circt;
//===----------------------------------------------------------------------===//
// Command-line options declaration
//===----------------------------------------------------------------------===//
@ -72,22 +75,21 @@ static cl::opt<bool, true> statistics(
/// traverses their IR to export the logical constraints from the given circuit
/// description to an internal circuit representation, lastly, these will be
/// compared and solved for equivalence.
static mlir::LogicalResult executeLEC(mlir::MLIRContext &context) {
static LogicalResult executeLEC(MLIRContext &context) {
// Parse the provided input files.
if (verbose)
lec::outs() << "Parsing input file\n";
mlir::OwningOpRef<mlir::ModuleOp> file1 =
mlir::parseSourceFile<mlir::ModuleOp>(fileName1, &context);
OwningOpRef<ModuleOp> file1 = parseSourceFile<ModuleOp>(fileName1, &context);
if (!file1)
return mlir::failure();
return failure();
mlir::OwningOpRef<mlir::ModuleOp> file2;
OwningOpRef<ModuleOp> file2;
if (!fileName2.empty()) {
if (verbose)
lec::outs() << "Parsing second input file\n";
file2 = mlir::parseSourceFile<mlir::ModuleOp>(fileName2, &context);
file2 = parseSourceFile<ModuleOp>(fileName2, &context);
if (!file2)
return mlir::failure();
return failure();
} else if (verbose)
lec::outs() << "Second input file not specified\n";
@ -101,9 +103,9 @@ static mlir::LogicalResult executeLEC(mlir::MLIRContext &context) {
if (verbose)
lec::outs() << "Analyzing the first circuit\n";
auto exporter = std::make_unique<LogicExporter>(moduleName1, c1);
mlir::ModuleOp m = file1.get();
ModuleOp m = file1.get();
if (failed(exporter->run(m)))
return mlir::failure();
return failure();
// Repeat the same procedure for the second circuit.
if (verbose)
@ -111,9 +113,9 @@ static mlir::LogicalResult executeLEC(mlir::MLIRContext &context) {
auto exporter2 = std::make_unique<LogicExporter>(moduleName2, c2);
// In case a second input file was not specified, the first input file will
// be used instead.
mlir::ModuleOp m2 = fileName2.empty() ? m : file2.get();
ModuleOp m2 = fileName2.empty() ? m : file2.get();
if (failed(exporter2->run(m2)))
return mlir::failure();
return failure();
// The logical constraints have been exported to their respective circuit
// representations and can now be solved for equivalence.
@ -129,7 +131,7 @@ static mlir::LogicalResult executeLEC(mlir::MLIRContext &context) {
int main(int argc, char **argv) {
// Configure the relevant command-line options.
cl::HideUnrelatedOptions(mainCategory);
mlir::registerMLIRContextCLOptions();
registerMLIRContextCLOptions();
cl::AddExtraVersionPrinter(
[](llvm::raw_ostream &os) { os << circt::getCirctVersion() << '\n'; });
@ -145,13 +147,13 @@ int main(int argc, char **argv) {
llvm::setBugReportMsg(circt::circtBugReportMsg);
// Register the supported CIRCT dialects and create a context to work with.
mlir::DialectRegistry registry;
DialectRegistry registry;
registry.insert<circt::comb::CombDialect, circt::hw::HWDialect>();
mlir::MLIRContext context(registry);
MLIRContext context(registry);
// Setup of diagnostic handling.
llvm::SourceMgr sourceMgr;
mlir::SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &context);
SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &context);
// Avoid printing a superfluous note on diagnostic emission.
context.printOpOnDiagnostic(false);