Unverified Commit 85894c95 authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #171 from tnguyen-ornl/tnguyen/mlir-value-semantics

Added MLIR optimization passes and code refactor
parents 9a14001d 09c51bf3
......@@ -152,4 +152,24 @@ def CreateStringLiteralOp : QuantumOp<"createString", []> {
p << "q.create_string(\"" << op.text() << "\")"; }];
}
// Cast QIR Result to bool (i1 type)
def ResultCastOp : QuantumOp<"resultCast", []> {
let arguments = (ins ResultType:$measure_result);
let results = (outs I1:$bit_result);
let printer = [{ auto op = *this;
p << "q.resultCast" << "(" << op.measure_result() << ") : " << op.bit_result().getType(); }];
}
// Sign-Unsign cast:
// Rationale: std dialect only accepts signless type (i.e. int but not uint)
// we need to have this cast op in the dialect to finally lower to LLVM cast
// which can handle int -> uint casting at the final lowering phase.
// Note: std.index_cast cannot handle int -> unit casting (one of the type must be an index type).
def IntegerCastOp : QuantumOp<"integerCast", []> {
let arguments = (ins AnyInteger:$input);
let results = (outs AnyInteger:$output);
let printer = [{ auto op = *this;
p << "q.integerCast" << "(" << op.input() << ") : " << op.output().getType(); }];
}
#endif // Quantum_OPS
\ No newline at end of file
......@@ -2,10 +2,43 @@
#include "Quantum/QuantumDialect.h"
#include "Quantum/QuantumOps.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/Transforms/InliningUtils.h"
using namespace mlir;
using namespace mlir::quantum;
namespace {
/// Inliner interface
/// This class defines the interface for handling inlining with Quantum
/// operations.
// We simplify inherit from the base interface class and override
/// the necessary methods.
struct QuantumInlinerInterface : public DialectInlinerInterface {
using DialectInlinerInterface::DialectInlinerInterface;
/// This hook checks to see if the given callable operation is legal to inline
/// into the given call.
/// Operations in Quantum dialect are always legal to inline.
bool isLegalToInline(Operation *call, Operation *callable,
bool wouldBeCloned) const final {
return true;
}
/// This hook checks to see if the given operation is legal to inline into the
/// given region.
/// Only inline VSOp for now:
// FIXME: there is a weird error when qalloc is inlined at MLIR level
// hence, just allow VSOp to be inlined for the timebeing.
// i.e. all quantum subroutines that only contain VSOp's can be inlined.
bool isLegalToInline(Operation *op, Region *regione, bool,
BlockAndValueMapping &) const final {
if (dyn_cast_or_null<mlir::quantum::ValueSemanticsInstOp>(op)) {
return true;
}
return false;
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Quantum dialect.
//===----------------------------------------------------------------------===//
......@@ -15,12 +48,13 @@ void QuantumDialect::initialize() {
#define GET_OP_LIST
#include "Quantum/QuantumOps.cpp.inc"
>();
addInterfaces<QuantumInlinerInterface>();
}
// static void print(mlir::OpAsmPrinter &printer, mlir::quantum::InstOp op) {
// printer << "q." << op.name() << "(" << *(op.qubits().begin());
// for (auto i = 1; i < op.qubits().size(); i++) {
// printer << ", " << op.qubits()[i];
// printer << ", " << op.qubits()[i];
// }
// printer << ")";
......
......@@ -3,14 +3,12 @@
#include "Quantum/QuantumDialect.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Builders.h"
bool isOpaqueTypeWithName(mlir::Type type, std::string dialect,
std::string type_name) {
if (type.isa<mlir::OpaqueType>() && dialect == "quantum") {
if (type_name == "Qubit") {
return true;
}
if (type_name == "Result") {
if (type_name == "Qubit" || type_name == "Result" || type_name == "Array" ||
type_name == "ArgvType" || type_name == "QregType" ||
type_name == "StringType") {
return true;
}
}
......
OPENQASM 3;
include "qelib1.inc";
const n = 2;
qubit q[n];
x q[0];
ry(1.2345) q[1];
y q[1];
x q[1];
\ No newline at end of file
OPENQASM 3;
include "qelib1.inc";
const n = 2;
qubit q[n];
// Rz can be moved forward (pass CX) to combine with z
rz(1.2345) q[0];
cx q[0], q[1];
z q[0];
......@@ -7,6 +7,7 @@
#include "openqasmv3_mlir_generator.hpp"
#include "qasm3_handler_utils.hpp"
#include "quantum_to_llvm.hpp"
#include "pass_manager.hpp"
using namespace clang;
......@@ -129,6 +130,7 @@ void Qasm3SyntaxHandler::GetReplacement(Preprocessor &PP, Declarator &D,
std::vector<std::string> unique_f_names{kernel_name};
mlir::PassManager pm(&context);
applyPassManagerCLOptions(pm);
qcor::configureOptimizationPasses(pm);
pm.addPass(std::make_unique<qcor::QuantumToLLVMLoweringPass>(true, unique_f_names));
auto module_op = (*module).getOperation();
if (mlir::failed(pm.run(module_op))) {
......
......@@ -38,7 +38,7 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
mlir::Identifier dialect = mlir::Identifier::get("quantum", context);
qubit_type = mlir::OpaqueType::get(context, dialect, qubit_type_name);
array_type = mlir::OpaqueType::get(context, dialect, array_type_name);
result_type = mlir::IntegerType::get(context, 1);
result_type = mlir::OpaqueType::get(context, dialect, result_type_name);
symbol_table.set_op_builder(builder);
}
......@@ -218,10 +218,18 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
"Cannot allocate and initialize memory, shape and number of initial "
"value indices is incorrect");
}
// Assert that the values to init the memref array
// must be of the expected type.
for (const auto &init_val : initial_values) {
assert(init_val.getType() == type);
}
// Allocate
auto allocation = allocate_1d_memory(location, shape, type);
// and initialize
for (int i = 0; i < initial_values.size(); i++) {
assert(initial_indices[i].getType().isa<mlir::IndexType>());
builder.create<mlir::StoreOp>(location, initial_values[i], allocation,
initial_indices[i]);
}
......
......@@ -86,3 +86,9 @@ add_executable(qasm3CompilerTester_Kernel test_kernel.cpp)
add_test(NAME qcor_qasm3_test_kernel COMMAND qasm3CompilerTester_Kernel)
target_include_directories(qasm3CompilerTester_Kernel PRIVATE . ../../ ${CMAKE_SOURCE_DIR}/tools/clang-wrapper/ ${CMAKE_SOURCE_DIR}/handlers ${XACC_ROOT}/include/gtest)
target_link_libraries(qasm3CompilerTester_Kernel qcor-mlir-api gtest gtest_main qcor-clang-wrapper qcor-syntax-handler ${CLANG_LIBS})
add_executable(qasm3CompilerTester_PassManager test_optimization.cpp)
add_test(NAME qcor_qasm3_test_pass_mamager COMMAND qasm3CompilerTester_PassManager)
target_include_directories(qasm3CompilerTester_PassManager PRIVATE . ../../ ${CMAKE_SOURCE_DIR}/tools/clang-wrapper/ ${CMAKE_SOURCE_DIR}/handlers ${XACC_ROOT}/include/gtest)
target_link_libraries(qasm3CompilerTester_PassManager qcor-mlir-api gtest gtest_main qcor-clang-wrapper qcor-syntax-handler ${CLANG_LIBS})
......@@ -215,10 +215,10 @@ QCOR_EXPECT_TRUE(m10[3] == 0);
QCOR_EXPECT_TRUE(m10[4] == 0);
QCOR_EXPECT_TRUE(m10[5] == 1);
)#";
auto mlir = qcor::mlir_compile("qasm3", alias_by_indicies, "test",
auto mlir = qcor::mlir_compile(alias_by_indicies, "test",
qcor::OutputType::MLIR, true);
std::cout << "MLIR:\n" << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", alias_by_indicies, "test"));
EXPECT_FALSE(qcor::execute(alias_by_indicies, "test"));
}
int main(int argc, char **argv) {
......
......@@ -70,9 +70,9 @@ QCOR_EXPECT_TRUE(ff == 3.14);
)#";
auto mlir =
qcor::mlir_compile("qasm3", src, "test", qcor::OutputType::MLIR, true);
qcor::mlir_compile(src, "test", qcor::OutputType::MLIR, true);
std::cout << "MLIR:\n" << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", src, "test"));
EXPECT_FALSE(qcor::execute(src, "test"));
}
int main(int argc, char **argv) {
......
......@@ -21,12 +21,12 @@ for i in [0:100] {
print(count);
QCOR_EXPECT_TRUE(count > 30);
)#";
auto mlir = qcor::mlir_compile("qasm3", check_pow, "check_pow",
auto mlir = qcor::mlir_compile(check_pow, "check_pow",
qcor::OutputType::MLIR, false);
std::cout << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", check_pow, "check_pow"));
EXPECT_FALSE(qcor::execute(check_pow, "check_pow"));
}
int main(int argc, char **argv) {
......
......@@ -19,10 +19,10 @@ QCOR_EXPECT_TRUE(b3 == 1);
QCOR_EXPECT_TRUE(b4 == 1);
)#";
auto mlir = qcor::mlir_compile("qasm3", uint_index, "uint_index",
auto mlir = qcor::mlir_compile(uint_index, "uint_index",
qcor::OutputType::MLIR, false);
std::cout << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", uint_index, "uint_index"));
EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}
TEST(qasm3VisitorTester, checkCastBitToInt) {
......@@ -34,12 +34,12 @@ int[4] t = int[4](c);
print(t);
QCOR_EXPECT_TRUE(t == 15);
)#";
auto mlir = qcor::mlir_compile("qasm3", cast_int, "cast_int",
auto mlir = qcor::mlir_compile(cast_int, "cast_int",
qcor::OutputType::MLIR, false);
std::cout << "cast_int MLIR:\n" << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", cast_int, "cast_int"));
EXPECT_FALSE(qcor::execute(cast_int, "cast_int"));
}
int main(int argc, char **argv) {
......
......@@ -12,10 +12,10 @@ result = (shots - num_parity_ones) / shots - num_parity_ones / shots;
test = result - .007812;
QCOR_EXPECT_TRUE(test < .01);
)#";
auto mlir = qcor::mlir_compile("qasm3", global_const, "global_const",
auto mlir = qcor::mlir_compile(global_const, "global_const",
qcor::OutputType::MLIR, false);
std::cout << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", global_const, "global_const"));
EXPECT_FALSE(qcor::execute(global_const, "global_const"));
}
int main(int argc, char **argv) {
......
......@@ -27,10 +27,10 @@ QCOR_EXPECT_TRUE(hit_continue_value == 2);
print("made it out of the loop");
)#";
auto mlir = qcor::mlir_compile("qasm3", uint_index, "uint_index",
auto mlir = qcor::mlir_compile(uint_index, "uint_index",
qcor::OutputType::MLIR, false);
std::cout << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", uint_index, "uint_index"));
EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}
......
......@@ -46,15 +46,15 @@ float[64] test = 3.14 - f;
QCOR_EXPECT_TRUE(test < .001);
)#";
auto mlir =
qcor::mlir_compile("qasm3", src, "test", qcor::OutputType::MLIR, true);
qcor::mlir_compile(src, "test", qcor::OutputType::MLIR, true);
std::cout << "MLIR:\n" << mlir << "\n";
auto llvmi =
qcor::mlir_compile("qasm3", src, "test", qcor::OutputType::LLVMMLIR, true);
qcor::mlir_compile(src, "test", qcor::OutputType::LLVMMLIR, true);
std::cout << "LLVM:\n" << llvmi << "\n";
auto llvm =
qcor::mlir_compile("qasm3", src, "test", qcor::OutputType::LLVMIR, true);
qcor::mlir_compile(src, "test", qcor::OutputType::LLVMIR, true);
std::cout << "LLVM:\n" << llvm << "\n";
EXPECT_FALSE(qcor::execute("qasm3", src, "test"));
EXPECT_FALSE(qcor::execute(src, "test"));
const std::string src2 = R"#(OPENQASM 3;
include "qelib1.inc";
......@@ -65,9 +65,9 @@ for i in [0:22] {
}
)#";
auto mlir2 =
qcor::mlir_compile("qasm3", src2, "test", qcor::OutputType::MLIR, true);
qcor::mlir_compile(src2, "test", qcor::OutputType::MLIR, true);
std::cout << "MLIR:\n" << mlir2 << "\n";
EXPECT_TRUE(qcor::execute("qasm3", src2, "test"));
EXPECT_TRUE(qcor::execute(src2, "test"));
}
......
......@@ -24,10 +24,10 @@ print(d);
)#";
auto mlir =
qcor::mlir_compile("qasm3", src, "test", qcor::OutputType::MLIR, true);
qcor::mlir_compile(src, "test", qcor::OutputType::MLIR, true);
std::cout << "MLIR:\n" << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", src, "test"));
EXPECT_FALSE(qcor::execute(src, "test"));
}
......
......@@ -20,11 +20,11 @@ print(j);
QCOR_EXPECT_TRUE(j == 20);
)#";
auto mlir = qcor::mlir_compile("qasm3", kernel_test, "kernel_test",
auto mlir = qcor::mlir_compile(kernel_test, "kernel_test",
qcor::OutputType::MLIR, true);
std::cout << mlir << "\n";
auto llvm = qcor::mlir_compile("qasm3", kernel_test, "kernel_test",
auto llvm = qcor::mlir_compile(kernel_test, "kernel_test",
qcor::OutputType::LLVMIR, true);
std::cout << llvm << "\n";
......@@ -43,7 +43,7 @@ int test_this(int i) { return i + 10; }
extra_code_to_link.push_back(std::move(module));
// -------------------------------------------//
EXPECT_FALSE(qcor::execute("qasm3", kernel_test, "kernel_test", extra_code_to_link, 0));
EXPECT_FALSE(qcor::execute(kernel_test, "kernel_test", extra_code_to_link, 0));
}
int main(int argc, char **argv) {
......
......@@ -47,10 +47,10 @@ QCOR_EXPECT_TRUE(loop_count == 12);
)#";
auto mlir = qcor::mlir_compile("qasm3", for_stmt, "for_stmt",
auto mlir = qcor::mlir_compile(for_stmt, "for_stmt",
qcor::OutputType::MLIR, false);
std::cout << "for_stmt MLIR:\n" << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", for_stmt, "for_stmt"));
EXPECT_FALSE(qcor::execute(for_stmt, "for_stmt"));
const std::string while_stmt = R"#(OPENQASM 3;
include "qelib1.inc";
......@@ -61,10 +61,10 @@ while (i < 10) {
}
QCOR_EXPECT_TRUE(i == 10);
)#";
auto mlir2 = qcor::mlir_compile("qasm3", while_stmt, "while_stmt",
auto mlir2 = qcor::mlir_compile(while_stmt, "while_stmt",
qcor::OutputType::MLIR, false);
std::cout << mlir2 << "\n";
EXPECT_FALSE(qcor::execute("qasm3", while_stmt, "while_stmt"));
EXPECT_FALSE(qcor::execute(while_stmt, "while_stmt"));
const std::string decrement = R"#(OPENQASM 3;
include "qelib1.inc";
......@@ -72,10 +72,10 @@ for j in [10:-1:0] {
print(j);
}
)#";
auto mlir3 = qcor::mlir_compile("qasm3", decrement, "decrement",
auto mlir3 = qcor::mlir_compile(decrement, "decrement",
qcor::OutputType::MLIR, false);
std::cout << mlir3 << "\n";
EXPECT_FALSE(qcor::execute("qasm3", decrement, "decrement"));
EXPECT_FALSE(qcor::execute(decrement, "decrement"));
}
int main(int argc, char **argv) {
......
......@@ -71,12 +71,12 @@ for i in [0:100] {
print(count);
)#";
auto mlir = qcor::mlir_compile("qasm3", check_pow, "check_pow",
auto mlir = qcor::mlir_compile(check_pow, "check_pow",
qcor::OutputType::MLIR, false);
std::cout << mlir << "\n";
EXPECT_FALSE(qcor::execute("qasm3", check_pow, "check_pow"));
EXPECT_FALSE(qcor::execute(check_pow, "check_pow"));
}
int main(int argc, char **argv) {
......
#include "qcor_mlir_api.hpp"
#include "gtest/gtest.h"
namespace {
// returns count of non-overlapping occurrences of 'sub' in 'str'
int countSubstring(const std::string &str, const std::string &sub) {
if (sub.length() == 0)
return 0;
int count = 0;
for (size_t offset = str.find(sub); offset != std::string::npos;
offset = str.find(sub, offset + sub.length())) {
++count;
}
return count;
}
} // namespace
TEST(qasm3PassManagerTester, checkIdentityPairRemoval) {
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
qubit q[2];
x q[0];
x q[0];
cx q[0], q[1];
cx q[0], q[1];
)#";
auto llvm =
qcor::mlir_compile(src, "test", qcor::OutputType::LLVMIR, true);
std::cout << "LLVM:\n" << llvm << "\n";
// No instrucions left
EXPECT_TRUE(llvm.find("__quantum__qis") == std::string::npos);
}
TEST(qasm3PassManagerTester, checkRotationMerge) {
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
qubit q[2];
x q[0];
z q[1];
rx(1.2345) q[0];
rz(2.4566) q[1];
)#";
auto llvm =
qcor::mlir_compile(src, "test_kernel", qcor::OutputType::LLVMIR, false);
std::cout << "LLVM:\n" << llvm << "\n";
// Get the function LLVM only (not __internal_mlir_XXXX, etc.)
llvm = llvm.substr(llvm.find("@test_kernel"));
// 2 instrucions left: rx and rz
EXPECT_EQ(countSubstring(llvm, "__quantum__qis"), 2);
EXPECT_EQ(countSubstring(llvm, "__quantum__qis__rx"), 1);
EXPECT_EQ(countSubstring(llvm, "__quantum__qis__rz"), 1);
}
TEST(qasm3PassManagerTester, checkSingleQubitGateMergeOpt) {
// Merge to X == rx(pi)
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
qubit q[2];
h q[0];
z q[0];
h q[0];
)#";
auto llvm =
qcor::mlir_compile(src, "test_kernel", qcor::OutputType::LLVMIR, false);
std::cout << "LLVM:\n" << llvm << "\n";
// Get the function LLVM only (not __internal_mlir_XXXX, etc.)
llvm = llvm.substr(llvm.find("@test_kernel"));
// 1 instrucions left: rx
EXPECT_EQ(countSubstring(llvm, "__quantum__qis"), 1);
EXPECT_EQ(countSubstring(llvm, "__quantum__qis__rx"), 1);
}
TEST(qasm3PassManagerTester, checkRemoveUnusedQirCalls) {
// Complete cancellation => remove extract and qalloc as well
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
qubit q[2];
cx q[0], q[1];
h q[0];
z q[0];
h q[0];
x q[0];
cx q[0], q[1];
)#";
auto llvm =
qcor::mlir_compile(src, "test_kernel", qcor::OutputType::LLVMIR, false);
std::cout << "LLVM:\n" << llvm << "\n";
// No gates, extract, or alloc/dealloc:
EXPECT_EQ(countSubstring(llvm, "__quantum__qis"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__array_get_element_ptr_1d"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_allocate_array"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_release_array"), 0);
}
TEST(qasm3PassManagerTester, checkInliner) {
// Inline a call ==> gate cancellation
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
gate oracle b {
x b;
}
qubit q[2];
x q[0];
oracle q[0];
)#";
auto llvm =
qcor::mlir_compile(src, "test_kernel", qcor::OutputType::LLVMIR, false);
std::cout << "LLVM:\n" << llvm << "\n";
// Get the main kernel section only (there is the oracle LLVM section as well)
llvm = llvm.substr(llvm.find("@test_kernel"));
const auto last = llvm.find_first_of("}");
llvm = llvm.substr(0, last + 1);
std::cout << "LLVM:\n" << llvm << "\n";
// No gates, extract, or alloc/dealloc:
EXPECT_EQ(countSubstring(llvm, "__quantum__qis"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__array_get_element_ptr_1d"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_allocate_array"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_release_array"), 0);
}
TEST(qasm3PassManagerTester, checkPermuteAndCancel) {
// Permute rz-cnot ==> gate cancellation
const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
qubit q[2];
rz(0.123) q[0];
cx q[0], q[1];
rz(-0.123) q[0];
cx q[0], q[1];
)#";
auto llvm =
qcor::mlir_compile(src, "test_kernel", qcor::OutputType::LLVMIR, false);
std::cout << "LLVM:\n" << llvm << "\n";
// Get the main kernel section only (there is the oracle LLVM section as well)
llvm = llvm.substr(llvm.find("@test_kernel"));
const auto last = llvm.find_first_of("}");
llvm = llvm.substr(0, last + 1);
std::cout << "LLVM:\n" << llvm << "\n";
// Cancel all => No gates, extract, or alloc/dealloc:
EXPECT_EQ(countSubstring(llvm, "__quantum__qis"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__array_get_element_ptr_1d"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_allocate_array"), 0);
EXPECT_EQ(countSubstring(llvm, "__quantum__rt__qubit_release_array"), 0);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
return ret;
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment