diff --git a/src/Passes/.clang-tidy b/src/Passes/.clang-tidy index a0f64b8d21..7260427b82 100644 --- a/src/Passes/.clang-tidy +++ b/src/Passes/.clang-tidy @@ -66,11 +66,23 @@ CheckOptions: - key: readability-identifier-naming.ProtectedMemberSuffix value: '_' - # Alias + # Type Alias and Enum Types / constants - key: readability-identifier-naming.TypeAliasCase value: 'CamelCase' - key: readability-identifier-naming.TypedefCase value: 'CamelCase' + - key: readability-identifier-naming.EnumCase + value: 'CamelCase' + - key: readability-identifier-naming.EnumConstantCase + value: 'CamelCase' + + # Globals, consts and enums + - key: readability-identifier-naming.GlobalConstantCase + value: 'UPPER_CASE' + - key: readability-identifier-naming.GlobalConstantPrefix + value: 'G_' + - key: readability-identifier-naming.ConstantCase + value: 'UPPER_CASE' # Functions - key: readability-identifier-naming.FunctionCase @@ -86,18 +98,6 @@ CheckOptions: - key: readability-identifier-naming.ParameterCase value: 'lower_case' - # Globals, consts and enums - - key: readability-identifier-naming.GlobalConstantCase - value: 'UPPER_CASE' - - key: readability-identifier-naming.GlobalConstantPrefix - value: 'G_' - - key: readability-identifier-naming.ConstantCase - value: 'UPPER_CASE' - - key: readability-identifier-naming.EnumCase - value: 'CamelCase' - - key: readability-identifier-naming.EnumConstantCase - value: 'CamelCase' - # Macros - key: readability-identifier-naming.MacroDefinitionCase value: 'UPPER_CASE' diff --git a/src/Passes/CMakeLists.txt b/src/Passes/CMakeLists.txt index 41ca853b39..f7ccafaa22 100644 --- a/src/Passes/CMakeLists.txt +++ b/src/Passes/CMakeLists.txt @@ -46,3 +46,4 @@ include_directories(${CMAKE_SOURCE_DIR}/src) # Adding the libraries add_subdirectory(libs) +add_subdirectory(tests) \ No newline at end of file diff --git a/src/Passes/CONTRIBUTING.md b/src/Passes/CONTRIBUTING.md index e2dde33ccb..edda9aed22 100644 --- a/src/Passes/CONTRIBUTING.md +++ b/src/Passes/CONTRIBUTING.md @@ -59,7 +59,7 @@ Prefer `#pragma once` over `#ifdef` protection. ## Code TODOs must contain owner name or Github issue ```sh -% ./manage runci +./manage runci (...) Passes/src/OpsCounter/OpsCounter.cpp:39:21: error: missing username/bug in TODO [google-readability-todo,-warnings-as-errors] // TODO: Fails to load if this is present diff --git a/src/Passes/Makefile b/src/Passes/Makefile index 40507c6184..919e20dc8a 100644 --- a/src/Passes/Makefile +++ b/src/Passes/Makefile @@ -4,3 +4,4 @@ nothing: clean: rm -rf Release/ rm -rf Debug/ + diff --git a/src/Passes/README.md b/src/Passes/README.md index 20d8eff1cb..a70a6835a8 100644 --- a/src/Passes/README.md +++ b/src/Passes/README.md @@ -185,7 +185,7 @@ and then make your target make [target] ``` -Valid targets are the name of the folders in `libs/` found in the passes root. +The default target is `all`. Other valid targets are the name of the folders in `libs/` found in the passes root. ## Running a pass @@ -204,7 +204,7 @@ For a detailed tutorial, see examples. To make it easy to create a new pass, we have created a few templates to get you started quickly: ```sh -% ./manage create-pass HelloWorld +./manage create-pass HelloWorld Available templates: 1. Function Pass @@ -215,9 +215,9 @@ Select a template:1 At the moment you only have one choice which is a function pass. Over time we will add additional templates. Once you have instantiated your template, you are ready to build it: ```sh -% mkdir Debug -% cd Debug -% cmake .. +mkdir Debug +cd Debug +cmake .. -- The C compiler identification is AppleClang 12.0.5.12050022 -- The CXX compiler identification is AppleClang 12.0.5.12050022 (...) @@ -225,7 +225,7 @@ At the moment you only have one choice which is a function pass. Over time we wi -- Generating done -- Build files have been written to: ./qsharp-compiler/src/Passes/Debug -% make +make [ 25%] Building CXX object libs/CMakeFiles/OpsCounter.dir/OpsCounter/OpsCounter.cpp.o [ 50%] Linking CXX shared library libOpsCounter.dylib @@ -240,9 +240,9 @@ template will not do much except for print the function names of your code. To t build an IR and run the pass: ```sh -% cd ../examples/ClassicalIrCommandline -% make -% opt -load-pass-plugin ../../Debug/libs/libHelloWorld.{dylib,so} --passes="hello-world" -disable-output classical-program.ll +cd ../examples/ClassicalIrCommandline +make +opt -load-pass-plugin ../../Debug/libs/libHelloWorld.{dylib,so} --passes="hello-world" -disable-output classical-program.ll ``` If everything worked, you should see output like this: @@ -303,7 +303,7 @@ that you use a docker image to perform these steps. TODO(TFR): The docker image One error that you may encounter is that an analysis pass does not load with output similar to this: ```sh -% opt -load-pass-plugin ../../Debug/libQSharpPasses.dylib -enable-debugify --passes="operation-counter" -disable-output classical-program.bc +opt -load-pass-plugin ../../Debug/libQSharpPasses.dylib -enable-debugify --passes="operation-counter" -disable-output classical-program.bc Failed to load passes from '../../Debug/libQSharpPasses.dylib'. Request ignored. opt: unknown pass name 'operation-counter' ``` diff --git a/src/Passes/docs/continous-integration.md b/src/Passes/docs/continous-integration.md new file mode 100644 index 0000000000..364d230883 --- /dev/null +++ b/src/Passes/docs/continous-integration.md @@ -0,0 +1,105 @@ +# 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/`, run the tests from the `Debug` folder: + +``` +lit tests/ -v +-- Testing: 2 tests, 2 workers -- +PASS: Quantum-Passes :: QubitAllocationAnalysis/case1.ll (1 of 2) +PASS: Quantum-Passes :: QubitAllocationAnalysis/case2.ll (2 of 2) + +Testing Time: 0.27s + Passed: 2 +``` + +# Continuous integration + +This component is the largest part of this PR. The continuous integration component includes: + +1. Style formatting to ensure that everything looks the same. This includes checking that relevant copyrights are in place. +2. Static analysis +3. Unit testing + +The automatic style enforcement is configurable with the ability to easily add or remove rules. Currently the source pipelines are defined as: + +```python +SOURCE_PIPELINES = [ + { + "name": "C++ Main", + "src": path.join(PROJECT_ROOT, "libs"), + + "pipelines": { + "hpp": [ + require_pragma_once, + enforce_cpp_license, + enforce_formatting + ], + "cpp": [ + enforce_cpp_license, + enforce_formatting + ] + } + }, + # ... +] +``` + +This part defines pipelines for `.hpp` files and `.cpp` files allowing the developer to add such requirements such as having copyright in the op of the source file and ensure that formatting follows that given by `.clang-format`. + +Each of these CI stages can executed individually using `./manage` or you can run the entire CI process by invoking `./manage runci`. An example of what this may look like is here: + +```zsh +./manage runci + +2021-07-21 14:38:04,896 - FormatChecker - ERROR - /Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/src/OpsCounter/OpsCounter.cpp was not correctly formatted. +2021-07-21 14:38:04,899 - FormatChecker - ERROR - Your code did not pass formatting. + +./manage stylecheck --fix-issues +./manage runci + +-- Found LLVM 11.1.0 +-- Using LLVMConfig.cmake in: /usr/local/opt/llvm@11/lib/cmake/llvm +-- Configuring done +-- Generating done +-- Build files have been written to: /Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/Debug +Consolidate compiler generated dependencies of target QSharpPasses +[ 50%] Building CXX object CMakeFiles/QSharpPasses.dir/src/OpsCounter/OpsCounter.cpp.o +[100%] Linking CXX shared library libQSharpPasses.dylib +ld: warning: directory not found for option '-L/usr/local/opt/llvm/lib' +[100%] Built target QSharpPasses +/Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/src/OpsCounter/OpsCounter.cpp:29:7: error: invalid case style for class 'LegacyOpsCounterPass' [readability-identifier-naming,-warnings-as-errors] +class LegacyOpsCounterPass : public FunctionPass + ^~~~~~~~~~~~~~~~~~~~ + CLegacyOpsCounterPass +113345 warnings generated. +Suppressed 113345 warnings (113344 in non-user code, 1 NOLINT). +Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. +1 warning treated as error +2021-07-21 14:38:40,191 - Linter - ERROR - /Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/src/OpsCounter/OpsCounter.cpp failed static analysis + +# ISSUES FIXED MANUALLY +./manage runci + +-- Found LLVM 11.1.0 +-- Using LLVMConfig.cmake in: /usr/local/opt/llvm@11/lib/cmake/llvm +-- Configuring done +-- Generating done +-- Build files have been written to: /Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/Debug +Consolidate compiler generated dependencies of target QSharpPasses +[ 50%] Building CXX object CMakeFiles/QSharpPasses.dir/src/OpsCounter/OpsCounter.cpp.o +[100%] Linking CXX shared library libQSharpPasses.dylib +ld: warning: directory not found for option '-L/usr/local/opt/llvm/lib' +[100%] Built target QSharpPasses +-- Found LLVM 11.1.0 +-- Using LLVMConfig.cmake in: /usr/local/opt/llvm@11/lib/cmake/llvm +-- Configuring done +-- Generating done +-- Build files have been written to: /Users/tfr/Documents/Projects/qsharp-compiler/src/QsPasses/Debug +Consolidate compiler generated dependencies of target QSharpPasses +[100%] Built target QSharpPasses +********************************* +No test configuration file found! +********************************* +``` + +The key idea here is to make it extremely easy to be complaint with the style guide, correct any issues that might come as a result of static analysis and at the same time enforce this when a PR is made. diff --git a/src/Passes/docs/library-structure.md b/src/Passes/docs/library-structure.md new file mode 100644 index 0000000000..ae0c42238b --- /dev/null +++ b/src/Passes/docs/library-structure.md @@ -0,0 +1,38 @@ +# Library structure for passes + +An important part of this PR is that it proposes a structure for passes: It is suggested that each pass has their own subcode base. The reason for this proposal is that it makes it very easy to add and remove passes as well as decide which passes to link against. Each pass is kept in its own subdirectory under `libs`: + +``` +libs +├── CMakeLists.txt +└── OpsCounter + ├── OpsCounter.cpp + └── OpsCounter.hpp +``` + +Adding a new pass is easy using the `manage` tool developed in this PR: + +``` +./manage create-pass HelloWorld +Available templates: + +1. Function Pass + +Select a template:1 +``` + +which results in a new pass code in the `libs`: + +``` +libs +├── CMakeLists.txt +├── HelloWorld +│ ├── HelloWorld.cpp +│ ├── HelloWorld.hpp +│ └── SPECIFICATION.md +└── OpsCounter + ├── OpsCounter.cpp + └── OpsCounter.hpp +``` + +A full example of how to create a basic function pass is included in the README.md file for anyone interested. diff --git a/src/Passes/examples/ClassicalIrCommandline/README.md b/src/Passes/examples/ClassicalIrCommandline/README.md index c8a894f93e..0b0d3185ac 100644 --- a/src/Passes/examples/ClassicalIrCommandline/README.md +++ b/src/Passes/examples/ClassicalIrCommandline/README.md @@ -8,13 +8,13 @@ IRs can be represented either by a human readible language or through bytecode. C programs former is generated by ```sh -% clang -O1 -S -emit-llvm classical-program.c -o classical-program.ll + clang -O1 -S -emit-llvm classical-program.c -o classical-program.ll ``` whereas the latter is generated by executing: ```sh -% clang -O1 -c -emit-llvm classical-program.c -o classical-program.bc + clang -O1 -c -emit-llvm classical-program.c -o classical-program.bc ``` This generates a nice and short IR which makes not too overwhelming to understand what is going on. @@ -24,7 +24,7 @@ This generates a nice and short IR which makes not too overwhelming to understan This part assumes that you have built the Passes library. ```sh -% opt -load ../../{Debug,Release}/libQSharpPasses.{dylib,so} -legacy-operation-counter -analyze classical-program.ll +opt -load ../../{Debug,Release}/libQSharpPasses.{dylib,so} -legacy-operation-counter -analyze classical-program.ll ``` ## Next-gen passes @@ -32,5 +32,5 @@ This part assumes that you have built the Passes library. This part assumes that you have built the Passes library. ```sh -% opt -load-pass-plugin ../../{Debug,Release}/libQSharpPasses.{dylib,so} --passes="operation-counter" -disable-output classical-program.bc +opt -load-pass-plugin ../../{Debug,Release}/libs/libQSharpPasses.{dylib,so} --passes="print" -disable-output classical-program.bc ``` diff --git a/src/Passes/examples/OptimisationUsingOpt/README.md b/src/Passes/examples/OptimisationUsingOpt/README.md index db128fef7a..1b4a4f851f 100644 --- a/src/Passes/examples/OptimisationUsingOpt/README.md +++ b/src/Passes/examples/OptimisationUsingOpt/README.md @@ -20,10 +20,10 @@ namespace Example { You find the code for this in the folder `SimpleExample`. To generate a QIR for this code, go to the folder and run ```sh -% cd SimpleExample/ -% dotnet clean SimpleExample.csproj +cd SimpleExample/ +dotnet clean SimpleExample.csproj (...) -% dotnet build SimpleExample.csproj -c Debug +dotnet build SimpleExample.csproj -c Debug ``` If everything went well, you should now have a subdirectory called `qir` and inside `qir`, you will find `SimpleExample.ll`. Depending on your compiler, diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp new file mode 100644 index 0000000000..00f2b75f5f --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp @@ -0,0 +1,18 @@ +#include + +void QuantumFunction(int32_t nQubits) +{ + volatile uint64_t x = 3; + for (uint64_t i = 0; i < x; ++i) + { + nQubits += nQubits; + } + int32_t qubits[nQubits]; +} + +int main() +{ + QuantumFunction(10); + QuantumFunction(3); + return 0; +} \ No newline at end of file diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.csproj b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.csproj new file mode 100644 index 0000000000..eeab572589 --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.csproj @@ -0,0 +1,9 @@ + + + + Exe + netcoreapp3.1 + true + + + diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs new file mode 100644 index 0000000000..0b5655ddec --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs @@ -0,0 +1,30 @@ +namespace Example { + @EntryPoint() + operation Main() : Int + { + + QuantumProgram(3,2,1); + QuantumProgram(4,X(2),4); + return 0; + } + + function X(value: Int): Int + { + return 3 * value; + } + + operation QuantumProgram(x: Int, h: Int, g: Int) : Unit { + let z = x * (x + 1) - 47; + let y = 3 * x; + + use qubits0 = Qubit[9]; + use qubits1 = Qubit[(y - 2)/2-z]; + use qubits2 = Qubit[y - g]; + use qubits3 = Qubit[h]; + use qubits4 = Qubit[X(x)]; + + for idxIteration in 0..g { + //Message(idxIteration); + } + } +} \ No newline at end of file diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile new file mode 100644 index 0000000000..59399d367e --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile @@ -0,0 +1,13 @@ +analysis-example.ll: + dotnet build ConstSizeArray.csproj + opt -S qir/ConstSizeArray.ll -O1 > ../analysis-example.ll + make clean + +comparison: + clang++ -S -emit-llvm -std=c++14 -stdlib=libc++ Comparison.cpp -o comparison.ll + +clean: + rm -rf bin + rm -rf obj + rm -rf qir + \ No newline at end of file diff --git a/src/Passes/examples/QubitAllocationAnalysis/Makefile b/src/Passes/examples/QubitAllocationAnalysis/Makefile new file mode 100644 index 0000000000..feedf8753f --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/Makefile @@ -0,0 +1,25 @@ +run-expand: build-qaa build-esa analysis-example.ll + opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib \ + -load-pass-plugin ../../Debug/libs/libExpandStaticAllocation.dylib --passes="expand-static-allocation" -S analysis-example.ll + + +run: build-qaa analysis-example.ll + opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll + + +build-prepare: + pushd ../../ && mkdir -p Debug && cd Debug && cmake ..&& popd || popd + +build-qaa: build-prepare + pushd ../../Debug && make QubitAllocationAnalysis && popd || popd + +build-esa: build-prepare + pushd ../../Debug && make ExpandStaticAllocation && popd || popd + + +analysis-example.ll: + cd ConstSizeArray && make analysis-example.ll + +clean: + cd ConstSizeArray && make clean + rm analysis-example.ll diff --git a/src/Passes/examples/QubitAllocationAnalysis/README.md b/src/Passes/examples/QubitAllocationAnalysis/README.md new file mode 100644 index 0000000000..515b641ba4 --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/README.md @@ -0,0 +1,209 @@ +# QubitAllocationAnalysis + +## Quick start + +The following depnds on: + +- A working LLVM installation, including paths correctly setup +- CMake +- C#, Q# and the .NET framework + +Running following command + +```sh +make run +``` + +will first build the pass, then build the QIR using Q# following by removing the noise using `opt` with optimisation level 1. Finally, it will execute the analysis pass and should provide you with information about qubit allocation in the Q# program defined in `ConstSizeArray/ConstSizeArray.qs`. + +## Detailed run + +From the Passes root (two levels up from this directory), make a new build + +```sh +mkdir Debug +cd Debug +cmake .. +``` + +and then compile the `QubitAllocationAnalysis`: + +```sh +make QubitAllocationAnalysis +``` + +Next return `examples/QubitAllocationAnalysis` and enter the directory `ConstSizeArray` to build the QIR: + +```sh +make analysis-example.ll +``` + +or execute the commands manually, + +```sh +dotnet build ConstSizeArray.csproj +opt -S qir/ConstSizeArray.ll -O1 > ../analysis-example.ll +make clean +``` + +Returning to `examples/QubitAllocationAnalysis`, the pass can now be ran by executing: + +```sh +opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll +``` + +## Example cases + +Below we will consider a few different examples. You can run them by updating the code in `ConstSizeArray/ConstSizeArray.qs` and executing `make run` from the `examples/QubitAllocationAnalysis` folder subsequently. You will need to delete `analysis-example.ll` between runs. + +### Trivially constant + +This is the simplest example we can think of: + +```qsharp +namespace Example { + @EntryPoint() + operation QuantumProgram() : Unit { + use qubits = Qubit[3]; + } +} +``` + +The corresponding QIR is: + +``` +; ModuleID = 'qir/ConstSizeArray.ll' +source_filename = "qir/ConstSizeArray.ll" + +%Array = type opaque + +define internal fastcc void @Example__QuantumProgram__body() unnamed_addr { +entry: + %qubits = call %Array* @__quantum__rt__qubit_allocate_array(i64 3) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits) + ret void +} + +; (...) +``` + +Running the pass procudes following output: + +``` +opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll + +Example__QuantumProgram__body +==================== + +qubits is trivially static with 3 qubits. +``` + +### Dependency case + +In some cases, a qubit array will be compile time constant in size if the function arguments +provided are compile-time constants. One example of this is: + +``` +namespace Example { + @EntryPoint() + operation Main() : Int + { + QuantumProgram(3); + QuantumProgram(4); + return 0; + } + + operation QuantumProgram(x: Int) : Unit { + use qubits = Qubit[x]; + } +} +``` + +The corresponding QIR is + +``` +; ModuleID = 'qir/ConstSizeArray.ll' +source_filename = "qir/ConstSizeArray.ll" + +%Array = type opaque +%String = type opaque + +define internal fastcc void @Example__Main__body() unnamed_addr { +entry: + call fastcc void @Example__QuantumProgram__body(i64 3) + call fastcc void @Example__QuantumProgram__body(i64 4) + ret void +} + +define internal fastcc void @Example__QuantumProgram__body(i64 %x) unnamed_addr { +entry: + %qubits = call %Array* @__quantum__rt__qubit_allocate_array(i64 %x) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits) + ret void +} +; ( ... ) + +``` + +The analyser returns following output: + +``` +opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll + +Example__QuantumProgram__body +==================== + +qubits depends on x being constant to be static. + +``` + +### Summary case + +Finally, we do a summary case that demonstrates some of the more elaborate cases: + +``` +namespace Example { + @EntryPoint() + operation Main() : Int + { + QuantumProgram(3,2,1); + QuantumProgram(4,9,4); + return 0; + } + + function X(value: Int): Int + { + return 3 * value; + } + + operation QuantumProgram(x: Int, h: Int, g: Int) : Unit { + let z = x * (x + 1) - 47; + let y = 3 * x; + + use qubits0 = Qubit[9]; + use qubits1 = Qubit[(y - 2)/2-z]; + use qubits2 = Qubit[y - g]; + use qubits3 = Qubit[h]; + use qubits4 = Qubit[X(x)]; + } +} +``` + +We will omit the QIR in the documenation as it is a long. The output of the anaysis is: + +``` +opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll + +Example__QuantumProgram__body +==================== + +qubits0 is trivially static with 9 qubits. +qubits1 depends on x being constant to be static. +qubits2 depends on x, g being constant to be static. +qubits3 depends on h being constant to be static. +qubits4 is dynamic. +``` diff --git a/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll b/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll new file mode 100644 index 0000000000..6f6c98c8e0 --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll @@ -0,0 +1,438 @@ +; ModuleID = 'qir/ConstSizeArray.ll' +source_filename = "qir/ConstSizeArray.ll" + +%Tuple = type opaque +%Qubit = type opaque +%Array = type opaque +%Result = type opaque +%Callable = type opaque +%String = type opaque + +@Microsoft__Quantum__Qir__Emission__M = internal constant [4 x void (%Tuple*, %Tuple*, %Tuple*)*] [void (%Tuple*, %Tuple*, %Tuple*)* @Microsoft__Quantum__Qir__Emission__M__body__wrapper, void (%Tuple*, %Tuple*, %Tuple*)* null, void (%Tuple*, %Tuple*, %Tuple*)* null, void (%Tuple*, %Tuple*, %Tuple*)* null] +@0 = internal constant [3 x i8] c", \00" +@1 = internal constant [2 x i8] c"[\00" +@2 = internal constant [2 x i8] c"]\00" + +declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) local_unnamed_addr + +declare void @__quantum__qis__cnot__adj(%Qubit*, %Qubit*) local_unnamed_addr + +declare void @__quantum__rt__array_update_alias_count(%Array*, i32) local_unnamed_addr + +declare %Tuple* @__quantum__rt__tuple_create(i64) local_unnamed_addr + +declare void @__quantum__rt__tuple_update_reference_count(%Tuple*, i32) local_unnamed_addr + +define internal fastcc %Result* @Microsoft__Quantum__Qir__Emission__M__body(%Qubit* %q) unnamed_addr { +entry: + %0 = call %Result* @__quantum__qis__m__body(%Qubit* %q) + ret %Result* %0 +} + +declare %Result* @__quantum__qis__m__body(%Qubit*) local_unnamed_addr + +define internal fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %a, %Qubit* %b, %Qubit* %c) unnamed_addr { +entry: + call void @__quantum__qis__cnot__body(%Qubit* %c, %Qubit* %b) + call void @__quantum__qis__cnot__body(%Qubit* %c, %Qubit* %a) + call void @__quantum__qis__toffoli__body(%Qubit* %a, %Qubit* %b, %Qubit* %c) + ret void +} + +declare void @__quantum__qis__toffoli__body(%Qubit*, %Qubit*, %Qubit*) local_unnamed_addr + +define internal fastcc void @Microsoft__Quantum__Qir__Emission__Majority__adj(%Qubit* %a, %Qubit* %b, %Qubit* %c) unnamed_addr { +entry: + call void @__quantum__qis__toffoli__adj(%Qubit* %a, %Qubit* %b, %Qubit* %c) + call void @__quantum__qis__cnot__adj(%Qubit* %c, %Qubit* %a) + call void @__quantum__qis__cnot__adj(%Qubit* %c, %Qubit* %b) + ret void +} + +declare void @__quantum__qis__toffoli__adj(%Qubit*, %Qubit*, %Qubit*) local_unnamed_addr + +define internal fastcc %Array* @Microsoft__Quantum__Qir__Emission__RunAdder__body() unnamed_addr { +entry: + %a = call %Array* @__quantum__rt__qubit_allocate_array(i64 4) + call void @__quantum__rt__array_update_alias_count(%Array* %a, i32 1) + %b = call %Array* @__quantum__rt__qubit_allocate_array(i64 4) + call void @__quantum__rt__array_update_alias_count(%Array* %b, i32 1) + %cin = call %Qubit* @__quantum__rt__qubit_allocate() + %cout = call %Qubit* @__quantum__rt__qubit_allocate() + %0 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 0) + %1 = bitcast i8* %0 to %Qubit** + %q = load %Qubit*, %Qubit** %1, align 8 + call void @__quantum__qis__x__body(%Qubit* %q) + %2 = call i64 @__quantum__rt__array_get_size_1d(%Array* %b) + %3 = add i64 %2, -1 + %.not1 = icmp slt i64 %3, 0 + br i1 %.not1, label %exit__1, label %body__1 + +body__1: ; preds = %entry, %body__1 + %4 = phi i64 [ %7, %body__1 ], [ 0, %entry ] + %5 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 %4) + %6 = bitcast i8* %5 to %Qubit** + %q__1 = load %Qubit*, %Qubit** %6, align 8 + call void @__quantum__qis__x__body(%Qubit* %q__1) + %7 = add i64 %4, 1 + %.not = icmp sgt i64 %7, %3 + br i1 %.not, label %exit__1, label %body__1 + +exit__1: ; preds = %body__1, %entry + %8 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 0) + %9 = bitcast i8* %8 to %Qubit** + %10 = load %Qubit*, %Qubit** %9, align 8 + %11 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 0) + %12 = bitcast i8* %11 to %Qubit** + %13 = load %Qubit*, %Qubit** %12, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %cin, %Qubit* %10, %Qubit* %13) + %14 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 0) + %15 = bitcast i8* %14 to %Qubit** + %16 = load %Qubit*, %Qubit** %15, align 8 + %17 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 1) + %18 = bitcast i8* %17 to %Qubit** + %19 = load %Qubit*, %Qubit** %18, align 8 + %20 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 1) + %21 = bitcast i8* %20 to %Qubit** + %22 = load %Qubit*, %Qubit** %21, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %16, %Qubit* %19, %Qubit* %22) + %23 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 1) + %24 = bitcast i8* %23 to %Qubit** + %25 = load %Qubit*, %Qubit** %24, align 8 + %26 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 2) + %27 = bitcast i8* %26 to %Qubit** + %28 = load %Qubit*, %Qubit** %27, align 8 + %29 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 2) + %30 = bitcast i8* %29 to %Qubit** + %31 = load %Qubit*, %Qubit** %30, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %25, %Qubit* %28, %Qubit* %31) + %32 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 2) + %33 = bitcast i8* %32 to %Qubit** + %34 = load %Qubit*, %Qubit** %33, align 8 + %35 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 3) + %36 = bitcast i8* %35 to %Qubit** + %37 = load %Qubit*, %Qubit** %36, align 8 + %38 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 3) + %39 = bitcast i8* %38 to %Qubit** + %40 = load %Qubit*, %Qubit** %39, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %34, %Qubit* %37, %Qubit* %40) + %41 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 3) + %42 = bitcast i8* %41 to %Qubit** + %c = load %Qubit*, %Qubit** %42, align 8 + call void @__quantum__qis__cnot__body(%Qubit* %c, %Qubit* %cout) + %43 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 2) + %44 = bitcast i8* %43 to %Qubit** + %45 = load %Qubit*, %Qubit** %44, align 8 + %46 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 3) + %47 = bitcast i8* %46 to %Qubit** + %48 = load %Qubit*, %Qubit** %47, align 8 + %49 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 3) + %50 = bitcast i8* %49 to %Qubit** + %51 = load %Qubit*, %Qubit** %50, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__adj(%Qubit* %45, %Qubit* %48, %Qubit* %51) + %52 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 1) + %53 = bitcast i8* %52 to %Qubit** + %54 = load %Qubit*, %Qubit** %53, align 8 + %55 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 2) + %56 = bitcast i8* %55 to %Qubit** + %57 = load %Qubit*, %Qubit** %56, align 8 + %58 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 2) + %59 = bitcast i8* %58 to %Qubit** + %60 = load %Qubit*, %Qubit** %59, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__adj(%Qubit* %54, %Qubit* %57, %Qubit* %60) + %61 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 0) + %62 = bitcast i8* %61 to %Qubit** + %63 = load %Qubit*, %Qubit** %62, align 8 + %64 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 1) + %65 = bitcast i8* %64 to %Qubit** + %66 = load %Qubit*, %Qubit** %65, align 8 + %67 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 1) + %68 = bitcast i8* %67 to %Qubit** + %69 = load %Qubit*, %Qubit** %68, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__adj(%Qubit* %63, %Qubit* %66, %Qubit* %69) + %70 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %b, i64 0) + %71 = bitcast i8* %70 to %Qubit** + %72 = load %Qubit*, %Qubit** %71, align 8 + %73 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %a, i64 0) + %74 = bitcast i8* %73 to %Qubit** + %75 = load %Qubit*, %Qubit** %74, align 8 + call fastcc void @Microsoft__Quantum__Qir__Emission__Majority__adj(%Qubit* %cin, %Qubit* %72, %Qubit* %75) + %76 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* nonnull @Microsoft__Quantum__Qir__Emission__M, [2 x void (%Tuple*, i32)*]* null, %Tuple* null) + %77 = call fastcc %Array* @Microsoft__Quantum__Qir__Emission___73da7dcac81a47ddabb1a0e30be3dfdb_ForEach__body(%Callable* %76, %Array* %b) + call void @__quantum__rt__array_update_alias_count(%Array* %b, i32 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %a, i32 -1) + call void @__quantum__rt__capture_update_reference_count(%Callable* %76, i32 -1) + call void @__quantum__rt__callable_update_reference_count(%Callable* %76, i32 -1) + call void @__quantum__rt__qubit_release(%Qubit* %cin) + call void @__quantum__rt__qubit_release(%Qubit* %cout) + call void @__quantum__rt__qubit_release_array(%Array* %b) + call void @__quantum__rt__qubit_release_array(%Array* %a) + ret %Array* %77 +} + +declare %Qubit* @__quantum__rt__qubit_allocate() local_unnamed_addr + +declare %Array* @__quantum__rt__qubit_allocate_array(i64) local_unnamed_addr + +declare void @__quantum__rt__qubit_release_array(%Array*) local_unnamed_addr + +declare void @__quantum__rt__qubit_release(%Qubit*) local_unnamed_addr + +declare i8* @__quantum__rt__array_get_element_ptr_1d(%Array*, i64) local_unnamed_addr + +declare void @__quantum__qis__x__body(%Qubit*) local_unnamed_addr + +declare i64 @__quantum__rt__array_get_size_1d(%Array*) local_unnamed_addr + +define internal fastcc %Array* @Microsoft__Quantum__Qir__Emission___73da7dcac81a47ddabb1a0e30be3dfdb_ForEach__body(%Callable* %action, %Array* %array) unnamed_addr { +entry: + call void @__quantum__rt__capture_update_alias_count(%Callable* %action, i32 1) + call void @__quantum__rt__callable_update_alias_count(%Callable* %action, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %array, i32 1) + %0 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 0) + call void @__quantum__rt__array_update_alias_count(%Array* %0, i32 1) + call void @__quantum__rt__array_update_reference_count(%Array* %0, i32 1) + %1 = call i64 @__quantum__rt__array_get_size_1d(%Array* %array) + %2 = add i64 %1, -1 + %.not9 = icmp slt i64 %2, 0 + br i1 %.not9, label %exit__1, label %body__1 + +body__1: ; preds = %entry, %exit__4 + %3 = phi i64 [ %32, %exit__4 ], [ 0, %entry ] + %res.010 = phi %Array* [ %14, %exit__4 ], [ %0, %entry ] + %4 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %array, i64 %3) + %5 = bitcast i8* %4 to %Qubit** + %item = load %Qubit*, %Qubit** %5, align 8 + %6 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 1) + %7 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %6, i64 0) + %8 = bitcast i8* %7 to %Result** + %9 = call %Tuple* @__quantum__rt__tuple_create(i64 8) + %10 = bitcast %Tuple* %9 to %Qubit** + store %Qubit* %item, %Qubit** %10, align 8 + %11 = call %Tuple* @__quantum__rt__tuple_create(i64 8) + call void @__quantum__rt__callable_invoke(%Callable* %action, %Tuple* %9, %Tuple* %11) + %12 = bitcast %Tuple* %11 to %Result** + %13 = load %Result*, %Result** %12, align 8 + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %9, i32 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %11, i32 -1) + store %Result* %13, %Result** %8, align 8 + %14 = call %Array* @__quantum__rt__array_concatenate(%Array* %res.010, %Array* %6) + %15 = call i64 @__quantum__rt__array_get_size_1d(%Array* %14) + %16 = add i64 %15, -1 + %.not57 = icmp slt i64 %16, 0 + br i1 %.not57, label %exit__2, label %body__2 + +exit__1: ; preds = %exit__4, %entry + %res.0.lcssa = phi %Array* [ %0, %entry ], [ %14, %exit__4 ] + call void @__quantum__rt__capture_update_alias_count(%Callable* %action, i32 -1) + call void @__quantum__rt__callable_update_alias_count(%Callable* %action, i32 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %array, i32 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %res.0.lcssa, i32 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %0, i32 -1) + ret %Array* %res.0.lcssa + +body__2: ; preds = %body__1, %body__2 + %17 = phi i64 [ %21, %body__2 ], [ 0, %body__1 ] + %18 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %14, i64 %17) + %19 = bitcast i8* %18 to %Result** + %20 = load %Result*, %Result** %19, align 8 + call void @__quantum__rt__result_update_reference_count(%Result* %20, i32 1) + %21 = add i64 %17, 1 + %.not5 = icmp sgt i64 %21, %16 + br i1 %.not5, label %exit__2, label %body__2 + +exit__2: ; preds = %body__2, %body__1 + call void @__quantum__rt__array_update_reference_count(%Array* %14, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %14, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %res.010, i32 -1) + %22 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %6, i64 0) + %23 = bitcast i8* %22 to %Result** + %24 = load %Result*, %Result** %23, align 8 + call void @__quantum__rt__result_update_reference_count(%Result* %24, i32 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %6, i32 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %14, i32 -1) + %25 = call i64 @__quantum__rt__array_get_size_1d(%Array* %res.010) + %26 = add i64 %25, -1 + %.not68 = icmp slt i64 %26, 0 + br i1 %.not68, label %exit__4, label %body__4 + +body__4: ; preds = %exit__2, %body__4 + %27 = phi i64 [ %31, %body__4 ], [ 0, %exit__2 ] + %28 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %res.010, i64 %27) + %29 = bitcast i8* %28 to %Result** + %30 = load %Result*, %Result** %29, align 8 + call void @__quantum__rt__result_update_reference_count(%Result* %30, i32 -1) + %31 = add i64 %27, 1 + %.not6 = icmp sgt i64 %31, %26 + br i1 %.not6, label %exit__4, label %body__4 + +exit__4: ; preds = %body__4, %exit__2 + call void @__quantum__rt__array_update_reference_count(%Array* %res.010, i32 -1) + %32 = add i64 %3, 1 + %.not = icmp sgt i64 %32, %2 + br i1 %.not, label %exit__1, label %body__1 +} + +define internal void @Microsoft__Quantum__Qir__Emission__M__body__wrapper(%Tuple* nocapture readnone %capture-tuple, %Tuple* nocapture readonly %arg-tuple, %Tuple* nocapture %result-tuple) { +entry: + %0 = bitcast %Tuple* %arg-tuple to %Qubit** + %1 = load %Qubit*, %Qubit** %0, align 8 + %2 = call fastcc %Result* @Microsoft__Quantum__Qir__Emission__M__body(%Qubit* %1) + %3 = bitcast %Tuple* %result-tuple to %Result** + store %Result* %2, %Result** %3, align 8 + ret void +} + +declare %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]*, [2 x void (%Tuple*, i32)*]*, %Tuple*) local_unnamed_addr + +declare void @__quantum__rt__capture_update_reference_count(%Callable*, i32) local_unnamed_addr + +declare void @__quantum__rt__callable_update_reference_count(%Callable*, i32) local_unnamed_addr + +declare void @__quantum__rt__capture_update_alias_count(%Callable*, i32) local_unnamed_addr + +declare void @__quantum__rt__callable_update_alias_count(%Callable*, i32) local_unnamed_addr + +declare %Array* @__quantum__rt__array_create_1d(i32, i64) local_unnamed_addr + +declare void @__quantum__rt__array_update_reference_count(%Array*, i32) local_unnamed_addr + +declare void @__quantum__rt__callable_invoke(%Callable*, %Tuple*, %Tuple*) local_unnamed_addr + +declare %Array* @__quantum__rt__array_concatenate(%Array*, %Array*) local_unnamed_addr + +declare void @__quantum__rt__result_update_reference_count(%Result*, i32) local_unnamed_addr + +declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr + +define { i64, i8* }* @Microsoft__Quantum__Qir__Emission__RunAdder__Interop() local_unnamed_addr #0 { +entry: + %0 = call fastcc %Array* @Microsoft__Quantum__Qir__Emission__RunAdder__body() + %1 = call i64 @__quantum__rt__array_get_size_1d(%Array* %0) + %2 = call i8* @__quantum__rt__memory_allocate(i64 %1) + %3 = ptrtoint i8* %2 to i64 + %4 = add i64 %1, -1 + %.not5 = icmp slt i64 %4, 0 + br i1 %.not5, label %exit__1, label %body__1 + +body__1: ; preds = %entry, %body__1 + %5 = phi i64 [ %14, %body__1 ], [ 0, %entry ] + %6 = add i64 %5, %3 + %7 = inttoptr i64 %6 to i8* + %8 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %0, i64 %5) + %9 = bitcast i8* %8 to %Result** + %10 = load %Result*, %Result** %9, align 8 + %11 = call %Result* @__quantum__rt__result_get_zero() + %12 = call i1 @__quantum__rt__result_equal(%Result* %10, %Result* %11) + %not. = xor i1 %12, true + %13 = sext i1 %not. to i8 + store i8 %13, i8* %7, align 1 + %14 = add i64 %5, 1 + %.not = icmp sgt i64 %14, %4 + br i1 %.not, label %exit__1, label %body__1 + +exit__1: ; preds = %body__1, %entry + %15 = call i8* @__quantum__rt__memory_allocate(i64 16) + %16 = bitcast i8* %15 to i64* + store i64 %1, i64* %16, align 4 + %17 = getelementptr i8, i8* %15, i64 8 + %18 = bitcast i8* %17 to i8** + store i8* %2, i8** %18, align 8 + %.not34 = icmp slt i64 %4, 0 + br i1 %.not34, label %exit__2, label %body__2 + +body__2: ; preds = %exit__1, %body__2 + %19 = phi i64 [ %23, %body__2 ], [ 0, %exit__1 ] + %20 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %0, i64 %19) + %21 = bitcast i8* %20 to %Result** + %22 = load %Result*, %Result** %21, align 8 + call void @__quantum__rt__result_update_reference_count(%Result* %22, i32 -1) + %23 = add i64 %19, 1 + %.not3 = icmp sgt i64 %23, %4 + br i1 %.not3, label %exit__2, label %body__2 + +exit__2: ; preds = %body__2, %exit__1 + %24 = bitcast i8* %15 to { i64, i8* }* + call void @__quantum__rt__array_update_reference_count(%Array* %0, i32 -1) + ret { i64, i8* }* %24 +} + +declare i8* @__quantum__rt__memory_allocate(i64) local_unnamed_addr + +declare %Result* @__quantum__rt__result_get_zero() local_unnamed_addr + +declare i1 @__quantum__rt__result_equal(%Result*, %Result*) local_unnamed_addr + +define void @Microsoft__Quantum__Qir__Emission__RunAdder() local_unnamed_addr #1 { +entry: + %0 = call fastcc %Array* @Microsoft__Quantum__Qir__Emission__RunAdder__body() + %1 = call %String* @__quantum__rt__string_create(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @0, i64 0, i64 0)) + %2 = call %String* @__quantum__rt__string_create(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i64 0, i64 0)) + call void @__quantum__rt__string_update_reference_count(%String* %2, i32 1) + %3 = call i64 @__quantum__rt__array_get_size_1d(%Array* %0) + %4 = add i64 %3, -1 + %.not7 = icmp slt i64 %4, 0 + br i1 %.not7, label %exit__1, label %body__1 + +body__1: ; preds = %entry, %condContinue__1 + %5 = phi i64 [ %14, %condContinue__1 ], [ 0, %entry ] + %6 = phi %String* [ %13, %condContinue__1 ], [ %2, %entry ] + %7 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %0, i64 %5) + %8 = bitcast i8* %7 to %Result** + %9 = load %Result*, %Result** %8, align 8 + %.not5 = icmp eq %String* %6, %2 + br i1 %.not5, label %condContinue__1, label %condTrue__1 + +condTrue__1: ; preds = %body__1 + %10 = call %String* @__quantum__rt__string_concatenate(%String* %6, %String* %1) + call void @__quantum__rt__string_update_reference_count(%String* %6, i32 -1) + br label %condContinue__1 + +condContinue__1: ; preds = %condTrue__1, %body__1 + %11 = phi %String* [ %10, %condTrue__1 ], [ %6, %body__1 ] + %12 = call %String* @__quantum__rt__result_to_string(%Result* %9) + %13 = call %String* @__quantum__rt__string_concatenate(%String* %11, %String* %12) + call void @__quantum__rt__string_update_reference_count(%String* %11, i32 -1) + call void @__quantum__rt__string_update_reference_count(%String* %12, i32 -1) + %14 = add i64 %5, 1 + %.not = icmp sgt i64 %14, %4 + br i1 %.not, label %exit__1, label %body__1 + +exit__1: ; preds = %condContinue__1, %entry + %.lcssa = phi %String* [ %2, %entry ], [ %13, %condContinue__1 ] + %15 = call %String* @__quantum__rt__string_create(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i64 0, i64 0)) + %16 = call %String* @__quantum__rt__string_concatenate(%String* %.lcssa, %String* %15) + call void @__quantum__rt__string_update_reference_count(%String* %.lcssa, i32 -1) + call void @__quantum__rt__string_update_reference_count(%String* %15, i32 -1) + call void @__quantum__rt__string_update_reference_count(%String* %1, i32 -1) + call void @__quantum__rt__string_update_reference_count(%String* %2, i32 -1) + call void @__quantum__rt__message(%String* %16) + %.not46 = icmp slt i64 %4, 0 + br i1 %.not46, label %exit__2, label %body__2 + +body__2: ; preds = %exit__1, %body__2 + %17 = phi i64 [ %21, %body__2 ], [ 0, %exit__1 ] + %18 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %0, i64 %17) + %19 = bitcast i8* %18 to %Result** + %20 = load %Result*, %Result** %19, align 8 + call void @__quantum__rt__result_update_reference_count(%Result* %20, i32 -1) + %21 = add i64 %17, 1 + %.not4 = icmp sgt i64 %21, %4 + br i1 %.not4, label %exit__2, label %body__2 + +exit__2: ; preds = %body__2, %exit__1 + call void @__quantum__rt__array_update_reference_count(%Array* %0, i32 -1) + call void @__quantum__rt__string_update_reference_count(%String* %16, i32 -1) + ret void +} + +declare void @__quantum__rt__message(%String*) local_unnamed_addr + +declare %String* @__quantum__rt__string_create(i8*) local_unnamed_addr + +declare %String* @__quantum__rt__string_concatenate(%String*, %String*) local_unnamed_addr + +declare %String* @__quantum__rt__result_to_string(%Result*) local_unnamed_addr + +attributes #0 = { "InteropFriendly" } +attributes #1 = { "EntryPoint" } diff --git a/src/Passes/include/Llvm.hpp b/src/Passes/include/Llvm.hpp index f24aef3726..80a4728b83 100644 --- a/src/Passes/include/Llvm.hpp +++ b/src/Passes/include/Llvm.hpp @@ -27,10 +27,24 @@ #pragma clang diagnostic ignored "-Weverything" #endif -#include "llvm/IR/LegacyPassManager.h" +// Passes #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +// Building +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Verifier.h" #if defined(__clang__) #pragma clang diagnostic pop diff --git a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp b/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp new file mode 100644 index 0000000000..5684864d7a --- /dev/null +++ b/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include "ExpandStaticAllocation/ExpandStaticAllocation.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + llvm::PreservedAnalyses ExpandStaticAllocationPass::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& fam) + { + // Pass body + for (auto& basic_block : function) + { + // Keeping track of instructions to remove in each block + std::vector to_remove; + + for (auto& instruction : basic_block) + { + // Finding calls + auto* call_instr = llvm::dyn_cast(&instruction); + if (call_instr == nullptr) + { + continue; + } + + ConstantArguments argument_constants{}; + std::vector remaining_arguments{}; + + auto callee_function = call_instr->getCalledFunction(); + auto& depenency_graph = fam.getResult(*callee_function); + + if (depenency_graph.size() > 0) + { + uint32_t idx = 0; + auto n = static_cast(callee_function->arg_size()); + + // Finding argument constants + while (idx < n) + { + auto arg = callee_function->getArg(idx); + auto value = call_instr->getArgOperand(idx); + + auto cst = llvm::dyn_cast(value); + if (cst != nullptr) + { + argument_constants[arg->getName().str()] = cst; + } + else + { + remaining_arguments.push_back(idx); + } + + ++idx; + } + + // Checking which arrays are constant for this + auto new_callee = expandFunctionCall(depenency_graph, *callee_function, argument_constants); + + // Replacing call if a new function was created + if (new_callee != nullptr) + { + llvm::IRBuilder<> builder(call_instr); + (void)call_instr; + + // List with new call arguments + std::vector new_arguments; + for (auto const& i : remaining_arguments) + { + // Getting the i'th argument + llvm::Value* arg = call_instr->getArgOperand(i); + + // Adding arguments that were not constant + if (argument_constants.find(arg->getName().str()) == argument_constants.end()) + { + new_arguments.push_back(arg); + } + } + + // Creating a new call + llvm::Value* new_call = builder.CreateCall(new_callee, new_arguments); + + // Replace all calls to old function with calls to new function + for (auto& use : call_instr->uses()) + { + llvm::User* user = use.getUser(); + user->setOperand(use.getOperandNo(), new_call); + } + + // Schedule original instruction for deletion + to_remove.push_back(&instruction); + } + } + } + + // Removing instructions + for (auto& instruction : to_remove) + { + if (!instruction->use_empty()) + { + instruction->replaceAllUsesWith(llvm::UndefValue::get(instruction->getType())); + } + instruction->eraseFromParent(); + } + } + + return llvm::PreservedAnalyses::none(); + } + + llvm::Function* ExpandStaticAllocationPass::expandFunctionCall( + QubitAllocationResult const& depenency_graph, + llvm::Function& callee, + ConstantArguments const& const_args) + { + bool should_replace_function = false; + if (!depenency_graph.empty()) + { + // Checking that any of all allocations in the function + // body becomes static from replacing constant function arguments + for (auto const& allocation : depenency_graph) + { + // Ignoring non-static allocations + if (!allocation.is_possibly_static) + { + continue; + } + + // Ignoring trivial allocations + if (allocation.depends_on.empty()) + { + continue; + } + + // Checking all dependencies are constant + bool all_const = true; + for (auto& name : allocation.depends_on) + { + all_const = all_const && (const_args.find(name) != const_args.end()); + } + + // In case that all dependencies are constant for this + // allocation, we should replace the function with one where + // the arguments are eliminated. + if (all_const) + { + should_replace_function = true; + } + } + } + + // Replacing function if needed + if (should_replace_function) + { + auto module = callee.getParent(); + auto& context = module->getContext(); + llvm::IRBuilder<> builder(context); + + // Copying the original function + llvm::ValueToValueMapTy remapper; + std::vector arg_types; + + // The user might be deleting arguments to the function by specifying them in + // the VMap. If so, we need to not add the arguments to the arg ty vector + // + for (auto const& arg : callee.args()) + { + // Skipping constant arguments + + if (const_args.find(arg.getName().str()) != const_args.end()) + { + continue; + } + + arg_types.push_back(arg.getType()); + } + + // Creating a new function + llvm::FunctionType* function_type = llvm::FunctionType::get( + callee.getFunctionType()->getReturnType(), arg_types, callee.getFunctionType()->isVarArg()); + auto function = llvm::Function::Create( + function_type, callee.getLinkage(), callee.getAddressSpace(), callee.getName(), module); + + // Copying the non-const arguments + auto dest_args_it = function->arg_begin(); + + for (auto const& arg : callee.args()) + { + auto const_it = const_args.find(arg.getName().str()); + if (const_it == const_args.end()) + { + // Mapping remaining function arguments + dest_args_it->setName(arg.getName()); + remapper[&arg] = &*dest_args_it++; + } + else + { + remapper[&arg] = llvm::ConstantInt::get(context, const_it->second->getValue()); + } + } + + llvm::SmallVector returns; // Ignore returns cloned. + + // TODO(tfr): In LLVM 13 upgrade 'true' to 'llvm::CloneFunctionChangeType::LocalChangesOnly' + llvm::CloneFunctionInto(function, &callee, remapper, true, returns, "", nullptr); + + verifyFunction(*function); + + return function; + } + + return nullptr; + } + + bool ExpandStaticAllocationPass::isRequired() + { + return true; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp b/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp new file mode 100644 index 0000000000..fbee619be2 --- /dev/null +++ b/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp @@ -0,0 +1,51 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include "QubitAllocationAnalysis/QubitAllocationAnalysis.hpp" + +#include + +namespace microsoft +{ +namespace quantum +{ + + class ExpandStaticAllocationPass : public llvm::PassInfoMixin + { + public: + using QubitAllocationResult = QubitAllocationAnalysisAnalytics::Result; + using ConstantArguments = std::unordered_map; + + /// Constructors and destructors + /// @{ + ExpandStaticAllocationPass() = default; + ExpandStaticAllocationPass(ExpandStaticAllocationPass const&) = default; + ExpandStaticAllocationPass(ExpandStaticAllocationPass&&) = default; + ~ExpandStaticAllocationPass() = default; + /// @} + + /// Operators + /// @{ + ExpandStaticAllocationPass& operator=(ExpandStaticAllocationPass const&) = default; + ExpandStaticAllocationPass& operator=(ExpandStaticAllocationPass&&) = default; + /// @} + + /// Functions required by LLVM + /// @{ + llvm::PreservedAnalyses run(llvm::Function& function, llvm::FunctionAnalysisManager& fam); + static bool isRequired(); + /// @} + + /// @{ + llvm::Function* expandFunctionCall( + QubitAllocationResult const& depenency_graph, + llvm::Function& callee, + ConstantArguments const& const_args); + /// @} + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp b/src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp new file mode 100644 index 0000000000..e73a64b7d8 --- /dev/null +++ b/src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include "ExpandStaticAllocation/ExpandStaticAllocation.hpp" + +#include +#include + +namespace +{ +llvm::PassPluginLibraryInfo getExpandStaticAllocationPluginInfo() +{ + using namespace microsoft::quantum; + using namespace llvm; + + return { + LLVM_PLUGIN_API_VERSION, "ExpandStaticAllocation", LLVM_VERSION_STRING, + [](PassBuilder& pb) + { + // Registering the pass + pb.registerPipelineParsingCallback( + [](StringRef name, FunctionPassManager& fpm, ArrayRef /*unused*/) + { + if (name == "expand-static-allocation") + { + fpm.addPass(ExpandStaticAllocationPass()); + return true; + } + + return false; + }); + }}; +} +} // namespace + +// Interface for loading the plugin +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() +{ + return getExpandStaticAllocationPluginInfo(); +} diff --git a/src/Passes/libs/ExpandStaticAllocation/SPECIFICATION.md b/src/Passes/libs/ExpandStaticAllocation/SPECIFICATION.md new file mode 100644 index 0000000000..5095eea8b3 --- /dev/null +++ b/src/Passes/libs/ExpandStaticAllocation/SPECIFICATION.md @@ -0,0 +1 @@ +# {ExpandStaticAllocation} Specification diff --git a/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp b/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp new file mode 100644 index 0000000000..ac03bc1f41 --- /dev/null +++ b/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include "QubitAllocationAnalysis/QubitAllocationAnalysis.hpp" + +#include +#include + +namespace +{ +// Interface to plugin +llvm::PassPluginLibraryInfo getQubitAllocationAnalysisPluginInfo() +{ + using namespace microsoft::quantum; + using namespace llvm; + + return { + LLVM_PLUGIN_API_VERSION, "QubitAllocationAnalysis", LLVM_VERSION_STRING, + [](PassBuilder& pb) + { + // Registering a printer for the anaylsis + pb.registerPipelineParsingCallback( + [](StringRef name, FunctionPassManager& fpm, ArrayRef /*unused*/) + { + if (name == "print") + { + fpm.addPass(QubitAllocationAnalysisPrinter(llvm::errs())); + return true; + } + return false; + }); + + pb.registerVectorizerStartEPCallback( + [](llvm::FunctionPassManager& fpm, llvm::PassBuilder::OptimizationLevel /*level*/) + { fpm.addPass(QubitAllocationAnalysisPrinter(llvm::errs())); }); + + // Registering the analysis module + pb.registerAnalysisRegistrationCallback( + [](FunctionAnalysisManager& fam) + { fam.registerPass([] { return QubitAllocationAnalysisAnalytics(); }); }); + }}; +} + +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() +{ + return getQubitAllocationAnalysisPluginInfo(); +} diff --git a/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.cpp b/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.cpp new file mode 100644 index 0000000000..60ef6aaeb7 --- /dev/null +++ b/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.cpp @@ -0,0 +1,321 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include "QubitAllocationAnalysis/QubitAllocationAnalysis.hpp" + +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + bool QubitAllocationAnalysisAnalytics::operandsConstant(Instruction const& instruction) const + { + // Default is true (i.e. the case of no operands) + bool ret = true; + + // Checking that all oprands are constant + for (auto& op : instruction.operands()) + { + + // An operand is constant if its value was previously generated from + // a const expression ... + auto const_arg = constantness_dependencies_.find(op) != constantness_dependencies_.end(); + + // ... or if it is just a compile time constant. Note that we + // delibrately only consider integers. We may expand this + // to other constants once we have function support. + auto cst = llvm::dyn_cast(op); + auto is_constant = (cst != nullptr); + + ret = ret && (const_arg || is_constant); + } + + return ret; + } + + void QubitAllocationAnalysisAnalytics::markPossibleConstant(Instruction& instruction) + { + // Creating arg dependencies + ArgList all_dependencies{}; + for (auto& op : instruction.operands()) + { + // If the operand has dependecies ... + auto it = constantness_dependencies_.find(op); + if (it != constantness_dependencies_.end()) + { + // ... we add these as a dependency for the + // resulting instructions value + for (auto& arg : it->second) + { + all_dependencies.insert(arg); + } + } + } + + // Adding full list of dependices to the dependency graph + constantness_dependencies_.insert({&instruction, all_dependencies}); + } + + void QubitAllocationAnalysisAnalytics::analyseCall(Instruction& instruction) + { + // Skipping debug code + if (instruction.isDebugOrPseudoInst()) + { + return; + } + + // Recovering the call information + auto* call_instr = llvm::dyn_cast(&instruction); + if (call_instr == nullptr) + { + return; + } + + // Getting the name of the function being called + auto target_function = call_instr->getCalledFunction(); + auto name = target_function->getName(); + + // TODO(tfr): Make use of TargetLibraryInfo + if (name != "__quantum__rt__qubit_allocate_array") + { + return; + } + + // We expect only a single argument with the number + // of qubits allocated + if (call_instr->arg_size() != 1) + { + llvm::errs() << "Expected exactly one argument\n"; + return; + } + + // Next we extract the argument ... + auto argument = call_instr->getArgOperand(0); + if (argument == nullptr) + { + llvm::errs() << "Failed getting the size argument\n"; + return; + } + + // ... and checks whether it is a result of a dependant + // const expression + auto it = constantness_dependencies_.find(argument); + if (it != constantness_dependencies_.end()) + { + // If it is, we add the details to the result list + QubitArray qubit_array; + qubit_array.is_possibly_static = true; + qubit_array.variable_name = instruction.getName().str(); + qubit_array.depends_on = it->second; + + // Pushing to the result + results_.push_back(std::move(qubit_array)); + return; + } + + // Otherwise, it may be a static allocation based on a constant (or + // folded constant) + auto cst = llvm::dyn_cast(argument); + if (cst != nullptr) + { + QubitArray qubit_array; + qubit_array.is_possibly_static = true; + qubit_array.variable_name = instruction.getName().str(); + qubit_array.size = cst->getZExtValue(); + + // Pushing to the result + results_.push_back(std::move(qubit_array)); + + return; + } + + // If neither of the previous is the case, we are dealing with a non-static array + QubitArray qubit_array; + qubit_array.is_possibly_static = false; + qubit_array.variable_name = instruction.getName().str(); + + // Storing the result + results_.push_back(std::move(qubit_array)); + } + + void QubitAllocationAnalysisAnalytics::analyseFunction(llvm::Function& function) + { + // Clearing results generated in a previous run + results_.clear(); + constantness_dependencies_.clear(); + + // Creating a list with function arguments + for (auto& arg : function.args()) + { + auto s = arg.getName().str(); + constantness_dependencies_.insert({&arg, {s}}); + } + + // Evaluating all expressions + for (auto& basic_block : function) + { + for (auto& instruction : basic_block) + { + auto opcode = instruction.getOpcode(); + switch (opcode) + { + case llvm::Instruction::Sub: + case llvm::Instruction::Add: + case llvm::Instruction::Mul: + case llvm::Instruction::Shl: + case llvm::Instruction::LShr: + case llvm::Instruction::AShr: + case llvm::Instruction::And: + case llvm::Instruction::Or: + case llvm::Instruction::Xor: + if (operandsConstant(instruction)) + { + markPossibleConstant(instruction); + } + break; + case llvm::Instruction::Call: + analyseCall(instruction); + break; + // Unanalysed statements + case llvm::Instruction::Ret: + case llvm::Instruction::Br: + case llvm::Instruction::Switch: + case llvm::Instruction::IndirectBr: + case llvm::Instruction::Invoke: + case llvm::Instruction::Resume: + case llvm::Instruction::Unreachable: + case llvm::Instruction::CleanupRet: + case llvm::Instruction::CatchRet: + case llvm::Instruction::CatchSwitch: + case llvm::Instruction::CallBr: + case llvm::Instruction::FNeg: + case llvm::Instruction::FAdd: + case llvm::Instruction::FSub: + case llvm::Instruction::FMul: + case llvm::Instruction::UDiv: + case llvm::Instruction::SDiv: + case llvm::Instruction::FDiv: + case llvm::Instruction::URem: + case llvm::Instruction::SRem: + case llvm::Instruction::FRem: + case llvm::Instruction::Alloca: + case llvm::Instruction::Load: + case llvm::Instruction::Store: + case llvm::Instruction::GetElementPtr: + case llvm::Instruction::Fence: + case llvm::Instruction::AtomicCmpXchg: + case llvm::Instruction::AtomicRMW: + case llvm::Instruction::Trunc: + case llvm::Instruction::ZExt: + case llvm::Instruction::SExt: + case llvm::Instruction::FPToUI: + case llvm::Instruction::FPToSI: + case llvm::Instruction::UIToFP: + case llvm::Instruction::SIToFP: + case llvm::Instruction::FPTrunc: + case llvm::Instruction::FPExt: + case llvm::Instruction::PtrToInt: + case llvm::Instruction::IntToPtr: + case llvm::Instruction::BitCast: + case llvm::Instruction::AddrSpaceCast: + case llvm::Instruction::CleanupPad: + case llvm::Instruction::CatchPad: + case llvm::Instruction::ICmp: + case llvm::Instruction::FCmp: + case llvm::Instruction::PHI: + case llvm::Instruction::Select: + case llvm::Instruction::UserOp1: + case llvm::Instruction::UserOp2: + case llvm::Instruction::VAArg: + case llvm::Instruction::ExtractElement: + case llvm::Instruction::InsertElement: + case llvm::Instruction::ShuffleVector: + case llvm::Instruction::ExtractValue: + case llvm::Instruction::InsertValue: + case llvm::Instruction::LandingPad: + // End of Binary Ops + default: + break; + } + } + } + } + + QubitAllocationAnalysisAnalytics::Result QubitAllocationAnalysisAnalytics::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& /*unused*/) + { + // Running functin analysis + analyseFunction(function); + + // ... and return the result. + return results_; + } + + QubitAllocationAnalysisPrinter::QubitAllocationAnalysisPrinter(llvm::raw_ostream& out_stream) + : out_stream_(out_stream) + { + } + + llvm::PreservedAnalyses QubitAllocationAnalysisPrinter::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& fam) + { + auto& results = fam.getResult(function); + + if (!results.empty()) + { + out_stream_ << function.getName() << "\n"; + out_stream_ << "====================" + << "\n\n"; + for (auto const& ret : results) + { + if (!ret.is_possibly_static) + { + out_stream_ << ret.variable_name << " is dynamic.\n"; + } + else + { + if (ret.depends_on.empty()) + { + out_stream_ << ret.variable_name << " is trivially static with " << ret.size << " qubits."; + } + else + { + out_stream_ << ret.variable_name << " depends on "; + bool first = true; + for (auto& x : ret.depends_on) + { + if (!first) + { + out_stream_ << ", "; + } + out_stream_ << x; + first = false; + } + out_stream_ << " being constant to be static."; + } + } + + out_stream_ << "\n"; + } + } + + return llvm::PreservedAnalyses::all(); + } + + bool QubitAllocationAnalysisPrinter::isRequired() + { + return true; + } + + llvm::AnalysisKey QubitAllocationAnalysisAnalytics::Key; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.hpp b/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.hpp new file mode 100644 index 0000000000..215388be56 --- /dev/null +++ b/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.hpp @@ -0,0 +1,115 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + class QubitAllocationAnalysisAnalytics : public llvm::AnalysisInfoMixin + { + public: + using String = std::string; + using ArgList = std::unordered_set; + + struct QubitArray + { + bool is_possibly_static{false}; ///< Indicates whether the array is + /// possibly static or not + /// + String variable_name{}; ///< Name of the qubit array + ArgList depends_on{}; ///< Function arguments that + /// determines if it is constant or not + /// + uint64_t size{static_cast(-1)}; ///< Size of the array if it can be deduced. + }; + + using Value = llvm::Value; + using DependencyGraph = std::unordered_map; + using ValueDependencyGraph = std::unordered_map; + + using Instruction = llvm::Instruction; + using Function = llvm::Function; + using Result = std::vector; + + /// Constructors and destructors + /// @{ + QubitAllocationAnalysisAnalytics() = default; + QubitAllocationAnalysisAnalytics(QubitAllocationAnalysisAnalytics const&) = delete; + QubitAllocationAnalysisAnalytics(QubitAllocationAnalysisAnalytics&&) = default; + ~QubitAllocationAnalysisAnalytics() = default; + /// @} + + /// Operators + /// @{ + QubitAllocationAnalysisAnalytics& operator=(QubitAllocationAnalysisAnalytics const&) = delete; + QubitAllocationAnalysisAnalytics& operator=(QubitAllocationAnalysisAnalytics&&) = delete; + /// @} + + /// Functions required by LLVM + /// @{ + Result run(llvm::Function& function, llvm::FunctionAnalysisManager& /*unused*/); + /// @} + + /// Function analysis + /// @{ + void analyseFunction(llvm::Function& function); + /// @} + + /// Instruction analysis + /// @{ + bool operandsConstant(Instruction const& instruction) const; + void markPossibleConstant(Instruction& instruction); + void analyseCall(Instruction& instruction); + /// @} + + private: + static llvm::AnalysisKey Key; // NOLINT + friend struct llvm::AnalysisInfoMixin; + + /// Analysis details + /// @{ + ValueDependencyGraph constantness_dependencies_{}; + /// @} + + /// Result + /// @{ + Result results_{}; + /// @} + }; + + class QubitAllocationAnalysisPrinter : public llvm::PassInfoMixin + { + public: + /// Constructors and destructors + /// @{ + explicit QubitAllocationAnalysisPrinter(llvm::raw_ostream& out_stream); + QubitAllocationAnalysisPrinter() = delete; + QubitAllocationAnalysisPrinter(QubitAllocationAnalysisPrinter const&) = delete; + QubitAllocationAnalysisPrinter(QubitAllocationAnalysisPrinter&&) = default; + ~QubitAllocationAnalysisPrinter() = default; + /// @} + + /// Operators + /// @{ + QubitAllocationAnalysisPrinter& operator=(QubitAllocationAnalysisPrinter const&) = delete; + QubitAllocationAnalysisPrinter& operator=(QubitAllocationAnalysisPrinter&&) = delete; + /// @} + + /// Functions required by LLVM + /// @{ + llvm::PreservedAnalyses run(llvm::Function& function, llvm::FunctionAnalysisManager& fam); + static bool isRequired(); + /// @} + private: + llvm::raw_ostream& out_stream_; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/libs/QubitAllocationAnalysis/SPECIFICATION.md b/src/Passes/libs/QubitAllocationAnalysis/SPECIFICATION.md new file mode 100644 index 0000000000..4c2781d605 --- /dev/null +++ b/src/Passes/libs/QubitAllocationAnalysis/SPECIFICATION.md @@ -0,0 +1,9 @@ +# Qubit Allocation Analysis + +## Purpose + +The purpose of this pass is to analyse the code for qubit allocations and identify +the allocation dependency. This helps subsequent transfomation passes expand the code +to, for instance, eliminate loops and classical logic. This is desirable as the control +logic for some quantum computing systems may be limited and one may therefore wish +to reduce its complexity as much as possible at compile time. diff --git a/src/Passes/requirements.txt b/src/Passes/requirements.txt index 77c1d85ae8..b937a83e88 100644 --- a/src/Passes/requirements.txt +++ b/src/Passes/requirements.txt @@ -1 +1,2 @@ click==8.0.1 +lit==12.0.1 diff --git a/src/Passes/site-packages/TasksCI/builder.py b/src/Passes/site-packages/TasksCI/builder.py index 1ca66a064e..7468d3e472 100644 --- a/src/Passes/site-packages/TasksCI/builder.py +++ b/src/Passes/site-packages/TasksCI/builder.py @@ -69,7 +69,17 @@ def run_tests(build_dir: str, concurrency: OptionalInt = None) -> None: """ Runs the unit tests given a build directory. """ + fail = False + # Running lit tests + lit_cmd = ["lit", "tests/", "-v"] + exit_code = subprocess.call(lit_cmd, cwd=build_dir) + + if exit_code != 0: + logger.error('Lit test failed') + fail = True + + # Running CMake tests cmake_cmd = [toolchain.discover_ctest()] if concurrency is not None: @@ -77,7 +87,10 @@ def run_tests(build_dir: str, concurrency: OptionalInt = None) -> None: exit_code = subprocess.call(cmake_cmd, cwd=build_dir) if exit_code != 0: - logger.error('Failed to configure project') + logger.error('CTest failed project') + fail = True + + if fail: sys.exit(exit_code) diff --git a/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/Lib{name}.cpp.tpl b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/Lib{name}.cpp.tpl new file mode 100644 index 0000000000..beb4589fe1 --- /dev/null +++ b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/Lib{name}.cpp.tpl @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" +#include "{name}/{name}.hpp" + +#include +#include + +namespace { +// Interface to plugin +llvm::PassPluginLibraryInfo get{name}PluginInfo() +{ + using namespace microsoft::quantum; + using namespace llvm; + + return { + LLVM_PLUGIN_API_VERSION, "{name}", LLVM_VERSION_STRING, [](PassBuilder &pb) { + // Registering a printer for the anaylsis + pb.registerPipelineParsingCallback([](StringRef name, FunctionPassManager &fpm, + ArrayRef /*unused*/) { + if (name == "print<{operation_name}>") + { + fpm.addPass({name}Printer(llvm::errs())); + return true; + } + return false; + }); + + pb.registerVectorizerStartEPCallback( + [](llvm::FunctionPassManager &fpm, llvm::PassBuilder::OptimizationLevel /*level*/) { + fpm.addPass({name}Printer(llvm::errs())); + }); + + // Registering the analysis module + pb.registerAnalysisRegistrationCallback([](FunctionAnalysisManager &fam) { + fam.registerPass([] { return {name}Analytics(); }); + }); + }}; +} + +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() +{ + return get{name}PluginInfo(); +} diff --git a/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/SPECIFICATION.md b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/SPECIFICATION.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.cpp.tpl b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.cpp.tpl new file mode 100644 index 0000000000..1d0eea61b4 --- /dev/null +++ b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.cpp.tpl @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "{name}/{name}.hpp" + +#include "Llvm.hpp" + +#include +#include + +namespace microsoft { +namespace quantum { +{name}Analytics::Result {name}Analytics::run(llvm::Function &/*function*/, + llvm::FunctionAnalysisManager & /*unused*/) +{ + {name}Analytics::Result result; + + // Collect analytics here + + return result; +} + + +{name}Printer::{name}Printer(llvm::raw_ostream& out_stream) + : out_stream_(out_stream) +{ +} + +llvm::PreservedAnalyses {name}Printer::run(llvm::Function & /*function*/, + llvm::FunctionAnalysisManager & /*fam*/) +{ + // auto &results = fam.getResult<{name}Analytics>(function); + + // Use analytics here + out_stream_ << "Analysis results are printed using this stream\n"; + + return llvm::PreservedAnalyses::all(); +} + +bool {name}Printer::isRequired() +{ + return true; +} + +llvm::AnalysisKey {name}Analytics::Key; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.hpp.tpl b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.hpp.tpl new file mode 100644 index 0000000000..5dd8e664d9 --- /dev/null +++ b/src/Passes/site-packages/TasksCI/templates/FunctionAnalysis/{name}.hpp.tpl @@ -0,0 +1,67 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm.hpp" + +namespace microsoft { +namespace quantum { + +class {name}Analytics : public llvm::AnalysisInfoMixin<{name}Analytics> +{ +public: + using Result = llvm::StringMap; ///< Change the type of the collected date here + + /// Constructors and destructors + /// @{ + {name}Analytics() = default; + {name}Analytics({name}Analytics const &) = delete; + {name}Analytics({name}Analytics &&) = default; + ~{name}Analytics() = default; + /// @} + + /// Operators + /// @{ + {name}Analytics &operator=({name}Analytics const &) = delete; + {name}Analytics &operator=({name}Analytics &&) = delete; + /// @} + + /// Functions required by LLVM + /// @{ + Result run(llvm::Function & function, llvm::FunctionAnalysisManager & /*unused*/); + /// @} + +private: + static llvm::AnalysisKey Key; // NOLINT + friend struct llvm::AnalysisInfoMixin<{name}Analytics>; +}; + +class {name}Printer : public llvm::PassInfoMixin<{name}Printer> +{ +public: + /// Constructors and destructors + /// @{ + explicit {name}Printer(llvm::raw_ostream& out_stream); + {name}Printer() = delete; + {name}Printer({name}Printer const &) = delete; + {name}Printer({name}Printer &&) = default; + ~{name}Printer() = default; + /// @} + + /// Operators + /// @{ + {name}Printer &operator=({name}Printer const &) = delete; + {name}Printer &operator=({name}Printer &&) = delete; + /// @} + + /// Functions required by LLVM + /// @{ + llvm::PreservedAnalyses run(llvm::Function & function, llvm::FunctionAnalysisManager & fam); + static bool isRequired(); + /// @} +private: + llvm::raw_ostream& out_stream_; +}; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/tests/CMakeLists.txt b/src/Passes/tests/CMakeLists.txt new file mode 100644 index 0000000000..60d980f867 --- /dev/null +++ b/src/Passes/tests/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LT_TEST_SHLIBEXT "${CMAKE_SHARED_LIBRARY_SUFFIX}") + +set(LT_TEST_SITE_CFG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in") +set(LT_TEST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +set(LIT_SITE_CFG_IN_HEADER "## Autogenerated from ${LT_TEST_SITE_CFG_INPUT}\n## Do not edit!") + +configure_file("${LT_TEST_SITE_CFG_INPUT}" + "${CMAKE_CURRENT_BINARY_DIR}/lit.cfg.py" @ONLY +) diff --git a/src/Passes/tests/QubitAllocationAnalysis/case1.ll b/src/Passes/tests/QubitAllocationAnalysis/case1.ll new file mode 100644 index 0000000000..cab5557980 --- /dev/null +++ b/src/Passes/tests/QubitAllocationAnalysis/case1.ll @@ -0,0 +1,15 @@ +; RUN: opt -load-pass-plugin %shlibdir/libQubitAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-1.ll -disable-output 2>&1\ +; RUN: | FileCheck %s + +;------------------------------------------------------------------------------ +; EXPECTED OUTPUT +;------------------------------------------------------------------------------ + +; CHECK: Example__QuantumProgram__body +; CHECK: ==================== + +; CHECK: qubits depends on x being constant to be static. + + + + diff --git a/src/Passes/tests/QubitAllocationAnalysis/case2.ll b/src/Passes/tests/QubitAllocationAnalysis/case2.ll new file mode 100644 index 0000000000..7f90c61a50 --- /dev/null +++ b/src/Passes/tests/QubitAllocationAnalysis/case2.ll @@ -0,0 +1,14 @@ +; RUN: opt -load-pass-plugin %shlibdir/libQubitAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-2.ll -disable-output 2>&1\ +; RUN: | FileCheck %s + +;------------------------------------------------------------------------------ +; EXPECTED OUTPUT +;------------------------------------------------------------------------------ + +; CHECK: Example__QuantumProgram__body +; CHECK: ==================== +; CHECK: qubits0 is trivially static with 9 qubits. +; CHECK: qubits1 depends on x being constant to be static. +; CHECK: qubits2 depends on x, g being constant to be static. +; CHECK: qubits3 depends on h being constant to be static. +; CHECK: qubits4 is dynamic. diff --git a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll new file mode 100644 index 0000000000..ea4ead0400 --- /dev/null +++ b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll @@ -0,0 +1,51 @@ +; ModuleID = 'qir/ConstSizeArray.ll' +source_filename = "qir/ConstSizeArray.ll" + +%Array = type opaque +%String = type opaque + +define internal fastcc void @Example__Main__body() unnamed_addr { +entry: + call fastcc void @Example__QuantumProgram__body(i64 3) + call fastcc void @Example__QuantumProgram__body(i64 4) + ret void +} + +define internal fastcc void @Example__QuantumProgram__body(i64 %x) unnamed_addr { +entry: + %qubits = call %Array* @__quantum__rt__qubit_allocate_array(i64 %x) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits) + ret void +} + +declare %Array* @__quantum__rt__qubit_allocate_array(i64) local_unnamed_addr + +declare void @__quantum__rt__qubit_release_array(%Array*) local_unnamed_addr + +declare void @__quantum__rt__array_update_alias_count(%Array*, i32) local_unnamed_addr + +declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr + +define i64 @Example__Main__Interop() local_unnamed_addr #0 { +entry: + call fastcc void @Example__Main__body() + ret i64 0 +} + +define void @Example__Main() local_unnamed_addr #1 { +entry: + call fastcc void @Example__Main__body() + %0 = call %String* @__quantum__rt__int_to_string(i64 0) + call void @__quantum__rt__message(%String* %0) + call void @__quantum__rt__string_update_reference_count(%String* %0, i32 -1) + ret void +} + +declare void @__quantum__rt__message(%String*) local_unnamed_addr + +declare %String* @__quantum__rt__int_to_string(i64) local_unnamed_addr + +attributes #0 = { "InteropFriendly" } +attributes #1 = { "EntryPoint" } diff --git a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll new file mode 100644 index 0000000000..b87010d9d2 --- /dev/null +++ b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll @@ -0,0 +1,84 @@ +; ModuleID = 'qir/ConstSizeArray.ll' +source_filename = "qir/ConstSizeArray.ll" + +%Array = type opaque +%String = type opaque + +define internal fastcc void @Example__Main__body() unnamed_addr { +entry: + call fastcc void @Example__QuantumProgram__body(i64 3, i64 2, i64 1) + call fastcc void @Example__QuantumProgram__body(i64 4, i64 9, i64 4) + ret void +} + +define internal fastcc void @Example__QuantumProgram__body(i64 %x, i64 %h, i64 %g) unnamed_addr { +entry: + %.neg = xor i64 %x, -1 + %.neg1 = mul i64 %.neg, %x + %z.neg = add i64 %.neg1, 47 + %y = mul i64 %x, 3 + %qubits0 = call %Array* @__quantum__rt__qubit_allocate_array(i64 9) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits0, i32 1) + %0 = add i64 %y, -2 + %1 = lshr i64 %0, 1 + %2 = add i64 %z.neg, %1 + %qubits1 = call %Array* @__quantum__rt__qubit_allocate_array(i64 %2) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits1, i32 1) + %3 = sub i64 %y, %g + %qubits2 = call %Array* @__quantum__rt__qubit_allocate_array(i64 %3) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits2, i32 1) + %qubits3 = call %Array* @__quantum__rt__qubit_allocate_array(i64 %h) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits3, i32 1) + %4 = call fastcc i64 @Example__X__body(i64 %x) + %qubits4 = call %Array* @__quantum__rt__qubit_allocate_array(i64 %4) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits4, i32 1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits4, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits4) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits3, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits3) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits2, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits2) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits1, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits1) + call void @__quantum__rt__array_update_alias_count(%Array* %qubits0, i32 -1) + call void @__quantum__rt__qubit_release_array(%Array* %qubits0) + ret void +} + +declare %Array* @__quantum__rt__qubit_allocate_array(i64) local_unnamed_addr + +declare void @__quantum__rt__qubit_release_array(%Array*) local_unnamed_addr + +declare void @__quantum__rt__array_update_alias_count(%Array*, i32) local_unnamed_addr + +; Function Attrs: norecurse nounwind readnone willreturn +define internal fastcc i64 @Example__X__body(i64 %value) unnamed_addr #0 { +entry: + %0 = mul i64 %value, 3 + ret i64 %0 +} + +declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr + +define i64 @Example__Main__Interop() local_unnamed_addr #1 { +entry: + call fastcc void @Example__Main__body() + ret i64 0 +} + +define void @Example__Main() local_unnamed_addr #2 { +entry: + call fastcc void @Example__Main__body() + %0 = call %String* @__quantum__rt__int_to_string(i64 0) + call void @__quantum__rt__message(%String* %0) + call void @__quantum__rt__string_update_reference_count(%String* %0, i32 -1) + ret void +} + +declare void @__quantum__rt__message(%String*) local_unnamed_addr + +declare %String* @__quantum__rt__int_to_string(i64) local_unnamed_addr + +attributes #0 = { norecurse nounwind readnone willreturn } +attributes #1 = { "InteropFriendly" } +attributes #2 = { "EntryPoint" } diff --git a/src/Passes/tests/lit.cfg.py b/src/Passes/tests/lit.cfg.py new file mode 100644 index 0000000000..68e1c797f7 --- /dev/null +++ b/src/Passes/tests/lit.cfg.py @@ -0,0 +1,37 @@ +# -*- Python -*- +import platform +import lit.formats +from lit.llvm import llvm_config +from lit.llvm.subst import ToolSubst +import shutil + +config.llvm_tools_dir = os.path.dirname(shutil.which("opt")) +config.name = 'Quantum-Passes' +config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) +config.suffixes = ['.ll'] +config.test_source_root = os.path.dirname(__file__) +config.excludes = ['inputs', "*/inputs", "**/inputs"] + +if platform.system() == 'Darwin': + tool_substitutions = [ + ToolSubst('%clang', "clang", + extra_args=["-isysroot", + "`xcrun --show-sdk-path`", + "-mlinker-version=0"]), + ] +else: + tool_substitutions = [ + ToolSubst('%clang', "clang", + ) + ] +llvm_config.add_tool_substitutions(tool_substitutions) +tools = ["opt", "lli", "not", "FileCheck", "clang"] +llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir) +config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) +config.substitutions.append(('%shlibdir', config.llvm_shlib_dir)) + + +# References: +# https://github.com/banach-space/llvm-tutor +# http://lists.llvm.org/pipermail/cfe-dev/2016-July/049868.html +# https://github.com/Homebrew/homebrew-core/issues/52461 diff --git a/src/Passes/tests/lit.site.cfg.py.in b/src/Passes/tests/lit.site.cfg.py.in new file mode 100644 index 0000000000..c6888bfbf2 --- /dev/null +++ b/src/Passes/tests/lit.site.cfg.py.in @@ -0,0 +1,15 @@ +import sys + +config.llvm_tools_dir = "@LT_LLVM_INSTALL_DIR@/bin" +config.llvm_shlib_ext = "@LT_TEST_SHLIBEXT@" +config.llvm_shlib_dir = "@CMAKE_BINARY_DIR@/libs" + +import lit.llvm +# lit_config is a global instance of LitConfig +lit.llvm.initialize(lit_config, config) + +# test_exec_root: The root path where tests should be run. +config.test_exec_root = os.path.join("@CMAKE_CURRENT_BINARY_DIR@") + +# Let the main config do the real work. +lit_config.load_config(config, "@LT_TEST_SRC_DIR@/lit.cfg.py")