diff --git a/build/passes-linux.yml b/build/passes-linux.yml index 58f666b149..29dc164c96 100644 --- a/build/passes-linux.yml +++ b/build/passes-linux.yml @@ -1,14 +1,21 @@ steps: - script: | - sudo apt update -y + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update -y + sudo apt-get install -y + sudo apt-get remove -y llvm-12 + sudo apt-get install -y curl pkg-config findutils wget sudo apt install -y clang-11 cmake clang-format-11 clang-tidy-11 sudo apt-get install -y llvm-11 lldb-11 llvm-11-dev libllvm11 llvm-11-runtime - export CC=clang-11 - export CXX=clang++ + sudo apt install -y python3 python3-pip + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 0 cd src/Passes/ - - pip install -r requirements.txt + pip install -r requirements.txt chmod +x manage - ./manage runci + export PYTHONUNBUFFERED=1 + export PYTHON_BIN_PATH=/usr/bin/python3 + export CC=clang-11 + export CXX=clang++-11 + ./manage runci displayName: Linux build and CI for passes diff --git a/src/Passes/CMakeLists.txt b/src/Passes/CMakeLists.txt index 758830396a..2ffe7403c0 100644 --- a/src/Passes/CMakeLists.txt +++ b/src/Passes/CMakeLists.txt @@ -1,15 +1,8 @@ cmake_minimum_required(VERSION 3.4.3) -project(Passes) - -if (WIN32) - message(STATUS "Adding C:\\Program Files\\LLVM\\lib to LLVM paths") - find_package(LLVM REQUIRED PATHS "C:\\Program Files\\LLVM\\lib" CONFIG ) -else() - find_package(LLVM REQUIRED CONFIG) -endif() - +project(QirPasses) +find_package(LLVM REQUIRED CONFIG) include(CheckCXXCompilerFlag) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") @@ -32,6 +25,16 @@ if(NOT LLVM_ENABLE_RTTI) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") endif() +# -fvisibility-inlines-hidden is set when building LLVM and on Darwin warnings +# are triggered if llvm-tutor is built without this flag (though otherwise it +# builds fine). For consistency, add it here too. +check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) +if(${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) + if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} STREQUAL "1") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") + endif() +endif() + # We export the compile commands which are needed by clang-tidy # to run the static analysis set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -41,8 +44,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include_directories(${LLVM_INCLUDE_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) add_definitions(${LLVM_DEFINITIONS}) -include_directories(${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_SOURCE_DIR}/Source) +llvm_map_components_to_libnames(llvm_libs support core irreader passes orcjit x86asmparser x86codegen x86desc x86disassembler x86info interpreter objcarcopts) + # Adding the libraries -add_subdirectory(libs) -add_subdirectory(tests) \ No newline at end of file +add_subdirectory(Source) +add_subdirectory(tests) + diff --git a/src/Passes/Dockerfile b/src/Passes/Dockerfile new file mode 100644 index 0000000000..85b4e86faf --- /dev/null +++ b/src/Passes/Dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:20.04 as bazel + +# basic dependencies - +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update -y && \ + apt-get install -y + +RUN apt-get install -y curl \ + pkg-config \ + findutils \ + wget + +# build dependencies +RUN apt install -y clang-11 cmake clang-format-11 clang-tidy-11 && \ + apt-get install -y llvm-11 lldb-11 llvm-11-dev libllvm11 llvm-11-runtime && \ + export CC=clang-11 && \ + export CXX=clang++ + +# Python +RUN apt install -y python3 python3-pip && \ + update-alternatives --install /usr/bin/python python /usr/bin/python3 0 + +ADD . /src/ +RUN cd /src/ && \ + pip install -r requirements.txt && \ + chmod +x manage + +# running the build +ENV CC=clang-11 \ + CXX=clang++-11 \ + PYTHONUNBUFFERED=1 \ + PYTHON_BIN_PATH=/usr/bin/python3 + +WORKDIR /src/ +RUN export CC=clang-11 && \ + export CXX=clang++-11 && \ + ./manage runci + diff --git a/src/Passes/README.md b/src/Passes/README.md index a70a6835a8..a3bbd12c13 100644 --- a/src/Passes/README.md +++ b/src/Passes/README.md @@ -1,172 +1,174 @@ -# QIR Passes for LLVM +# Profile adoption tool -This library defines [LLVM passes](https://llvm.org/docs/Passes.html) used for analysing, optimising and transforming the IR. The QIR pass library is a dynamic library that can be compiled and ran separately from the -rest of the project code. While it is not clear whether this possible at the moment, we hope that it will be possible to write passes that enforce the [QIR specification](https://github.com/microsoft/qsharp-language/tree/main/Specifications/QIR). - -## What do LLVM passes do? +# Getting started -Before getting started, we here provide a few examples of classical use cases for [LLVM passes](https://llvm.org/docs/Passes.html). You find additional [instructive examples here][1]. +## Quick start -**Example 1: Transformation**. As a first example of what [LLVM passes](https://llvm.org/docs/Passes.html) can do, we look at optimisation. Consider a compiler which -compiles +Once the project is built (see next sections), you can transform a QIR according to a profile as follows: -```c -double test(double x) { - return (1+2+x)*(x+(1+2)); -} +```sh +./Source/Apps/qat --generate --profile baseProfile -S ../examples/QirAllocationAnalysis/analysis-example.ll ``` -into following IR: +Likewise, you can validate that a QIR follows a specification by running (Note, not implemented yet): -``` -define double @test(double %x) { -entry: - %addtmp = fadd double 3.000000e+00, %x - %addtmp1 = fadd double %x, 3.000000e+00 - %multmp = fmul double %addtmp, %addtmp1 - ret double %multmp -} +```sh +./Source/Apps/qat --validate --profile baseProfile -S ../examples/QirAllocationAnalysis/analysis-example.ll ``` -This code is obviously inefficient as we could get rid of one operation by rewritting the code to: +## Example -```c -double test(double x) { - double y = 3+x; - return y * y; -} -``` - -One purpose of [LLVM passes](https://llvm.org/docs/Passes.html) is to allow automatic transformation from the above IR to the IR: +In this example, we start with a QIR generated by the Q# frontend. Rather than giving the full 3445 lines of QIR, we instead give the frontend code: ``` -define double @test(double %x) { -entry: - %addtmp = fadd double %x, 3.000000e+00 - %multmp = fmul double %addtmp, %addtmp - ret double %multmp +namespace TeleportChain { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Preparation; + + operation PrepareEntangledPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl { + H(left); + CNOT(left, right); + } + + operation ApplyCorrection(src : Qubit, intermediary : Qubit, dest : Qubit) : Unit { + if (MResetZ(src) == One) { Z(dest); } + if (MResetZ(intermediary) == One) { X(dest); } + } + + operation TeleportQubitUsingPresharedEntanglement(src : Qubit, intermediary : Qubit, dest : Qubit) : Unit { + Adjoint PrepareEntangledPair(src, intermediary); + ApplyCorrection(src, intermediary, dest); + } + + operation TeleportQubit(src : Qubit, dest : Qubit) : Unit { + use intermediary = Qubit(); + PrepareEntangledPair(intermediary, dest); + TeleportQubitUsingPresharedEntanglement(src, intermediary, dest); + } + + operation DemonstrateEntanglementSwapping() : (Result, Result) { + use (reference, src, intermediary, dest) = (Qubit(), Qubit(), Qubit(), Qubit()); + PrepareEntangledPair(reference, src); + TeleportQubit(src, dest); + return (MResetZ(reference), MResetZ(dest)); + } + + @EntryPoint() + operation DemonstrateTeleportationUsingPresharedEntanglement() : Unit { + let nPairs = 2; + use (leftMessage, rightMessage, leftPreshared, rightPreshared) = (Qubit(), Qubit(), Qubit[nPairs], Qubit[nPairs]); + PrepareEntangledPair(leftMessage, rightMessage); + for i in 0..nPairs-1 { + PrepareEntangledPair(leftPreshared[i], rightPreshared[i]); + } + + TeleportQubitUsingPresharedEntanglement(rightMessage, leftPreshared[0], rightPreshared[0]); + for i in 1..nPairs-1 { + TeleportQubitUsingPresharedEntanglement(rightPreshared[i-1], leftPreshared[i], rightPreshared[i]); + } + + let _ = MResetZ(leftMessage); + let _ = MResetZ(rightPreshared[nPairs-1]); + } } ``` -**Example 2: Analytics**. Another example of useful passes are those generating and collecting statistics about the program. For instance, one analytics program -makes sense for classical programs is to count instructions used to implement functions. Take the C program: - -```c -int foo(int x) -{ - return x; -} - -void bar(int x, int y) -{ - foo(x + y); -} - -int main() -{ - foo(2); - bar(3, 2); +The corresponding QIR can be generated by going to `examples/QubitAllocationAnalysis` and run `make analysis-example.ll`. This step requires that you have a working installation of Q#. Once compiled and the initial QIR is generated and saved in the file `analysis-example.ll`, we execute the command - return 0; -} +``` +./Source/Apps/qat --generate --profile baseProfile -S ./analysis-example.ll ``` -which produces follow IR (without optimisation): +The QAT tool will now attempt to map the QIR in `analysis-example.ll` into a QIR which is compatible with the base format. Removing type and function declarations, the correspoding code reads: -```language -define dso_local i32 @foo(i32 %0) #0 { - %2 = alloca i32, align 4 - store i32 %0, i32* %2, align 4 - %3 = load i32, i32* %2, align 4 - ret i32 %3 -} +``` +; ModuleID = './analysis-example.ll' +source_filename = "./analysis-example.ll" -define dso_local void @bar(i32 %0, i32 %1) #0 { - %3 = alloca i32, align 4 - %4 = alloca i32, align 4 - store i32 %0, i32* %3, align 4 - store i32 %1, i32* %4, align 4 - %5 = load i32, i32* %3, align 4 - %6 = load i32, i32* %4, align 4 - %7 = add nsw i32 %5, %6 - %8 = call i32 @foo(i32 %7) +define internal fastcc void @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__body() unnamed_addr { +entry: + call void @__quantum__qis__h(%Qubit* null) + call void @__quantum__qis__cnot(%Qubit* null, %Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h(%Qubit* null) + call void @__quantum__qis__cnot(%Qubit* null, %Qubit* nonnull inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__h(%Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cnot(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Qubit* nonnull inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cnot(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Qubit* null) + call void @__quantum__qis__h(%Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + %0 = call i1 @__quantum__qir__read_result(%Result* nonnull inttoptr (i64 4 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 4 to %Result*)) + call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + br i1 %0, label %then0__1.i.i, label %continue__1.i.i + +then0__1.i.i: ; preds = %entry + call void @__quantum__qis__z(%Qubit* nonnull inttoptr (i64 2 to %Qubit*)) + br label %continue__1.i.i + +continue__1.i.i: ; preds = %then0__1.i.i, %entry + %1 = call i1 @__quantum__qir__read_result(%Result* nonnull inttoptr (i64 5 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* null, %Result* nonnull inttoptr (i64 5 to %Result*)) + call void @__quantum__qis__reset__body(%Qubit* null) + br i1 %1, label %then0__2.i.i, label %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.1.exit + +then0__2.i.i: ; preds = %continue__1.i.i + call void @__quantum__qis__x(%Qubit* nonnull inttoptr (i64 2 to %Qubit*)) + br label %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.1.exit + +TeleportChain__TeleportQubitUsingPresharedEntanglement__body.1.exit: ; preds = %continue__1.i.i, %then0__2.i.i + call void @__quantum__qis__cnot(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h(%Qubit* nonnull inttoptr (i64 2 to %Qubit*)) + %2 = call i1 @__quantum__qir__read_result(%Result* nonnull inttoptr (i64 6 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*), %Result* nonnull inttoptr (i64 6 to %Result*)) + call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 2 to %Qubit*)) + br i1 %2, label %then0__1.i.i2, label %continue__1.i.i3 + +then0__1.i.i2: ; preds = %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.1.exit + call void @__quantum__qis__z(%Qubit* nonnull inttoptr (i64 3 to %Qubit*)) + br label %continue__1.i.i3 + +continue__1.i.i3: ; preds = %then0__1.i.i2, %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.1.exit + %3 = call i1 @__quantum__qir__read_result(%Result* nonnull inttoptr (i64 7 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* nonnull inttoptr (i64 7 to %Result*)) + call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*)) + br i1 %3, label %then0__2.i.i4, label %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.2.exit + +then0__2.i.i4: ; preds = %continue__1.i.i3 + call void @__quantum__qis__x(%Qubit* nonnull inttoptr (i64 3 to %Qubit*)) + br label %TeleportChain__TeleportQubitUsingPresharedEntanglement__body.2.exit + +TeleportChain__TeleportQubitUsingPresharedEntanglement__body.2.exit: ; preds = %continue__1.i.i3, %then0__2.i.i4 + call void @__quantum__qis__mz__body(%Qubit* null, %Result* null) + call void @__quantum__qis__reset__body(%Qubit* null) + call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*), %Result* nonnull inttoptr (i64 1 to %Result*)) + call void @__quantum__qis__reset__body(%Qubit* nonnull inttoptr (i64 3 to %Qubit*)) ret void } -define dso_local i32 @main() #0 { - %1 = alloca i32, align 4 - store i32 0, i32* %1, align 4 - %2 = call i32 @foo(i32 2) - call void @bar(i32 3, i32 2) - ret i32 0 -} ``` -A stat pass for this code, would collect following statisics: +We note the absence of loops, and that qubit registers are "allocated" at compile time meaning that each qubit instance is assigned a unique ID. As some code may be dead and optimised away, the qubit allocation is not garantueed to be sequential at this point in time. Future work will include writing a qubit ID remapper which will allow qubit IDs to become strictly increasing with no gaps inbetween. -```text -Stats for 'foo' -=========================== -Opcode # Used ---------------------------- -load 1 -ret 1 -alloca 1 -store 1 ---------------------------- - -Stats for 'bar' -=========================== -Opcode # Used ---------------------------- -load 2 -add 1 -ret 1 -alloca 2 -store 2 -call 1 ---------------------------- - -Stats for 'main' -=========================== -Opcode # Used ---------------------------- -ret 1 -alloca 1 -store 1 -call 2 ---------------------------- -``` - -**Example 3: Code validation**. A third use case is code validation. For example, one could write a pass to check whether bounds are exceeded on [static arrays][2]. -Note that this is a non-standard usecase as such analysis is usually made using the AST rather than at the IR level. - -**References** - -- [1] https://github.com/banach-space/llvm-tutor#analysis-vs-transformation-pass -- [2] https://github.com/victor-fdez/llvm-array-check-pass - -## Out-of-source Pass - -This library is build as set of out-of-source-passes. All this means is that we will not be downloading the LLVM repository and modifying this repository directly. You can read more [here](https://llvm.org/docs/CMake.html#cmake-out-of-source-pass). - -# Getting started +We also note that the function `TeleportChain__TeleportQubitUsingPresharedEntanglement__body` was cloned twice. This is due to the allocation of qubits and the function being called twice. At present, the analyser does not take qubit release into account and just assumes that it will never be released due to the complicated nature for dealing with nested functions at compile time. ## Dependencies This library is written in C++ and depends on: -- LLVM +- LLVM 11 Additional development dependencies include: - CMake - clang-format - clang-tidy +- Python 3 -## Building the passes +## Configuring the build directory -To build the passes, create a new build directory and switch to that directory: +To build the tool, create a new build directory and switch to that directory: ```sh mkdir Debug @@ -187,19 +189,119 @@ make [target] The default target is `all`. Other valid targets are the name of the folders in `libs/` found in the passes root. -## Running a pass +## Building QAT -You can run a pass using [opt](https://llvm.org/docs/CommandGuide/opt.html) as follows: +First ```sh -cd examples/ClassicalIrCommandline -make emit-llvm-bc -opt -load-pass-plugin ../../{Debug,Release}/libOpsCounter.{dylib,so} --passes="print" -disable-output classical-program.bc +cd Debug +make qat ``` -For a detailed tutorial, see examples. +then + +```sh +./Source/Apps/qat +``` + +# Brief developer guide + +## Extending the profile logic + +### Implementing a profile transformation pass + +As an example of how one can implement a new profile pass, we here show the implementational details of our example pass which allows mapping the teleportation code to the base profile: + +```c++ + pb.registerPipelineParsingCallback([](StringRef name, FunctionPassManager &fpm, + ArrayRef /*unused*/) { + // Base profile + if (name == "restrict-qir") + { + RuleSet rule_set; + + // Defining the mapping + auto factory = RuleFactory(rule_set); + + factory.useStaticQuantumArrayAllocation(); + factory.useStaticQuantumAllocation(); + factory.useStaticResultAllocation(); + + factory.optimiseBranchQuatumOne(); + // factory.optimiseBranchQuatumZero(); + + factory.disableReferenceCounting(); + factory.disableAliasCounting(); + factory.disableStringSupport(); + + fpm.addPass(TransformationRulePass(std::move(rule_set))); + return true; + } + + return false; + }); + }}; +``` + +Transformations of the IR will happen on the basis of what rules are added to the rule set. The purpose of the factory is to make easy to add rules that serve a single purpose as well as making a basis for making rules unit testable. + +### Implementing new rules + +Implementing new rules consists of two steps: Defining a pattern that one wish to replace and implementing the corresponding replacement logic. Inside a factory member function, this look as follows: + +```c++ + auto get_element = + Call("__quantum__rt__array_get_element_ptr_1d", "arrayName"_cap = _, "index"_cap = _); + auto cast_pattern = BitCast("getElement"_cap = get_element); + auto load_pattern = Load("cast"_cap = cast_pattern); + + addRule({std::move(load_pattern), access_replacer}); +``` + +where `addRule` adds the rule to the current rule set. + +### Capturing patterns + +The pattern defined in this snippet matches IR like: + +```c++ + %0 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %leftPreshared, i64 0) + %1 = bitcast i8* %0 to %Qubit** + %2 = load %Qubit*, %Qubit** %1, align 8 +``` + +In the above rule, the first and a second argument of `__quantum__rt__array_get_element_ptr_1d` is captured as `arrayName` and `index`, respectively. Likewise, the bitcast instruction is captured as `cast`. Each of these captures will be available inside the replacement function `access_replacer`. + +### Implementing replacement logic + +After a positive match is found, the lead instruction alongside a IRBuilder, a capture table and a replacement table is passed to the replacement function. Here is an example on how one can access the captured variables to perform a transformation of the IR: + +```c++ + auto access_replacer = [qubit_alloc_manager](Builder &builder, Value *val, Captures &cap, + Replacements &replacements) { + // ... + auto cst = llvm::dyn_cast(cap["index"]); + // ... + auto llvm_size = cst->getValue(); + auto offset = qubit_alloc_manager->getOffset(cap["arrayName"]->getName().str()); -## Creating a new pass + auto idx = llvm::APInt(llvm_size.getBitWidth(), llvm_size.getZExtValue() + offset); + auto new_index = llvm::ConstantInt::get(builder.getContext(), idx); + auto instr = new llvm::IntToPtrInst(new_index, ptr_type); + instr->takeName(val); + + // Replacing the lead instruction with a the new instruction + replacements.push_back({llvm::dyn_cast(val), instr}); + + // Deleting the getelement and cast operations + replacements.push_back({llvm::dyn_cast(cap["getElement"]), nullptr}); + replacements.push_back({llvm::dyn_cast(cap["cast"]), nullptr}); + + return true; + }; +``` + +## Implementing a new pass To make it easy to create a new pass, we have created a few templates to get you started quickly: @@ -253,7 +355,19 @@ Implement your pass here: bar Implement your pass here: main ``` -## CI +## Running a pass + +You can run a pass using [opt](https://llvm.org/docs/CommandGuide/opt.html) as follows: + +```sh +cd examples/ClassicalIrCommandline +make emit-llvm-bc +opt -load-pass-plugin ../../{Debug,Release}/libOpsCounter.{dylib,so} --passes="print" -disable-output classical-program.bc +``` + +For a detailed tutorial, see examples. + +# CI Before making a pull request with changes to this library, please ensure that style checks passes, that the code compiles, unit test passes and that there are no erros found by the static analyser. @@ -296,6 +410,158 @@ You can run all processes by running: As `clang-tidy` and `clang-format` acts slightly different from version to version and on different platforms, it is recommended that you use a docker image to perform these steps. TODO(TFR): The docker image is not added yet and this will be documented in the future. +# Introduction to passes + +Amongst other things, this library defines [LLVM passes](https://llvm.org/docs/Passes.html) used for analysing, optimising and transforming the IR. The QIR pass library is a dynamic library that can be compiled and ran separately from the +rest of the project code. + +## What do LLVM passes do? + +Before getting started, we here provide a few examples of classical use cases for [LLVM passes](https://llvm.org/docs/Passes.html). You find additional [instructive examples here][1]. + +**Example 1: Transformation**. As a first example of what [LLVM passes](https://llvm.org/docs/Passes.html) can do, we look at optimisation. Consider a compiler which +compiles + +```c +double test(double x) { + return (1+2+x)*(x+(1+2)); +} +``` + +into following IR: + +``` +define double @test(double %x) { +entry: + %addtmp = fadd double 3.000000e+00, %x + %addtmp1 = fadd double %x, 3.000000e+00 + %multmp = fmul double %addtmp, %addtmp1 + ret double %multmp +} +``` + +This code is obviously inefficient as we could get rid of one operation by rewritting the code to: + +```c +double test(double x) { + double y = 3+x; + return y * y; +} +``` + +One purpose of [LLVM passes](https://llvm.org/docs/Passes.html) is to allow automatic transformation from the above IR to the IR: + +``` +define double @test(double %x) { +entry: + %addtmp = fadd double %x, 3.000000e+00 + %multmp = fmul double %addtmp, %addtmp + ret double %multmp +} +``` + +**Example 2: Analytics**. Another example of useful passes are those generating and collecting statistics about the program. For instance, one analytics program +makes sense for classical programs is to count instructions used to implement functions. Take the C program: + +```c +int foo(int x) +{ + return x; +} + +void bar(int x, int y) +{ + foo(x + y); +} + +int main() +{ + foo(2); + bar(3, 2); + + return 0; +} +``` + +which produces follow IR (without optimisation): + +```language +define dso_local i32 @foo(i32 %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + ret i32 %3 +} + +define dso_local void @bar(i32 %0, i32 %1) #0 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + store i32 %1, i32* %4, align 4 + %5 = load i32, i32* %3, align 4 + %6 = load i32, i32* %4, align 4 + %7 = add nsw i32 %5, %6 + %8 = call i32 @foo(i32 %7) + ret void +} + +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 @foo(i32 2) + call void @bar(i32 3, i32 2) + ret i32 0 +} +``` + +A stat pass for this code, would collect following statisics: + +```text +Stats for 'foo' +=========================== +Opcode # Used +--------------------------- +load 1 +ret 1 +alloca 1 +store 1 +--------------------------- + +Stats for 'bar' +=========================== +Opcode # Used +--------------------------- +load 2 +add 1 +ret 1 +alloca 2 +store 2 +call 1 +--------------------------- + +Stats for 'main' +=========================== +Opcode # Used +--------------------------- +ret 1 +alloca 1 +store 1 +call 2 +--------------------------- +``` + +**Example 3: Code validation**. A third use case is code validation. For example, one could write a pass to check whether bounds are exceeded on [static arrays][2]. +Note that this is a non-standard usecase as such analysis is usually made using the AST rather than at the IR level. + +**References** + +- [1] https://github.com/banach-space/llvm-tutor#analysis-vs-transformation-pass +- [2] https://github.com/victor-fdez/llvm-array-check-pass + +## Out-of-source Pass + +This library is build as set of out-of-source-passes. All this means is that we will not be downloading the LLVM repository and modifying this repository directly. You can read more [here](https://llvm.org/docs/CMake.html#cmake-out-of-source-pass). + # Developer FAQ ## Pass does not load diff --git a/src/Passes/Source/AllocationManager/AllocationManager.cpp b/src/Passes/Source/AllocationManager/AllocationManager.cpp new file mode 100644 index 0000000000..ca672ea8a1 --- /dev/null +++ b/src/Passes/Source/AllocationManager/AllocationManager.cpp @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AllocationManager/AllocationManager.hpp" + +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + AllocationManager::AllocationManagerPtr AllocationManager::createNew() + { + AllocationManagerPtr ret; + ret.reset(new AllocationManager()); + + return ret; + } + + AllocationManager::Index AllocationManager::allocate() + { + auto ret = start_; + ++start_; + return ret; + } + + void AllocationManager::allocate(String const& name, Index const& size, bool value_only) + { + if (resources_.find(name) != resources_.end()) + { + throw std::runtime_error("Resource with name " + name + " already exists."); + } + + resources_[name].resize(size); + for (auto& v : resources_[name]) + { + v = nullptr; + } + + // Creating a memory segment mappign in case we are dealing with qubits + if (!value_only) + { + MemoryMapping map; + map.name = name; + map.index = mappings_.size(); + map.size = size; + + if (name_to_index_.find(map.name) != name_to_index_.end()) + { + throw std::runtime_error("Memory segment with name " + map.name + " already exists."); + } + + name_to_index_[map.name] = map.index; + map.start = start_; + start_ += size; + + map.end = map.start + size; + mappings_.emplace_back(std::move(map)); + } + } + + AllocationManager::Resource& AllocationManager::get(String const& name) + { + auto it = resources_.find(name); + if (it == resources_.end()) + { + throw std::runtime_error("Resource with name " + name + " does not exists."); + } + return it->second; + } + + AllocationManager::Index AllocationManager::getOffset(String const& name) const + { + auto it = name_to_index_.find(name); + if (it == name_to_index_.end()) + { + throw std::runtime_error("Memory segment with name " + name + " not found."); + } + auto index = it->second; + + return mappings_[index].start; + } + + void AllocationManager::release() {} + + void AllocationManager::release(String const& name) + { + auto it = name_to_index_.find(name); + if (it == name_to_index_.end()) + { + throw std::runtime_error("Memory segment with name " + name + " not found."); + } + name_to_index_.erase(it); + + auto it2 = resources_.find(name); + if (it2 == resources_.end()) + { + throw std::runtime_error("Resource with name " + name + " does not exists."); + } + resources_.erase(it2); + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/AllocationManager/AllocationManager.hpp b/src/Passes/Source/AllocationManager/AllocationManager.hpp new file mode 100644 index 0000000000..8b41d78c57 --- /dev/null +++ b/src/Passes/Source/AllocationManager/AllocationManager.hpp @@ -0,0 +1,103 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" + +#include +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + // TODO(QAT-private-issue-35): work out similarities and differences between resource allocation at + // runtime and compile time to make common interface and/or implementation depending on what is most + // suited. + + class AllocationManager + { + public: + /// Defines a named register/memory segment with start + /// position, end position and size. + struct MemoryMapping + { + using Index = uint64_t; + using String = std::string; + + String name{""}; ///< Name of the segment, if any given + Index index{0}; ///< Index of the allocation + Index size{0}; ///< Size of memory segment + Index start{0}; ///< Start index of memory segment + Index end{0}; ///< Index not included in memory segment + }; + + using Index = uint64_t; + using String = std::string; + using AllocationManagerPtr = std::shared_ptr; + using Resource = std::vector; + using Resources = std::unordered_map; + using NameToIndex = std::unordered_map; + using Mappings = std::vector; + + /// Pointer construction + /// @{ + /// Creates a new allocation manager. The manager is kept + /// as a shared pointer to enable allocation accross diffent + /// passes and/or replacement rules. + static AllocationManagerPtr createNew(); + /// @} + + /// Allocation and release functions + /// @{ + /// Allocates a single address. + Index allocate(); + + /// Allocates a name segment of a given size. + void allocate(String const& name, Index const& size, bool value_only = false); + + /// Gets the offset of a name segment or address. + Index getOffset(String const& name) const; + + /// Releases unnamed address. + void release(); + + /// Releases the named segment or address. + void release(String const& name); + + /// Retrieves a named resource. + Resource& get(String const& name); + /// @} + + private: + /// Private constructors + /// @{ + /// Public construction of this object is only allowed + /// as a shared pointer. To create a new AllocationManager, + /// use AllocationManager::createNew(). + AllocationManager() = default; + /// @} + + /// Memory mapping + /// @{ + /// Each allocation has a register/memory mapping which + /// keeps track of the allocation index, the segment size + /// and its name (if any). + NameToIndex name_to_index_; + Mappings mappings_; + /// @} + + /// Compile-time resources + /// @{ + /// Compile-time allocated resources that keeps a pointer + /// to the corresponding Values. + Resources resources_; + /// @} + + Index start_{0}; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Apps/CMakeLists.txt b/src/Passes/Source/Apps/CMakeLists.txt new file mode 100644 index 0000000000..8a3be9264a --- /dev/null +++ b/src/Passes/Source/Apps/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(qat Qat/Qat.cpp Qat/LlvmAnalysis.cpp) + +target_link_libraries(qat ${llvm_libs}) +target_link_libraries(qat ExpandStaticAllocation QirAllocationAnalysis TransformationRule Rules AllocationManager Commandline Profiles) \ No newline at end of file diff --git a/src/Passes/Source/Apps/Qat/LlvmAnalysis.cpp b/src/Passes/Source/Apps/Qat/LlvmAnalysis.cpp new file mode 100644 index 0000000000..47b8bbc500 --- /dev/null +++ b/src/Passes/Source/Apps/Qat/LlvmAnalysis.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Apps/Qat/LlvmAnalysis.hpp" + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + + LlvmAnalyser::LlvmAnalyser(bool debug) + : loop_analysis_manager_{debug} + , function_analysis_manager_{debug} + , gscc_analysis_manager_{debug} + , module_analysis_manager_{debug} + { + pass_builder_.registerModuleAnalyses(module_analysis_manager_); + pass_builder_.registerCGSCCAnalyses(gscc_analysis_manager_); + pass_builder_.registerFunctionAnalyses(function_analysis_manager_); + pass_builder_.registerLoopAnalyses(loop_analysis_manager_); + + pass_builder_.crossRegisterProxies( + loop_analysis_manager_, function_analysis_manager_, gscc_analysis_manager_, module_analysis_manager_); + } + + llvm::PassBuilder& LlvmAnalyser::passBuilder() + { + return pass_builder_; + } + llvm::LoopAnalysisManager& LlvmAnalyser::loopAnalysisManager() + { + return loop_analysis_manager_; + } + llvm::FunctionAnalysisManager& LlvmAnalyser::functionAnalysisManager() + { + return function_analysis_manager_; + } + llvm::CGSCCAnalysisManager& LlvmAnalyser::gsccAnalysisManager() + { + return gscc_analysis_manager_; + } + llvm::ModuleAnalysisManager& LlvmAnalyser::moduleAnalysisManager() + { + return module_analysis_manager_; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Apps/Qat/LlvmAnalysis.hpp b/src/Passes/Source/Apps/Qat/LlvmAnalysis.hpp new file mode 100644 index 0000000000..20d1e814da --- /dev/null +++ b/src/Passes/Source/Apps/Qat/LlvmAnalysis.hpp @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + + class LlvmAnalyser + { + public: + /// Constructors + /// @{ + explicit LlvmAnalyser(bool debug); + + // Default construction not allowed as this leads + // to invalid configuration of the managers. + LlvmAnalyser() = delete; + + // Copy construction prohibited due to restrictions + // on the member variables. + LlvmAnalyser(LlvmAnalyser const&) = delete; + + // Prefer move construction at all times. + LlvmAnalyser(LlvmAnalyser&&) = default; + + // Default deconstruction. + ~LlvmAnalyser() = default; + /// @} + + /// Acccess member functions + /// @{ + llvm::PassBuilder& passBuilder(); + llvm::LoopAnalysisManager& loopAnalysisManager(); + llvm::FunctionAnalysisManager& functionAnalysisManager(); + llvm::CGSCCAnalysisManager& gsccAnalysisManager(); + llvm::ModuleAnalysisManager& moduleAnalysisManager(); + /// @} + private: + /// Objects used to run a set of passes + /// @{ + llvm::PassBuilder pass_builder_; + llvm::LoopAnalysisManager loop_analysis_manager_; + llvm::FunctionAnalysisManager function_analysis_manager_; + llvm::CGSCCAnalysisManager gscc_analysis_manager_; + llvm::ModuleAnalysisManager module_analysis_manager_; + /// @} + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Apps/Qat/Qat.cpp b/src/Passes/Source/Apps/Qat/Qat.cpp new file mode 100644 index 0000000000..8c29258fe1 --- /dev/null +++ b/src/Passes/Source/Apps/Qat/Qat.cpp @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Apps/Qat/LlvmAnalysis.hpp" +#include "Commandline/ParameterParser.hpp" +#include "Commandline/Settings.hpp" +#include "Profiles/BaseProfile.hpp" +#include "Profiles/IProfile.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include +#include + +using namespace llvm; +using namespace microsoft::quantum; + +int main(int argc, char** argv) +{ + try + { + // Parsing commmandline arguments + Settings settings{ + {{"debug", "false"}, + {"generate", "false"}, + {"validate", "false"}, + {"profile", "base-profile"}, + {"S", "false"}}}; + + ParameterParser parser(settings); + parser.addFlag("debug"); + parser.addFlag("generate"); + parser.addFlag("validate"); + parser.addFlag("S"); + + parser.parseArgs(argc, argv); + + if (parser.arguments().empty()) + { + std::cerr << "Usage: " << argv[0] << " [options] filename" << std::endl; + exit(-1); + } + + // Loading IR + LLVMContext context; + SMDiagnostic error; + auto module = parseIRFile(parser.getArg(0), error, context); + + if (!module) + { + std::cerr << "Invalid IR." << std::endl; + exit(-1); + } + + // Extracting commandline parameters + bool debug = settings.get("debug") == "true"; + bool generate = settings.get("generate") == "true"; + bool validate = settings.get("validate") == "true"; + auto optimisation_level = llvm::PassBuilder::OptimizationLevel::O1; + std::shared_ptr profile = std::make_shared(); + + // In case we debug, we also print the settings to allow provide a full + // picture of what is going. + if (debug) + { + settings.print(); + } + + // Checking if we are asked to generate a new QIR. If so, we will use + // the profile to setup passes to + if (generate) + { + // Creating pass builder + LlvmAnalyser analyser{debug}; + + // Preparing pass for generation based on profile + profile->addFunctionAnalyses(analyser.functionAnalysisManager()); + auto module_pass_manager = + profile->createGenerationModulePass(analyser.passBuilder(), optimisation_level, debug); + + // Running the pass built by the profile + module_pass_manager.run(*module, analyser.moduleAnalysisManager()); + + // Priniting either human readible LL code or byte + // code as a result, depending on the users preference. + if (settings.get("S") == "true") + { + llvm::errs() << *module << "\n"; + } + else + { + llvm::errs() << "Byte code ouput is not supported yet. Please add -S to get human readible " + "LL code.\n"; + } + } + + if (validate) + { + // Creating pass builder + LlvmAnalyser analyser{debug}; + + // Creating a validation pass manager + auto module_pass_manager = + profile->createValidationModulePass(analyser.passBuilder(), optimisation_level, debug); + module_pass_manager.run(*module, analyser.moduleAnalysisManager()); + } + } + catch (std::exception const& e) + { + llvm::errs() << "An error occured: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/Passes/Source/CMakeLists.txt b/src/Passes/Source/CMakeLists.txt new file mode 100644 index 0000000000..4830ee92f7 --- /dev/null +++ b/src/Passes/Source/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 3.4.3) + +# Creating Allocation Manager library +file(GLOB COMMANDLINE_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Commandline/*.cpp) +add_library(Commandline + SHARED + ${COMMANDLINE_SRC}) + +target_include_directories( + Commandline + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_link_libraries(Commandline + "$<$:-undefined dynamic_lookup>") + + +# Creating Allocation Manager library +file(GLOB ALLOCATION_MGR_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/AllocationManager/*.cpp) +add_library(AllocationManager + SHARED + ${ALLOCATION_MGR_SOURCE}) + +target_include_directories( + AllocationManager + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_link_libraries(AllocationManager + "$<$:-undefined dynamic_lookup>") + +# Creating the rules library +file(GLOB RULES_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Rules/*.cpp) +file(GLOB RULES_OPRANDS_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Rules/Operands/*.cpp) +file(GLOB RULES_NOTATION_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Rules/Notation/*.cpp) + +add_library(Rules + SHARED + ${RULES_SOURCE} + ${RULES_OPRANDS_SOURCE} + ${RULES_NOTATION_SOURCE} ) + +target_include_directories( + Rules + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_link_libraries(Rules AllocationManager) +target_link_libraries(Rules + "$<$:-undefined dynamic_lookup>") + + +# Creating all of the passes library +add_subdirectory(Passes) + + + + +# Creating Allocation Manager library +file(GLOB PROFILE_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Profiles/*.cpp) +add_library(Profiles + SHARED + ${PROFILE_SOURCE}) + +target_include_directories( + Profiles + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_link_libraries(Profiles + "$<$:-undefined dynamic_lookup>") + + + +# Creating all commandline apps +add_subdirectory(Apps) \ No newline at end of file diff --git a/src/Passes/Source/Commandline/ParameterParser.cpp b/src/Passes/Source/Commandline/ParameterParser.cpp new file mode 100644 index 0000000000..73729adf10 --- /dev/null +++ b/src/Passes/Source/Commandline/ParameterParser.cpp @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Commandline/ParameterParser.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + ParameterParser::ParameterParser(Settings& settings) + : settings_{settings} + { + } + + void ParameterParser::parseArgs(int argc, char** argv) + { + uint64_t i = 1; + std::vector values; + while (i < static_cast(argc)) + { + values.push_back(parseSingleArg(argv[i])); + ++i; + } + + i = 0; + while (i < values.size()) + { + auto& v = values[i]; + ++i; + + if (!v.is_key) + { + arguments_.push_back(v.value); + continue; + } + + if (i >= values.size()) + { + settings_[v.value] = "true"; + continue; + } + + auto& v2 = values[i]; + if (!v2.is_key && hasValue(v.value)) + { + settings_[v.value] = v2.value; + ++i; + continue; + } + + settings_[v.value] = "true"; + } + } + + void ParameterParser::addFlag(String const& v) + { + flags_.insert(v); + } + + ParameterParser::Arguments const& ParameterParser::arguments() const + { + return arguments_; + } + ParameterParser::String const& ParameterParser::getArg(uint64_t const& n) + { + return arguments_[n]; + } + + ParameterParser::ParsedValue ParameterParser::parseSingleArg(String key) + { + bool is_key = false; + if (key.size() > 2 && key.substr(0, 2) == "--") + { + is_key = true; + key = key.substr(2); + } + else if (key.size() > 1 && key.substr(0, 1) == "-") + { + is_key = true; + key = key.substr(1); + } + return {is_key, key}; + } + + bool ParameterParser::hasValue(String const& key) + { + if (flags_.find(key) != flags_.end()) + { + return false; + } + + return true; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Commandline/ParameterParser.hpp b/src/Passes/Source/Commandline/ParameterParser.hpp new file mode 100644 index 0000000000..ba002eff0c --- /dev/null +++ b/src/Passes/Source/Commandline/ParameterParser.hpp @@ -0,0 +1,81 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Commandline/Settings.hpp" + +#include +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + class ParameterParser + { + public: + using String = std::string; + using Arguments = std::vector; + using Flags = std::unordered_set; + + /// Construction and deconstrution configuration + /// @{ + /// Parameter parsers requires a setting class to store + /// parameters passed. + explicit ParameterParser(Settings& settings); + + // Allow move semantics only. No default construction + ParameterParser() = delete; + ParameterParser(ParameterParser const& other) = delete; + ParameterParser(ParameterParser&& other) = default; + ~ParameterParser() = default; + /// @} + + /// Configuration + /// @{ + + /// Marks a name as a flag (as opposed to an option). + /// This ensures that no parameter is expected after + /// the flag is specified. For instance `--debug` is + /// a flag as opposed to `--log-level 3` which is an + /// option. + void addFlag(String const& v); + /// @} + + /// Operation + /// @{ + /// Parses the command line arguments given the argc and argv + /// from the main function. + void parseArgs(int argc, char** argv); + + /// Returns list of arguments without flags and/or options + /// included. + Arguments const& arguments() const; + String const& getArg(uint64_t const& n); + /// @} + private: + struct ParsedValue + { + bool is_key{false}; + String value; + }; + + /// Helper functions and variables + /// @{ + ParsedValue parseSingleArg(String key); + bool hasValue(String const& key); + Flags flags_{}; + /// @} + + /// Storage of parsed data + /// @{ + Settings& settings_; + Arguments arguments_{}; + /// @} + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Commandline/Settings.hpp b/src/Passes/Source/Commandline/Settings.hpp new file mode 100644 index 0000000000..d5c46a79d9 --- /dev/null +++ b/src/Passes/Source/Commandline/Settings.hpp @@ -0,0 +1,68 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + class Settings + { + public: + using String = std::string; + + using SettingsMap = std::unordered_map; + explicit Settings(SettingsMap default_settings) + : settings_{std::move(default_settings)} + { + } + + String get(String const& name, String const& default_value) + { + auto it = settings_.find(name); + if (it == settings_.end()) + { + return default_value; + } + + return it->second; + } + + String get(String const& name) + { + auto it = settings_.find(name); + if (it == settings_.end()) + { + throw std::runtime_error("Could not find setting '" + name + "'."); + } + + return it->second; + } + + void print() + { + std::cout << "Settings" << std::endl; + for (auto& s : settings_) + { + std::cout << std::setw(20) << s.first << ": " << s.second << std::endl; + } + } + + String& operator[](String const& key) + { + return settings_[key]; + } + + private: + SettingsMap settings_; + friend class ParameterParser; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/include/Llvm.hpp b/src/Passes/Source/Llvm/Llvm.hpp similarity index 84% rename from src/Passes/include/Llvm.hpp rename to src/Passes/Source/Llvm/Llvm.hpp index 80a4728b83..4979a02f1d 100644 --- a/src/Passes/include/Llvm.hpp +++ b/src/Passes/Source/Llvm/Llvm.hpp @@ -41,11 +41,21 @@ #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" +// Reader tool +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Transforms/IPO/Inliner.h" +#include "llvm/Transforms/Scalar/LoopUnrollPass.h" + +// Profiles +#include "llvm/LinkAllPasses.h" +#include "llvm/Transforms/Scalar/ADCE.h" +#include "llvm/Transforms/Scalar/DCE.h" + #if defined(__clang__) #pragma clang diagnostic pop #endif diff --git a/src/Passes/libs/CMakeLists.txt b/src/Passes/Source/Passes/CMakeLists.txt similarity index 95% rename from src/Passes/libs/CMakeLists.txt rename to src/Passes/Source/Passes/CMakeLists.txt index 3cc4850ad9..e9529fec7c 100644 --- a/src/Passes/libs/CMakeLists.txt +++ b/src/Passes/Source/Passes/CMakeLists.txt @@ -37,6 +37,7 @@ foreach(pass_plugin ${ALL_PASSES}) # Linking + target_link_libraries(${pass_plugin} Rules) target_link_libraries(${pass_plugin} "$<$:-undefined dynamic_lookup>") diff --git a/src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.cpp b/src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.cpp new file mode 100644 index 0000000000..5f045ce4ed --- /dev/null +++ b/src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.cpp @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + /// This pass traverse the IR and uses the QirAllocationAnalysis to determine + /// if a function call results in qubit and/or result allocation. If that is the case, + /// it makes a copy of the function and replaces the function call with a call to the + /// new function. + 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& use_quantum = fam.getResult(*callee_function); + + if (use_quantum.value) + { + 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(*callee_function, argument_constants); + + // Replacing call if a new function was created + if (new_callee != nullptr) + { + llvm::IRBuilder<> builder(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); + new_call->takeName(call_instr); + + // Replace all calls to old function with calls to new function + instruction.replaceAllUsesWith(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( + llvm::Function& callee, + ConstantArguments const& const_args) + { + 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(QAT-private-issue-28): In LLVM 13 upgrade 'true' to + // 'llvm::CloneFunctionChangeType::LocalChangesOnly' + llvm::CloneFunctionInto(function, &callee, remapper, true, returns, "", nullptr); + + verifyFunction(*function); + + return function; + } + + bool ExpandStaticAllocationPass::isRequired() + { + return true; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp b/src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp similarity index 73% rename from src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp rename to src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp index fbee619be2..7f3a7bb8a8 100644 --- a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.hpp +++ b/src/Passes/Source/Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp @@ -2,9 +2,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "Llvm.hpp" +#include "Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp" -#include "QubitAllocationAnalysis/QubitAllocationAnalysis.hpp" +#include "Llvm/Llvm.hpp" #include @@ -13,10 +13,12 @@ namespace microsoft namespace quantum { + /// This class copies functions which does static qubit and/or result allocation. This is done + /// to ensure that qubits/result registers are not reused but instead assigned unique ids. class ExpandStaticAllocationPass : public llvm::PassInfoMixin { public: - using QubitAllocationResult = QubitAllocationAnalysisAnalytics::Result; + using QubitAllocationResult = QirAllocationAnalysis::Result; using ConstantArguments = std::unordered_map; /// Constructors and destructors @@ -40,10 +42,7 @@ namespace quantum /// @} /// @{ - llvm::Function* expandFunctionCall( - QubitAllocationResult const& depenency_graph, - llvm::Function& callee, - ConstantArguments const& const_args); + llvm::Function* expandFunctionCall(llvm::Function& callee, ConstantArguments const& const_args); /// @} }; diff --git a/src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp b/src/Passes/Source/Passes/ExpandStaticAllocation/LibExpandStaticAllocation.cpp similarity index 92% rename from src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp rename to src/Passes/Source/Passes/ExpandStaticAllocation/LibExpandStaticAllocation.cpp index fa4291001e..56c72d6b03 100644 --- a/src/Passes/libs/ExpandStaticAllocation/LibExpandStaticAllocation.cpp +++ b/src/Passes/Source/Passes/ExpandStaticAllocation/LibExpandStaticAllocation.cpp @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "Llvm.hpp" +#include "Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp" -#include "ExpandStaticAllocation/ExpandStaticAllocation.hpp" +#include "Llvm/Llvm.hpp" #include #include diff --git a/src/Passes/libs/OpsCounter/LibOpsCounter.cpp b/src/Passes/Source/Passes/QirAllocationAnalysis/LibQirAllocationAnalysis.cpp similarity index 60% rename from src/Passes/libs/OpsCounter/LibOpsCounter.cpp rename to src/Passes/Source/Passes/QirAllocationAnalysis/LibQirAllocationAnalysis.cpp index a07379f656..47ad891e73 100644 --- a/src/Passes/libs/OpsCounter/LibOpsCounter.cpp +++ b/src/Passes/Source/Passes/QirAllocationAnalysis/LibQirAllocationAnalysis.cpp @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "Llvm.hpp" +#include "Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp" -#include "OpsCounter/OpsCounter.hpp" +#include "Llvm/Llvm.hpp" #include #include @@ -11,18 +11,18 @@ namespace { // Interface to plugin -llvm::PassPluginLibraryInfo getOpsCounterPluginInfo() +llvm::PassPluginLibraryInfo getQirAllocationAnalysisPluginInfo() { using namespace microsoft::quantum; using namespace llvm; - return {LLVM_PLUGIN_API_VERSION, "OpsCounter", LLVM_VERSION_STRING, [](PassBuilder& pb) { - // Registering the printer + return {LLVM_PLUGIN_API_VERSION, "QirAllocationAnalysis", LLVM_VERSION_STRING, [](PassBuilder& pb) { + // Registering a printer for the anaylsis pb.registerPipelineParsingCallback( [](StringRef name, FunctionPassManager& fpm, ArrayRef /*unused*/) { - if (name == "print") + if (name == "print") { - fpm.addPass(OpsCounterPrinter(llvm::errs())); + fpm.addPass(QirAllocationAnalysisPrinter(llvm::errs())); return true; } return false; @@ -30,12 +30,12 @@ llvm::PassPluginLibraryInfo getOpsCounterPluginInfo() pb.registerVectorizerStartEPCallback( [](llvm::FunctionPassManager& fpm, llvm::PassBuilder::OptimizationLevel /*level*/) { - fpm.addPass(OpsCounterPrinter(llvm::errs())); + fpm.addPass(QirAllocationAnalysisPrinter(llvm::errs())); }); // Registering the analysis module pb.registerAnalysisRegistrationCallback( - [](FunctionAnalysisManager& fam) { fam.registerPass([] { return OpsCounterAnalytics(); }); }); + [](FunctionAnalysisManager& fam) { fam.registerPass([] { return QirAllocationAnalysis(); }); }); }}; } @@ -43,5 +43,5 @@ llvm::PassPluginLibraryInfo getOpsCounterPluginInfo() extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { - return getOpsCounterPluginInfo(); + return getQirAllocationAnalysisPluginInfo(); } diff --git a/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.cpp b/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.cpp new file mode 100644 index 0000000000..fea4377fee --- /dev/null +++ b/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.cpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + QirAllocationAnalysis::Result QirAllocationAnalysis::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& /*unused*/) + { + for (auto& basic_block : function) + { + for (auto& instr : basic_block) + { + auto call_instr = llvm::dyn_cast(&instr); + if (call_instr == nullptr) + { + continue; + } + auto target_function = call_instr->getCalledFunction(); + auto name = target_function->getName(); + + // Checking for qubit allocation + if (name == "__quantum__rt__qubit_allocate") + { + return {true}; + } + + if (name == "__quantum__rt__qubit_allocate_array") + { + return {true}; + } + + // Checking for result allocation + if (name == "__quantum__qis__m__body") + { + return {true}; + } + } + } + + return {false}; + } + + QirAllocationAnalysisPrinter::QirAllocationAnalysisPrinter(llvm::raw_ostream& out_stream) + : out_stream_(out_stream) + { + } + + llvm::PreservedAnalyses QirAllocationAnalysisPrinter::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& fam) + { + auto& result = fam.getResult(function); + + if (result.value) + { + out_stream_ << function.getName() << " contains quantum allocations.\n"; + } + else + { + out_stream_ << function.getName() << " is logic only.\n"; + } + return llvm::PreservedAnalyses::all(); + } + + bool QirAllocationAnalysisPrinter::isRequired() + { + return true; + } + + llvm::AnalysisKey QirAllocationAnalysis::Key; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp b/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp new file mode 100644 index 0000000000..3d6c6f3c21 --- /dev/null +++ b/src/Passes/Source/Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp @@ -0,0 +1,83 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + /// QirAllocationAnalysis is a LLVM pass that statistics on the usage of operations which allocates + /// resources such as qubits and results. + class QirAllocationAnalysis : public llvm::AnalysisInfoMixin + { + public: + using String = std::string; + + /// Result annotation. Contains a single value which + /// is true if the function uses allocations of qubits or results. + struct Result + { + bool value{false}; + }; + + /// Constructors and destructors + /// @{ + QirAllocationAnalysis() = default; + QirAllocationAnalysis(QirAllocationAnalysis const&) = delete; + QirAllocationAnalysis(QirAllocationAnalysis&&) = default; + ~QirAllocationAnalysis() = default; + /// @} + + /// Operators + /// @{ + QirAllocationAnalysis& operator=(QirAllocationAnalysis const&) = delete; + QirAllocationAnalysis& operator=(QirAllocationAnalysis&&) = delete; + /// @} + + /// Functions required by LLVM + /// @{ + Result run(llvm::Function& function, llvm::FunctionAnalysisManager& /*unused*/); + /// @} + + private: + static llvm::AnalysisKey Key; // NOLINT + friend struct llvm::AnalysisInfoMixin; + }; + + /// QirAllocationAnalysisPrinter is a LLVM pass class that prints statistics generated by + /// QirAllocationAnalysis. + class QirAllocationAnalysisPrinter : public llvm::PassInfoMixin + { + public: + /// Constructors and destructors + /// @{ + explicit QirAllocationAnalysisPrinter(llvm::raw_ostream& out_stream); + QirAllocationAnalysisPrinter() = delete; + QirAllocationAnalysisPrinter(QirAllocationAnalysisPrinter const&) = delete; + QirAllocationAnalysisPrinter(QirAllocationAnalysisPrinter&&) = default; + ~QirAllocationAnalysisPrinter() = default; + /// @} + + /// Operators + /// @{ + QirAllocationAnalysisPrinter& operator=(QirAllocationAnalysisPrinter const&) = delete; + QirAllocationAnalysisPrinter& operator=(QirAllocationAnalysisPrinter&&) = 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/Source/Passes/TransformationRule/LibTransformationRule.cpp b/src/Passes/Source/Passes/TransformationRule/LibTransformationRule.cpp new file mode 100644 index 0000000000..41a9e19eef --- /dev/null +++ b/src/Passes/Source/Passes/TransformationRule/LibTransformationRule.cpp @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Passes/TransformationRule/TransformationRule.hpp" +#include "Rules/Factory.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace +{ +llvm::PassPluginLibraryInfo getTransformationRulePluginInfo() +{ + using namespace microsoft::quantum; + using namespace llvm; + + return {LLVM_PLUGIN_API_VERSION, "TransformationRule", LLVM_VERSION_STRING, [](PassBuilder& pb) { + // Registering the pass + pb.registerPipelineParsingCallback( + [](StringRef name, FunctionPassManager& fpm, ArrayRef /*unused*/) { + // Base profile + if (name == "restrict-qir") + { + // Defining a harded coded set of rules as LLVM does not provide means + // to configure passes through opt. + RuleSet rule_set; + + // Defining the mapping + auto factory = RuleFactory(rule_set); + + factory.useStaticQubitArrayAllocation(); + factory.useStaticQubitAllocation(); + factory.useStaticResultAllocation(); + + factory.optimiseBranchQuatumOne(); + // factory.optimiseBranchQuatumZero(); + + factory.disableReferenceCounting(); + factory.disableAliasCounting(); + factory.disableStringSupport(); + + fpm.addPass(TransformationRulePass(std::move(rule_set))); + return true; + } + + return false; + }); + }}; +} +} // namespace + +// Interface for loading the plugin +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() +{ + return getTransformationRulePluginInfo(); +} diff --git a/src/Passes/Source/Passes/TransformationRule/TransformationRule.cpp b/src/Passes/Source/Passes/TransformationRule/TransformationRule.cpp new file mode 100644 index 0000000000..297fbd8d4e --- /dev/null +++ b/src/Passes/Source/Passes/TransformationRule/TransformationRule.cpp @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Passes/TransformationRule/TransformationRule.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + TransformationRulePass::TransformationRulePass(RuleSet&& rule_set) + : rule_set_{std::move(rule_set)} + { + } + + llvm::PreservedAnalyses TransformationRulePass::run( + llvm::Function& function, + llvm::FunctionAnalysisManager& /*fam*/) + { + replacements_.clear(); + + // For every instruction in every block, we attempt a match + // and replace. + for (auto& basic_block : function) + { + for (auto& instr : basic_block) + { + rule_set_.matchAndReplace(&instr, replacements_); + } + } + + // Applying all replacements + for (auto it = replacements_.rbegin(); it != replacements_.rend(); ++it) + { + auto instr1 = llvm::dyn_cast(it->first); + if (instr1 == nullptr) + { + llvm::errs() << "; WARNING: cannot deal with non-instruction replacements\n"; + continue; + } + + // Cheking if have a replacement for the instruction + if (it->second != nullptr) + { + // ... if so, we just replace it, + auto instr2 = llvm::dyn_cast(it->second); + if (instr2 == nullptr) + { + llvm::errs() << "; WARNING: cannot replace instruction with non-instruction\n"; + continue; + } + llvm::ReplaceInstWithInst(instr1, instr2); + } + else + { + // ... otherwise we delete the the instruction + // Removing all uses + if (!instr1->use_empty()) + { + instr1->replaceAllUsesWith(llvm::UndefValue::get(instr1->getType())); + } + + // And finally we delete the instruction + instr1->eraseFromParent(); + } + } + + // If we did not change the IR, we report that we preserved all + if (replacements_.empty()) + { + return llvm::PreservedAnalyses::all(); + } + + // ... and otherwise, we report that we preserved none. + return llvm::PreservedAnalyses::none(); + } + + bool TransformationRulePass::isRequired() + { + return true; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Passes/TransformationRule/TransformationRule.hpp b/src/Passes/Source/Passes/TransformationRule/TransformationRule.hpp new file mode 100644 index 0000000000..a74a845bcf --- /dev/null +++ b/src/Passes/Source/Passes/TransformationRule/TransformationRule.hpp @@ -0,0 +1,55 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/RuleSet.hpp" + +#include "Llvm/Llvm.hpp" + +#include + +namespace microsoft +{ +namespace quantum +{ + + /// This class applies a set of transformation rules to the IR to transform it into a new IR. The + /// rules are added using the RuleSet class which allows the developer to create one or more rules + /// on how to transform the IR. + class TransformationRulePass : public llvm::PassInfoMixin + { + public: + using Replacements = ReplacementRule::Replacements; + using Instruction = llvm::Instruction; + using Rules = std::vector; + using Value = llvm::Value; + using Builder = ReplacementRule::Builder; + using AllocationManagerPtr = AllocationManager::AllocationManagerPtr; + + /// Constructors and destructors + /// @{ + explicit TransformationRulePass(RuleSet&& rule_set); + TransformationRulePass(TransformationRulePass const&) = delete; + TransformationRulePass(TransformationRulePass&&) = default; + ~TransformationRulePass() = default; + /// @} + + /// Operators + /// @{ + TransformationRulePass& operator=(TransformationRulePass const&) = delete; + TransformationRulePass& operator=(TransformationRulePass&&) = default; + /// @} + + /// Functions required by LLVM + /// @{ + llvm::PreservedAnalyses run(llvm::Function& function, llvm::FunctionAnalysisManager& fam); + static bool isRequired(); + /// @} + + private: + RuleSet rule_set_{}; + Replacements replacements_; ///< Registered replacements to be executed. + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Profiles/BaseProfile.cpp b/src/Passes/Source/Profiles/BaseProfile.cpp new file mode 100644 index 0000000000..e6db328b21 --- /dev/null +++ b/src/Passes/Source/Profiles/BaseProfile.cpp @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Passes/ExpandStaticAllocation/ExpandStaticAllocation.hpp" +#include "Passes/QirAllocationAnalysis/QirAllocationAnalysis.hpp" +#include "Passes/TransformationRule/TransformationRule.hpp" +#include "Profiles/BaseProfile.hpp" +#include "Rules/Factory.hpp" + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + + llvm::ModulePassManager BaseProfile::createGenerationModulePass( + llvm::PassBuilder& pass_builder, + llvm::PassBuilder::OptimizationLevel& optimisation_level, + bool debug) + { + auto ret = pass_builder.buildPerModuleDefaultPipeline(optimisation_level); + // buildPerModuleDefaultPipeline buildModuleOptimizationPipeline + auto function_pass_manager = pass_builder.buildFunctionSimplificationPipeline( + optimisation_level, llvm::PassBuilder::ThinLTOPhase::None, debug); + + auto inliner_pass = + pass_builder.buildInlinerPipeline(optimisation_level, llvm::PassBuilder::ThinLTOPhase::None, debug); + + // TODO(QAT-private-issue-29): Determine if static expansion should happen as a module pass + // instead of a function pass + function_pass_manager.addPass(ExpandStaticAllocationPass()); + + RuleSet rule_set; + + // Defining the mapping + auto factory = RuleFactory(rule_set); + + factory.useStaticQubitArrayAllocation(); + factory.useStaticQubitAllocation(); + factory.useStaticResultAllocation(); + + factory.optimiseBranchQuatumOne(); + // factory.optimiseBranchQuatumZero(); + + factory.disableReferenceCounting(); + factory.disableAliasCounting(); + factory.disableStringSupport(); + + function_pass_manager.addPass(TransformationRulePass(std::move(rule_set))); + + // Eliminate dead code + function_pass_manager.addPass(llvm::DCEPass()); + function_pass_manager.addPass(llvm::ADCEPass()); + + ret.addPass(createModuleToFunctionPassAdaptor(std::move(function_pass_manager))); + + // TODO(QAT-private-issue-30): Mordernise: Upon upgrading to LLVM 12 or 13, change CGPM to + // ret.addPass(llvm::createModuleToCGSCCPassAdaptor(std::move(CGPM))); + + ret.addPass(llvm::AlwaysInlinerPass()); + ret.addPass(std::move(inliner_pass)); + + return ret; + } + + llvm::ModulePassManager BaseProfile::createValidationModulePass( + llvm::PassBuilder&, + llvm::PassBuilder::OptimizationLevel&, + bool) + { + throw std::runtime_error("Validator not implmented yet"); + } + + void BaseProfile::addFunctionAnalyses(FunctionAnalysisManager& fam) + { + fam.registerPass([] { return QirAllocationAnalysis(); }); + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Profiles/BaseProfile.hpp b/src/Passes/Source/Profiles/BaseProfile.hpp new file mode 100644 index 0000000000..8bdd4be35f --- /dev/null +++ b/src/Passes/Source/Profiles/BaseProfile.hpp @@ -0,0 +1,29 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Profiles/IProfile.hpp" + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + + class BaseProfile : public IProfile + { + public: + llvm::ModulePassManager createGenerationModulePass( + PassBuilder& pass_builder, + OptimizationLevel& optimisation_level, + bool debug) override; + llvm::ModulePassManager createValidationModulePass( + PassBuilder& pass_builder, + OptimizationLevel& optimisation_level, + bool debug) override; + void addFunctionAnalyses(FunctionAnalysisManager& fam) override; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Profiles/IProfile.cpp b/src/Passes/Source/Profiles/IProfile.cpp new file mode 100644 index 0000000000..01ef4d2924 --- /dev/null +++ b/src/Passes/Source/Profiles/IProfile.cpp @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Profiles/IProfile.hpp" + +namespace microsoft +{ +namespace quantum +{ + + IProfile::~IProfile() = default; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Profiles/IProfile.hpp b/src/Passes/Source/Profiles/IProfile.hpp new file mode 100644 index 0000000000..30d9fe041c --- /dev/null +++ b/src/Passes/Source/Profiles/IProfile.hpp @@ -0,0 +1,33 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + + class IProfile + { + public: + using PassBuilder = llvm::PassBuilder; + using OptimizationLevel = PassBuilder::OptimizationLevel; + using FunctionAnalysisManager = llvm::FunctionAnalysisManager; + + IProfile() = default; + virtual ~IProfile(); + virtual llvm::ModulePassManager createGenerationModulePass( + PassBuilder& pass_builder, + OptimizationLevel& optimisation_level, + bool debug) = 0; + virtual llvm::ModulePassManager createValidationModulePass( + PassBuilder& pass_builder, + OptimizationLevel& optimisation_level, + bool debug) = 0; + virtual void addFunctionAnalyses(FunctionAnalysisManager& fam) = 0; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Factory.cpp b/src/Passes/Source/Rules/Factory.cpp new file mode 100644 index 0000000000..32d6483c6e --- /dev/null +++ b/src/Passes/Source/Rules/Factory.cpp @@ -0,0 +1,327 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Factory.hpp" +#include "Rules/Notation/Notation.hpp" + +#include "Llvm/Llvm.hpp" + +namespace microsoft +{ +namespace quantum +{ + using ReplacementRulePtr = RuleFactory::ReplacementRulePtr; + using namespace microsoft::quantum::notation; + + RuleFactory::RuleFactory(RuleSet& rule_set) + : rule_set_{rule_set} + , qubit_alloc_manager_{AllocationManager::createNew()} + , result_alloc_manager_{AllocationManager::createNew()} + { + } + + RuleFactory::AllocationManagerPtr RuleFactory::qubitAllocationManager() const + { + return qubit_alloc_manager_; + } + + RuleFactory::AllocationManagerPtr RuleFactory::resultAllocationManager() const + { + return result_alloc_manager_; + } + + void RuleFactory::removeFunctionCall(String const& name) + { + addRule({callByNameOnly(name), deleteInstruction()}); + } + + void RuleFactory::useStaticQubitArrayAllocation() + { + // TODO(QAT-private-issue-32): Use weak pointers to capture allocation managers + auto qubit_alloc_manager = qubit_alloc_manager_; + + /// Allocation + auto allocation_replacer = + [qubit_alloc_manager](Builder&, Value* val, Captures& cap, Replacements& replacements) { + auto cst = llvm::dyn_cast(cap["size"]); + if (cst == nullptr) + { + return false; + } + + auto llvm_size = cst->getValue(); + auto name = val->getName().str(); + qubit_alloc_manager->allocate(name, llvm_size.getZExtValue()); + + replacements.push_back({llvm::dyn_cast(val), nullptr}); + return true; + }; + + addRule({call("__quantum__rt__qubit_allocate_array", "size"_cap = _), allocation_replacer}); + + /// Array access replacement + auto access_replacer = + [qubit_alloc_manager](Builder& builder, Value* val, Captures& cap, Replacements& replacements) { + // Getting the type pointer + + auto ptr_type = llvm::dyn_cast(val->getType()); + if (ptr_type == nullptr) + { + return false; + } + + // Get the index and testing that it is a constant int + auto cst = llvm::dyn_cast(cap["index"]); + if (cst == nullptr) + { + // ... if not, we cannot perform the mapping. + return false; + } + + // Computing the index by getting the current index value and offseting by + // the offset at which the qubit array is allocated. + auto llvm_size = cst->getValue(); + auto offset = qubit_alloc_manager->getOffset(cap["arrayName"]->getName().str()); + + // Creating a new index APInt that is shifted by the offset of the allocation + auto idx = llvm::APInt(llvm_size.getBitWidth(), llvm_size.getZExtValue() + offset); + + // Computing offset + auto new_index = llvm::ConstantInt::get(builder.getContext(), idx); + + // Converting pointer + auto instr = new llvm::IntToPtrInst(new_index, ptr_type); + instr->takeName(val); + + // Replacing the instruction with new instruction + replacements.push_back({llvm::dyn_cast(val), instr}); + + // Deleting the getelement and cast operations + replacements.push_back({llvm::dyn_cast(cap["getElement"]), nullptr}); + replacements.push_back({llvm::dyn_cast(cap["cast"]), nullptr}); + + return true; + }; + + auto get_element = call("__quantum__rt__array_get_element_ptr_1d", "arrayName"_cap = _, "index"_cap = _); + auto cast_pattern = bitCast("getElement"_cap = get_element); + auto load_pattern = load("cast"_cap = cast_pattern); + + addRule({std::move(load_pattern), access_replacer}); + + /// Release replacement + auto deleter = deleteInstruction(); + addRule( + {call("__quantum__rt__qubit_release_array", "name"_cap = _), + [qubit_alloc_manager, deleter](Builder& builder, Value* val, Captures& cap, Replacements& rep) { + qubit_alloc_manager->release(cap["name"]->getName().str()); + return deleter(builder, val, cap, rep); + } + + }); + } + + void RuleFactory::useStaticQubitAllocation() + { + auto qubit_alloc_manager = qubit_alloc_manager_; + auto allocation_replacer = + [qubit_alloc_manager](Builder& builder, Value* val, Captures&, Replacements& replacements) { + // Getting the type pointer + auto ptr_type = llvm::dyn_cast(val->getType()); + if (ptr_type == nullptr) + { + return false; + } + + // Computing the index by getting the current index value and offseting by + // the offset at which the qubit array is allocated. + auto offset = qubit_alloc_manager->allocate(); + + // Creating a new index APInt that is shifted by the offset of the allocation + // TODO(QAT-private-issue-32): Make default integer width a module parameter or extract from QIR + auto idx = llvm::APInt(64, offset); + + // Computing offset + auto new_index = llvm::ConstantInt::get(builder.getContext(), idx); + + auto instr = new llvm::IntToPtrInst(new_index, ptr_type); + instr->takeName(val); + + // Replacing the instruction with new instruction + replacements.push_back({llvm::dyn_cast(val), instr}); + + return true; + }; + addRule({call("__quantum__rt__qubit_allocate"), allocation_replacer}); + + /// Release replacement + auto deleter = deleteInstruction(); + addRule( + {call("__quantum__rt__qubit_release", "name"_cap = _), + [qubit_alloc_manager, deleter](Builder& builder, Value* val, Captures& cap, Replacements& rep) { + qubit_alloc_manager->release(); + return deleter(builder, val, cap, rep); + } + + }); + } + + void RuleFactory::useStaticResultAllocation() + { + auto result_alloc_manager = result_alloc_manager_; + auto replace_measurement = + [result_alloc_manager](Builder& builder, Value* val, Captures& cap, Replacements& replacements) { + // Getting the type pointer + auto ptr_type = llvm::dyn_cast(val->getType()); + if (ptr_type == nullptr) + { + return false; + } + + // Computing the index by getting the current index value and offseting by + // the offset at which the qubit array is allocated. + auto offset = result_alloc_manager->allocate(); + + // Creating a new index APInt that is shifted by the offset of the allocation + // TODO(QAT-private-issue-32): Make default integer width a module parameter or extract from QIR + auto idx = llvm::APInt(64, offset); + + // Computing offset + auto new_index = llvm::ConstantInt::get(builder.getContext(), idx); + + auto instr = new llvm::IntToPtrInst(new_index, ptr_type); + instr->takeName(val); + + auto module = llvm::dyn_cast(val)->getModule(); + auto function = module->getFunction("__quantum__qis__mz__body"); + + std::vector arguments; + arguments.push_back(cap["qubit"]); + arguments.push_back(instr); + + if (!function) + { + std::vector types; + types.resize(arguments.size()); + for (uint64_t i = 0; i < types.size(); ++i) + { + types[i] = arguments[i]->getType(); + } + + auto return_type = llvm::Type::getVoidTy(val->getContext()); + + llvm::FunctionType* fnc_type = llvm::FunctionType::get(return_type, types, false); + function = llvm::Function::Create( + fnc_type, llvm::Function::ExternalLinkage, "__quantum__qis__mz__body", module); + } + + // Ensuring we are inserting after the instruction being deleted + builder.SetInsertPoint(llvm::dyn_cast(val)->getNextNode()); + + builder.CreateCall(function, arguments); + + // Replacing the instruction with new instruction + replacements.push_back({llvm::dyn_cast(val), instr}); + + return true; + }; + + addRule({call("__quantum__qis__m__body", "qubit"_cap = _), std::move(replace_measurement)}); + } + + void RuleFactory::optimiseBranchQuatumOne() + { + auto replace_branch_positive = [](Builder& builder, Value* val, Captures& cap, Replacements& replacements) { + auto result = cap["result"]; + auto cond = llvm::dyn_cast(cap["cond"]); + // Replacing result + auto module = llvm::dyn_cast(val)->getModule(); + auto function = module->getFunction("__quantum__qir__read_result"); + std::vector arguments; + arguments.push_back(result); + + if (!function) + { + std::vector types; + types.resize(arguments.size()); + for (uint64_t i = 0; i < types.size(); ++i) + { + types[i] = arguments[i]->getType(); + } + + auto return_type = llvm::Type::getInt1Ty(val->getContext()); + + llvm::FunctionType* fnc_type = llvm::FunctionType::get(return_type, types, false); + function = llvm::Function::Create( + fnc_type, llvm::Function::ExternalLinkage, "__quantum__qir__read_result", module); + } + + builder.SetInsertPoint(llvm::dyn_cast(val)); + auto new_call = builder.CreateCall(function, arguments); + new_call->takeName(cond); + + for (auto& use : cond->uses()) + { + llvm::User* user = use.getUser(); + user->setOperand(use.getOperandNo(), new_call); + } + cond->replaceAllUsesWith(new_call); + + // Deleting the previous condition and function to fetch one + replacements.push_back({cond, nullptr}); + replacements.push_back({cap["one"], nullptr}); + + return false; + }; + + /* + Here is an example IR for which we want to make a match: + + %1 = call %Result* @__quantum__rt__result_get_one() + %2 = call i1 @__quantum__rt__result_equal(%Result* %0, %Result* %1) + br i1 %2, label %then0__1, label %continue__1 + */ + + // Variations of get_one + auto get_one = call("__quantum__rt__result_get_one"); + addRule( + {branch("cond"_cap = call("__quantum__rt__result_equal", "result"_cap = _, "one"_cap = get_one), _, _), + replace_branch_positive}); + + addRule( + {branch("cond"_cap = call("__quantum__rt__result_equal", "one"_cap = get_one, "result"_cap = _), _, _), + replace_branch_positive}); + } + + void RuleFactory::disableReferenceCounting() + { + removeFunctionCall("__quantum__rt__array_update_reference_count"); + removeFunctionCall("__quantum__rt__string_update_reference_count"); + removeFunctionCall("__quantum__rt__result_update_reference_count"); + } + + void RuleFactory::disableAliasCounting() + { + removeFunctionCall("__quantum__rt__array_update_alias_count"); + removeFunctionCall("__quantum__rt__string_update_alias_count"); + removeFunctionCall("__quantum__rt__result_update_alias_count"); + } + + void RuleFactory::disableStringSupport() + { + removeFunctionCall("__quantum__rt__string_create"); + removeFunctionCall("__quantum__rt__string_release"); + removeFunctionCall("__quantum__rt__message"); + } + + ReplacementRulePtr RuleFactory::addRule(ReplacementRule&& rule) + { + auto ret = std::make_shared(std::move(rule)); + + rule_set_.addRule(ret); + + return ret; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Factory.hpp b/src/Passes/Source/Rules/Factory.hpp new file mode 100644 index 0000000000..9746db3a02 --- /dev/null +++ b/src/Passes/Source/Rules/Factory.hpp @@ -0,0 +1,92 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AllocationManager/AllocationManager.hpp" +#include "Rules/ReplacementRule.hpp" +#include "Rules/RuleSet.hpp" + +#include "Llvm/Llvm.hpp" + +#include + +namespace microsoft +{ +namespace quantum +{ + + /// Rule factory provides a high-level methods to build a ruleset that + /// enforces certain aspects of QIR transformation. + class RuleFactory + { + public: + using String = std::string; + using ReplacementRulePtr = std::shared_ptr; + using AllocationManagerPtr = AllocationManager::AllocationManagerPtr; + using Replacements = ReplacementRule::Replacements; + using Captures = IOperandPrototype::Captures; + using Instruction = llvm::Instruction; + using Value = llvm::Value; + using Builder = ReplacementRule::Builder; + + /// Constructor configuration. Explicit construction with + /// rule set to be configured, which can be moved using move + /// semantics. No copy allowed. + /// @{ + explicit RuleFactory(RuleSet& rule_set); + RuleFactory() = delete; + RuleFactory(RuleFactory const&) = delete; + RuleFactory(RuleFactory&&) = default; + ~RuleFactory() = default; + /// @} + + /// Generic rules + /// @{ + /// Removes all calls to functions with a specified name. + /// This function matches on name alone and ignores function + /// arguments. + void removeFunctionCall(String const& name); + /// @} + + /// Conventions + /// @{ + void useStaticQubitArrayAllocation(); + void useStaticQubitAllocation(); + void useStaticResultAllocation(); + /// @} + + /// Optimisations + /// @{ + void optimiseBranchQuatumOne(); + void optimiseBranchQuatumZero(); + /// @} + + /// Disabling by feature + /// @{ + void disableReferenceCounting(); + void disableAliasCounting(); + void disableStringSupport(); + /// @} + + /// Allocation Managers + /// @{ + AllocationManagerPtr qubitAllocationManager() const; + AllocationManagerPtr resultAllocationManager() const; + /// @} + private: + ReplacementRulePtr addRule(ReplacementRule&& rule); + + /// Affected artefacts + /// @{ + RuleSet& rule_set_; ///< The ruleset we are building + /// @} + + /// Allocation managers. Allocation managers for different types + /// @{ + AllocationManagerPtr qubit_alloc_manager_{nullptr}; + AllocationManagerPtr result_alloc_manager_{nullptr}; + /// @} + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/BitCast.cpp b/src/Passes/Source/Rules/Notation/BitCast.cpp new file mode 100644 index 0000000000..5493b80bd8 --- /dev/null +++ b/src/Passes/Source/Rules/Notation/BitCast.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + IOperandPrototypePtr bitCast(IOperandPrototypePtr const& arg) + { + auto cast_pattern = std::make_shared(); + + cast_pattern->addChild(arg); + return static_cast(cast_pattern); + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Branch.cpp b/src/Passes/Source/Rules/Notation/Branch.cpp new file mode 100644 index 0000000000..bc7aecae9c --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Branch.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + IOperandPrototypePtr branch( + IOperandPrototypePtr const& cond, + IOperandPrototypePtr const& arg1, + IOperandPrototypePtr const& arg2) + { + auto branch_pattern = std::make_shared(); + + branch_pattern->addChild(cond); + branch_pattern->addChild(arg1); + branch_pattern->addChild(arg2); + + return static_cast(branch_pattern); + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Call.ipp b/src/Passes/Source/Rules/Notation/Call.ipp new file mode 100644 index 0000000000..1c2ade7517 --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Call.ipp @@ -0,0 +1,41 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" +#include "Rules/ReplacementRule.hpp" + +#include +#include + +namespace microsoft { +namespace quantum { +namespace notation { + +using IOperandPrototypePtr = std::shared_ptr; + +template +IOperandPrototypePtr call(std::string const &name, Args... args) +{ + IOperandPrototypePtr ret = std::make_shared(name); + std::vector arguments{args...}; + + // Adding arguments to matching + for (auto &a : arguments) + { + ret->addChild(a); + } + + // Function name is kept in the last operand + ret->addChild(std::make_shared()); + + return ret; +} + +} // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/CallByNameOnly.cpp b/src/Passes/Source/Rules/Notation/CallByNameOnly.cpp new file mode 100644 index 0000000000..c48b64ad1b --- /dev/null +++ b/src/Passes/Source/Rules/Notation/CallByNameOnly.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + IOperandPrototypePtr callByNameOnly(std::string const& name) + { + IOperandPrototypePtr ret = std::make_shared(name); + return ret; + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Capture.cpp b/src/Passes/Source/Rules/Notation/Capture.cpp new file mode 100644 index 0000000000..f278c857ca --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Capture.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + Capture::Capture(std::string const& name) + : name_{name} + { + } + + IOperandPrototypePtr Capture::operator=(IOperandPrototypePtr const& other) // NOLINT + { + auto ret = other->copy(); + ret->enableCapture(name_); + return ret; + } + + Capture operator""_cap(char const* name, std::size_t) + { + return Capture(name); + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Load.cpp b/src/Passes/Source/Rules/Notation/Load.cpp new file mode 100644 index 0000000000..9b91c10435 --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Load.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + IOperandPrototypePtr load(IOperandPrototypePtr const& arg) + { + auto ret = std::make_shared(); + + ret->addChild(arg); + return static_cast(ret); + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Notation.cpp b/src/Passes/Source/Rules/Notation/Notation.cpp new file mode 100644 index 0000000000..aede0716a5 --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Notation.cpp @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + /// Replacement function to delete an instruction. This is a shorthand notation for deleting + /// an instruction that can be used with a custom rule when building a ruleset. This function + /// can be used with shorthand notation for patterns as follows: + /// ```c++ + /// addRule({callByNameOnly(name), deleteInstruction()}); + /// ``` + /// to delete the instructions that calls functions with the name `name`. + std::function + deleteInstruction() + { + return [](ReplacementRule::Builder&, ReplacementRule::Value* val, ReplacementRule::Captures&, + ReplacementRule::Replacements& replacements) { + replacements.push_back({llvm::dyn_cast(val), nullptr}); + return true; + }; + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Notation.hpp b/src/Passes/Source/Rules/Notation/Notation.hpp new file mode 100644 index 0000000000..254b4ceedb --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Notation.hpp @@ -0,0 +1,65 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Call.ipp" +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + class Capture + { + public: + explicit Capture(std::string const& name); + // Note that this operator is delibrately unconventional + IOperandPrototypePtr operator=(IOperandPrototypePtr const& other); // NOLINT + + private: + std::string name_{}; + }; + + /// @{ + template IOperandPrototypePtr call(std::string const& name, Args... args); + IOperandPrototypePtr callByNameOnly(std::string const& name); + IOperandPrototypePtr bitCast(IOperandPrototypePtr const& arg); + IOperandPrototypePtr branch( + IOperandPrototypePtr const& cond, + IOperandPrototypePtr const& arg1, + IOperandPrototypePtr const& arg2); + IOperandPrototypePtr load(IOperandPrototypePtr const& arg); + IOperandPrototypePtr store(IOperandPrototypePtr const& target, IOperandPrototypePtr const& value); + /// @} + + /// @{ + static std::shared_ptr _ = std::make_shared(); + /// @} + + /// @{ + std::function + deleteInstruction(); + + /// @} + + Capture operator""_cap(char const* name, std::size_t); + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Notation/Store.cpp b/src/Passes/Source/Rules/Notation/Store.cpp new file mode 100644 index 0000000000..f772d9c013 --- /dev/null +++ b/src/Passes/Source/Rules/Notation/Store.cpp @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Notation/Notation.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + namespace notation + { + + using IOperandPrototypePtr = std::shared_ptr; + + IOperandPrototypePtr store(IOperandPrototypePtr const& target, IOperandPrototypePtr const& value) + { + auto ret = std::make_shared(); + + ret->addChild(target); + ret->addChild(value); + return static_cast(ret); + } + + } // namespace notation +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/OperandPrototype.cpp b/src/Passes/Source/Rules/OperandPrototype.cpp new file mode 100644 index 0000000000..3a425a3404 --- /dev/null +++ b/src/Passes/Source/Rules/OperandPrototype.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" + +namespace microsoft +{ +namespace quantum +{ + + IOperandPrototype::~IOperandPrototype() = default; + bool IOperandPrototype::matchChildren(Value* value, Captures& captures) const + { + auto user = llvm::dyn_cast(value); + if (!children_.empty()) + { + if (user == nullptr) + { + return false; + } + + if (user->getNumOperands() != children_.size()) + { + return false; + } + + uint64_t i = 0; + while (i < children_.size()) + { + auto v = user->getOperand(static_cast(i)); + if (!children_[i]->match(v, captures)) + { + return false; + } + ++i; + } + + return true; + } + + // TODO(QAT-private-issue-33): value may be other type than llvm::User. Check other relevant types + // and deal with it. + + return true; + } + + void IOperandPrototype::addChild(Child const& child) + { + children_.push_back(child); + } + + void IOperandPrototype::enableCapture(std::string capture_name) + { + capture_name_ = std::move(capture_name); + } + + bool IOperandPrototype::fail(Value* /*value*/, Captures& /*captures*/) const + { + return false; + } + + bool IOperandPrototype::success(Value* value, Captures& captures) const + { + capture(value, captures); + + auto ret = matchChildren(value, captures); + if (!ret) + { + uncapture(value, captures); + } + return ret; + } + + void IOperandPrototype::capture(Value* value, Captures& captures) const + { + if (!capture_name_.empty()) + { + captures[capture_name_] = value; + } + } + + void IOperandPrototype::uncapture(Value* /*value*/, Captures& captures) const + { + if (!capture_name_.empty()) + { + captures.erase(captures.find(capture_name_)); + } + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/OperandPrototype.hpp b/src/Passes/Source/Rules/OperandPrototype.hpp new file mode 100644 index 0000000000..1b0269f76d --- /dev/null +++ b/src/Passes/Source/Rules/OperandPrototype.hpp @@ -0,0 +1,90 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + /// IOperandPrototype describes an IR pattern and allows matching against + /// LLVMs llvm::Value type. Each value may or may not be captured during the + /// matching process which means that they are stored in a map under a given name. + /// Capturing is enabled using `enableCapture(name)` which sets the name the + /// value should be stored under. + class IOperandPrototype + { + public: + using Instruction = llvm::Instruction; + using String = std::string; + using Value = llvm::Value; + using Child = std::shared_ptr; + using Children = std::vector; + using Captures = std::unordered_map; + + /// Constructors and desctructors + /// @{ + IOperandPrototype() = default; + virtual ~IOperandPrototype(); + /// @} + + /// Interface functions + /// @{ + virtual bool match(Value* value, Captures& captures) const = 0; + virtual Child copy() const = 0; + /// @} + + /// Shared functionality + /// @{ + + /// Adds a child to be matched against the matchees children. Children + /// are matched in order and by size. + void addChild(Child const& child); + + /// Flags that this operand should be captured. This function ensures + /// that the captured operand is given a name. The subsequent logic + /// in this class is responsible for capturing (upon match) and + /// uncapturing (upon backtrack) with specified name + void enableCapture(std::string capture_name); + /// @} + protected: + /// Function to indicate match success or failure. Either of these + /// must be called prior to return from an implementation of + /// IOperandPrototype::match. + /// @{ + bool fail(Value* value, Captures& captures) const; + bool success(Value* value, Captures& captures) const; + /// @} + + /// Helper functions for the capture logic. + /// @{ + bool matchChildren(Value* value, Captures& captures) const; + void capture(Value* value, Captures& captures) const; + void uncapture(Value* value, Captures& captures) const; + /// @} + + /// Helper functions for operation + /// @{ + /// Shallow copy of the operand to allow name change + /// of the capture + void copyPropertiesFrom(IOperandPrototype const& other) + { + capture_name_ = other.capture_name_; + children_ = other.children_; + } + /// @} + private: + /// Data variables for common matching functionality + /// @{ + std::string capture_name_{""}; ///< Name to captured value. Empty means no capture. + Children children_{}; ///< Children to match aginst the values children. + /// @} + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Any.cpp b/src/Passes/Source/Rules/Operands/Any.cpp new file mode 100644 index 0000000000..9da3766e78 --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Any.cpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" +#include "Rules/Operands/Any.hpp" + +namespace microsoft +{ +namespace quantum +{ + + AnyPattern::AnyPattern() = default; + AnyPattern::~AnyPattern() = default; + bool AnyPattern::match(Value* instr, Captures& captures) const + { + return success(instr, captures); + } + + AnyPattern::Child AnyPattern::copy() const + { + return std::make_shared(); + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Any.hpp b/src/Passes/Source/Rules/Operands/Any.hpp new file mode 100644 index 0000000000..3a9bd4ad69 --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Any.hpp @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + class AnyPattern : public IOperandPrototype + { + public: + AnyPattern(); + ~AnyPattern() override; + bool match(Value* instr, Captures& captures) const override; + Child copy() const override; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Call.cpp b/src/Passes/Source/Rules/Operands/Call.cpp new file mode 100644 index 0000000000..20b8eda0fd --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Call.cpp @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" +#include "Rules/Operands/Call.hpp" + +namespace microsoft +{ +namespace quantum +{ + + CallPattern::CallPattern(String const& name) + : name_{name} + { + } + + CallPattern::~CallPattern() = default; + + bool CallPattern::match(Value* instr, Captures& captures) const + { + auto* call_instr = llvm::dyn_cast(instr); + if (call_instr == nullptr) + { + return fail(instr, captures); + } + + auto target_function = call_instr->getCalledFunction(); + auto name = target_function->getName(); + + if (name != name_) + { + return fail(instr, captures); + } + + return success(instr, captures); + } + + CallPattern::Child CallPattern::copy() const + { + auto ret = std::make_shared(name_); + ret->copyPropertiesFrom(*this); + return std::move(ret); + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Call.hpp b/src/Passes/Source/Rules/Operands/Call.hpp new file mode 100644 index 0000000000..11c6eafa08 --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Call.hpp @@ -0,0 +1,33 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + class CallPattern : public IOperandPrototype + { + public: + using String = std::string; + explicit CallPattern(String const& name); + + ~CallPattern() override; + + bool match(Value* instr, Captures& captures) const override; + Child copy() const override; + + private: + String name_{}; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Instruction.cpp b/src/Passes/Source/Rules/Operands/Instruction.cpp new file mode 100644 index 0000000000..04305c99fb --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Instruction.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" +#include "Rules/Operands/Instruction.hpp" + +namespace microsoft +{ +namespace quantum +{ + + template InstructionPattern::~InstructionPattern() = default; + template bool InstructionPattern::match(Value* instr, Captures& captures) const + { + auto* load_instr = llvm::dyn_cast(instr); + if (load_instr == nullptr) + { + return fail(instr, captures); + } + + return success(instr, captures); + } + + template typename InstructionPattern::Child InstructionPattern::copy() const + { + auto ret = std::make_shared>(); + ret->copyPropertiesFrom(*this); + return std::move(ret); + } + +// TODO(QAT-private-issue-34): This seems to be a bug in LLVM. Template instantiations in +// a single translation unit is not supposed to reinstantiate across other +// translation units. +// +// However, it is suspecious that htis problem has been around since Clang 8. +// so this needs more investigation. For now, this work around suffices +// See +// https://bugs.llvm.org/show_bug.cgi?id=18733 +// https://stackoverflow.com/questions/56041900/why-does-explicit-template-instantiation-result-in-weak-template-vtables-warning +// for more information +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-template-vtables" + template class InstructionPattern; + template class InstructionPattern; + template class InstructionPattern; + template class InstructionPattern; +#pragma clang diagnostic pop + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/Operands/Instruction.hpp b/src/Passes/Source/Rules/Operands/Instruction.hpp new file mode 100644 index 0000000000..1cb8d8b96d --- /dev/null +++ b/src/Passes/Source/Rules/Operands/Instruction.hpp @@ -0,0 +1,32 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/OperandPrototype.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + template class InstructionPattern : public IOperandPrototype + { + public: + using IOperandPrototype::IOperandPrototype; + ~InstructionPattern() override; + bool match(Value* instr, Captures& captures) const override; + Child copy() const override; + }; + + using StorePattern = InstructionPattern; + using LoadPattern = InstructionPattern; + using BitCastPattern = InstructionPattern; + using BranchPattern = InstructionPattern; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/ReplacementRule.cpp b/src/Passes/Source/Rules/ReplacementRule.cpp new file mode 100644 index 0000000000..486d429f95 --- /dev/null +++ b/src/Passes/Source/Rules/ReplacementRule.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/ReplacementRule.hpp" + +namespace microsoft +{ +namespace quantum +{ + + ReplacementRule::ReplacementRule(IOperandPrototypePtr&& pattern, ReplaceFunction&& replacer) + : pattern_{std::move(pattern)} + , replacer_{std::move(replacer)} + { + } + + void ReplacementRule::setPattern(IOperandPrototypePtr&& pattern) + { + pattern_ = std::move(pattern); + } + + void ReplacementRule::setReplacer(ReplaceFunction const& replacer) + { + replacer_ = replacer; + } + + bool ReplacementRule::match(Value* value, Captures& captures) const + { + if (pattern_ == nullptr) + { + return false; + } + + return pattern_->match(value, captures); + } + + bool ReplacementRule::replace(Builder& builder, Value* value, Captures& captures, Replacements& replacements) const + { + if (replacer_) + { + return replacer_(builder, value, captures, replacements); + } + + return false; + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/ReplacementRule.hpp b/src/Passes/Source/Rules/ReplacementRule.hpp new file mode 100644 index 0000000000..a277087fd9 --- /dev/null +++ b/src/Passes/Source/Rules/ReplacementRule.hpp @@ -0,0 +1,68 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "Rules/Operands/Any.hpp" +#include "Rules/Operands/Call.hpp" +#include "Rules/Operands/Instruction.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + /// Rule that describes a pattern and how to make a replacement of the matched values. + /// The class contains a OperandPrototype which is used to test whether an LLVM IR value + /// follows a specific pattern. The class also holds a function pointer to logic that + /// allows replacement of the specified value. + class ReplacementRule + { + public: + using Captures = IOperandPrototype::Captures; + using Instruction = llvm::Instruction; + using Value = llvm::Value; + using IOperandPrototypePtr = std::shared_ptr; + using Builder = llvm::IRBuilder<>; + using Replacements = std::vector>; + using ReplaceFunction = std::function; + + /// Constructorss and destructors + /// @{ + ReplacementRule() = default; + ReplacementRule(IOperandPrototypePtr&& pattern, ReplaceFunction&& replacer); + /// @} + + /// Rule configuration + /// @{ + + /// Sets the pattern describing logic to be replaced. + void setPattern(IOperandPrototypePtr&& pattern); + + /// Sets the replacer logic which given a successful match will perform + /// a replacement on the IR. + void setReplacer(ReplaceFunction const& replacer); + /// @} + + /// Operation + /// @{ + /// Tests whether a given value matches the rule pattern and store captures. + /// The function returns true if the match was successful in which case captures + /// are recorded. + bool match(Value* value, Captures& captures) const; + + /// Invokes the replacer given a matched value and its corresponding captures + // + bool replace(Builder& builder, Value* value, Captures& captures, Replacements& replacements) const; + /// @} + private: + IOperandPrototypePtr pattern_{nullptr}; + ReplaceFunction replacer_{nullptr}; + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/RuleSet.cpp b/src/Passes/Source/Rules/RuleSet.cpp new file mode 100644 index 0000000000..817864159c --- /dev/null +++ b/src/Passes/Source/Rules/RuleSet.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AllocationManager/AllocationManager.hpp" +#include "Rules/Factory.hpp" +#include "Rules/ReplacementRule.hpp" +#include "Rules/RuleSet.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include +namespace microsoft +{ +namespace quantum +{ + + bool RuleSet::matchAndReplace(Instruction* value, Replacements& replacements) + { + Captures captures; + for (auto const& rule : rules_) + { + // Checking if the rule is matched and keep track of captured nodes + if (rule->match(value, captures)) + { + + // If it is matched, we attempt to replace it + llvm::IRBuilder<> builder{value}; + if (rule->replace(builder, value, captures, replacements)) + { + return true; + } + } + } + return false; + } + + void RuleSet::addRule(ReplacementRulePtr const& rule) + { + rules_.push_back(rule); + } + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/Source/Rules/RuleSet.hpp b/src/Passes/Source/Rules/RuleSet.hpp new file mode 100644 index 0000000000..44571fe4b0 --- /dev/null +++ b/src/Passes/Source/Rules/RuleSet.hpp @@ -0,0 +1,68 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AllocationManager/AllocationManager.hpp" +#include "Rules/OperandPrototype.hpp" +#include "Rules/ReplacementRule.hpp" + +#include "Llvm/Llvm.hpp" + +#include +#include + +namespace microsoft +{ +namespace quantum +{ + + /// RuleSet contains a set of replacement rules and the corresponding logic + /// to apply the rules. The class allows one to apply the rules by which + /// each rule is tested one-by-one until a successful attempt at performing + /// a replace has happened, or the list was exhausted. + class RuleSet + { + public: + using ReplacementRulePtr = std::shared_ptr; + using Rules = std::vector; + using Replacements = ReplacementRule::Replacements; + using Captures = IOperandPrototype::Captures; + using Instruction = llvm::Instruction; + using Value = llvm::Value; + using Builder = ReplacementRule::Builder; + using AllocationManagerPtr = AllocationManager::AllocationManagerPtr; + + /// Constructors + /// @{ + RuleSet() = default; + RuleSet(RuleSet const&) = default; + RuleSet(RuleSet&&) = default; + ~RuleSet() = default; + /// @} + + /// Operators + /// @{ + RuleSet& operator=(RuleSet const&) = default; + RuleSet& operator=(RuleSet&&) = default; + /// @} + + /// Operating rule sets + /// @{ + /// Matches patterns and runs the replacement routines if a match + /// is found. The function returns true if a pattern is matched and + /// and the replacement was a success. In all other cases, it returns + /// false. + bool matchAndReplace(Instruction* value, Replacements& replacements); + /// @} + + /// Set up and configuration + /// @{ + /// Adds a new replacement rule to the set. + void addRule(ReplacementRulePtr const& rule); + /// @} + private: + Rules rules_; ///< Rules that describes QIR mappings + }; + +} // namespace quantum +} // namespace microsoft diff --git a/src/Passes/docs/base-profile-transformations.md b/src/Passes/docs/base-profile-transformations.md index 07fda627ed..eb113300ed 100644 --- a/src/Passes/docs/base-profile-transformations.md +++ b/src/Passes/docs/base-profile-transformations.md @@ -106,7 +106,7 @@ exit__1: ; preds = %header__1 } ``` -After applying the our [demo profile transformation](https://github.com/troelsfr/qsharp-compiler/tree/feature/profile-pass/src/Passes/examples/QubitAllocationAnalysis), the QIR is reduced to: +After applying the our demo profile transformation, the QIR is reduced to: ``` define void @Feasibility__QubitMapping__Interop() local_unnamed_addr #0 { diff --git a/src/Passes/docs/continous-integration.md b/src/Passes/docs/continous-integration.md index 364d230883..6d6b81fc70 100644 --- a/src/Passes/docs/continous-integration.md +++ b/src/Passes/docs/continous-integration.md @@ -5,8 +5,8 @@ In order to run the tests, you first need to build the library. Assuming that th ``` 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) +PASS: Quantum-Passes :: QirAllocationAnalysis/case1.ll (1 of 2) +PASS: Quantum-Passes :: QirAllocationAnalysis/case2.ll (2 of 2) Testing Time: 0.27s Passed: 2 diff --git a/src/Passes/examples/ClassicalIrCommandline/Makefile b/src/Passes/examples/ClassicalIrCommandline/Makefile index 1dd41d69e7..35f0e05627 100644 --- a/src/Passes/examples/ClassicalIrCommandline/Makefile +++ b/src/Passes/examples/ClassicalIrCommandline/Makefile @@ -1,13 +1,12 @@ emit-llvm: - clang -O0 -S -emit-llvm classical-program.c -o classical-program.ll + clang -O0 -fno-inline -S -emit-llvm classical-program.c -o classical-program.ll emit-llvm-bc: clang -O0 -c -emit-llvm classical-program.c -o classical-program.bc debug-ng-pass-mac: emit-llvm-bc - opt -load-pass-plugin ../../Debug/libQSharpPasses.dylib -debug --passes="operation-counter" -disable-output classical-program.bc - + opt -load-pass-plugin ../../Debug/Source/Passes/libOpsCounter.dylib --passes="print" -disable-output classical-program.bc clean: diff --git a/src/Passes/examples/ClassicalIrCommandline/README.md b/src/Passes/examples/ClassicalIrCommandline/README.md index 0b0d3185ac..67a9133371 100644 --- a/src/Passes/examples/ClassicalIrCommandline/README.md +++ b/src/Passes/examples/ClassicalIrCommandline/README.md @@ -19,18 +19,12 @@ whereas the latter is generated by executing: This generates a nice and short IR which makes not too overwhelming to understand what is going on. -## Legacy passes - -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 -``` - ## Next-gen passes This part assumes that you have built the Passes library. ```sh -opt -load-pass-plugin ../../{Debug,Release}/libs/libQSharpPasses.{dylib,so} --passes="print" -disable-output classical-program.bc +opt -load-pass-plugin ../../{Debug,Release}/libs/libOpsCounter.{dylib,so} --passes="print" -disable-output classical-program.bc ``` + +opt -O3 -S classical-program.ll diff --git a/src/Passes/examples/ClassicalIrCommandline/classical-program.c b/src/Passes/examples/ClassicalIrCommandline/classical-program.c index ae56c14d68..7de7c1287c 100644 --- a/src/Passes/examples/ClassicalIrCommandline/classical-program.c +++ b/src/Passes/examples/ClassicalIrCommandline/classical-program.c @@ -3,7 +3,7 @@ int foo(int x) return x; } -void bar(int x, int y) +inline void bar(int x, int y) { foo(x + y); } diff --git a/src/Passes/examples/ClassicalIrCommandline/classical-program.ll b/src/Passes/examples/ClassicalIrCommandline/classical-program.ll index 5ad71d9d0b..d7464c030d 100644 --- a/src/Passes/examples/ClassicalIrCommandline/classical-program.ll +++ b/src/Passes/examples/ClassicalIrCommandline/classical-program.ll @@ -1,123 +1,58 @@ -; ModuleID = 'classical-program.cpp' -source_filename = "classical-program.cpp" +; ModuleID = 'classical-program.c' +source_filename = "classical-program.c" target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx11.0.0" -%"class.std::__1::basic_ostream" = type { i32 (...)**, %"class.std::__1::basic_ios.base" } -%"class.std::__1::basic_ios.base" = type <{ %"class.std::__1::ios_base", %"class.std::__1::basic_ostream"*, i32 }> -%"class.std::__1::ios_base" = type { i32 (...)**, i32, i64, i64, i32, i32, i8*, i8*, void (i32, %"class.std::__1::ios_base"*, i32)**, i32*, i64, i64, i64*, i64, i64, i8**, i64, i64 } -%"class.std::__1::locale::id" = type <{ %"struct.std::__1::once_flag", i32, [4 x i8] }> -%"struct.std::__1::once_flag" = type { i64 } -%"class.std::__1::locale" = type { %"class.std::__1::locale::__imp"* } -%"class.std::__1::locale::__imp" = type opaque -%"class.std::__1::locale::facet" = type { %"class.std::__1::__shared_count" } -%"class.std::__1::__shared_count" = type { i32 (...)**, i64 } -%"class.std::__1::ctype" = type <{ %"class.std::__1::locale::facet", i32*, i8, [7 x i8] }> - -@_ZNSt3__14coutE = external global %"class.std::__1::basic_ostream", align 8 -@_ZNSt3__15ctypeIcE2idE = external global %"class.std::__1::locale::id", align 8 - -; Function Attrs: norecurse ssp uwtable mustprogress -define dso_local i32 @main() local_unnamed_addr #0 personality i32 (...)* @__gxx_personality_v0 { - %1 = alloca %"class.std::__1::locale", align 8 - %2 = tail call i32 @_Z9fibonaccii(i32 3) - %3 = tail call nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8) @_ZNSt3__14coutE, i32 %2) - %4 = bitcast %"class.std::__1::basic_ostream"* %3 to i8** - %5 = load i8*, i8** %4, align 8, !tbaa !3 - %6 = getelementptr i8, i8* %5, i64 -24 - %7 = bitcast i8* %6 to i64* - %8 = load i64, i64* %7, align 8 - %9 = bitcast %"class.std::__1::basic_ostream"* %3 to i8* - %10 = getelementptr inbounds i8, i8* %9, i64 %8 - %11 = bitcast %"class.std::__1::locale"* %1 to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %11) #5 - %12 = bitcast i8* %10 to %"class.std::__1::ios_base"* - call void @_ZNKSt3__18ios_base6getlocEv(%"class.std::__1::locale"* nonnull sret(%"class.std::__1::locale") align 8 %1, %"class.std::__1::ios_base"* nonnull dereferenceable(136) %12) - %13 = invoke %"class.std::__1::locale::facet"* @_ZNKSt3__16locale9use_facetERNS0_2idE(%"class.std::__1::locale"* nonnull dereferenceable(8) %1, %"class.std::__1::locale::id"* nonnull align 8 dereferenceable(12) @_ZNSt3__15ctypeIcE2idE) - to label %14 unwind label %21 - -14: ; preds = %0 - %15 = bitcast %"class.std::__1::locale::facet"* %13 to %"class.std::__1::ctype"* - %16 = bitcast %"class.std::__1::locale::facet"* %13 to i8 (%"class.std::__1::ctype"*, i8)*** - %17 = load i8 (%"class.std::__1::ctype"*, i8)**, i8 (%"class.std::__1::ctype"*, i8)*** %16, align 8, !tbaa !3 - %18 = getelementptr inbounds i8 (%"class.std::__1::ctype"*, i8)*, i8 (%"class.std::__1::ctype"*, i8)** %17, i64 7 - %19 = load i8 (%"class.std::__1::ctype"*, i8)*, i8 (%"class.std::__1::ctype"*, i8)** %18, align 8 - %20 = invoke signext i8 %19(%"class.std::__1::ctype"* nonnull dereferenceable(25) %15, i8 signext 10) - to label %23 unwind label %21 - -21: ; preds = %14, %0 - %22 = landingpad { i8*, i32 } - cleanup - call void @_ZNSt3__16localeD1Ev(%"class.std::__1::locale"* nonnull dereferenceable(8) %1) #5 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %11) #5 - resume { i8*, i32 } %22 - -23: ; preds = %14 - call void @_ZNSt3__16localeD1Ev(%"class.std::__1::locale"* nonnull dereferenceable(8) %1) #5 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %11) #5 - %24 = call nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8) %3, i8 signext %20) - %25 = call nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8) %3) - ret i32 0 +; Function Attrs: noinline nounwind optnone ssp uwtable +define i32 @foo(i32 %0) #0 !dbg !8 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + call void @llvm.dbg.declare(metadata i32* %2, metadata !12, metadata !DIExpression()), !dbg !13 + %3 = load i32, i32* %2, align 4, !dbg !14 + ret i32 %3, !dbg !15 } -declare nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8), i32) local_unnamed_addr #1 +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 -; Function Attrs: ssp uwtable mustprogress -define linkonce_odr i32 @_Z9fibonaccii(i32 %0) local_unnamed_addr #2 { - %2 = icmp slt i32 %0, 2 - br i1 %2, label %13, label %3 - -3: ; preds = %1, %3 - %4 = phi i32 [ %8, %3 ], [ %0, %1 ] - %5 = phi i32 [ %9, %3 ], [ 0, %1 ] - %6 = add nsw i32 %4, -1 - %7 = tail call i32 @_Z9fibonaccii(i32 %6) - %8 = add nsw i32 %4, -2 - %9 = add nsw i32 %7, %5 - %10 = icmp slt i32 %4, 4 - br i1 %10, label %11, label %3 - -11: ; preds = %3 - %12 = add i32 %9, 1 - br label %13 - -13: ; preds = %11, %1 - %14 = phi i32 [ 1, %1 ], [ %12, %11 ] - ret i32 %14 +; Function Attrs: noinline nounwind optnone ssp uwtable +define i32 @main() #0 !dbg !16 { + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 @foo(i32 2), !dbg !19 + call void @bar(i32 3, i32 2), !dbg !20 + ret i32 0, !dbg !21 } -declare nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8), i8 signext) local_unnamed_addr #1 - -declare nonnull align 8 dereferenceable(8) %"class.std::__1::basic_ostream"* @_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv(%"class.std::__1::basic_ostream"* nonnull dereferenceable(8)) local_unnamed_addr #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 - -declare void @_ZNKSt3__18ios_base6getlocEv(%"class.std::__1::locale"* sret(%"class.std::__1::locale") align 8, %"class.std::__1::ios_base"* nonnull dereferenceable(136)) local_unnamed_addr #1 - -declare i32 @__gxx_personality_v0(...) - -; Function Attrs: nounwind -declare void @_ZNSt3__16localeD1Ev(%"class.std::__1::locale"* nonnull dereferenceable(8)) unnamed_addr #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 - -declare %"class.std::__1::locale::facet"* @_ZNKSt3__16locale9use_facetERNS0_2idE(%"class.std::__1::locale"* nonnull dereferenceable(8), %"class.std::__1::locale::id"* nonnull align 8 dereferenceable(12)) local_unnamed_addr #1 - -attributes #0 = { norecurse ssp uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { ssp uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { argmemonly nofree nosync nounwind willreturn } -attributes #4 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { nounwind } - -!llvm.module.flags = !{!0, !1} -!llvm.ident = !{!2} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"PIC Level", i32 2} -!2 = !{!"Homebrew clang version 12.0.1"} -!3 = !{!4, !4, i64 0} -!4 = !{!"vtable pointer", !5, i64 0} -!5 = !{!"Simple C++ TBAA"} +declare void @bar(i32, i32) #2 + +attributes #0 = { noinline nounwind optnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable willreturn } +attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.1.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None, sysroot: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", sdk: "MacOSX.sdk") +!1 = !DIFile(filename: "classical-program.c", directory: "/Users/tfr/Documents/Projects/qsharp-compiler/src/Passes/examples/ClassicalIrCommandline") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.1.0"} +!8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 1, column: 13, scope: !8) +!14 = !DILocation(line: 3, column: 10, scope: !8) +!15 = !DILocation(line: 3, column: 3, scope: !8) +!16 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 11, type: !17, scopeLine: 12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!17 = !DISubroutineType(types: !18) +!18 = !{!11} +!19 = !DILocation(line: 13, column: 3, scope: !16) +!20 = !DILocation(line: 14, column: 3, scope: !16) +!21 = !DILocation(line: 16, column: 3, scope: !16) diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp deleted file mode 100644 index 00f2b75f5f..0000000000 --- a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Comparison.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#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 deleted file mode 100644 index eeab572589..0000000000 --- a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - Exe - netcoreapp3.1 - true - - - diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs b/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs deleted file mode 100644 index 0b5655ddec..0000000000 --- a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/ConstSizeArray.qs +++ /dev/null @@ -1,30 +0,0 @@ -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/Makefile b/src/Passes/examples/QubitAllocationAnalysis/Makefile index feedf8753f..97f49577bb 100644 --- a/src/Passes/examples/QubitAllocationAnalysis/Makefile +++ b/src/Passes/examples/QubitAllocationAnalysis/Makefile @@ -1,25 +1,38 @@ 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 + opt -load-pass-plugin ../../Debug/Source/Passes/libQirAllocationAnalysis.dylib \ + -load-pass-plugin ../../Debug/Source/Passes/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 + # TODO(tfr): Add comments + opt -load-pass-plugin ../../Debug/Source/Passes/libQirAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll + +run-replace: build-ir build-qaa build-esa analysis-example.ll +# opt -loop-unroll -unroll-count=3 -unroll-allow-partial + opt -load-pass-plugin ../../Debug/Source/Passes/libQirAllocationAnalysis.dylib \ + -load-pass-plugin ../../Debug/Source/Passes/libExpandStaticAllocation.dylib --passes="expand-static-allocation" -S analysis-example.ll > analysis-example-step1.ll + opt -load-pass-plugin ../../Debug/Source/Passes/libTransformationRule.dylib --passes="loop-simplify,loop-unroll,restrict-qir" -S analysis-example-step1.ll > analysis-example-final.ll + opt --passes="inline" -S test2.ll | opt -O1 -S build-prepare: pushd ../../ && mkdir -p Debug && cd Debug && cmake ..&& popd || popd build-qaa: build-prepare - pushd ../../Debug && make QubitAllocationAnalysis && popd || popd + pushd ../../Debug && make QirAllocationAnalysis && popd || popd build-esa: build-prepare pushd ../../Debug && make ExpandStaticAllocation && popd || popd +build-ir: build-prepare + pushd ../../Debug && make TransformationRule && popd || popd + analysis-example.ll: - cd ConstSizeArray && make analysis-example.ll + cd TeleportChain && make analysis-example.ll clean: - cd ConstSizeArray && make clean + cd TeleportChain && make clean rm analysis-example.ll + rm analysis-example-step1.ll + rm analysis-example-final.ll diff --git a/src/Passes/examples/QubitAllocationAnalysis/README.md b/src/Passes/examples/QubitAllocationAnalysis/README.md index 515b641ba4..e704f2a85c 100644 --- a/src/Passes/examples/QubitAllocationAnalysis/README.md +++ b/src/Passes/examples/QubitAllocationAnalysis/README.md @@ -1,4 +1,4 @@ -# QubitAllocationAnalysis +# QirAllocationAnalysis ## Quick start @@ -14,7 +14,7 @@ Running following command 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`. +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 `TeleportChain/TeleportChain.qs`. ## Detailed run @@ -26,13 +26,13 @@ cd Debug cmake .. ``` -and then compile the `QubitAllocationAnalysis`: +and then compile the `QirAllocationAnalysis`: ```sh -make QubitAllocationAnalysis +make QirAllocationAnalysis ``` -Next return `examples/QubitAllocationAnalysis` and enter the directory `ConstSizeArray` to build the QIR: +Next return `examples/QirAllocationAnalysis` and enter the directory `TeleportChain` to build the QIR: ```sh make analysis-example.ll @@ -41,20 +41,20 @@ make analysis-example.ll or execute the commands manually, ```sh -dotnet build ConstSizeArray.csproj -opt -S qir/ConstSizeArray.ll -O1 > ../analysis-example.ll +dotnet build TeleportChain.csproj +opt -S qir/TeleportChain.ll -O1 > ../analysis-example.ll make clean ``` -Returning to `examples/QubitAllocationAnalysis`, the pass can now be ran by executing: +Returning to `examples/QirAllocationAnalysis`, the pass can now be ran by executing: ```sh -opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll +opt -load-pass-plugin ../../Debug/libs/libQirAllocationAnalysis.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. +Below we will consider a few different examples. You can run them by updating the code in `TeleportChain/TeleportChain.qs` and executing `make run` from the `examples/QirAllocationAnalysis` folder subsequently. You will need to delete `analysis-example.ll` between runs. ### Trivially constant @@ -72,8 +72,8 @@ namespace Example { The corresponding QIR is: ``` -; ModuleID = 'qir/ConstSizeArray.ll' -source_filename = "qir/ConstSizeArray.ll" +; ModuleID = 'qir/TeleportChain.ll' +source_filename = "qir/TeleportChain.ll" %Array = type opaque @@ -92,7 +92,7 @@ entry: Running the pass procudes following output: ``` -opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll +opt -load-pass-plugin ../../Debug/libs/libQirAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll Example__QuantumProgram__body ==================== @@ -124,8 +124,8 @@ namespace Example { The corresponding QIR is ``` -; ModuleID = 'qir/ConstSizeArray.ll' -source_filename = "qir/ConstSizeArray.ll" +; ModuleID = 'qir/TeleportChain.ll' +source_filename = "qir/TeleportChain.ll" %Array = type opaque %String = type opaque @@ -152,7 +152,7 @@ entry: The analyser returns following output: ``` -opt -load-pass-plugin ../../Debug/libs/libQubitAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll +opt -load-pass-plugin ../../Debug/libs/libQirAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll Example__QuantumProgram__body ==================== @@ -196,7 +196,7 @@ namespace Example { 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 +opt -load-pass-plugin ../../Debug/libs/libQirAllocationAnalysis.dylib --passes="print" -disable-output analysis-example.ll Example__QuantumProgram__body ==================== diff --git a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/Makefile similarity index 63% rename from src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile rename to src/Passes/examples/QubitAllocationAnalysis/TeleportChain/Makefile index 59399d367e..e5af26dbf1 100644 --- a/src/Passes/examples/QubitAllocationAnalysis/ConstSizeArray/Makefile +++ b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/Makefile @@ -1,6 +1,6 @@ analysis-example.ll: - dotnet build ConstSizeArray.csproj - opt -S qir/ConstSizeArray.ll -O1 > ../analysis-example.ll + dotnet build TeleportChain.csproj + opt -S qir/TeleportChain.ll -O1 > ../analysis-example.ll make clean comparison: diff --git a/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.csproj b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.csproj new file mode 100644 index 0000000000..122be0c8dd --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.csproj @@ -0,0 +1,13 @@ + + + + Exe + netcoreapp3.1 + true + + + + + + + diff --git a/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.qs b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.qs new file mode 100644 index 0000000000..257b84fbd7 --- /dev/null +++ b/src/Passes/examples/QubitAllocationAnalysis/TeleportChain/TeleportChain.qs @@ -0,0 +1,40 @@ +namespace TeleportChain { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Preparation; + + operation PrepareEntangledPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl { + H(left); + CNOT(left, right); + } + + operation ApplyCorrection(src : Qubit, intermediary : Qubit, dest : Qubit) : Unit { + if (MResetZ(src) == One) { Z(dest); } + if (MResetZ(intermediary) == One) { X(dest); } + } + + operation TeleportQubitUsingPresharedEntanglement(src : Qubit, intermediary : Qubit, dest : Qubit) : Unit { + Adjoint PrepareEntangledPair(src, intermediary); + ApplyCorrection(src, intermediary, dest); + } + + @EntryPoint() + operation DemonstrateTeleportationUsingPresharedEntanglement() : Result { + let nPairs = 2; + use (leftMessage, rightMessage, leftPreshared, rightPreshared) = (Qubit(), Qubit(), Qubit[nPairs], Qubit[nPairs]); + PrepareEntangledPair(leftMessage, rightMessage); + for i in 0..nPairs-1 { + PrepareEntangledPair(leftPreshared[i], rightPreshared[i]); + } + + TeleportQubitUsingPresharedEntanglement(rightMessage, leftPreshared[0], rightPreshared[0]); + for i in 1..nPairs-1 { + TeleportQubitUsingPresharedEntanglement(rightPreshared[i-1], leftPreshared[i], rightPreshared[i]); + } + + let _ = MResetZ(leftMessage); + return MResetZ(rightPreshared[nPairs-1]); + } +} \ No newline at end of file diff --git a/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll b/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll index 6f6c98c8e0..bd51c64d95 100644 --- a/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll +++ b/src/Passes/examples/QubitAllocationAnalysis/analysis-example.ll @@ -1,438 +1,215 @@ -; ModuleID = 'qir/ConstSizeArray.ll' -source_filename = "qir/ConstSizeArray.ll" +; ModuleID = 'qir/TeleportChain.ll' +source_filename = "qir/TeleportChain.ll" -%Tuple = type opaque %Qubit = type opaque -%Array = type opaque %Result = type opaque -%Callable = type opaque +%Array = 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 void @TeleportChain__ApplyCorrection__body(%Qubit* %src, %Qubit* %intermediary, %Qubit* %dest) unnamed_addr { +entry: + %0 = call fastcc %Result* @Microsoft__Quantum__Measurement__MResetZ__body(%Qubit* %src) + %1 = call %Result* @__quantum__rt__result_get_one() + %2 = call i1 @__quantum__rt__result_equal(%Result* %0, %Result* %1) + call void @__quantum__rt__result_update_reference_count(%Result* %0, i32 -1) + br i1 %2, label %then0__1, label %continue__1 + +then0__1: ; preds = %entry + call fastcc void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %dest) + br label %continue__1 + +continue__1: ; preds = %then0__1, %entry + %3 = call fastcc %Result* @Microsoft__Quantum__Measurement__MResetZ__body(%Qubit* %intermediary) + %4 = call %Result* @__quantum__rt__result_get_one() + %5 = call i1 @__quantum__rt__result_equal(%Result* %3, %Result* %4) + call void @__quantum__rt__result_update_reference_count(%Result* %3, i32 -1) + br i1 %5, label %then0__2, label %continue__2 + +then0__2: ; preds = %continue__1 + call fastcc void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %dest) + br label %continue__2 + +continue__2: ; preds = %then0__2, %continue__1 + ret void +} -define internal fastcc %Result* @Microsoft__Quantum__Qir__Emission__M__body(%Qubit* %q) unnamed_addr { +define internal fastcc %Result* @Microsoft__Quantum__Measurement__MResetZ__body(%Qubit* %target) unnamed_addr { entry: - %0 = call %Result* @__quantum__qis__m__body(%Qubit* %q) - ret %Result* %0 + %result = call %Result* @__quantum__qis__m__body(%Qubit* %target) + call void @__quantum__qis__reset__body(%Qubit* %target) + ret %Result* %result } -declare %Result* @__quantum__qis__m__body(%Qubit*) local_unnamed_addr +declare %Result* @__quantum__rt__result_get_one() local_unnamed_addr + +declare i1 @__quantum__rt__result_equal(%Result*, %Result*) local_unnamed_addr + +declare void @__quantum__rt__result_update_reference_count(%Result*, i32) local_unnamed_addr -define internal fastcc void @Microsoft__Quantum__Qir__Emission__Majority__body(%Qubit* %a, %Qubit* %b, %Qubit* %c) unnamed_addr { +define internal fastcc void @Microsoft__Quantum__Intrinsic__Z__body(%Qubit* %qubit) 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) + call void @__quantum__qis__z(%Qubit* %qubit) 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 { +define internal fastcc void @Microsoft__Quantum__Intrinsic__X__body(%Qubit* %qubit) 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) + call void @__quantum__qis__x(%Qubit* %qubit) 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 { +define internal fastcc %Result* @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__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) + %leftMessage = call %Qubit* @__quantum__rt__qubit_allocate() + %rightMessage = call %Qubit* @__quantum__rt__qubit_allocate() + %leftPreshared = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) + call void @__quantum__rt__array_update_alias_count(%Array* %leftPreshared, i32 1) + %rightPreshared = call %Array* @__quantum__rt__qubit_allocate_array(i64 2) + call void @__quantum__rt__array_update_alias_count(%Array* %rightPreshared, i32 1) + call fastcc void @TeleportChain__PrepareEntangledPair__body(%Qubit* %leftMessage, %Qubit* %rightMessage) + %0 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %leftPreshared, 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 + %2 = load %Qubit*, %Qubit** %1, align 8 + %3 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 0) + %4 = bitcast i8* %3 to %Qubit** + %5 = load %Qubit*, %Qubit** %4, align 8 + call fastcc void @TeleportChain__PrepareEntangledPair__body(%Qubit* %2, %Qubit* %5) + %6 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %leftPreshared, i64 1) + %7 = bitcast i8* %6 to %Qubit** + %8 = load %Qubit*, %Qubit** %7, align 8 + %9 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 1) + %10 = bitcast i8* %9 to %Qubit** + %11 = load %Qubit*, %Qubit** %10, align 8 + call fastcc void @TeleportChain__PrepareEntangledPair__body(%Qubit* %8, %Qubit* %11) + %12 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %leftPreshared, i64 0) + %13 = bitcast i8* %12 to %Qubit** + %14 = load %Qubit*, %Qubit** %13, align 8 + %15 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 0) + %16 = bitcast i8* %15 to %Qubit** + %17 = load %Qubit*, %Qubit** %16, align 8 + call fastcc void @TeleportChain__TeleportQubitUsingPresharedEntanglement__body(%Qubit* %rightMessage, %Qubit* %14, %Qubit* %17) + %18 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 0) + %19 = bitcast i8* %18 to %Qubit** + %20 = load %Qubit*, %Qubit** %19, align 8 + %21 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %leftPreshared, i64 1) + %22 = bitcast i8* %21 to %Qubit** + %23 = load %Qubit*, %Qubit** %22, align 8 + %24 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 1) + %25 = bitcast i8* %24 to %Qubit** + %26 = load %Qubit*, %Qubit** %25, align 8 + call fastcc void @TeleportChain__TeleportQubitUsingPresharedEntanglement__body(%Qubit* %20, %Qubit* %23, %Qubit* %26) + %27 = call fastcc %Result* @Microsoft__Quantum__Measurement__MResetZ__body(%Qubit* %leftMessage) + %28 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %rightPreshared, i64 1) + %29 = bitcast i8* %28 to %Qubit** + %30 = load %Qubit*, %Qubit** %29, align 8 + %31 = call fastcc %Result* @Microsoft__Quantum__Measurement__MResetZ__body(%Qubit* %30) + call void @__quantum__rt__array_update_alias_count(%Array* %leftPreshared, i32 -1) + call void @__quantum__rt__array_update_alias_count(%Array* %rightPreshared, i32 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %27, i32 -1) + call void @__quantum__rt__qubit_release(%Qubit* %leftMessage) + call void @__quantum__rt__qubit_release(%Qubit* %rightMessage) + call void @__quantum__rt__qubit_release_array(%Array* %leftPreshared) + call void @__quantum__rt__qubit_release_array(%Array* %rightPreshared) + ret %Result* %31 } 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 void @__quantum__rt__qubit_release_array(%Array*) local_unnamed_addr -declare i64 @__quantum__rt__array_get_size_1d(%Array*) local_unnamed_addr +declare void @__quantum__rt__array_update_alias_count(%Array*, i32) local_unnamed_addr -define internal fastcc %Array* @Microsoft__Quantum__Qir__Emission___73da7dcac81a47ddabb1a0e30be3dfdb_ForEach__body(%Callable* %action, %Array* %array) unnamed_addr { +define internal fastcc void @TeleportChain__PrepareEntangledPair__body(%Qubit* %left, %Qubit* %right) 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 + call fastcc void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %left) + call fastcc void @Microsoft__Quantum__Intrinsic__CNOT__body(%Qubit* %left, %Qubit* %right) + ret void } -define internal void @Microsoft__Quantum__Qir__Emission__M__body__wrapper(%Tuple* nocapture readnone %capture-tuple, %Tuple* nocapture readonly %arg-tuple, %Tuple* nocapture %result-tuple) { +declare i8* @__quantum__rt__array_get_element_ptr_1d(%Array*, i64) local_unnamed_addr + +define internal fastcc void @TeleportChain__TeleportQubitUsingPresharedEntanglement__body(%Qubit* %src, %Qubit* %intermediary, %Qubit* %dest) unnamed_addr { 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 + call fastcc void @TeleportChain__PrepareEntangledPair__adj(%Qubit* %src, %Qubit* %intermediary) + call fastcc void @TeleportChain__ApplyCorrection__body(%Qubit* %src, %Qubit* %intermediary, %Qubit* %dest) ret void } -declare %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]*, [2 x void (%Tuple*, i32)*]*, %Tuple*) local_unnamed_addr +define internal fastcc void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %qubit) unnamed_addr { +entry: + call void @__quantum__qis__h(%Qubit* %qubit) + ret void +} -declare void @__quantum__rt__capture_update_reference_count(%Callable*, i32) local_unnamed_addr +define internal fastcc void @Microsoft__Quantum__Intrinsic__CNOT__body(%Qubit* %control, %Qubit* %target) unnamed_addr { +entry: + call void @__quantum__qis__cnot(%Qubit* %control, %Qubit* %target) + ret void +} -declare void @__quantum__rt__callable_update_reference_count(%Callable*, i32) local_unnamed_addr +define internal fastcc void @TeleportChain__PrepareEntangledPair__adj(%Qubit* %left, %Qubit* %right) unnamed_addr { +entry: + call fastcc void @Microsoft__Quantum__Intrinsic__CNOT__adj(%Qubit* %left, %Qubit* %right) + call fastcc void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %left) + ret void +} -declare void @__quantum__rt__capture_update_alias_count(%Callable*, i32) local_unnamed_addr +define internal fastcc void @Microsoft__Quantum__Intrinsic__CNOT__adj(%Qubit* %control, %Qubit* %target) unnamed_addr { +entry: + call fastcc void @Microsoft__Quantum__Intrinsic__CNOT__body(%Qubit* %control, %Qubit* %target) + ret void +} -declare void @__quantum__rt__callable_update_alias_count(%Callable*, i32) local_unnamed_addr +define internal fastcc void @Microsoft__Quantum__Intrinsic__H__adj(%Qubit* %qubit) unnamed_addr { +entry: + call fastcc void @Microsoft__Quantum__Intrinsic__H__body(%Qubit* %qubit) + ret void +} -declare %Array* @__quantum__rt__array_create_1d(i32, i64) local_unnamed_addr +declare void @__quantum__qis__cnot(%Qubit*, %Qubit*) local_unnamed_addr -declare void @__quantum__rt__array_update_reference_count(%Array*, i32) local_unnamed_addr +declare void @__quantum__qis__h(%Qubit*) local_unnamed_addr -declare void @__quantum__rt__callable_invoke(%Callable*, %Tuple*, %Tuple*) local_unnamed_addr +declare void @__quantum__qis__x(%Qubit*) local_unnamed_addr -declare %Array* @__quantum__rt__array_concatenate(%Array*, %Array*) local_unnamed_addr +declare void @__quantum__qis__z(%Qubit*) local_unnamed_addr -declare void @__quantum__rt__result_update_reference_count(%Result*, i32) local_unnamed_addr +declare %Result* @__quantum__qis__m__body(%Qubit*) local_unnamed_addr -declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr +declare void @__quantum__qis__reset__body(%Qubit*) local_unnamed_addr -define { i64, i8* }* @Microsoft__Quantum__Qir__Emission__RunAdder__Interop() local_unnamed_addr #0 { +define i8 @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__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 + %0 = call fastcc %Result* @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__body() + %1 = call %Result* @__quantum__rt__result_get_zero() + %2 = call i1 @__quantum__rt__result_equal(%Result* %0, %Result* %1) + %not. = xor i1 %2, true + %3 = sext i1 %not. to i8 + call void @__quantum__rt__result_update_reference_count(%Result* %0, i32 -1) + ret i8 %3 } -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 { +define void @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement() 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) + %0 = call fastcc %Result* @TeleportChain__DemonstrateTeleportationUsingPresharedEntanglement__body() + %1 = call %String* @__quantum__rt__result_to_string(%Result* %0) + call void @__quantum__rt__message(%String* %1) + call void @__quantum__rt__result_update_reference_count(%Result* %0, 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 +declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr + attributes #0 = { "InteropFriendly" } attributes #1 = { "EntryPoint" } diff --git a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp b/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp deleted file mode 100644 index 5684864d7a..0000000000 --- a/src/Passes/libs/ExpandStaticAllocation/ExpandStaticAllocation.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// 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/SPECIFICATION.md b/src/Passes/libs/ExpandStaticAllocation/SPECIFICATION.md deleted file mode 100644 index 5095eea8b3..0000000000 --- a/src/Passes/libs/ExpandStaticAllocation/SPECIFICATION.md +++ /dev/null @@ -1 +0,0 @@ -# {ExpandStaticAllocation} Specification diff --git a/src/Passes/libs/OpsCounter/OpsCounter.cpp b/src/Passes/libs/OpsCounter/OpsCounter.cpp deleted file mode 100644 index 99c2e5c41b..0000000000 --- a/src/Passes/libs/OpsCounter/OpsCounter.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Llvm.hpp" - -#include "OpsCounter/OpsCounter.hpp" - -#include -#include - -namespace microsoft -{ -namespace quantum -{ - OpsCounterAnalytics::Result OpsCounterAnalytics::run( - llvm::Function& function, - llvm::FunctionAnalysisManager& /*unused*/) - { - OpsCounterAnalytics::Result opcode_map; - for (auto& basic_block : function) - { - for (auto& instruction : basic_block) - { - /* - TODO(tfr): Enable this block once we upgrade to LLVM 12 or above - if (instruction.isDebugOrPseudoInst()) - { - continue; - } - */ - - auto name = instruction.getOpcodeName(); - - if (opcode_map.find(name) == opcode_map.end()) - { - opcode_map[instruction.getOpcodeName()] = 1; - } - else - { - opcode_map[instruction.getOpcodeName()]++; - } - } - } - - return opcode_map; - } - - OpsCounterPrinter::OpsCounterPrinter(llvm::raw_ostream& out_stream) - : out_stream_(out_stream) - { - } - - llvm::PreservedAnalyses OpsCounterPrinter::run(llvm::Function& function, llvm::FunctionAnalysisManager& fam) - { - auto& opcode_map = fam.getResult(function); - - out_stream_ << "Stats for '" << function.getName() << "'\n"; - out_stream_ << "===========================\n"; - - constexpr auto STR1 = "Opcode"; - constexpr auto STR2 = "# Used"; - out_stream_ << llvm::format("%-15s %-8s\n", STR1, STR2); - out_stream_ << "---------------------------" - << "\n"; - - for (auto const& instruction : opcode_map) - { - out_stream_ << llvm::format("%-15s %-8lu\n", instruction.first().str().c_str(), instruction.second); - } - out_stream_ << "---------------------------" - << "\n\n"; - - return llvm::PreservedAnalyses::all(); - } - - bool OpsCounterPrinter::isRequired() - { - return true; - } - - llvm::AnalysisKey OpsCounterAnalytics::Key; - -} // namespace quantum -} // namespace microsoft diff --git a/src/Passes/libs/OpsCounter/OpsCounter.hpp b/src/Passes/libs/OpsCounter/OpsCounter.hpp deleted file mode 100644 index 09f73362f1..0000000000 --- a/src/Passes/libs/OpsCounter/OpsCounter.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Llvm.hpp" - -namespace microsoft -{ -namespace quantum -{ - - class OpsCounterAnalytics : public llvm::AnalysisInfoMixin - { - public: - using Result = llvm::StringMap; - - /// Constructors and destructors - /// @{ - OpsCounterAnalytics() = default; - OpsCounterAnalytics(OpsCounterAnalytics const&) = delete; - OpsCounterAnalytics(OpsCounterAnalytics&&) = default; - ~OpsCounterAnalytics() = default; - /// @} - - /// Operators - /// @{ - OpsCounterAnalytics& operator=(OpsCounterAnalytics const&) = delete; - OpsCounterAnalytics& operator=(OpsCounterAnalytics&&) = delete; - /// @} - - /// Functions required by LLVM - /// @{ - Result run(llvm::Function& function, llvm::FunctionAnalysisManager& /*unused*/); - /// @} - - private: - static llvm::AnalysisKey Key; // NOLINT - friend struct llvm::AnalysisInfoMixin; - }; - - class OpsCounterPrinter : public llvm::PassInfoMixin - { - public: - /// Constructors and destructors - /// @{ - explicit OpsCounterPrinter(llvm::raw_ostream& out_stream); - OpsCounterPrinter() = delete; - OpsCounterPrinter(OpsCounterPrinter const&) = delete; - OpsCounterPrinter(OpsCounterPrinter&&) = default; - ~OpsCounterPrinter() = default; - /// @} - - /// Operators - /// @{ - OpsCounterPrinter& operator=(OpsCounterPrinter const&) = delete; - OpsCounterPrinter& operator=(OpsCounterPrinter&&) = 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/OpsCounter/SPECIFICATION.md b/src/Passes/libs/OpsCounter/SPECIFICATION.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp b/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp deleted file mode 100644 index 991bcd2f08..0000000000 --- a/src/Passes/libs/QubitAllocationAnalysis/LibQubitAllocationAnalysis.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// 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 deleted file mode 100644 index a3f130e591..0000000000 --- a/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// 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 - /* - TODO(tfr): enable in LLVM12 - 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 deleted file mode 100644 index 215388be56..0000000000 --- a/src/Passes/libs/QubitAllocationAnalysis/QubitAllocationAnalysis.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#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 deleted file mode 100644 index 4c2781d605..0000000000 --- a/src/Passes/libs/QubitAllocationAnalysis/SPECIFICATION.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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/site-packages/TasksCI/cli.py b/src/Passes/site-packages/TasksCI/cli.py index c9bc93c524..748f0cd56d 100644 --- a/src/Passes/site-packages/TasksCI/cli.py +++ b/src/Passes/site-packages/TasksCI/cli.py @@ -156,7 +156,7 @@ def create_pass(name: str, template: OptionalStr) -> None: """ # Checking whether the target already exists - target_dir = os.path.join(SOURCE_DIR, "libs", name) + target_dir = os.path.join(SOURCE_DIR, "Source", name) if os.path.exists(target_dir): logger.error("Pass '{}' already exists".format(name)) exit(-1) diff --git a/src/Passes/site-packages/TasksCI/formatting.py b/src/Passes/site-packages/TasksCI/formatting.py index ae00834177..6250d94f66 100644 --- a/src/Passes/site-packages/TasksCI/formatting.py +++ b/src/Passes/site-packages/TasksCI/formatting.py @@ -107,7 +107,7 @@ def enforce_formatting(filename: str, contents: str, cursor: int, fix_issues: bo SOURCE_PIPELINES = [ { "name": "C++ Main", - "src": path.join(PROJECT_ROOT, "libs"), + "src": path.join(PROJECT_ROOT, "Source"), "pipelines": { "hpp": [ diff --git a/src/Passes/site-packages/TasksCI/linting.py b/src/Passes/site-packages/TasksCI/linting.py index b8313bcb4b..61eb2d98dd 100644 --- a/src/Passes/site-packages/TasksCI/linting.py +++ b/src/Passes/site-packages/TasksCI/linting.py @@ -54,7 +54,7 @@ def run_clang_tidy(build_dir: str, filename: str, fix_issues: bool = False) -> b cmd = [clang_tidy_binary] output_file = os.path.abspath(os.path.join(build_dir, 'clang_tidy_fixes.yaml')) - cmd.append('-header-filter=".*\\/(Passes)\\/(libs)\\/.*"') + cmd.append('-header-filter=".*\\/(Passes)\\/(Source)\\/.*"') cmd.append('-p=' + build_dir) cmd.append('-export-fixes={}'.format(output_file)) cmd.append('--use-color') @@ -104,7 +104,7 @@ def main_cpp(fix_issues: bool) -> bool: logger.info("Linting") build_dir = os.path.join(PROJECT_ROOT, "Debug") - source_dir = os.path.join(PROJECT_ROOT, "libs") + source_dir = os.path.join(PROJECT_ROOT, "Source") generator = None extensions = ["cpp"] diff --git a/src/Passes/tests/QubitAllocationAnalysis/case1.ll b/src/Passes/tests/QubitAllocationAnalysis/case1.deprecated similarity index 65% rename from src/Passes/tests/QubitAllocationAnalysis/case1.ll rename to src/Passes/tests/QubitAllocationAnalysis/case1.deprecated index cab5557980..230a3dac66 100644 --- a/src/Passes/tests/QubitAllocationAnalysis/case1.ll +++ b/src/Passes/tests/QubitAllocationAnalysis/case1.deprecated @@ -1,4 +1,4 @@ -; RUN: opt -load-pass-plugin %shlibdir/libQubitAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-1.ll -disable-output 2>&1\ +; RUN: opt -load-pass-plugin %shlibdir/libQirAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-1.ll -disable-output 2>&1\ ; RUN: | FileCheck %s ;------------------------------------------------------------------------------ diff --git a/src/Passes/tests/QubitAllocationAnalysis/case2.ll b/src/Passes/tests/QubitAllocationAnalysis/case2.deprecrated similarity index 75% rename from src/Passes/tests/QubitAllocationAnalysis/case2.ll rename to src/Passes/tests/QubitAllocationAnalysis/case2.deprecrated index 7f90c61a50..3a13a71027 100644 --- a/src/Passes/tests/QubitAllocationAnalysis/case2.ll +++ b/src/Passes/tests/QubitAllocationAnalysis/case2.deprecrated @@ -1,4 +1,4 @@ -; RUN: opt -load-pass-plugin %shlibdir/libQubitAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-2.ll -disable-output 2>&1\ +; RUN: opt -load-pass-plugin %shlibdir/libQirAllocationAnalysis%shlibext -passes="print" %S/inputs/static-qubit-arrays-2.ll -disable-output 2>&1\ ; RUN: | FileCheck %s ;------------------------------------------------------------------------------ diff --git a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.deprecated similarity index 95% rename from src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll rename to src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.deprecated index ea4ead0400..5c9c6ade0c 100644 --- a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.ll +++ b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-1.deprecated @@ -1,5 +1,5 @@ -; ModuleID = 'qir/ConstSizeArray.ll' -source_filename = "qir/ConstSizeArray.ll" +; ModuleID = 'qir/TeleportChain.ll' +source_filename = "qir/TeleportChain.ll" %Array = type opaque %String = type opaque diff --git a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.deprecated similarity index 97% rename from src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll rename to src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.deprecated index b87010d9d2..2ac826a845 100644 --- a/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.ll +++ b/src/Passes/tests/QubitAllocationAnalysis/inputs/static-qubit-arrays-2.deprecated @@ -1,5 +1,5 @@ -; ModuleID = 'qir/ConstSizeArray.ll' -source_filename = "qir/ConstSizeArray.ll" +; ModuleID = 'qir/TeleportChain.ll' +source_filename = "qir/TeleportChain.ll" %Array = type opaque %String = type opaque