diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000..152f6d6b86e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] + +members = [ + "src/Simulation/qdk_sim_rs", + "src/Qir/microsoft-quantum-qir-runtime-sys", +] \ No newline at end of file diff --git a/build/pack.ps1 b/build/pack.ps1 index 6bb5edcdf49..37882ee7581 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -129,10 +129,10 @@ function Pack-Crate() { $OutPath = Resolve-Path (Join-Path $PSScriptRoot $OutPath); } Push-Location (Join-Path $PSScriptRoot $PackageDirectory) - cargo package; - # Copy only the .crate file, since we don't need all the intermediate - # artifacts brought in by the full folder under target/package. - Copy-Item -Force (Join-Path . "target" "package" "*.crate") $OutPath; + cargo package; + # Copy only the .crate file, since we don't need all the intermediate + # artifacts brought in by the full folder under target/package. + Copy-Item -Force (Join-Path $PSScriptRoot .. "target" "package" "*.crate") $OutPath; Pop-Location } diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/Cargo.toml b/src/Qir/microsoft-quantum-qir-runtime-sys/Cargo.toml new file mode 100644 index 00000000000..237bdcaa69a --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "microsoft-quantum-qir-runtime-sys" +version = "0.1.0" +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cty = "0.2.1" +libloading = "0.7.0" +log = "0.4.14" +tempfile = "3.2.0" +llvm-sys = { version = "110", optional = true } +inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", default-features = false, features = ["llvm11-0", "target-x86"], optional = true } +lazy_static = "1.4.0" + +[build-dependencies] +cmake = "0.1.46" +bindgen = "0.59.1" +cc = "1.0.71" +which = "4.2.2" + +[lib] + + +[features] +runtime = [] +foundation = [] +llvm-libloading = ["llvm-sys", "inkwell"] +default = ["runtime", "foundation"] diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/build.rs b/src/Qir/microsoft-quantum-qir-runtime-sys/build.rs new file mode 100644 index 00000000000..287da25af7c --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/build.rs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use cmake::Config; +use std::boxed::Box; +use std::env; +use std::error::Error; + +fn main() -> Result<(), Box> { + println!("cargo:rerun-if-env-changed=TARGET"); + println!("cargo:rerun-if-changed=build.rs"); + + for (key, value) in env::vars() { + println!("{}: {}", key, value); + } + if cfg!(target_os = "windows") { + let path_to_runtime_src = "..\\Runtime"; + compile_runtime_libraries(path_to_runtime_src)?; + } else { + let path_to_runtime_src = "../Runtime"; + compile_runtime_libraries(path_to_runtime_src)?; + } + + Ok(()) +} + +fn compile_runtime_libraries(path_to_runtime_src: &str) -> Result<(), Box> { + let mut config = Config::new(path_to_runtime_src); + + if cfg!(target_os = "windows") { + config.static_crt(true); + } + + set_compiler(&mut config)?; + set_profile(&mut config)?; + + config.generator("Ninja"); + + let _ = config.build(); + Ok(()) +} + +// https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#how-do-i-use-a-different-compiler +// We set this here as setting it in the cmakefile is discouraged +fn set_compiler(config: &mut Config) -> Result<(), Box>{ + if cfg!(target_os = "linux") { + let mut c_cfg = cc::Build::new(); + let clang_11 = which::which("clang-11")?; + c_cfg.compiler(clang_11); + config.init_c_cfg(c_cfg); + + let mut cxx_cfg = cc::Build::new(); + let clangpp_11 = which::which("clang++-11")?; + cxx_cfg.compiler(clangpp_11); + config.init_cxx_cfg(cxx_cfg); + } else if cfg!(target_os = "windows") { + let mut c_cfg = cc::Build::new(); + let clang = which::which("clang.exe")?; + c_cfg.compiler(clang); + config.init_c_cfg(c_cfg); + + let mut cxx_cfg = cc::Build::new(); + let clangpp = which::which("clang++.exe")?; + cxx_cfg.compiler(clangpp); + config.init_cxx_cfg(cxx_cfg); + } else if cfg!(target_os = "macos") { + // Use macos default + } else { + panic!("Unsupported platform") + } + Ok(()) +} + +fn set_profile(config: &mut Config) -> Result<(), Box> { + config.define("CMAKE_BUILD_TYPE", "RelWithDebInfo"); + config.define("CMAKE_C_COMPILER_WORKS", "1"); + config.define("CMAKE_CXX_COMPILER_WORKS", "1"); + Ok(()) +} diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/src/foundation.rs b/src/Qir/microsoft-quantum-qir-runtime-sys/src/foundation.rs new file mode 100644 index 00000000000..f061706b3ea --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/src/foundation.rs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use lazy_static::lazy_static; + +use libloading::Library; + +#[cfg(target_os = "linux")] +const FOUNDATION_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QSharpFoundation/libMicrosoft.Quantum.Qir.QSharp.Foundation.so" +)); + +#[cfg(target_os = "macos")] +const FOUNDATION_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QSharpFoundation/libMicrosoft.Quantum.Qir.QSharp.Foundation.dylib" +)); + +#[cfg(target_os = "windows")] +const FOUNDATION_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QSharpFoundation/Microsoft.Quantum.Qir.QSharp.Foundation.dll" +)); + +lazy_static! { + pub(crate) static ref FOUNDATION_LIBRARY: Library = unsafe { + crate::qir_libloading::load_library_bytes( + "Microsoft.Quantum.Qir.QSharp.Foundation", + FOUNDATION_BYTES, + ) + .unwrap() + }; +} + +pub struct QSharpFoundation {} + +impl QSharpFoundation { + pub fn new() -> QSharpFoundation { + let _ = FOUNDATION_LIBRARY; + QSharpFoundation {} + } +} + +#[cfg(test)] +mod tests { + use crate::foundation::QSharpFoundation; + + #[test] + fn library_loads_on_new() { + let _ = QSharpFoundation::new(); + } + #[test] + fn library_can_be_initialized_multiple_times() { + let _ = QSharpFoundation::new(); + let _ = QSharpFoundation::new(); + let _ = QSharpFoundation::new(); + } +} diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/src/lib.rs b/src/Qir/microsoft-quantum-qir-runtime-sys/src/lib.rs new file mode 100644 index 00000000000..5e2313ba80a --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#[cfg(feature = "foundation")] +pub mod foundation; + +#[cfg(feature = "runtime")] +pub mod runtime; + +mod qir_libloading; diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/src/qir_libloading.rs b/src/Qir/microsoft-quantum-qir-runtime-sys/src/qir_libloading.rs new file mode 100644 index 00000000000..0fdfd627505 --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/src/qir_libloading.rs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use libloading::{library_filename, Library}; +use log; +use std::error::Error; +use std::path::Path; +use tempfile::tempdir; + +pub(crate) fn write_library>( + path: P, + lib: &'static [u8], +) -> Result<(), Box> { + log::debug!("Writing {}", path.as_ref().display()); + std::fs::write(path, lib)?; + Ok(()) +} + +pub(crate) unsafe fn load_library_bytes( + base_name: &str, + lib: &'static [u8], +) -> Result> { + let name = library_filename(base_name) + .into_string() + .expect("Could not get library name as string"); + let path = tempdir().expect(""); + let filepath = path.as_ref().join(name); + write_library(&filepath, lib)?; + let library = load_library(&filepath)?; + Ok(library) +} + +pub(crate) unsafe fn load_library>(path: P) -> Result> { + log::debug!("Loading {}", path.as_ref().display()); + let library = Library::new(path.as_ref().as_os_str())?; + + #[cfg(feature = "llvm-libloading")] + load_library_with_llvm(path); + + Ok(library) +} + +#[cfg(feature = "llvm-libloading")] +fn load_library_with_llvm>(path: P) { + let library_path = path + .as_ref() + .to_str() + .expect("Could not convert library path to &str"); + let was_loaded_by_llvm = inkwell::support::load_library_permanently(library_path); + if was_loaded_by_llvm { + log::error!("Failed to load {} into LLVM", library_path); + } else { + log::debug!("Loaded {} into LLVM", library_path); + } +} diff --git a/src/Qir/microsoft-quantum-qir-runtime-sys/src/runtime.rs b/src/Qir/microsoft-quantum-qir-runtime-sys/src/runtime.rs new file mode 100644 index 00000000000..948a0200235 --- /dev/null +++ b/src/Qir/microsoft-quantum-qir-runtime-sys/src/runtime.rs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use lazy_static::lazy_static; + +use std::ffi::CString; + +use cty; +use libloading::Library; + +#[cfg(target_os = "linux")] +const RUNTIME_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QIR/libMicrosoft.Quantum.Qir.Runtime.so" +)); + +#[cfg(target_os = "macos")] +const RUNTIME_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QIR/libMicrosoft.Quantum.Qir.Runtime.dylib" +)); + +#[cfg(target_os = "windows")] +const RUNTIME_BYTES: &'static [u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/build/lib/QIR/Microsoft.Quantum.Qir.Runtime.dll" +)); + +lazy_static! { + pub(crate) static ref RUNTIME_LIBRARY: Library = unsafe { + crate::qir_libloading::load_library_bytes("Microsoft.Quantum.Qir.Runtime", RUNTIME_BYTES) + .unwrap() + }; +} + +pub type QUBIT = u64; + +#[repr(C)] +pub struct QirArray { + private: [u8; 0], +} + +pub type IRuntimeDriver = cty::c_void; + +pub struct BasicRuntimeDriver {} + +impl BasicRuntimeDriver { + pub unsafe fn initialize_qir_context(track_allocated_objects: bool) { + // The libloading calls need to be used instead of the extern "C" calls + // to prevent linkage. Python can't init the lib if we take a hard + // dependency on the library + let driver = QirRuntime::create_basic_runtime_driver(); + QirRuntime::initialize_qir_context(driver, track_allocated_objects); + } +} + +pub struct QirRuntime {} + +impl QirRuntime { + pub fn new() -> QirRuntime { + let _ = RUNTIME_LIBRARY; + QirRuntime {} + } + + pub unsafe fn create_basic_runtime_driver() -> *mut cty::c_void { + let library = &RUNTIME_LIBRARY; + let create = library + .get:: *mut IRuntimeDriver>( + CString::new("CreateBasicRuntimeDriver") + .unwrap() + .as_bytes_with_nul(), + ) + .unwrap(); + create() + } + + pub unsafe fn initialize_qir_context(driver: *mut cty::c_void, track_allocated_objects: bool) { + let library = &RUNTIME_LIBRARY; + let init = library + .get::( + CString::new("InitializeQirContext") + .unwrap() + .as_bytes_with_nul(), + ) + .unwrap(); + init(driver, track_allocated_objects) + } + + pub unsafe fn quantum_rt_array_get_element_ptr_1d( + array: *mut QirArray, + index: i64, + ) -> *mut cty::c_char { + let library = &RUNTIME_LIBRARY; + let get_element_ptr = library + .get:: *mut cty::c_char>( + CString::new("__quantum__rt__array_get_element_ptr_1d") + .unwrap() + .as_bytes_with_nul(), + ) + .unwrap(); + get_element_ptr(array, index) + } +} + +#[cfg(test)] +mod tests { + use crate::runtime::QirRuntime; + #[test] + fn library_loads_on_new() { + let _ = QirRuntime::new(); + } + #[test] + fn library_can_be_initialized_multiple_times() { + let _ = QirRuntime::new(); + let _ = QirRuntime::new(); + let _ = QirRuntime::new(); + } +}