Commit ef0bfe0d authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

adding the prototype qasm3 syntax handler.

parent 29de0aba
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -44,3 +44,4 @@ endif()
install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)

add_subdirectory(tests)
add_subdirectory(handler)
 No newline at end of file
+31 −0
Original line number Diff line number Diff line


set(ANTLR_LIB ${XACC_ROOT}/lib/libantlr4-runtime.so)
if (APPLE)
  set(ANTLR_LIB ${XACC_ROOT}/lib/libantlr4-runtime.dylib)
endif()

set(LIBRARY_NAME qasm3-syntax-handler)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
add_library(${LIBRARY_NAME}
            SHARED
            qasm3_handler.cpp)

target_include_directories(${LIBRARY_NAME} PUBLIC . .. ${CMAKE_SOURCE_DIR}/mlir/parsers/qasm3/antlr/generated/ ${CMAKE_SOURCE_DIR}/mlir/parsers/qasm3/utils/ ${XACC_ROOT}/include/antlr4-runtime ${CMAKE_SOURCE_DIR}/mlir/dialect/include ${CMAKE_BINARY_DIR}/mlir/dialect/include ${CLANG_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} )

target_link_libraries(${LIBRARY_NAME}
                      PRIVATE ${CLANG_LIBS} ${LLVM_LIBS} MLIRQuantum MLIRIR MLIRStandard qcor-mlir-api)

if(APPLE)
  set_target_properties(${LIBRARY_NAME}
                        PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${XACC_ROOT}/lib;${LLVM_INSTALL_PREFIX}/lib")
  set_target_properties(${LIBRARY_NAME}
                        PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else()
  set_target_properties(${LIBRARY_NAME}
                        PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:${XACC_ROOT}/lib:${LLVM_INSTALL_PREFIX}/lib")
  set_target_properties(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-shared")
endif()


install(TARGETS ${LIBRARY_NAME} DESTINATION clang-plugins)
+51 −0
Original line number Diff line number Diff line
#include "qir-qrt.hpp"
using qubit = Qubit*;

[[clang::syntax(qasm3)]] int test(int i, qubit q, qubit r) {
  int ten = 10 * i;
  h q;
  cx q, r;
  bit c[2];
  c[0] = measure q;
  c[1] = measure r;
  reset q;
  reset r;
  return int[32](c);
}

// Run with...
// clang++ -std=c++17 -fplugin=/path/to/libqasm3-syntax-handler.so -I /path/to/qcor/include/qcor -I/path/to/xacc/include/xacc -c qasm3_test.cpp
// llc -filetype=obj test.bc (test comes from kernel name, so will need to do this for all kernels)
// clang++ -L /path/to/install/lib -lqir-qrt -lqcor -lxacc -lqrt -lCppMicroServices test.o qasm3_test.o
// ./a.out

int main(int argc, char** argv) {
  int x = 10;

  // Figure out how to initialize automatically
  __quantum__rt__initialize(argc, reinterpret_cast<int8_t**>(argv));
  
  
  // Connect this to qalloc(...)
  auto qreg = __quantum__rt__qubit_allocate_array(2);

  // Should be able to get qubit from operator[] on qreg
  auto qbit_mem = __quantum__rt__array_get_element_ptr_1d(qreg, 0);
  auto qbit = reinterpret_cast<Qubit**>(qbit_mem)[0];
  auto qbit_mem2 = __quantum__rt__array_get_element_ptr_1d(qreg, 1);
  auto qbit2 = reinterpret_cast<Qubit**>(qbit_mem2)[0];

  // Run bell test...
  int ones = 0, zeros = 0;
  for (int i = 0; i < 50; i++) {
    auto y = test(x, qbit, qbit2);
    // should be binary-as-int 00 = 0, or 11 = 3
    if (y == 3) {
      ones++;
    } else {
      zeros++;
    }
  }
  printf("Result: 11:%d, 00:%d\n", ones, zeros);
  return 0;
}
 No newline at end of file
+169 −0
Original line number Diff line number Diff line
#include "qasm3_handler.hpp"

#include <iostream>
#include <regex>
#include <sstream>

#include "qasm3_handler_utils.hpp"
#include "openqasmv3_mlir_generator.hpp"
#include "quantum_to_llvm.hpp"

using namespace clang;

namespace qcor {

void Qasm3SyntaxHandler::GetReplacement(Preprocessor &PP, Declarator &D,
                                        CachedTokens &Toks,
                                        llvm::raw_string_ostream &OS) {
  // Get the function name
  auto kernel_name = D.getName().Identifier->getName().str();

  // Create the MLIRContext and load the dialects
  mlir::MLIRContext context;
  context
      .loadDialect<mlir::quantum::QuantumDialect, mlir::StandardOpsDialect>();
  
  // Create the mlir generator for qasm3
  OpenQasmV3MLIRGenerator mlir_generator(context);

  // Build up the function source code
  std::stringstream ss;
  for (auto &Tok : Toks) {
    ss << " ";
    ss << PP.getSpelling(Tok);
  }
  std::string src = ss.str();

  // Loop through function arguments and
  // build up associated mlir::Type arguments,
  // For vectors use Array *
  std::vector<mlir::Type> arg_types;
  std::vector<std::string> program_parameters, arg_type_strs;
  const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
  for (unsigned int ii = 0; ii < FTI.NumParams; ii++) {

    // Get parameters as a ParmVarDecl
    auto &paramInfo = FTI.Params[ii];
    auto &decl = paramInfo.Param;
    auto parm_var_decl = cast<ParmVarDecl>(decl);
    // Get the type pointer
    auto type = parm_var_decl->getType().getTypePtr();

    // Get VarName and Type as strings
    Token IdentToken, TypeToken;
    PP.getRawToken(paramInfo.IdentLoc, IdentToken);
    PP.getRawToken(decl->getBeginLoc(), TypeToken);
    auto var = PP.getSpelling(IdentToken);
    auto type_str = PP.getSpelling(TypeToken);

    // Add them to the vectors
    program_parameters.push_back(var);
    arg_type_strs.push_back(type_str);
 
    // Convert type to a mlir type
    mlir::Type t = convertClangType(type, type_str, context);
    arg_types.push_back(t);
  }

  // std::cout << "SRC:\n" << ss.str() << "\n";

  // Get the return type as an mlir type, 
  // as well as a string
  std::string ret_type_str = "";
  mlir::Type return_type = convertReturnType(D.getDeclSpec(), ret_type_str, context);

  // Init the MLIRGen
  mlir_generator.initialize_mlirgen(kernel_name, arg_types, program_parameters,
                                    return_type);

  // Run the MLIRGen
  mlir_generator.mlirgen(src);
  
  // Finalize and get the Module
  mlir_generator.finalize_mlirgen();
  auto module = mlir_generator.get_module();
  // module->dump();

  // Lower the module to LLVM IR bit code file
  DiagnosticEngine &engine = context.getDiagEngine();

  // Handle the reported diagnostic.
  // Return success to signal that the diagnostic has either been fully
  // processed, or failure if the diagnostic should be propagated to the
  // previous handlers.
  engine.registerHandler([&](mlir::Diagnostic &diag) -> LogicalResult {
    std::cout << "Dumping Module after error.\n";
    module->dump();
    for (auto &n : diag.getNotes()) {
      std::string s;
      llvm::raw_string_ostream os(s);
      n.print(os);
      os.flush();
      std::cout << "DiagnosticEngine Note: " << s << "\n";
    }
    bool should_propagate_diagnostic = true;
    return failure(should_propagate_diagnostic);
  });

  // Create the PassManager for lowering to LLVM MLIR and run it
  std::vector<std::string> unique_f_names{kernel_name};
  mlir::PassManager pm(&context);
  applyPassManagerCLOptions(pm);
  pm.addPass(std::make_unique<qcor::QuantumToLLVMLoweringPass>(unique_f_names));
  auto module_op = (*module).getOperation();
  if (mlir::failed(pm.run(module_op))) {
    std::cout << "Pass Manager Failed\n";
  }
  // Now lower MLIR to LLVM IR
  llvm::LLVMContext llvmContext;
  auto llvmModule = mlir::translateModuleToLLVMIR(*module, llvmContext);

  // Optimize the LLVM IR
  llvm::InitializeNativeTarget();
  llvm::InitializeNativeTargetAsmPrinter();
  auto optPipeline = mlir::makeOptimizingTransformer(3, 0, nullptr);
  if (auto err = optPipeline(llvmModule.get())) {
    llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
  }

  // llvmModule->dump();
  // Write the LLVM IR to a bitcode file
  std::error_code ec;
  llvm::ToolOutputFile result(kernel_name + ".bc", ec, llvm::sys::fs::F_None);
  WriteBitcodeToFile(*llvmModule, result.os());
  result.keep();

  // Update the source code
  std::stringstream sss;
  sss << "extern \"C\" { " << ret_type_str << " __internal_mlir_" << kernel_name
      << "(" << arg_type_strs[0];
  for (int i = 1; i < arg_type_strs.size(); i++) {
    sss << ", " << arg_type_strs[i];
  }
  sss << ");}\n";
  // Rewrite the function to call the internal function
  sss << getDeclText(PP, D).str() << "{\n";
  sss << "return __internal_mlir_" << kernel_name << "("
      << program_parameters[0];
  for (int i = 1; i < program_parameters.size(); i++) {
    sss << ", " << program_parameters[i];
  }
  sss << ");\n";
  sss << "}\n";

  // std::cout << "NEW CODE:\n" << sss.str() << "\n";
  OS << sss.str();
  return;
}

void Qasm3SyntaxHandler::AddToPredefines(llvm::raw_string_ostream &OS) {}
}  // namespace qcor

static SyntaxHandlerRegistry::Add<qcor::Qasm3SyntaxHandler> X(
    "qasm3", "qasm3 quantum kernel syntax handler");

// /usr/local/aideqc/llvm/bin/clang++ -std=c++17
// -fplugin=/home/cades/.xacc/clang-plugins/libqasm3-syntax-handler.so -c
// qasm3_test.cpp /usr/local/aideqc/llvm/bin/llc -filetype=obj test.bc
// /usr/local/aideqc/llvm/bin/clang++ test.o qasm3_test.o
// ./a.out
 No newline at end of file
+19 −0
Original line number Diff line number Diff line
#pragma once

#include "clang/Parse/Parser.h"

using namespace clang;

namespace qcor {


class Qasm3SyntaxHandler : public SyntaxHandler {
public:
  Qasm3SyntaxHandler() : SyntaxHandler("qasm3") {}
  void GetReplacement(Preprocessor &PP, Declarator &D, CachedTokens &Toks,
                      llvm::raw_string_ostream &OS) override;
  
 
  void AddToPredefines(llvm::raw_string_ostream &OS) override;
};
} // namespace qcor
 No newline at end of file
Loading