Unverified Commit 9b6910e0 authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #213 from tnguyen-ornl/tnguyen/update-qasm3

OpenQASM3 compiler update
parents 75d9e8ad b5045f85
Loading
Loading
Loading
Loading
Loading
+1 −11
Original line number Diff line number Diff line
@@ -6,17 +6,7 @@ if (APPLE)
  set(ANTLR_LIB ${XACC_ROOT}/lib/libantlr4-runtime.dylib)
endif()

file(GLOB SRC *.cpp antlr/generated/*.cpp utils/*.cpp 
   visitor_handlers/quantum_types_handler.cpp
   visitor_handlers/quantum_instruction_handler.cpp
   visitor_handlers/classical_types_handler.cpp
   visitor_handlers/measurement_handler.cpp
   visitor_handlers/loop_stmt_handler.cpp
   visitor_handlers/conditional_handler.cpp
   visitor_handlers/subroutine_handler.cpp
   visitor_handlers/alias_handler.cpp
   visitor_handlers/compute_action_handler.cpp
   )
file(GLOB SRC *.cpp antlr/generated/*.cpp utils/*.cpp visitor_handlers/*.cpp)

add_library(${LIBRARY_NAME} SHARED ${SRC})
target_compile_features(${LIBRARY_NAME} 
+40 −62
Original line number Diff line number Diff line
@@ -114,64 +114,9 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
  // Visit the compute-action-uncompute expression
  antlrcpp::Any visitCompute_action_stmt(qasm3Parser::Compute_action_stmtContext *context) override;

  // QCOR_EXPECT_TRUE handler
  antlrcpp::Any visitQcor_test_statement(
      qasm3Parser::Qcor_test_statementContext* context) override {
    auto location = get_location(builder, file_name, context);

    auto boolean_expr = context->booleanExpression();
    qasm3_expression_generator exp_generator(builder, symbol_table, file_name);
    exp_generator.visit(boolean_expr);
    auto expr_value = exp_generator.current_value;

    // So we have a conditional result, want
    // to negate it and see if == true
    expr_value = builder.create<mlir::CmpIOp>(
        location, mlir::CmpIPredicate::ne, expr_value,
        get_or_create_constant_integer_value(1, location, builder.getI1Type(),
                                             symbol_table, builder));

    auto currRegion = builder.getBlock()->getParent();
    auto savept = builder.saveInsertionPoint();
    auto thenBlock = builder.createBlock(currRegion, currRegion->end());
    mlir::Block* exitBlock = builder.createBlock(currRegion, currRegion->end());

    // Build up the THEN Block
    builder.setInsertionPointToStart(thenBlock);

    auto sl = "QCOR Test Failure: " + context->getText() + "\n";
    llvm::StringRef string_type_name("StringType");
    mlir::Identifier dialect =
        mlir::Identifier::get("quantum", builder.getContext());
    auto str_type =
        mlir::OpaqueType::get(builder.getContext(), dialect, string_type_name);
    auto str_attr = builder.getStringAttr(sl);

    std::hash<std::string> hasher;
    auto hash = hasher(sl);
    std::stringstream ss;
    ss << "__internal_string_literal__" << hash;
    std::string var_name = ss.str();
    auto var_name_attr = builder.getStringAttr(var_name);

    auto string_literal = builder.create<mlir::quantum::CreateStringLiteralOp>(
        location, str_type, str_attr, var_name_attr);
    builder.create<mlir::quantum::PrintOp>(
        location, llvm::makeArrayRef(std::vector<mlir::Value>{string_literal}));

    auto integer_attr = mlir::IntegerAttr::get(builder.getI32Type(), 1);
    auto ret = builder.create<mlir::ConstantOp>(location, integer_attr);
    builder.create<mlir::ReturnOp>(location, llvm::ArrayRef<mlir::Value>(ret));

    // Restore the insertion point and create the conditional statement
    builder.restoreInsertionPoint(savept);
    builder.create<mlir::CondBranchOp>(location, expr_value, thenBlock,
                                       exitBlock);
    builder.setInsertionPointToStart(exitBlock);

    symbol_table.set_last_created_block(exitBlock);

    return 0;
  }
      qasm3Parser::Qcor_test_statementContext *context) override;

  antlrcpp::Any visitPragma(qasm3Parser::PragmaContext *ctx) override {
    // Handle the #pragma { export; } directive
@@ -192,11 +137,6 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
  mlir::ModuleOp m_module;
  std::string file_name = "";
  bool enable_nisq_ifelse = false;  
  // We keep reference to these blocks so that
  // we can handle break/continue correctly
  mlir::Block* current_loop_exit_block;
  mlir::Block* current_loop_header_block;
  mlir::Block* current_loop_incrementor_block;

  // The symbol table, keeps track of current scope
  ScopedSymbolTable symbol_table;
@@ -216,6 +156,24 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
  mlir::Type array_type;
  mlir::Type result_type;

  // Loop control vars for break/continue implementation with Region-based
  // Affine/SCF Ops.
  // Strategy:
  /// - A break-able for loop will have a bool (first in the pair) to control
  /// the loop body execution. i.e., bypass the whole loop if the break
  /// condition is triggered.
  /// - The second bool is the continue condition which will bypass all
  /// the remaining ops in the body.
  /// We use a stack to handle nested loops, which are all break-able.
  std::stack<std::pair<mlir::Value, mlir::Value>> loop_control_directive_bool_vars;

  // Early return loop control directive: return statement in the loop body.
  // This will escape all loops until the *FuncOp* body and return.
  // Note: MLIR validation will require ReturnOp in the **Region** of a FuncOp.
  // First value: the boolean to control the early return (if true)
  // Second value: the return value.
  std::optional<std::pair<mlir::Value, std::optional<mlir::Value>>>
      region_early_return_vars;
  // This method will add correct number of InstOps
  // based on quantum gate broadcasting
  void createInstOps_HandleBroadcast(std::string name,
@@ -226,6 +184,26 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor {
                                     mlir::Location location,
                                     antlr4::ParserRuleContext* context);

  // Helper to handle range-based for loop
  void createRangeBasedForLoop(qasm3Parser::LoopStatementContext *context);
  // Helper to handle set-based for loop:
  // e.g., for i in {1,4,6,7}:
  void createSetBasedForLoop(qasm3Parser::LoopStatementContext *context);
  // While loop
  void createWhileLoop(qasm3Parser::LoopStatementContext *context);
  // Insert MLIR loop break
  void insertLoopBreak(mlir::Location &location,
                       mlir::OpBuilder *optional_builder = nullptr);
  void insertLoopContinue(mlir::Location &location,
                       mlir::OpBuilder *optional_builder = nullptr);
  void handleReturnInLoop(mlir::Location &location);
  // Insert a conditional return.
  // Assert that the insert location is *returnable*
  // i.e., in the FuncOp region.
  void conditionalReturn(mlir::Location &location, mlir::Value cond,
                         mlir::Value returnVal,
                         mlir::OpBuilder *optional_builder = nullptr);

  // This function serves as a utility for creating a MemRef and
  // corresponding AllocOp of a given 1d shape. It will also store
  // initial values to all elements of the 1d array.
+330 −26
Original line number Diff line number Diff line
@@ -15,43 +15,42 @@ int countSubstring(const std::string &str, const std::string &sub) {
}
} // namespace

// Check Affine-SCF constructs
TEST(qasm3VisitorTester, checkCFG_AffineScf) {
  const std::string qasm_code = R"#(OPENQASM 3;
TEST(qasm3VisitorTester, checkCtrlDirectives) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

int[64] iterate_value = 0;
int[64] value_5 = 0;
int[64] value_2 = 0;
int[64] hit_continue_value = 0;
for i in [0:10] {
    iterate_value = i;
    if (i == 5) {
        print("Iterate over 5");
        value_5 = 5;
        print("breaking at 5");
        break;
    }
    if (i == 2) {
        print("Iterate over 2");
        value_2 = 2;
       
        hit_continue_value = i;
        print("continuing at 2");
        continue;
    }
    print("i = ", i);
}

QCOR_EXPECT_TRUE(iterate_value == 9);
QCOR_EXPECT_TRUE(value_5 == 5);
QCOR_EXPECT_TRUE(value_2 == 2);
QCOR_EXPECT_TRUE(iterate_value == 5);
QCOR_EXPECT_TRUE(hit_continue_value == 2);

print("made it out of the loop");

)#";
  auto mlir = qcor::mlir_compile(qasm_code, "affine_scf",
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  // 1 for loop, 2 if blocks
  // We're now using Affine and SCF
  EXPECT_EQ(countSubstring(mlir, "affine.for"), 1);
  EXPECT_EQ(countSubstring(mlir, "scf.if"), 2);
  EXPECT_FALSE(qcor::execute(qasm_code, "affine_scf"));
  EXPECT_GT(countSubstring(mlir, "scf.if"), 1);
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

TEST(qasm3VisitorTester, checkCtrlDirectives) {
TEST(qasm3VisitorTester, checkCtrlDirectivesComplex) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

@@ -62,20 +61,324 @@ for i in [0:10] {
    if (i == 5) {
        print("breaking at 5");
        break;
    } else {
      if (i == 3) { 
        print("breaking at 3");
        break;
      }
    }
    if (i == 2) {
      hit_continue_value = i;
      print("continuing at 2");
      continue;
    }

    if (iterate_value == 2) {
      hit_continue_value = 5;
      print("SHOULD NEVER BE HERE!!!");
    } 
    
    print("i = ", i);
}

QCOR_EXPECT_TRUE(iterate_value == 5);
QCOR_EXPECT_TRUE(hit_continue_value == 2);
// The break at 3 in the else loop will be activated first.
QCOR_EXPECT_TRUE(iterate_value == 3);
print("made it out of the loop");)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  // We're now using Affine and SCF
  EXPECT_EQ(countSubstring(mlir, "affine.for"), 1);
  EXPECT_GT(countSubstring(mlir, "scf.if"), 1);
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

print("made it out of the loop");
TEST(qasm3VisitorTester, checkCtrlDirectivesSetBasedForLoop) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

int[64] sum_value = 0;
int[64] break_value = 0;
int[64] loop_count = 0;

for val in {1,3,5,7} {
  print("iter: ", val);
  if (val < 4) {
    sum_value += val;
  } else {
    break_value = val;
    break;
  }

  loop_count += 1;
}

print(sum_value);
print(loop_count);
print(break_value);
QCOR_EXPECT_TRUE(sum_value == 4);
QCOR_EXPECT_TRUE(loop_count == 2);
QCOR_EXPECT_TRUE(break_value == 5);)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  // Make sure we're using Affine and SCF
  EXPECT_EQ(countSubstring(mlir, "affine.for"), 1);
  EXPECT_GT(countSubstring(mlir, "scf.if"), 1);
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

TEST(qasm3VisitorTester, checkCtrlDirectivesWhileLoop) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

int[32] i = 0;
int[32] j = 0;
while (i < 10) {
  // Before break
  i += 1;
  if (i == 8) {
    print("Breaking at", i);
    break;
  }
  // After break
  j += 1;
}

print("make to the end, i =", i);
print("make to the end, j =", j);
QCOR_EXPECT_TRUE(i == 8);
QCOR_EXPECT_TRUE(j == 7);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  // Make sure we're using Affine While loop
  EXPECT_EQ(countSubstring(mlir, "scf.while"), 1);
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

TEST(qasm3VisitorTester, checkEarlyReturn) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

def generate_number(int[64]: count) -> int[64] {
  for i in [0:count] {
    if (i > 10) {
      print("Return at ", i);
      return 5;
    }
    print("i =", i);
  }

  print("make it to the end");
  return 1;  
}

int[64] val1 = generate_number(4);
print("Result 1 =", val1);
QCOR_EXPECT_TRUE(val1 == 1);
// Call it with 20 -> activate the early return
int[64] val2 = generate_number(20);
print("Result 2 =", val2);
QCOR_EXPECT_TRUE(val2 == 5);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

// Coverage for https://github.com/ORNL-QCI/qcor/issues/211 as well
TEST(qasm3VisitorTester, checkEarlyReturnWith211) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

def generate_number(int[64]: count) -> int[64] {
  for i in [0:count] {
    if (i > 10) {
      print("Return at ", i);
      return 3;
    }
    print("i =", i);
  }

  print("make it to the end");
  return count;  
}

int[64] arg_val = 7;
int[64] val1 = generate_number(arg_val);
print("Result 1 =", val1);
QCOR_EXPECT_TRUE(val1 == arg_val);
// Call it with 20 -> activate the early return
int[64] val2 = generate_number(20);
print("Result 2 =", val2);
QCOR_EXPECT_TRUE(val2 == 3);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

TEST(qasm3VisitorTester, checkEarlyReturnNestedLoop) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

def generate_number(int[64]: break_value, int[64]: max_run) -> int[64] {
  int[64] run_count = 0;
  for i in [0:10] {
    for j in [0:10] {
      run_count += 1;
      if (run_count > max_run) {
        print("Exceeding max run count of", max_run);
        return 3;
      }
      
      if (i == j && i > break_value) {
        print("Return at i = ", i);
        print("Return at j = ", j);
        return run_count;
      }

      print("i =", i);
      print("j =", j);
    }
    print("Out of inner loop");
  }

  print("make it to the end");
  return 0;  
}

// Case 1: run to the end.
int[64] val = generate_number(10, 100);
print("Result =", val);
QCOR_EXPECT_TRUE(val == 0);

// Case 2: Return @ (i == j && i > break_value) 
// i = 0: 10; i = 1: 10; i = 2: j = 0, 1, 2 
// => 23 runs (return run_count in this path)
val = generate_number(1, 100);
print("Result =", val);
QCOR_EXPECT_TRUE(val == 23);

// Case 3: return due to max_run limit
// limit to 20 (less than 23) => return value 3
val = generate_number(1, 20);
print("Result =", val);
QCOR_EXPECT_TRUE(val == 3);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

// Nesting for and while loops
TEST(qasm3VisitorTester, checkEarlyReturnNestedWhileLoop) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

def generate_number(int[64]: break_value, int[64]: max_run) -> int[64] {
  int[64] run_count = 0;
  int[64] i = 0;
  while(run_count < max_run) {
    for j in [0:10] {
      run_count += 1;
      if (i == j && i > break_value) {
        print("Return at i = ", i);
        print("Return at j = ", j);
        return run_count;
      }

      print("i =", i);
      print("j =", j);
    }
    i += 1;
    print("Out of inner loop");
  }

  print("make it to the end");
  return 0;  
}

// Case 1: early return @ (i == j && i > break_value) 
// => 23 runs (return run_count in this path)
int[64] val = generate_number(1, 100);
print("Result =", val);
QCOR_EXPECT_TRUE(val == 23);

// Case 2: return at the end
// Make it to the end since run_count will hit 20 
// before hitting the early return condition.
val = generate_number(1, 20);
print("Result =", val);
QCOR_EXPECT_TRUE(val == 0);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir << "\n";
  EXPECT_FALSE(qcor::execute(uint_index, "uint_index"));
}

// Test a complex construction with nesting of different loop types,
// break, return, etc...
TEST(qasm3VisitorTester, checkControlDirectiveKitchenSink) {
  const std::string uint_index = R"#(OPENQASM 3;
include "qelib1.inc";

def find_number(int[64]: target, int[64]: max_run) -> int[64] {
  int[64] run_count = 0;
  for i in { 1, 3, 5, 7, 9} {
    while(run_count < max_run) {
      for j in [0:10] {
        run_count += 1;
        if (i == target && j == i) {
          print("Return at i = ", i);
          print("Return at j = ", j);
          print("Return run_count = ", run_count);
          return run_count;
        }

        print("i =", i);
        print("j =", j);
      }
      // Finish the searching [0->10], break the while loop
      // This is a weird construction,
      // just for testing.
      break;
    }
    print("Make it out of the loop");
    print("run_count = ", run_count);
  }
  
  print("Not found");
  return 0;
}

int[64] val = find_number(3, 100);
print("Result  =", val);
// Find number 3 at 14 iterations.
QCOR_EXPECT_TRUE(val == 14);


val = find_number(2, 100);
print("Result =", val);
// Cannot find number 2 in the set:
QCOR_EXPECT_TRUE(val == 0);

val = find_number(7, 20);
print("Result =", val);
// Cannot find number 7 with only 20 iterations
QCOR_EXPECT_TRUE(val == 0);

val = find_number(7, 100);
print("Result =", val);
// But will find it at iteration 38...
QCOR_EXPECT_TRUE(val == 38);
)#";
  auto mlir = qcor::mlir_compile(uint_index, "uint_index",
                                 qcor::OutputType::MLIR, false);
@@ -164,12 +467,13 @@ QCOR_EXPECT_TRUE(c[3] == 1);
)#";
  // Make sure we can compile this in FTQC.
  // i.e., usual if ...
  auto mlir = qcor::mlir_compile(qasm_code, "iqpe",
                                 qcor::OutputType::LLVMIR, false);
  auto mlir =
      qcor::mlir_compile(qasm_code, "iqpe", qcor::OutputType::LLVMIR, false);
  std::cout << mlir << "\n";
  // Execute (FTQC + optimization): validate expected results: 1101
  EXPECT_FALSE(qcor::execute(qasm_code, "iqpe"));
}


int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  auto ret = RUN_ALL_TESTS();
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ QCOR_EXPECT_TRUE(i == 10);
  auto mlir2 = qcor::mlir_compile(while_stmt, "while_stmt",
                                 qcor::OutputType::MLIR, false);
  std::cout << mlir2 << "\n";
  // We're using SCF while loop:
  EXPECT_TRUE(mlir2.find("scf.while") != std::string::npos);
  EXPECT_FALSE(qcor::execute(while_stmt, "while_stmt"));

    const std::string decrement = R"#(OPENQASM 3;
+150 −17
Original line number Diff line number Diff line
@@ -25,8 +25,7 @@ x q[0];
cx q[0], q[1];
cx q[0], q[1];
)#";
  auto llvm =
      qcor::mlir_compile(src, "test", qcor::OutputType::LLVMIR, true);
  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);
@@ -423,8 +422,8 @@ for i in [0:2*n + 1] {
 x qb;
}
)#";
    auto llvm =
        qcor::mlir_compile(src, "test_kernel1", qcor::OutputType::LLVMIR, false);
    auto llvm = qcor::mlir_compile(src, "test_kernel1",
                                   qcor::OutputType::LLVMIR, false);
    std::cout << "LLVM:\n" << llvm << "\n";

    // Get the main kernel section only
@@ -437,6 +436,140 @@ for i in [0:2*n + 1] {
  }
}

TEST(qasm3PassManagerTester, checkConditionalBlock) {
  const std::string src = R"#(OPENQASM 3;
include "qelib1.inc";
bit c;
qubit q[2];

h q[0];
x q[1];
c = measure q[1];
if (c == 1) {
  // Always execute:
  z q[0];
}

h q[0];

// This should be: H - Z - H == X 
// Check that the two H's before and after 'if'
// don't connect.
// i.e., checking that we don't accidentally cancel the two H gates
// => left with Z => measure 0 vs. expected 1.
bit d;
d = measure q[0];
print("measure =", d);
QCOR_EXPECT_TRUE(d == 1);
)#";
  auto llvm =
      qcor::mlir_compile(src, "test_kernel1", qcor::OutputType::LLVMIR, false);
  std::cout << "LLVM:\n" << llvm << "\n";

  // Get the main kernel section only
  llvm = llvm.substr(llvm.find("@__internal_mlir_test_kernel1"));
  const auto last = llvm.find_first_of("}");
  llvm = llvm.substr(0, last + 1);
  std::cout << "LLVM:\n" << llvm << "\n";
  // 2 Hadamard gates, 1 Z gate
  // (Z gate in the conditional block)
  EXPECT_EQ(countSubstring(llvm, "__quantum__qis__h"), 2);
  EXPECT_EQ(countSubstring(llvm, "__quantum__qis__z"), 1);
  EXPECT_FALSE(qcor::execute(src, "test_kernel1"));
}

TEST(qasm3PassManagerTester, checkCPhaseMerge) {
  const std::string qasm_code = R"#(OPENQASM 3;
include "qelib1.inc";

// Expected to get 4 bits (iteratively) of 1011 (or 1101 LSB) = 11(decimal):
// phi_est = 11/16 (denom = 16 since we have 4 bits)
// => phi = 2pi * 11/16 = 11pi/8 = 2pi - 5pi/8
// i.e. we estimate the -5*pi/8 angle...
qubit q[2];
const bits_precision = 4;
bit c[bits_precision];

// Prepare the eigen-state: |1>
x q[1];

// First bit
h q[0];
// Controlled rotation: CU^k
for i in [0:8] {
  cphase(-5*pi/8) q[0], q[1];
}
h q[0];
// Measure and reset
measure q[0] -> c[0];
reset q[0];

// Second bit
h q[0];
for i in [0:4] {
  cphase(-5*pi/8) q[0], q[1];
}
// Conditional rotation
if (c[0] == 1) {
  rz(-pi/2) q[0];
}
h q[0];
// Measure and reset
measure q[0] -> c[1];
reset q[0];

// Third bit
h q[0];
for i in [0:2] {
  cphase(-5*pi/8) q[0], q[1];
}
// Conditional rotation
if (c[0] == 1) {
  rz(-pi/4) q[0];
}
if (c[1] == 1) {
  rz(-pi/2) q[0];
}
h q[0];
// Measure and reset
measure q[0] -> c[2];
reset q[0];

// Fourth bit
h q[0];
cphase(-5*pi/8) q[0], q[1];
// Conditional rotation
if (c[0] == 1) {
  rz(-pi/8) q[0];
}
if (c[1] == 1) {
  rz(-pi/4) q[0];
}
if (c[2] == 1) {
  rz(-pi/2) q[0];
}
h q[0];
measure q[0] -> c[3];

print(c[0], c[1], c[2], c[3]);
QCOR_EXPECT_TRUE(c[0] == 1);
QCOR_EXPECT_TRUE(c[1] == 1);
QCOR_EXPECT_TRUE(c[2] == 0);
QCOR_EXPECT_TRUE(c[3] == 1);
)#";
  auto llvm =
      qcor::mlir_compile(qasm_code, "test_kernel1", qcor::OutputType::LLVMIR, false);
  std::cout << "LLVM:\n" << llvm << "\n";

  // Get the main kernel section only
  llvm = llvm.substr(llvm.find("@__internal_mlir_test_kernel1"));
  const auto last = llvm.find_first_of("}");
  llvm = llvm.substr(0, last + 1);
  std::cout << "LLVM:\n" << llvm << "\n";
  // All CPhase gates in for loops have been unrolled and then merged.
  EXPECT_EQ(countSubstring(llvm, "__quantum__qis__cphase"), 4);
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  auto ret = RUN_ALL_TESTS();
Loading