Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
64fe189
Vendor the pinned OpenQASM 3.1 grammar
burgholzer Jul 15, 2026
4832ba2
Add an experimental typed OQ3 dialect
burgholzer Jul 15, 2026
e2daad9
Build a staged OpenQASM 3.1 frontend
burgholzer Jul 15, 2026
666d894
Document the OQ3 foundation ExecPlan
burgholzer Jul 15, 2026
3e2b0aa
Treat OpenQASM 3.0 as current OpenQASM 3
burgholzer Jul 15, 2026
a7e53a8
Replace the ANTLR frontend with a handwritten-parser plan
burgholzer Jul 15, 2026
885a556
Update the OQ3 foundation progress
burgholzer Jul 15, 2026
737db8a
Replace the direct OpenQASM MLIR translator
burgholzer Jul 15, 2026
5f2f309
Simplify modifiers and complete scalar expressions
burgholzer Jul 15, 2026
734948c
Stabilize OQ3 frontend invariants
burgholzer Jul 15, 2026
0bc6d2c
Fix OQ3 dialect documentation generation
burgholzer Jul 15, 2026
4766f7d
Fix OQ3 documentation and MLIR linting
burgholzer Jul 15, 2026
c8e93be
🎨 pre-commit fixes
pre-commit-ci[bot] Jul 15, 2026
105e3f5
Fix OpenQASM frontend lint diagnostics
burgholzer Jul 15, 2026
310fcc2
Expand OpenQASM frontend coverage
burgholzer Jul 15, 2026
4c02537
Cover OpenQASM language builtins
burgholzer Jul 15, 2026
ccde1ba
Fix OpenQASM docs build dependencies
burgholzer Jul 15, 2026
ebd70f8
Cover canonical OQ3 gate lowering
burgholzer Jul 15, 2026
9d3316b
Complete staged OpenQASM frontend
burgholzer Jul 16, 2026
bcca207
Remove intermediate OpenQASM dialect
burgholzer Jul 16, 2026
79e7a8a
Tighten OpenQASM pipeline contracts
burgholzer Jul 16, 2026
99ad652
Close OpenQASM pipeline acceptance gaps
burgholzer Jul 16, 2026
d0a197a
Clean up OpenQASM frontend residue
burgholzer Jul 16, 2026
21bb4bd
Format OpenQASM support tables after rebase
burgholzer Jul 16, 2026
67d697b
Fix OpenQASM Windows and lint CI
burgholzer Jul 16, 2026
4cfef76
Clean up OpenQASM frontend internals
burgholzer Jul 16, 2026
5e610fc
Broaden OpenQASM QC acceptance
burgholzer Jul 16, 2026
508c4cd
Lower control-flow assertions to QIR
burgholzer Jul 16, 2026
727c244
Separate standard and jeff compiler contracts
burgholzer Jul 16, 2026
0ccdb32
Document OpenQASM pipeline contracts
burgholzer Jul 16, 2026
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
774 changes: 774 additions & 0 deletions .agent/plans/oq3-foundation.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ docs/api/

# PyBuilder
.pybuilder/
target/
/target/

# Jupyter Notebook
.ipynb_checkpoints
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ releases may include breaking changes.

### Added

- ✨ Add an LLVM-native staged OpenQASM frontend with typed semantic analysis
and direct QC emission, including lexical scope, assignment, inclusive ranges,
and structured control flow ([#1910]) ([**@burgholzer**])
- ✨ Add Python bindings for the MQT Compiler Collection ([#1815])
([**@burgholzer**], [**@denialhaag**])
- ✨ Add support for QDMI child devices to the driver and FoMaC libraries
Expand Down Expand Up @@ -638,6 +641,7 @@ changelogs._
<!-- PR links -->

[#1914]: https://github.com/munich-quantum-toolkit/core/pull/1914
[#1910]: https://github.com/munich-quantum-toolkit/core/pull/1910
[#1904]: https://github.com/munich-quantum-toolkit/core/pull/1904
[#1897]: https://github.com/munich-quantum-toolkit/core/pull/1897
[#1895]: https://github.com/munich-quantum-toolkit/core/pull/1895
Expand Down
57 changes: 57 additions & 0 deletions docs/mlir/OpenQASM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# OpenQASM input

The compiler reads OpenQASM 3 through a staged lexer, parser, and semantic
analyzer, then emits the QC dialect directly. OpenQASM is an input language, not
an intermediate dialect: a successful translation contains QC and standard MLIR
operations only. Translation fails before returning a module when the QC target
cannot represent an accepted source feature.

## Parser and semantic support

| Feature | Status | Restriction | Representative test |
| -------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| OpenQASM 3.0 and 3.1 | Supported | Later 3.x versions are rejected | `PreservesExactAndOptionalVersionSemantics` |
| `stdgates.inc` and nested includes | Supported | Includes are expanded textually with bounded depth and source locations | `ExpandsNestedIncludesAtTheirSourceLocations` |
| Qubits, bits, and `bool`, `int`, `uint`, `float` scalars | Supported | Width-qualified integer and floating types are not yet supported | `RejectsUnsupportedIntegerDeclarations` |
| Lexical scope, assignment, and constants | Supported | Mutable global values cannot be captured by gate definitions | `TracksLexicalScopeAndEnclosingAssignments` |
| `if`, inclusive `for`, and `while` | Supported | Gate bodies contain gate calls and loops over gate calls only | `EmitsStructuredLoopsWithCarriedMutableState` |
| Expressions and scalar math functions | Supported | Operations are checked against the implemented scalar type rules | `AcceptsAllScalarOperatorsAndComparisonPredicates` |
| Dynamic qubit and bit indexing | Supported | Target restrictions and the combined emission budget are listed below | `DispatchesDynamicQubitGatesWithStructuredControlFlow` |
| Primitive, broadcast, and custom gates | Supported | Recursive definitions and mismatched broadcast widths are rejected | `BroadcastsRegistersAlongsideScalarQubits` |
| `inv`, `ctrl`, `negctrl`, and `pow` modifiers | Parsed and semantically checked | Target support differs below | `RejectsInvalidGateControlAndBroadcastShapes` |
| `input` declarations, subroutines, and `extern` | Recognized and rejected by the parser | `input`, `def`, `return`, and `extern` are reserved but are not in the implemented grammar | `DiagnosesUnsupportedReservedFeatureSyntax` |
| Calibration, timing, `duration`, and `stretch` | Recognized and rejected by the parser | `defcalgrammar`, `cal`, `defcal`, `delay`, `durationof`, `duration`, and `stretch` are not implemented | `DiagnosesUnsupportedReservedFeatureSyntax` |
| `array`, `complex`, `angle`, and aliases | Recognized and rejected by the parser | Aggregate, complex, angle, and `let` alias declarations have no typed representation yet | `RejectsUnsupportedReservedWordsAsIdentifiers` |
| `switch`, `break`, and `continue` | Recognized and rejected by the parser | These control-flow forms are reserved but are not in the implemented grammar | `RejectsUnsupportedReservedWordsAsIdentifiers` |
| Bitwise and shift operators | Parsed and rejected semantically | Explicitly sized `uint`, `bit`, or `angle` operands are required by the language and are not implemented | `RejectsInvalidProgramsAcrossSemanticFamilies` |

## Translation and compiler support

The standard compiler path is OpenQASM to QC, optimized QCO, reconstructed QC,
and QIR. This is the primary acceptance contract. Serialization through `jeff`
is optional: a smaller positive corpus exercises it, while known incompatible
programs are accepted by the standard path and fail explicitly when QCO is
converted to `jeff`. Base refers to direct production of the QIR Base Profile.

| Feature | Parse | Semantics | QC | Standard Adaptive QIR | `jeff` | Base | Restriction or rejection reason | Representative test |
| ----------------------------------------------- | --------- | --------- | -------------------------------------------- | --------------------- | ------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| Primitive and custom gates | Supported | Supported | Supported | Supported | Supported in the positive corpus | Supported | Custom gates are expanded during QC emission | `broadcast_custom_gate` |
| Gate arithmetic and math parameters | Supported | Supported | Supported | Supported | Supported in the positive corpus | Supported | Scalar `pow()` is distinct from the gate modifier | `math_parameters` |
| Broadcast gates | Supported | Supported | Supported | Supported | Supported in the positive corpus | Supported | Operands must have compatible widths | `broadcast_custom_gate` |
| `inv`, `ctrl`, and `negctrl` | Supported | Supported | Supported | Supported | Supported in the positive corpus | Not in the tested Base subset | Modifiers on custom gates that require structured control flow are rejected | `RejectsModifiersOnTransitivelyStructuredCustomGatesAtQCTarget` |
| `pow @` | Supported | Supported | Rejected | Rejected | Rejected before `jeff` | Rejected | The QC dialect has no power modifier yet; translation reports the source location | `RejectsPowerAtTheQCTargetBoundary` |
| `if` and nested `if`/`for` | Supported | Supported | Supported | Supported | Supported in the positive corpus | Adaptive only | The Base corpus is intentionally straight-line | `nested_static_control_flow` |
| Measurement-controlled `while` | Supported | Supported | Supported | Supported | Supported in the positive corpus | Adaptive only | Requires runtime classical control | `measurement_controlled_while` |
| Loop-carried mutable bit and floating state | Supported | Supported | Supported | Supported | Supported in the positive corpus | Adaptive only | Carried state remains SSA values through QC, QCO, and reconstructed QC | `mutable_loop_state`, `scalar_loop_state` |
| Checked signed and wrapping unsigned arithmetic | Supported | Supported | Supported with runtime assertions | Supported | Rejected at QCO-to-`jeff` when live | Not in the tested Base subset | Signed overflow and invalid division are asserted; unsigned arithmetic wraps at 64 bits | `checked_integer_state` |
| Constant inclusive ranges | Supported | Supported | Supported with constant `scf.for` bounds | Supported | Supported when the body is compatible | Adaptive only | Positive, negative, empty, singleton, non-divisible, and boundary ranges avoid runtime trip-count math | `UsesConstantBoundsForStaticInclusiveRanges` |
| Dynamic inclusive ranges | Supported | Supported | Supported with comparison-driven `scf.while` | Supported | Rejected at QCO-to-`jeff` | Adaptive only | A dynamic zero step is asserted; iteration uses no division-based trip count | `dynamic_range` |
| Dynamic indexing resolved by optimization | Supported | Supported | Supported with bounds assertions | Supported | Supported when assertions fold away | Not in the tested Base subset | Straight-line constants and equal-constant branch joins can be simplified before `jeff` | `resolved_dynamic_index`, `equal_constant_index_join` |
| General runtime and induction-variable indexing | Supported | Supported | Supported with bounds assertions | Supported | Rejected at QCO-to-`jeff` | Not in the tested Base subset | The standard QIR path lowers the assertions; `jeff` cannot currently represent them | `runtime_dynamic_index`, `induction_variable_index` |
| Measurement, reset, and barrier | Supported | Supported | Supported | Supported | Supported in the positive corpus | Measurement and barrier supported | Reset is Adaptive-only | `reset`, `barrier` |
| Projected QC emission budget | Supported | Supported | Rejected above 100000 primitive applications | Rejected | Rejected before `jeff` | Rejected | One overflow-safe projection composes custom-gate expansion with dynamic-dispatch multiplicity | `ComposesDispatchAndCustomGateExpansionBudgets` |

The integration suites use public compiler APIs. The broad corpus must complete
the standard pipeline and the smaller `jeff` corpus must round-trip. The
incompatible corpus records failures at `intoJeff()` so those limitations do not
reduce the set of programs accepted by QC or QIR.
3 changes: 3 additions & 0 deletions docs/mlir/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ We define multiple dialects, each with its dedicated purpose:
- The {doc}`QTensor dialect <QTensor>` adds support for one-dimensional tensors
of qubits with linear typing and is used in the QCO dialect to represent
collections of qubits such as registers.
- The {doc}`OpenQASM frontend <OpenQASM>` translates supported OpenQASM 3 input
directly to QC.

These dialects define various canonicalization and transformation passes that
enable the compilation of quantum programs to native quantum hardware. For
Expand All @@ -30,6 +32,7 @@ python_compiler_collection
QC
QCO
QTensor
OpenQASM
Conversions
```

Expand Down
76 changes: 63 additions & 13 deletions mlir/.clang-tidy
Original file line number Diff line number Diff line change
@@ -1,13 +1,63 @@
InheritParentConfig: true
Checks: |
llvm-namespace-comment,
llvm-prefer-isa-or-dyn-cast-in-conditionals,
llvm-prefer-register-over-unsigned,
llvm-prefer-static-over-anonymous-namespace,
llvm-twine-local,
-bugprone-throwing-static-initialization,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
-misc-use-anonymous-namespace,
-modernize-type-traits,
-*-const-correctness,
InheritParentConfig: false

Checks: >
-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-branch-clone,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-suspicious-semicolon,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-unused-raii,
bugprone-unused-return-value,
misc-redundant-expression,
misc-static-assert,
misc-unused-using-decls,
modernize-use-bool-literals,
modernize-loop-convert,
modernize-make-unique,
modernize-raw-string-literal,
modernize-use-equals-default,
modernize-use-default-member-init,
modernize-use-emplace,
modernize-use-nullptr,
modernize-use-override,
modernize-use-using,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-algorithm,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-no-automatic-move,
performance-trivially-destructible,
performance-unnecessary-copy-initialization,
performance-unnecessary-value-param,
readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-container-size-empty,
readability-inconsistent-declaration-parameter-name,
readability-misleading-indentation,
readability-redundant-control-flow,
readability-redundant-smartptr-get,
readability-simplify-boolean-expr,
readability-simplify-subscript-expr,
readability-use-anyofallof

CheckOptions:
- key: readability-identifier-naming.MemberCase
value: camelBack
- key: readability-identifier-naming.ParameterCase
value: camelBack
- key: readability-identifier-naming.VariableCase
value: camelBack
- key: modernize-use-using.IgnoreExternC
value: true
FormatStyle: file
168 changes: 168 additions & 0 deletions mlir/include/mlir/Target/OpenQASM/Detail/OpenQASMLexer.h

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you move all header files from this directory to mlir/include/mlir/Target/OpenQASM?

Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#pragma once

#include <llvm/ADT/StringRef.h>
#include <llvm/Support/SMLoc.h>
#include <mlir/Support/LLVM.h>

#include <cstdint>

namespace mlir::oq3::frontend::detail {

/// The kind of a lexical token.
enum class TokenKind : uint8_t {
Eof,
Error,
UnterminatedComment,
UnsupportedKeyword,

// Keywords
OpenQASM,
Include,
Const,
Qubit,
Qreg,
Bit,
CReg,
Gate,
Opaque,
Output,
Barrier,
Reset,
Measure,
If,
Else,
For,
While,
In,
Gphase,
Inv,
Pow,
Ctrl,
NegCtrl,

// Types
Int,
Uint,
Bool,
Float,
Angle,
Duration,
True,
False,

// Identifiers and literals
Identifier,
HardwareQubit,
StringLiteral,
IntegerLiteral,
FloatLiteral,

// Punctuation
LParen,
RParen,
LBracket,
RBracket,
LBrace,
RBrace,
Comma,
Semicolon,
Colon,
Arrow,
At,

// Operators
Equals,
Plus,
Minus,
Asterisk,
DoubleAsterisk,
Slash,
Percent,
Tilde,
Amp,
Pipe,
Caret,
ShiftLeft,
ShiftRight,
ExclamationPoint,
AmpAmp, // `&&`
PipePipe, // `||`
CompoundAssign, // `+=`, `-=`, ...

// Comparisons
EqualsEquals,
NotEquals,
Less,
LessEquals,
Greater,
GreaterEquals,
};

/// A human-readable name for @p kind, used in diagnostics.
[[nodiscard]] StringRef describe(TokenKind kind);

/**
* @brief A single lexical token.
*
* @details
* Spellings are zero-copy views into the source buffer. Literals are
* pre-parsed.
*/
struct Token {
TokenKind kind = TokenKind::Eof;
SMLoc loc;
StringRef identifier; ///< For `Identifier` tokens.
StringRef stringValue; ///< For `StringLiteral` tokens.
StringRef spelling; ///< Zero-copy source spelling.
uint64_t intValue = 0;
double floatValue = 0.0;
};

/**
* @brief A zero-copy lexer over an OpenQASM 3 source buffer.
*
* @details
* The lexer holds pointers into the buffer and produces tokens on demand
* without allocating. Token locations are `SMLoc`s into the buffer, so they can
* be resolved to line/column via the owning `llvm::SourceMgr`.
*/
class Lexer {
public:
explicit Lexer(StringRef buffer) : cur(buffer.begin()), end(buffer.end()) {}

/// Produce the next token, or an `Eof`/`Error` token at the end/on failure.
[[nodiscard]] Token next();

private:
[[nodiscard]] bool atEnd() const { return cur == end; }

/// The character after the cursor, or `'\0'` at the end of the buffer.
[[nodiscard]] char peek() const { return (cur + 1) != end ? cur[1] : '\0'; }

/**
* @brief Skip whitespace and comments.
* @return The start of an unterminated block comment, or `nullptr` if the
* trivia is well-formed.
*/
[[nodiscard]] const char* skipTrivia();

[[nodiscard]] Token lexIdentifierOrKeyword(const char* start);
[[nodiscard]] Token lexNumber(const char* start);
[[nodiscard]] Token lexString(const char* start, char quote);
[[nodiscard]] Token lexHardwareQubit(const char* start);

const char* cur;
const char* end;
};

} // namespace mlir::oq3::frontend::detail
Loading
Loading