From 38ad37d8be92660e570ad6636b2b6f5226847289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Troels=20F=2E=20R=C3=B8nnow?= <574350+troelsfr@users.noreply.github.com> Date: Tue, 26 Jul 2022 20:13:38 +0200 Subject: [PATCH] Documentation update (#73) * Updating debugger documentation * Updating docs * Updating docs * Fixing issue #59 * Removing unwanted files * Updating documentation and fixing issues in the profile generator * Creating commant for formatting * Creating commant for formatting * Updating docker image * Finishing issue #10 * Formatting * Fixing tests * Resolving #45 * Updating for CI to pass * Removing .orig files --- Makefile | 4 + .../DeveloperGuide/ArchitectureOverview.md | 103 +++++++++- docs/src/DeveloperGuide/CodeQuality.md | 29 +-- docs/src/UserGuide/BuildingLibrary.md | 47 ++++- docs/src/UserGuide/DebuggingIR.md | 186 ++++++++++++++++++ docs/src/UserGuide/GoalsAndAssumptions.md | 114 +++++++++++ docs/src/index.md | 2 + qir/qat/Apps/Qat/Qat.cpp | 53 ++--- .../Tests/Unit/StaticResultAllocation.cpp | 23 ++- styleguide.md | 33 +++- 10 files changed, 536 insertions(+), 58 deletions(-) create mode 100644 docs/src/UserGuide/GoalsAndAssumptions.md diff --git a/Makefile b/Makefile index 026931b99..97b8f1a85 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,10 @@ linux-docker: linux-ci: linux-docker docker run -it -v ${PWD}:/src --rm -t qir-passes-ubuntu:latest ./manage runci +format-in-docker: linux-docker + docker run -it --rm -v ${PWD}:/src/ -t qir-passes-ubuntu:latest ./manage stylecheck --fix-issues + + test-examples: mkdir -p Debug cd Debug && cmake .. && make qat diff --git a/docs/src/DeveloperGuide/ArchitectureOverview.md b/docs/src/DeveloperGuide/ArchitectureOverview.md index 14540b7e7..472960886 100644 --- a/docs/src/DeveloperGuide/ArchitectureOverview.md +++ b/docs/src/DeveloperGuide/ArchitectureOverview.md @@ -57,7 +57,7 @@ similar mechanism that allows to activate or deactivate existing (and new) LLVM passes. We would thus need something like ```sh -qat --always-inline --apply -S filename.ll +qat --always-inline --profile base --apply -S filename.ll ``` where `--apply` tells the tool to apply the profile and `-S` tells the tool to @@ -65,7 +65,7 @@ emit human readable LLVM IR code. Furthermore, if the inline pass is provided as an external module, we would need to be able load it ```sh -qat --always-inline --load path/to/lib.(dylib|so|dll) --apply -S filename.ll +qat --always-inline --profile base --load path/to/lib.(dylib|so|dll) --apply -S filename.ll ``` We note that from a developer point of view, the underlying code would also need @@ -118,4 +118,101 @@ needs while benefiting from the components that QAT ships with. ## Architecture description -TODO(issue-9): Yet to be written +On a high level, the process of the IRs can be divided into three main tasks: 1) +Loading the QIR, 2) creating a generator and validator and 3) transform and +validate the QIR. We summarize this process in the diagram below, listing the +components and settings used in the process and how they feed into one and +another: + +```text +┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ + User input +└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ +┌─────────────────────────────▼─────────────────────────────┐ +│ Configuration and parameter parser │ +└─────────────┬───────────────────────────────┬─────────────┘ +┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +│ IR file list │ │ Profile config │ +└─────────────┬─────────────┘ └─────────────┬─────────────┘ +┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +│ Module loader │ │ Profile Generator │ +└─────────────┬─────────────┘ └─────────────┬─────────────┘ +┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +│ Single module │ │ Profile │ +│ transformations │ └──────┬──────────────┬─────┘ +└─────────────┬─────────────┘ ┌──────▼─────┐ ┌──────▼─────┐ +┌─────────────▼─────────────┐ │ │ │ │ +│ Adding debug symbols ├───▶ Generation ├─┼▶Validation ├─────┐ +└───────────────────────────┘ │ │ │ │ │ + └──────┬─────┘ └──────┬─────┘ │ + ┌──────▼──────────────▼─────┐ │ + │ Logger │ │ + └───────────────────────────┘ │ + │ │ + ▼ ▼ + ┌ ─ ─ ─ ─ ─ ─ ─ ─ ┐┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ + Standard error Standard Output: + │ or file: ││ Resulting IR │ + JSON Logs + └ ─ ─ ─ ─ ─ ─ ─ ─ ┘└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ +``` + +The profile consists of a generator and a validator. The generator is +responsible for performing as many transformations as possible to get the +original QIR to be compliant with the selected profile. This is done by each of +the components taking a configuration which then installs LLVM passes to execute +said transactions. This is illustrated on the left hand-side in the following +figure whereas the pass execution is illustrated on the right hand-side: + +```text + ┌ ─ ─ ─ ─ ─ ─ ─ ┐ + LLVM module +┌───┐ └ ─ ─ ─│─ ─ ─ ─ ┘ +│ │ │ Apply profile +│ C │ │ process +│ o │ ┌ Generator ─ ─ ─│─ ─ ─ ─ ─ +│ n │If ┌───────▼───────┐ │ +│ f │component Install│ │ Inline pass │ +│ i │active: ┌───────────────────┐passes ┌───┐ └───────┬───────┘ │ +│ g ├─────────▶ LLVM passes │───────┼─▶ │ ┌───────▼───────┐ +│ u │ └───────────────────┘ │ │ │ Unroll pass │ │ +│ r │ │ │ P │ └───────┬───────┘ +│ a │ ┌───────────────────┐ │ a │ ┌───────▼───────┐ │ +│ t ├─────────▶ Transformation │───────┼─▶ s │ │Transform pass │ +│ i │ └───────────────────┘ │ s │ └───────┬───────┘ │ +│ o │ │ │ │ ┌───────▼───────┐ +│ n │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ m │ │ Inline pass │ │ +│ ├─────────▶ LLVM passes ───────┼─▶ a │ └───────┬───────┘ +│ m │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ n │ ┌───────▼───────┐ │ +│ a │ │ │ a │ │ Fold pass │ +│ n │ ┌───────────────────┐ │ g │ └───────┬───────┘ │ +│ a ├─────────▶ Grouping │───────┼─▶ e │ ┌───────▼───────┐ +│ g │ └───────────────────┘ │ r │ │ Group pass │ │ +│ e │ │ │ s │ └───────┬───────┘ +│ r │ │ │ │ │ +│ │ │ │ │ │ +└───┘ └───┘ │ │ + └ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ + │ + ┌ ─ ─ ─ ▼ ─ ─ ─ ┐ + Output module + └ ─ ─ ─ ─ ─ ─ ─ ┘ +``` + +For each component available, QAT checks if the component is active and if it +is, the components setup function is ran with its configuration class. This +class can be configured from the command line or through the profile. We note +that the figure does not contain a comprehensive list of passes that can be +installed. Whether or not any of the listed passes is added to the pass managers +is happening at the discretion of each of the components and is further subject +to the configuration provided to these components. This means that depending on +the profile configuration, the pass may perform one task or another. This is in +particular true for the transformation pass which uses a set of rules to perform +replacements in the IR. + +The transformation component is a highly configurable component that does +replacements of pieces of the DAG in the IR with using a custom replacer +function. The infrastructure is written such that it is possible to express a +new pattern in just a couple of lines and the developer can focus on the +replacement routine rather than the task of capturing the right segment of +instructions. diff --git a/docs/src/DeveloperGuide/CodeQuality.md b/docs/src/DeveloperGuide/CodeQuality.md index de836d29f..960300981 100644 --- a/docs/src/DeveloperGuide/CodeQuality.md +++ b/docs/src/DeveloperGuide/CodeQuality.md @@ -47,23 +47,30 @@ version and on different platforms, it is recommended that you use a Docker image to perform these steps. TODO(issue-10): The Docker image is not added yet and this will be documented in the future. -## Running tests +## Style checking using Docker -In order to run the tests, you first need to build the library. Assuming that -this is already done and the corresponding build is in `Debug/`, run the tests -from the `Debug` folder: +In some cases it is not possible to replicate the necessary toolchain to perform +style checks on the code. To address this, this repository is equipped with a +Docker image that allows the developer to run the CI script: ```sh -lit tests/ -v --- Testing: 2 tests, 2 workers -- -PASS: Quantum-Passes :: QirAllocationAnalysis/case1.ll (1 of 2) -PASS: Quantum-Passes :: QirAllocationAnalysis/case2.ll (2 of 2) +make linux-ci +``` -Testing Time: 0.27s - Passed: 2 +as well as automatically fix style issues where possible: + +```sh +make format-in-docker ``` -The C++ test suite can also be ran from the debug by first building all targets: +The latter command will mount the current source directory inside docker and use +the Ubuntu image to run the style check. This image was created with the version +of the tool that is officially supported by the maintainers. + +## Running tests + +In order to run the tests, you first need to build the library. Assuming that +this is already done and the corresponding build is in `Debug/`: ```sh cmake .. diff --git a/docs/src/UserGuide/BuildingLibrary.md b/docs/src/UserGuide/BuildingLibrary.md index 867d97a26..8e9d3709e 100644 --- a/docs/src/UserGuide/BuildingLibrary.md +++ b/docs/src/UserGuide/BuildingLibrary.md @@ -65,22 +65,46 @@ tools and packages are used: - Python packages specified in `requirements.txt` - clang-format - clang-tidy +- NodeJS & Prettier The installation process varies depending on the platform you use. The following subsections provide details on how to install these tools on each platform. ### On macOS -TODO(issue-45): Create developer prerequisites for macOS in docs. +To install LLVM version 11, we use the `brew` package `llvm@11` which will +install the LLVM libraries alongside with Clang 11, Clang format and Clang Tidy: + +```sh +brew install llvm@11 +``` + +Python and NodeJS can also be installed using `brew`: + +```sh +brew install python@3.9 node@16 +``` + +After successful installation, you may need to update your environment variables +to ensure that CMake will use the right version of Clang as well as the LLVM +libraries: + +```sh +export CC=clang-11 +export CXX=clang++-11 +export LDFLAGS="-L/usr/local/opt/llvm@11/lib -Wl,-rpath,/usr/local/opt/llvm@11/lib" +export CPPFLAGS="-I/usr/local/opt/llvm@11/include" +``` ### On Ubuntu 20.04 -Installing the Clang tools on Ubuntu along with Python can be accomplished by -running these commands: +Installing the Clang tools on Ubuntu along with Python and NodeJS can be +accomplished by running these commands: ```sh apt install clang-format-13 clang-tidy-13 apt install python3 python3-pip +apt install nodejs npm ``` We recommend that you use version 13 of Clang in order to be consistent with the @@ -98,8 +122,9 @@ export CXX=clang++-13 Last but not least, we install the Python libraries that are required: ```sh - pip install -r requirements.txt - chmod +x manage +pip install -r requirements.txt +chmod +x manage +npm install -g prettier@2.2.1 ``` As a result, the `manage` tool will be available for you to help you make sure @@ -121,7 +146,17 @@ git submodule update --init --recursive ### Installing LLVM on macOS -TODO(issue-45): Document installation process +If you have previously installed the packages under +[Developer prerequisites](#developer-prerequisites), you do not need to take any +additional steps for macOS. If you are relying on the natively installed Clang, +you may need to install the LLVM libraries: + +```sh +brew install llvm@11 +``` + +Additionally, you may need to update your `LDFLAGS` and `CPPFLAGS` for the +toolchain to work properly. ### Installing LLVM on Ubuntu 20.04 diff --git a/docs/src/UserGuide/DebuggingIR.md b/docs/src/UserGuide/DebuggingIR.md index e32d88806..b7b4d9f4d 100644 --- a/docs/src/UserGuide/DebuggingIR.md +++ b/docs/src/UserGuide/DebuggingIR.md @@ -126,3 +126,189 @@ Process 25334 stopped In this way, we can now trace the origin of an issue to the LLVM IR rather than the original source file. + +## Mixing IR with runtime + +In this section we consider a slightly more complicated example where we want to +debug an IR combined with a runtime. Our runtime looks as follows: + +```c++ +int foo(); +int ir_recurse(int); + +int standard_recurse(int n) +{ + if (n <= 0) + { + return foo(); + } + return ir_recurse(n - 1); +} + +int main() +{ + int x = 2; + return standard_recurse(20) + 2 * x; +} +``` + +As in the previous example, our IR will produce a segmentation fault. To ensure +that we get a stack trace containing traces of both the runtime and the IR, we +use a recursive function that alternates between using functionality in the +runtime and the main IR itself: + +```c++ +int standard_recurse(int); + +int foo() +{ + int *ptr = nullptr; + ptr[10299] = ptr[10299 * 2777] + 2; + return ptr[99]; +} + +int ir_recurse(int n) +{ + if (n <= 0) + { + return foo(); + } + return standard_recurse(n - 1); +} +``` + +As in the previous example, we annotate `foo.ll` with debug information using +QAT before creating the object file and linking: + +```sh + clang++ -c -g -o bin/runtime.o runtime.cpp + clang++ -c -S -emit-llvm foo.cpp + qat -S --add-ir-debug foo.ll > foo.dbg.ll + mkdir -p bin + llc -filetype=obj -o bin/foo.dbg.o foo.dbg.ll + clang++ -g bin/foo.dbg.o bin/runtime.o -o bin/qir_program +``` + +Running the debugger, we can now execute the program and produce a stack trace +as in the previous examples: + +```lldb +Current executable set to '/path/to/test/bin/qir_program' (x86_64). +(lldb) r +Process 54371 launched: '/path/to/test/bin/qir_program' (x86_64) +Process 54371 stopped +* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x6d1a00c) + frame #0: 0x0000000100003e89 qir_program`_Z3foov at foo.ll:12:34 + 9 store i32* null, i32** %1, align 8 + 10 %2 = load i32*, i32** %1, align 8 + 11 %3 = getelementptr inbounds i32, i32* %2, i64 28600323 +-> 12 %4 = load i32, i32* %3, align 4 + 13 %5 = add nsw i32 %4, 2 + 14 %6 = load i32*, i32** %1, align 8 + 15 %7 = getelementptr inbounds i32, i32* %6, i64 10299 +(lldb) bt +* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x6d1a00c) + * frame #0: 0x0000000100003e89 qir_program`_Z3foov at foo.ll:12:34 + frame #1: 0x0000000100003efa qir_program`standard_recurse(n=0) at runtime.cpp:9:12 + frame #2: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #3: 0x0000000100003f0f qir_program`standard_recurse(n=2) at runtime.cpp:11:10 + frame #4: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #5: 0x0000000100003f0f qir_program`standard_recurse(n=4) at runtime.cpp:11:10 + frame #6: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #7: 0x0000000100003f0f qir_program`standard_recurse(n=6) at runtime.cpp:11:10 + frame #8: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #9: 0x0000000100003f0f qir_program`standard_recurse(n=8) at runtime.cpp:11:10 + frame #10: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #11: 0x0000000100003f0f qir_program`standard_recurse(n=10) at runtime.cpp:11:10 + frame #12: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #13: 0x0000000100003f0f qir_program`standard_recurse(n=12) at runtime.cpp:11:10 + frame #14: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #15: 0x0000000100003f0f qir_program`standard_recurse(n=14) at runtime.cpp:11:10 + frame #16: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #17: 0x0000000100003f0f qir_program`standard_recurse(n=16) at runtime.cpp:11:10 + frame #18: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #19: 0x0000000100003f0f qir_program`standard_recurse(n=18) at runtime.cpp:11:10 + frame #20: 0x0000000100003ecb qir_program`_Z10ir_recursei at foo.ll:40:49 + frame #21: 0x0000000100003f0f qir_program`standard_recurse(n=20) at runtime.cpp:11:10 + frame #22: 0x0000000100003f40 qir_program`main at runtime.cpp:17:10 + frame #23: 0x000000010000d51e dyld`start + 462 +(lldb) f 1 +frame #1: 0x0000000100003efa qir_program`standard_recurse(n=0) at runtime.cpp:9:12 + 6 { + 7 if (n <= 0) + 8 { +-> 9 return foo(); + 10 } + 11 return ir_recurse(n - 1); + 12 } +(lldb) +``` + +We note how this incorporates traces from both `runtime.cpp` and `foo.ll` with +exact line reference. In this way, it is possible to debug in a similar manner +to what we are used to from classical computing. + +## Multi-IR projects + +Occasionally, we may want combine multiple IRs to produce one debuggable +executable. We note that we can choose two paths: Either we link at the IR +level, or we link at the object level. Unfortunately, LLVM does not produce +adequate debug symbols when multiple compilation units are defined in the same +IR. Hence, if we wish to follow the path of the former, we first need to combine +the IRs and then add debug information with reference to the _one combined +file_: + +```sh +qat -S foo.ll bar.ll > combined.ll +qat -S --strip-existing-dbg --add-ir-debug combined.ll > combined.dbg.ll +``` + +However, this approach looses the information about the location of the original +files `foo.ll` and `bar.ll`: + +```lldb + * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x6d1a00c) + * frame #0: 0x0000000100003ec9 combined_example`_Z3foov at combined.ll:11:34 + frame #1: 0x0000000100003f2e combined_example`_Z16standard_recursei at combined.ll:57:37 + frame #2: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #3: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #4: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #5: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #6: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #7: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #8: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #9: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #10: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #11: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #12: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #13: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #14: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #15: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #16: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #17: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #18: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #19: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #20: 0x0000000100003f0b combined_example`_Z10ir_recursei at combined.ll:38:49 + frame #21: 0x0000000100003f3b combined_example`_Z16standard_recursei at combined.ll:64:53 + frame #22: 0x0000000100003f6a combined_example`main at combined.ll:82:57 + frame #23: 0x000000010000d51e dyld`start + 462 +``` + +To keep information about the original files, we have to turn to the second +approach where we create separate object files. This will prevent some +optimisations such as inlining, but will ensure that we can trace locations in +the individual files: + +```sh + clang++ -c -S -emit-llvm foo.cpp + clang++ -c -S -emit-llvm bar.cpp + ${QAT_BINARY} -S --add-ir-debug foo.ll > foo.dbg.ll + ${QAT_BINARY} -S --add-ir-debug bar.ll > bar.dbg.ll + mkdir -p bin + llc -filetype=obj -o bin/foo.dbg.o foo.dbg.ll + llc -filetype=obj -o bin/bar.dbg.o bar.dbg.ll + clang++ -g bin/foo.dbg.o bin/bar.dbg.o -o bin/foo_bar_example +``` + +The executable will now contain debug symbols referring to `foo.ll` and +`bar.ll`. diff --git a/docs/src/UserGuide/GoalsAndAssumptions.md b/docs/src/UserGuide/GoalsAndAssumptions.md new file mode 100644 index 000000000..c2e156ed5 --- /dev/null +++ b/docs/src/UserGuide/GoalsAndAssumptions.md @@ -0,0 +1,114 @@ +# QAT: Goals, assumptions and limitations + +## Goals + +QAT primary purpose is to manipulate and validate QIR via a command-line +interface. Concretely, QAT aims at performing following tasks: + +- Simplifying logic +- Transforming logic +- Adding debugging using LLVM IRs as the source +- Validating the logic compatibility with a specific profile + +One of the biggest strength of the LLVM infrastructure is the large amount of +optimizations available. For instance, the LLVM optimization tool `opt` allows +constant folding, unrolling of loops, inlining of functions and branch +elimination amongst others. These optimizations are useful in order to simplify +complicated programs to something that can be ran on processors with a limited +classical instruction set. + +Transforming logic involves transforming qubit allocations into static +allocations, separating logic and removing unwanted logic. In case of qubit +allocations, the generic method to acquire a qubit resource is through +`__quantum__rt__qubit_allocate` and release again through +`__quantum__rt__qubit_release`. Such runtime functions are not currently +supported by available hardware. More generally, available hardware does not +have support for any runtime functions which makes it inadequate way to express +quantum programs. While we expect that runtime libraries will become +increasingly available as the industry develops, it is an immediate need to +eliminate these from the QIR. QAT is capable of solving this problem in a +multitude of ways. In case of `__quantum__rt__qubit_allocate`, QAT has a pass +which allows to replace these with static qubit numbers under certain +conditions. Another example of a QAT transformation is the ability to separate +classical driver logic from the quantum programs in mixed quantum-classical +programs. + +The third goal of QAT is to allow QIRs to be annotate with debug information +based on an input LLVM IR as the source file. The intend is to provide +functionality to help developers to debug at an IR level by incorporating means +to annotating debug information referring to the `.ll` file. This functionality +complements existing debug information and can, optionally, override it if +needed. + +The fourth goal of QAT is to validate whether or not a QIR is compliant with the +capabilities of a specific quantum system. This is done through profiles which +are configurations describing allowing types, instructions, functions and modes +of operating. + +## Assumptions & limitations + +In the following subsections we will outline some of the assumptions and +limitations of the current implementation of algorithms addressing the previous +listed tasks. This document does not discuss the assumptions and limitations of +experimental features such as recursion unfolding, but limits itself to what we +consider stable in the latest release. + +### Simplifying logic through LLVM passes + +For logic simplification, it is an assumption that the passes used will not +perform any transformation causing unsupported instructions. This is a strong +assumption as LLVM, for instance, may attempt to vectorize loops thereby making +use of vector registers and corresponding instructions. To mitigate this, we +have limit the LLVM passes available to a handful and parametrized the +configuration of them. This serves to increase reproducibility across platforms +as well as to provide control over the pass behaviour. However, it does not +provide a strict guarantee that these types of transformations will not occur. +We therefore rely on the validation functionality to ensure that the final +program is compliant with the targeted architecture. + +### Transforming abstractions + +Transformations to replace dynamic qubit allocation with static qubit allocation +relies on a strong assumption that no subcircuit rely on recursion. The core +issue that gives rise to this assumption is that it is not possible to replace a +dynamic qubit allocation with a static one inside a program unless there is +strict guarantees that 1) the function does not call itself and 2) that the +acquisition time of the qubit is restricted to the life time of the function +scope. + +Transformations that remove reference counting relies on an assumption that all +arrays and other ADTs are successfully removed. This assumption may not be true +and would under normal circumstances be caught be the validation step assuming +an appropriate profile is used. + +Transformations that remove strings, assumes that strings and functions +consuming or producing strings can be treated as "debug" statements which has no +other side effect than to output the string content to the screen. Such an +assumption may not hold true for frontends that uses dictionaries with strings +as key for instance. + +### Annotating QIR with Debug information + +A core assumption related to annotating the QIR with debug information is that +the human readable version of the IR (the `.ll`-file) is the source of truth and +can be referred to by line and column numbers rather than the bitcode. This +assumption may not hold true as the different versions of LLVM may produce +different `.ll` files which are not necessarily compatible with one and another. +Therefore, if the bitcode file is used as input in a cloud service, the referred +to line and column numbers may differ from those produced locally. + +### Logic separation + +The logic separation functionality assumes that instructions belonging to the +primary (CPU) and secondary (QPU) processing units can be distinguished solely +by its operation signatures. That is, `void` function calls which only takes +`Qubit*` and `Result*` as arguments are pure quantum instructions whereas +function calls that returns classical entities (such as `i8`, `i64` etc.) but +take `Qubit*` and `Result*` as arguments are read out functions. This puts a +natural limit to what can be executed on the QPU since this approach does not +allow it for any classical instruction to make its way to the QPU. + +Another assumption is that QPU is performing a single run with multiple quantum +programs to be loaded and executed. Subsequently, it is the assumption that the +machine stays coherent throughout the full program execution which is not +necessarily the case. diff --git a/docs/src/index.md b/docs/src/index.md index 4797a19a4..d048d0551 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -10,6 +10,8 @@ User guide: - [Building the library](UserGuide/BuildingLibrary.md) - [Introduction to profiles](UserGuide/IntroductionToProfiles.md) - Applying a profile (TODO(issue-8): Yet to be written) +- [Debugging IR](UserGuide/DebuggingIR.md) +- [QAT: Assumptions and Restrictions](UserGuide/GoalsAndAssumptions.md) Developer guide: diff --git a/qir/qat/Apps/Qat/Qat.cpp b/qir/qat/Apps/Qat/Qat.cpp index 57b580841..f263a722b 100644 --- a/qir/qat/Apps/Qat/Qat.cpp +++ b/qir/qat/Apps/Qat/Qat.cpp @@ -16,29 +16,36 @@ /// /// /// ``` -/// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -/// User input │ │ "Use" relation -/// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▼ -/// │ argc, argv -/// ▼ ─ ─▶ "Produce" relation -/// ┌──────────────────────────────┐ -/// │ ParameterParser │◀─┐ Setup arguments -/// └──────────────────────────────┘ │ -/// Load config │ │ -/// ▼ │ -/// ┌──────────────────────────────┐ │ ┌──────────────────────────────────┐ -/// │ ConfigurationManager │──┘ ┌ ─ ─ ─▶│ Ruleset │ -/// └──────────────────────────────┘ └──────────────────────────────────┘ -/// Provide config │ │ │ Rules for -/// ▼ ▼ transformation -/// ┌───────────────────────────────┐─ ─ ─ ┘ ┌──────────────────────────────────┐ -/// │ ProfileGenerator │─ ─ ─ ─ ─ ─ ─▶│ TransformationRulesPass │ -/// └───────────────────────────────┘ └──────────────────────────────────┘ -/// │ LLVM module -/// ▼ pass -/// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┌──────────────────────────────────┐ -/// Output │◀─ ─ ─ ─ ─ ─ ─ ┤ QAT / LLVM Module Pass Manager │ -/// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ stdout └──────────────────────────────────┘ +/// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ +/// User input +/// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ +/// ┌─────────────────────────────▼─────────────────────────────┐ +/// │ Configuration and paramater parser │ +/// └─────────────┬───────────────────────────────┬─────────────┘ +/// ┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +/// │ LLVM (Q)IRs │ │ Profile config │ +/// └─────────────┬─────────────┘ └─────────────┬─────────────┘ +/// ┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +/// │ Module loader │ │ Profile Generator │ +/// └─────────────┬─────────────┘ └─────────────┬─────────────┘ +/// ┌─────────────▼─────────────┐ ┌─────────────▼─────────────┐ +/// │ Single module │ │ Profile │ +/// │ transformations │ └──────┬──────────────┬─────┘ +/// └─────────────┬─────────────┘ ┌──────▼─────┐ ┌──────▼─────┐ +/// ┌─────────────▼─────────────┐ │ │ │ │ +/// │ Adding debug symbols ├───▶ Generation ├─┼▶Validation ├─────┐ +/// └───────────────────────────┘ │ │ │ │ │ +/// └──────┬─────┘ └──────┬─────┘ │ +/// ┌──────▼──────────────▼─────┐ │ +/// │ Logger │ │ +/// └───────────────────────────┘ │ +/// │ │ +/// ▼ ▼ +/// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ┐┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ +/// Standard error Standard Output: +/// │ or file: ││ Resulting IR │ +/// JSON Logs +/// └ ─ ─ ─ ─ ─ ─ ─ ─ ┘└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ /// ``` /// /// diff --git a/qir/qat/Rules/Tests/Unit/StaticResultAllocation.cpp b/qir/qat/Rules/Tests/Unit/StaticResultAllocation.cpp index ec1f71c1f..276981049 100644 --- a/qir/qat/Rules/Tests/Unit/StaticResultAllocation.cpp +++ b/qir/qat/Rules/Tests/Unit/StaticResultAllocation.cpp @@ -65,18 +65,17 @@ TEST(RuleSetTestSuite, ResultTranslatedTo) ir_manip->applyProfile(profile); - EXPECT_TRUE(ir_manip->hasInstructionSequence({ - "%result1 = inttoptr i64 0 to %Result*", - "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result1)", - "%result2 = inttoptr i64 1 to %Result*", - "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result2)", - "%result3 = inttoptr i64 2 to %Result*", - "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result3)", - "%result4 = inttoptr i64 3 to %Result*", - "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result4)", - "%result5 = inttoptr i64 4 to %Result*", - "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result5)", - })); + EXPECT_TRUE(ir_manip->hasInstructionSequence( + {"%result1 = inttoptr i64 0 to %Result*", + "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result1)", + "%result2 = inttoptr i64 1 to %Result*", + "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result2)", + "%result3 = inttoptr i64 2 to %Result*", + "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result3)", + "%result4 = inttoptr i64 3 to %Result*", + "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result4)", + "%result5 = inttoptr i64 4 to %Result*", + "call void @__quantum__qis__mz__body(%Qubit* null, %Result* %result5)"})); EXPECT_FALSE( ir_manip->hasInstructionSequence({ diff --git a/styleguide.md b/styleguide.md index c5dd1c0c5..332802864 100644 --- a/styleguide.md +++ b/styleguide.md @@ -7,9 +7,36 @@ case you do not feel like reading this style guide, just run ./manage runci ``` -from the `src/Passes` directory before making a pull request. This script -enforces all requirements described below programmatically. You can then refer -to this guide for an explanation for why and how. +from the root directory before making a pull request. This script enforces all +requirements described below programmatically. You can then refer to this guide +for an explanation for why and how. + +In case you have formatting issues, the manage script do the majority of the +formatting foryou. If your toolchain on your local machine is set up correctly, +you can simply run + +```sh +./manage stylecheck --fix-issues +``` + +to automatically format all source files in compliance with the styles chosen +for this project. To avoid different versions of the tool chain creating +different formats or complaining about different static issues, we have created +a docker image to serve as "the source of truth". In case your toolchain differ, +you can use the provided docker image, just running: + +```sh +make format-in-docker +``` + +This will mount your local source directory and perform formatting of the code. +Likewise, if you need to run CI in the docker image, you can run: + +```sh +make linux-ci +``` + +which will execute `./manage runci` inside the docker image. ## Why do we need a style guide?