Skip to content

Commit 2309bc7

Browse files
authored
👨‍💻 Add implicit location handling to program builders and new utils tests (#1446)
## Description This PR makes use of the newly-discovered `ImplicitLocOpBuilder` in MLIR, which makes it way more convenient to use the OpBuilder interface, because it does not require specifying a location for `create` calls (a place where we, more often than not, simply used `UnknownLoc`). Furthermore, this PR also introduces explicit locations for the builders, which seems to be good practice in general and is favored over using the unknown location. This also refactors the QIR Program Builder to inherit from MLIR's builder class, which it did not previously. Overall, this streamlines quite a bit of code, including the tests recently added in #1443. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer <burgholzer@me.com>
1 parent d28f4ba commit 2309bc7

File tree

8 files changed

+196
-203
lines changed

8 files changed

+196
-203
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel
1111

1212
### Added
1313

14-
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1428], [#1430], [#1436], [#1443]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**])
14+
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**])
1515

1616
### Changed
1717

@@ -307,6 +307,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
307307

308308
<!-- PR links -->
309309

310+
[#1446]: https://github.com/munich-quantum-toolkit/core/pull/1446
310311
[#1443]: https://github.com/munich-quantum-toolkit/core/pull/1443
311312
[#1437]: https://github.com/munich-quantum-toolkit/core/pull/1437
312313
[#1436]: https://github.com/munich-quantum-toolkit/core/pull/1436

mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace mlir::qc {
5151
* auto module = builder.finalize();
5252
* ```
5353
*/
54-
class QCProgramBuilder final : public OpBuilder {
54+
class QCProgramBuilder final : public ImplicitLocOpBuilder {
5555
public:
5656
/**
5757
* @brief Construct a new QCProgramBuilder
@@ -887,7 +887,6 @@ class QCProgramBuilder final : public OpBuilder {
887887

888888
private:
889889
MLIRContext* ctx{};
890-
Location loc;
891890
ModuleOp module;
892891

893892
/// Track allocated qubits for automatic deallocation

mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ namespace mlir::qco {
6060
* auto module = builder.finalize();
6161
* ```
6262
*/
63-
class QCOProgramBuilder final : public OpBuilder {
63+
class QCOProgramBuilder final : public ImplicitLocOpBuilder {
6464
public:
6565
/**
6666
* @brief Construct a new QCOProgramBuilder
@@ -1048,7 +1048,6 @@ class QCOProgramBuilder final : public OpBuilder {
10481048

10491049
private:
10501050
MLIRContext* ctx{};
1051-
Location loc;
10521051
ModuleOp module;
10531052

10541053
/// Check if the builder has been finalized

mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ namespace mlir::qir {
7070
* auto module = builder.finalize();
7171
* ```
7272
*/
73-
class QIRProgramBuilder {
73+
class QIRProgramBuilder final : public ImplicitLocOpBuilder {
7474
public:
7575
/**
7676
* @brief Construct a new QIRProgramBuilder
@@ -138,7 +138,7 @@ class QIRProgramBuilder {
138138
*/
139139
struct Bit {
140140
/// Name of the register containing this bit
141-
std::string registerName;
141+
llvm::StringRef registerName;
142142
/// Size of the register containing this bit
143143
int64_t registerSize{};
144144
/// Index of this bit within the register
@@ -811,10 +811,10 @@ class QIRProgramBuilder {
811811
OwningOpRef<ModuleOp> finalize();
812812

813813
private:
814-
OpBuilder builder;
814+
/// The main module
815815
ModuleOp module;
816-
Location loc;
817816

817+
/// The main function
818818
LLVM::LLVMFuncOp mainFunc;
819819

820820
/// Allocator and StringSaver for stable StringRefs
@@ -842,6 +842,12 @@ class QIRProgramBuilder {
842842
/// Track qubit and result counts for QIR metadata
843843
QIRMetadata metadata_;
844844

845+
/// Helper variable for storing the LLVM pointer type
846+
LLVM::LLVMPointerType ptrType;
847+
848+
/// Helper variable for storing the LLVM void type
849+
LLVM::LLVMVoidType voidType;
850+
845851
/**
846852
* @brief Helper to create a LLVM CallOp
847853
*

mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp

Lines changed: 39 additions & 41 deletions
Large diffs are not rendered by default.

mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <mlir/Dialect/Func/IR/FuncOps.h>
2424
#include <mlir/IR/Builders.h>
2525
#include <mlir/IR/BuiltinOps.h>
26+
#include <mlir/IR/Location.h>
2627
#include <mlir/IR/MLIRContext.h>
2728
#include <mlir/IR/OwningOpRef.h>
2829
#include <mlir/IR/Value.h>
@@ -34,8 +35,9 @@
3435
namespace mlir::qco {
3536

3637
QCOProgramBuilder::QCOProgramBuilder(MLIRContext* context)
37-
: OpBuilder(context), ctx(context), loc(getUnknownLoc()),
38-
module(ModuleOp::create(loc)) {
38+
: ImplicitLocOpBuilder(
39+
FileLineColLoc::get(context, "<qco-program-builder>", 1, 1), context),
40+
ctx(context), module(ModuleOp::create(*this)) {
3941
ctx->loadDialect<QCODialect>();
4042
}
4143

@@ -45,7 +47,7 @@ void QCOProgramBuilder::initialize() {
4547

4648
// Create main function as entry point
4749
auto funcType = getFunctionType({}, {getI64Type()});
48-
auto mainFunc = func::FuncOp::create(*this, loc, "main", funcType);
50+
auto mainFunc = func::FuncOp::create(*this, "main", funcType);
4951

5052
// Add entry_point attribute to identify the main function
5153
auto entryPointAttr = getStringAttr("entry_point");
@@ -59,7 +61,7 @@ void QCOProgramBuilder::initialize() {
5961
Value QCOProgramBuilder::allocQubit() {
6062
checkFinalized();
6163

62-
auto allocOp = AllocOp::create(*this, loc);
64+
auto allocOp = AllocOp::create(*this);
6365
const auto qubit = allocOp.getResult();
6466

6567
// Track the allocated qubit as valid
@@ -76,7 +78,7 @@ Value QCOProgramBuilder::staticQubit(const int64_t index) {
7678
}
7779

7880
auto indexAttr = getI64IntegerAttr(index);
79-
auto staticOp = StaticOp::create(*this, loc, indexAttr);
81+
auto staticOp = StaticOp::create(*this, indexAttr);
8082
const auto qubit = staticOp.getQubit();
8183

8284
// Track the static qubit as valid
@@ -102,7 +104,7 @@ QCOProgramBuilder::allocQubitRegister(const int64_t size,
102104

103105
for (int64_t i = 0; i < size; ++i) {
104106
const auto indexAttr = getI64IntegerAttr(i);
105-
auto allocOp = AllocOp::create(*this, loc, nameAttr, sizeAttr, indexAttr);
107+
auto allocOp = AllocOp::create(*this, nameAttr, sizeAttr, indexAttr);
106108
const auto& qubit = qubits.emplace_back(allocOp.getResult());
107109
// Track the allocated qubit as valid
108110
validQubits.insert(qubit);
@@ -156,7 +158,7 @@ void QCOProgramBuilder::updateQubitTracking(Value inputQubit,
156158
std::pair<Value, Value> QCOProgramBuilder::measure(Value qubit) {
157159
checkFinalized();
158160

159-
auto measureOp = MeasureOp::create(*this, loc, qubit);
161+
auto measureOp = MeasureOp::create(*this, qubit);
160162
auto qubitOut = measureOp.getQubitOut();
161163
auto result = measureOp.getResult();
162164

@@ -173,7 +175,7 @@ Value QCOProgramBuilder::measure(Value qubit, const Bit& bit) {
173175
auto sizeAttr = getI64IntegerAttr(bit.registerSize);
174176
auto indexAttr = getI64IntegerAttr(bit.registerIndex);
175177
auto measureOp =
176-
MeasureOp::create(*this, loc, qubit, nameAttr, sizeAttr, indexAttr);
178+
MeasureOp::create(*this, qubit, nameAttr, sizeAttr, indexAttr);
177179
const auto qubitOut = measureOp.getQubitOut();
178180

179181
// Update tracking
@@ -185,7 +187,7 @@ Value QCOProgramBuilder::measure(Value qubit, const Bit& bit) {
185187
Value QCOProgramBuilder::reset(Value qubit) {
186188
checkFinalized();
187189

188-
auto resetOp = ResetOp::create(*this, loc, qubit);
190+
auto resetOp = ResetOp::create(*this, qubit);
189191
const auto qubitOut = resetOp.getQubitOut();
190192

191193
// Update tracking
@@ -203,7 +205,7 @@ Value QCOProgramBuilder::reset(Value qubit) {
203205
#define DEFINE_ZERO_TARGET_ONE_PARAMETER(OP_CLASS, OP_NAME, PARAM) \
204206
void QCOProgramBuilder::OP_NAME(const std::variant<double, Value>&(PARAM)) { \
205207
checkFinalized(); \
206-
OP_CLASS::create(*this, loc, PARAM); \
208+
OP_CLASS::create(*this, PARAM); \
207209
} \
208210
Value QCOProgramBuilder::c##OP_NAME( \
209211
const std::variant<double, Value>&(PARAM), Value control) { \
@@ -239,7 +241,7 @@ DEFINE_ZERO_TARGET_ONE_PARAMETER(GPhaseOp, gphase, theta)
239241
#define DEFINE_ONE_TARGET_ZERO_PARAMETER(OP_CLASS, OP_NAME) \
240242
Value QCOProgramBuilder::OP_NAME(Value qubit) { \
241243
checkFinalized(); \
242-
auto op = OP_CLASS::create(*this, loc, qubit); \
244+
auto op = OP_CLASS::create(*this, qubit); \
243245
const auto& qubitOut = op.getQubitOut(); \
244246
updateQubitTracking(qubit, qubitOut); \
245247
return qubitOut; \
@@ -284,7 +286,7 @@ DEFINE_ONE_TARGET_ZERO_PARAMETER(SXdgOp, sxdg)
284286
Value QCOProgramBuilder::OP_NAME(const std::variant<double, Value>&(PARAM), \
285287
Value qubit) { \
286288
checkFinalized(); \
287-
auto op = OP_CLASS::create(*this, loc, qubit, PARAM); \
289+
auto op = OP_CLASS::create(*this, qubit, PARAM); \
288290
const auto& qubitOut = op.getQubitOut(); \
289291
updateQubitTracking(qubit, qubitOut); \
290292
return qubitOut; \
@@ -325,7 +327,7 @@ DEFINE_ONE_TARGET_ONE_PARAMETER(POp, p, phi)
325327
const std::variant<double, Value>&(PARAM2), \
326328
Value qubit) { \
327329
checkFinalized(); \
328-
auto op = OP_CLASS::create(*this, loc, qubit, PARAM1, PARAM2); \
330+
auto op = OP_CLASS::create(*this, qubit, PARAM1, PARAM2); \
329331
const auto& qubitOut = op.getQubitOut(); \
330332
updateQubitTracking(qubit, qubitOut); \
331333
return qubitOut; \
@@ -368,7 +370,7 @@ DEFINE_ONE_TARGET_TWO_PARAMETER(U2Op, u2, phi, lambda)
368370
const std::variant<double, Value>&(PARAM3), \
369371
Value qubit) { \
370372
checkFinalized(); \
371-
auto op = OP_CLASS::create(*this, loc, qubit, PARAM1, PARAM2, PARAM3); \
373+
auto op = OP_CLASS::create(*this, qubit, PARAM1, PARAM2, PARAM3); \
372374
const auto& qubitOut = op.getQubitOut(); \
373375
updateQubitTracking(qubit, qubitOut); \
374376
return qubitOut; \
@@ -409,7 +411,7 @@ DEFINE_ONE_TARGET_THREE_PARAMETER(UOp, u, theta, phi, lambda)
409411
std::pair<Value, Value> QCOProgramBuilder::OP_NAME(Value qubit0, \
410412
Value qubit1) { \
411413
checkFinalized(); \
412-
auto op = OP_CLASS::create(*this, loc, qubit0, qubit1); \
414+
auto op = OP_CLASS::create(*this, qubit0, qubit1); \
413415
const auto& qubit0Out = op.getQubit0Out(); \
414416
const auto& qubit1Out = op.getQubit1Out(); \
415417
updateQubitTracking(qubit0, qubit0Out); \
@@ -453,7 +455,7 @@ DEFINE_TWO_TARGET_ZERO_PARAMETER(ECROp, ecr)
453455
std::pair<Value, Value> QCOProgramBuilder::OP_NAME( \
454456
const std::variant<double, Value>&(PARAM), Value qubit0, Value qubit1) { \
455457
checkFinalized(); \
456-
auto op = OP_CLASS::create(*this, loc, qubit0, qubit1, PARAM); \
458+
auto op = OP_CLASS::create(*this, qubit0, qubit1, PARAM); \
457459
const auto& qubit0Out = op.getQubit0Out(); \
458460
const auto& qubit1Out = op.getQubit1Out(); \
459461
updateQubitTracking(qubit0, qubit0Out); \
@@ -501,7 +503,7 @@ DEFINE_TWO_TARGET_ONE_PARAMETER(RZZOp, rzz, theta)
501503
const std::variant<double, Value>&(PARAM2), Value qubit0, \
502504
Value qubit1) { \
503505
checkFinalized(); \
504-
auto op = OP_CLASS::create(*this, loc, qubit0, qubit1, PARAM1, PARAM2); \
506+
auto op = OP_CLASS::create(*this, qubit0, qubit1, PARAM1, PARAM2); \
505507
const auto& qubit0Out = op.getQubit0Out(); \
506508
const auto& qubit1Out = op.getQubit1Out(); \
507509
updateQubitTracking(qubit0, qubit0Out); \
@@ -548,7 +550,7 @@ DEFINE_TWO_TARGET_TWO_PARAMETER(XXMinusYYOp, xx_minus_yy, theta, beta)
548550
ValueRange QCOProgramBuilder::barrier(ValueRange qubits) {
549551
checkFinalized();
550552

551-
auto op = BarrierOp::create(*this, loc, qubits);
553+
auto op = BarrierOp::create(*this, qubits);
552554
const auto& qubitsOut = op.getQubitsOut();
553555
for (const auto& [inputQubit, outputQubit] : llvm::zip(qubits, qubitsOut)) {
554556
updateQubitTracking(inputQubit, outputQubit);
@@ -565,17 +567,17 @@ std::pair<ValueRange, ValueRange> QCOProgramBuilder::ctrl(
565567
llvm::function_ref<llvm::SmallVector<Value>(ValueRange)> body) {
566568
checkFinalized();
567569

568-
auto ctrlOp = CtrlOp::create(*this, loc, controls, targets);
570+
auto ctrlOp = CtrlOp::create(*this, controls, targets);
569571
auto& block = ctrlOp.getBodyRegion().emplaceBlock();
570572
const auto qubitType = QubitType::get(getContext());
571573
for (const auto target : targets) {
572-
const auto arg = block.addArgument(qubitType, loc);
574+
const auto arg = block.addArgument(qubitType, getLoc());
573575
updateQubitTracking(target, arg);
574576
}
575577
const InsertionGuard guard(*this);
576578
setInsertionPointToStart(&block);
577579
const auto innerTargetsOut = body(block.getArguments());
578-
YieldOp::create(*this, loc, innerTargetsOut);
580+
YieldOp::create(*this, innerTargetsOut);
579581

580582
if (innerTargetsOut.size() != targets.size()) {
581583
llvm::reportFatalUsageError(
@@ -606,7 +608,7 @@ QCOProgramBuilder& QCOProgramBuilder::dealloc(Value qubit) {
606608
validateQubitValue(qubit);
607609
validQubits.erase(qubit);
608610

609-
DeallocOp::create(*this, loc, qubit);
611+
DeallocOp::create(*this, qubit);
610612

611613
return *this;
612614
}
@@ -655,16 +657,16 @@ OwningOpRef<ModuleOp> QCOProgramBuilder::finalize() {
655657
return opA->isBeforeInBlock(opB);
656658
});
657659
for (auto qubit : sortedQubits) {
658-
DeallocOp::create(*this, loc, qubit);
660+
DeallocOp::create(*this, qubit);
659661
}
660662

661663
validQubits.clear();
662664

663665
// Create constant 0 for successful exit code
664-
auto exitCode = arith::ConstantOp::create(*this, loc, getI64IntegerAttr(0));
666+
auto exitCode = arith::ConstantOp::create(*this, getI64IntegerAttr(0));
665667

666668
// Add return statement with exit code 0 to the main function
667-
func::ReturnOp::create(*this, loc, ValueRange{exitCode});
669+
func::ReturnOp::create(*this, ValueRange{exitCode});
668670

669671
// Invalidate context to prevent use-after-finalize
670672
ctx = nullptr;

0 commit comments

Comments
 (0)