From 431959b6987f2a7f94dd9229ad1aee589a0a8d3d Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 27 Dec 2022 06:46:03 -0800 Subject: [PATCH] Metadata (#223) * Adding param, module, and function metadata Co-authored-by: Sarah Marshall <33814365+samarsha@users.noreply.github.com> --- examples/dynamic_allocation.py | 111 +++++++++--- pyqir/README.md | 2 +- pyqir/pyqir/__init__.py | 8 + pyqir/pyqir/_native.pyi | 80 ++++++++- pyqir/pyqir/_simple.py | 36 +++- pyqir/src/core.rs | 6 +- pyqir/src/lib.rs | 1 + pyqir/src/metadata.rs | 167 ++++++++++++++++++ pyqir/src/module.rs | 117 +++++++++++- pyqir/src/python.rs | 7 +- pyqir/src/values.rs | 16 +- .../tests/resources/test_empty_false_block.ll | 9 +- .../tests/resources/test_empty_true_block.ll | 9 +- pyqir/tests/resources/test_if_empty_blocks.ll | 9 +- pyqir/tests/resources/test_nested_blocks.ll | 9 +- .../teleportchain.baseprofile.ll.reference | 2 +- pyqir/tests/test_module_attributes.py | 91 ++++++++++ pyqir/tests/test_simplemodule.py | 30 ++++ qirlib/build.rs | 62 +++++-- qirlib/external.rs | 16 ++ qirlib/llvm-wrapper/LLVMWrapper.h | 9 + qirlib/llvm-wrapper/MetadataWrapper.cpp | 27 +++ qirlib/llvm-wrapper/ModuleWrapper.cpp | 75 ++++++++ .../module/many_required_qubits_results.ll | 2 +- .../tests/module/one_required_qubit.ll | 2 +- .../tests/module/one_required_result.ll | 2 +- .../module/zero_required_qubits_results.ll | 2 +- qirlib/resources/tests/qis/barrier.ll | 2 +- qirlib/resources/tests/qis/ccx.ll | 2 +- qirlib/resources/tests/qis/cx.ll | 2 +- qirlib/resources/tests/qis/cz.ll | 2 +- qirlib/resources/tests/qis/empty_if.ll | 5 +- qirlib/resources/tests/qis/h.ll | 2 +- qirlib/resources/tests/qis/if_else.ll | 5 +- .../resources/tests/qis/if_else_continue.ll | 5 +- qirlib/resources/tests/qis/if_else_else.ll | 5 +- qirlib/resources/tests/qis/if_else_then.ll | 5 +- qirlib/resources/tests/qis/if_then.ll | 5 +- .../resources/tests/qis/if_then_continue.ll | 5 +- qirlib/resources/tests/qis/if_then_else.ll | 5 +- .../tests/qis/if_then_else_continue.ll | 5 +- qirlib/resources/tests/qis/if_then_then.ll | 5 +- .../tests/qis/if_unmeasured_result.ll | 2 +- qirlib/resources/tests/qis/mz.ll | 5 +- qirlib/resources/tests/qis/read_result.ll | 2 +- qirlib/resources/tests/qis/reset.ll | 2 +- qirlib/resources/tests/qis/rx.ll | 2 +- qirlib/resources/tests/qis/ry.ll | 2 +- qirlib/resources/tests/qis/rz.ll | 2 +- qirlib/resources/tests/qis/s.ll | 2 +- qirlib/resources/tests/qis/s_adj.ll | 2 +- qirlib/resources/tests/qis/swap.ll | 2 +- qirlib/resources/tests/qis/t.ll | 2 +- qirlib/resources/tests/qis/t_adj.ll | 2 +- qirlib/resources/tests/qis/x.ll | 2 +- qirlib/resources/tests/qis/y.ll | 2 +- qirlib/resources/tests/qis/z.ll | 2 +- .../resources/tests/rt/array_record_output.ll | 2 +- qirlib/resources/tests/rt/initialize.ll | 2 +- .../tests/rt/result_record_output.ll | 2 +- .../resources/tests/rt/tuple_record_output.ll | 2 +- .../tests/test_unknown_external_func.ll | 14 -- qirlib/src/lib.rs | 6 + qirlib/src/llvm_wrapper.rs | 30 ++++ qirlib/src/metadata.rs | 15 ++ qirlib/src/module.rs | 63 +++++++ qirlib/src/qis.rs | 29 ++- qirlib/src/tests.rs | 2 + qirlib/src/values.rs | 42 ++++- 69 files changed, 1082 insertions(+), 127 deletions(-) create mode 100644 pyqir/src/metadata.rs create mode 100644 pyqir/tests/test_module_attributes.py create mode 100644 pyqir/tests/test_simplemodule.py create mode 100644 qirlib/llvm-wrapper/LLVMWrapper.h create mode 100644 qirlib/llvm-wrapper/MetadataWrapper.cpp create mode 100644 qirlib/llvm-wrapper/ModuleWrapper.cpp delete mode 100644 qirlib/resources/tests/test_unknown_external_func.ll create mode 100644 qirlib/src/llvm_wrapper.rs create mode 100644 qirlib/src/metadata.rs create mode 100644 qirlib/src/module.rs diff --git a/examples/dynamic_allocation.py b/examples/dynamic_allocation.py index b96aff33..ddd6352a 100644 --- a/examples/dynamic_allocation.py +++ b/examples/dynamic_allocation.py @@ -2,53 +2,118 @@ # Licensed under the MIT License. import pyqir +from pyqir import ( + BasicBlock, + Builder, + Context, + Function, + Linkage, + Module, + ModuleFlagBehavior, +) + +context = Context() +mod = Module(context, "dynamic_allocation") +builder = Builder(context) -mod = pyqir.SimpleModule("dynamic_allocation", num_qubits=0, num_results=0) -qubit_type = pyqir.qubit_type(mod.context) -result_type = pyqir.result_type(mod.context) +# Define module flags +i1 = pyqir.IntType(context, 1) +i32 = pyqir.IntType(context, 32) + +mod.add_flag( + ModuleFlagBehavior.ERROR, + "qir_major_version", + pyqir.const(i32, 1), +) + +mod.add_flag( + ModuleFlagBehavior.MAX, + "qir_minor_version", + pyqir.const(i32, 0), +) + +mod.add_flag( + ModuleFlagBehavior.ERROR, + "dynamic_qubit_management", + pyqir.const(i1, True), +) + +mod.add_flag( + ModuleFlagBehavior.ERROR, + "dynamic_result_management", + pyqir.const(i1, True), +) + +# define external calls and type definitions +qubit_type = pyqir.qubit_type(context) +result_type = pyqir.result_type(context) # PyQIR assumes you want to use static allocation for qubits and results, but # you can still use dynamic allocation by manually calling the appropriate # runtime functions. -qubit_allocate = mod.add_external_function( - "__quantum__rt__qubit_allocate", pyqir.FunctionType(qubit_type, []) +qubit_allocate = Function( + pyqir.FunctionType(qubit_type, []), + Linkage.EXTERNAL, + "__quantum__rt__qubit_allocate", + mod, ) -qubit_release = mod.add_external_function( + +qubit_release = Function( + pyqir.FunctionType(pyqir.Type.void(context), [qubit_type]), + Linkage.EXTERNAL, "__quantum__rt__qubit_release", - pyqir.FunctionType(pyqir.Type.void(mod.context), [qubit_type]), + mod, ) -result_get_one = mod.add_external_function( - "__quantum__rt__result_get_one", pyqir.FunctionType(result_type, []) + +result_get_one = Function( + pyqir.FunctionType(result_type, []), + Linkage.EXTERNAL, + "__quantum__rt__result_get_one", + mod, ) -result_equal = mod.add_external_function( + +result_equal = Function( + pyqir.FunctionType(pyqir.IntType(context, 1), [result_type, result_type]), + Linkage.EXTERNAL, "__quantum__rt__result_equal", - pyqir.FunctionType(pyqir.IntType(mod.context, 1), [result_type, result_type]), + mod, ) -m = mod.add_external_function( - "__quantum__qis__m__body", pyqir.FunctionType(result_type, [qubit_type]) + +m = Function( + pyqir.FunctionType(result_type, [qubit_type]), + Linkage.EXTERNAL, + "__quantum__qis__m__body", + mod, ) -# Instead of mod.qubits[i], use __quantum__rt__qubit_allocate. -qubit_return = mod.builder.call(qubit_allocate, []) +# Create entry point +num_qubits = 1 +num_results = 1 +entry_point = pyqir.entry_point(mod, "main", num_qubits, num_results) +builder.insert_at_end(BasicBlock(context, "entry", entry_point)) + +# Define entry point body +qubit_return = builder.call(qubit_allocate, []) + assert qubit_return is not None qubit = qubit_return -qis = pyqir.BasicQisBuilder(mod.builder) +qis = pyqir.BasicQisBuilder(builder) qis.h(qubit) # Instead of qis.mz, use __quantum__qis__m__body. -result = mod.builder.call(m, [qubit]) +result = builder.call(m, [qubit]) assert result is not None -# Instead of mod.if_result, use __quantum__rt__result_equal and mod.if_. -one = mod.builder.call(result_get_one, []) +# Instead of if_result, use __quantum__rt__result_equal and mod.if_. +one = builder.call(result_get_one, []) assert one is not None -result_is_one = mod.builder.call(result_equal, [result, one]) +result_is_one = builder.call(result_equal, [result, one]) assert result_is_one is not None -mod.builder.if_(result_is_one, lambda: qis.reset(qubit)) +builder.if_(result_is_one, lambda: qis.reset(qubit)) # Be sure to release any allocated qubits when you're done with them. -mod.builder.call(qubit_release, [qubit]) +builder.call(qubit_release, [qubit]) if __name__ == "__main__": - print(mod.ir()) + print(str(mod)) diff --git a/pyqir/README.md b/pyqir/README.md index b73ae176..d831817a 100644 --- a/pyqir/README.md +++ b/pyqir/README.md @@ -66,7 +66,7 @@ declare void @__quantum__qis__h__body(%Qubit*) declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) attributes #0 = { "EntryPoint" "requiredQubits"="2" "requiredResults"="2" } ``` diff --git a/pyqir/pyqir/__init__.py b/pyqir/pyqir/__init__.py index cae91bee..0762825b 100644 --- a/pyqir/pyqir/__init__.py +++ b/pyqir/pyqir/__init__.py @@ -10,6 +10,7 @@ Builder, Call, Constant, + ConstantAsMetadata, Context, FCmp, FloatConstant, @@ -22,7 +23,10 @@ IntPredicate, IntType, Linkage, + Metadata, + MetadataString, Module, + ModuleFlagBehavior, Opcode, Phi, PointerType, @@ -60,6 +64,7 @@ "Builder", "Call", "Constant", + "ConstantAsMetadata", "Context", "FCmp", "FloatConstant", @@ -72,7 +77,10 @@ "IntPredicate", "IntType", "Linkage", + "Metadata", + "MetadataString", "Module", + "ModuleFlagBehavior", "Opcode", "Phi", "PointerType", diff --git a/pyqir/pyqir/_native.pyi b/pyqir/pyqir/_native.pyi index b1f1e250..edd49de6 100644 --- a/pyqir/pyqir/_native.pyi +++ b/pyqir/pyqir/_native.pyi @@ -505,6 +505,29 @@ class Module: def context(self) -> Context: """The LLVM context.""" ... + def add_flag( + self, behavior: ModuleFlagBehavior, id: str, flag: Union[Metadata, Constant] + ) -> None: + """ + Adds a flag to the llvm.module.flags metadata + + See https://llvm.org/docs/LangRef.html#module-flags-metadata + + :param ModuleFlagBehavior behavior: flag specifying the behavior when two (or more) modules are merged together + :param str id: string that is a unique ID for the metadata. + :param Union[Metadata, Constant] flag: value of the flag + """ + ... + def get_flag(self, id: str) -> Optional[Metadata]: + """ + Gets the flag value from the llvm.module.flags metadata for a given id + + See https://llvm.org/docs/LangRef.html#module-flags-metadata + + :param id: metadata string that is a unique ID for the metadata. + :returns: value of the flag if found, otherwise None + """ + ... def verify(self) -> Optional[str]: """ Verifies that this module is valid. @@ -516,6 +539,17 @@ class Module: """Converts this module into an LLVM IR string.""" ... +class ModuleFlagBehavior(Enum): + """Module flag behavior choices""" + + ERROR: ModuleFlagBehavior + WARNING: ModuleFlagBehavior + REQUIRE: ModuleFlagBehavior + OVERRIDE: ModuleFlagBehavior + APPEND: ModuleFlagBehavior + APPEND_UNIQUE: ModuleFlagBehavior + MAX: ModuleFlagBehavior + class Opcode(Enum): """An instruction opcode.""" @@ -672,6 +706,35 @@ class Type: """Whether this type is the bool type.""" ... +class Metadata: + """A metadata value.""" + + ... + +class MetadataString(Metadata): + """A metadata string""" + + def __init__(self, context: Context, string: str) -> None: + """ + Creates a metadata string + + :param context: The LLVM context. + :param string: the value of the metadata string to create + """ + ... + @property + def value(self) -> str: + """The underlying metadata string value.""" + ... + +class ConstantAsMetadata(Metadata): + """A metadata constant value.""" + + @property + def value(self) -> Constant: + """The underlying metadata constant value.""" + ... + class Value: """A value.""" @@ -695,15 +758,22 @@ def const(ty: Type, value: Union[bool, int, float]) -> Constant: ... def entry_point( - module: Module, name: str, required_num_qubits: int, required_num_results: int + module: Module, + name: str, + required_num_qubits: int, + required_num_results: int, + qir_profiles: Optional[str] = "custom", + output_labeling_schema: Optional[str] = "", ) -> Function: """ Creates an entry point. - :param module: The parent module. - :param name: The entry point name. - :param required_num_qubits: The number of qubits required by the entry point. - :param required_num_results: The number of results required by the entry point. + :param Module module: The parent module. + :param str name: The entry point name. + :param int required_num_qubits: The number of qubits required by the entry point. + :param int required_num_results: The number of results required by the entry point. + :param Optional[str] qir_profiles: Value identifying the profile the entry point has been compiled for. Use base_profile when QIR is compliant. + :param Optional[str] output_labeling_schema: An arbitrary string value that identifies the schema used by a compiler frontend that produced the IR to label the recorded output :returns: An entry point. """ ... diff --git a/pyqir/pyqir/_simple.py b/pyqir/pyqir/_simple.py index e75de5bc..e928cb4b 100644 --- a/pyqir/pyqir/_simple.py +++ b/pyqir/pyqir/_simple.py @@ -13,6 +13,7 @@ FunctionType, Linkage, Module, + ModuleFlagBehavior, Value, ) @@ -35,10 +36,10 @@ def __init__( """ Initializes a simple module. - :param name: The name of the module. - :param num_qubits: The number of statically allocated qubits. - :param num_results: The number of statically allocated results. - :param context: The LLVM context. + :param str name: The name of the module. + :param str num_qubits: The number of statically allocated qubits. + :param int num_results: The number of statically allocated results. + :param Optional[Context] context: The LLVM context. """ if context is None: @@ -52,6 +53,33 @@ def __init__( entry_point = pyqir.entry_point(self._module, "main", num_qubits, num_results) self._builder.insert_at_end(BasicBlock(context, "entry", entry_point)) + i1 = pyqir.IntType(context, 1) + i32 = pyqir.IntType(context, 32) + + self._module.add_flag( + ModuleFlagBehavior.ERROR, + "qir_major_version", + pyqir.const(i32, 1), + ) + + self._module.add_flag( + ModuleFlagBehavior.MAX, + "qir_minor_version", + pyqir.const(i32, 0), + ) + + self._module.add_flag( + ModuleFlagBehavior.ERROR, + "dynamic_qubit_management", + pyqir.const(i1, False), + ) + + self._module.add_flag( + ModuleFlagBehavior.ERROR, + "dynamic_result_management", + pyqir.const(i1, False), + ) + @property def context(self) -> Context: """The LLVM context.""" diff --git a/pyqir/src/core.rs b/pyqir/src/core.rs index f58134f0..38e8712d 100644 --- a/pyqir/src/core.rs +++ b/pyqir/src/core.rs @@ -1,8 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![allow(clippy::used_underscore_binding)] + +use llvm_sys::core::LLVMContextCreate; +#[allow(deprecated)] use llvm_sys::{ - core::{LLVMContextCreate, LLVMContextDispose, LLVMDisposeMemoryBuffer, LLVMDisposeMessage}, + core::{LLVMContextDispose, LLVMDisposeMemoryBuffer, LLVMDisposeMessage}, prelude::*, LLVMContext, LLVMMemoryBuffer, }; diff --git a/pyqir/src/lib.rs b/pyqir/src/lib.rs index e7d284e2..c2ea2bca 100644 --- a/pyqir/src/lib.rs +++ b/pyqir/src/lib.rs @@ -16,6 +16,7 @@ extern crate llvm_sys_140 as llvm_sys; mod builder; mod core; mod instructions; +mod metadata; mod module; mod python; mod qis; diff --git a/pyqir/src/metadata.rs b/pyqir/src/metadata.rs new file mode 100644 index 00000000..2c55174b --- /dev/null +++ b/pyqir/src/metadata.rs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#![allow(clippy::used_underscore_binding)] + +use crate::{ + core::{Context, Message}, + values::{Constant, Owner}, +}; +#[allow(clippy::wildcard_imports)] +use llvm_sys::{core::*, prelude::*}; +use llvm_sys::{ + debuginfo::{LLVMGetMetadataKind, LLVMMetadataKind}, + LLVMOpaqueMetadata, +}; +use pyo3::{conversion::ToPyObject, exceptions::PyValueError, prelude::*}; +use std::{ffi::CString, ops::Deref, ptr::NonNull, slice, str}; + +/// A metadata value or node. +#[pyclass(subclass, unsendable)] +pub(crate) struct Metadata { + value: NonNull, + owner: Owner, +} + +#[pymethods] +impl Metadata { + fn __str__(&self, py: Python) -> String { + unsafe { + let context = self.owner.context(py).borrow(py).as_ptr(); + let value = LLVMMetadataAsValue(context, self.as_ptr()); + Message::from_raw(LLVMPrintValueToString(value)) + .to_str() + .unwrap() + .to_string() + } + } +} + +impl Metadata { + pub(crate) unsafe fn from_raw( + py: Python, + owner: Owner, + md: LLVMMetadataRef, + ) -> PyResult { + match LLVMGetMetadataKind(md) { + LLVMMetadataKind::LLVMMDStringMetadataKind => { + Ok(Py::new(py, MetadataString::from_raw(py, owner, md)?)?.to_object(py)) + } + LLVMMetadataKind::LLVMConstantAsMetadataMetadataKind => { + ConstantAsMetadata::from_raw(py, owner, md) + } + _ => { + let value = NonNull::new(md).expect("Value is null."); + Ok(Py::new(py, Self { value, owner })?.to_object(py)) + } + } + } + + pub(crate) fn owner(&self) -> &Owner { + &self.owner + } +} + +impl Deref for Metadata { + type Target = NonNull; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +/// A metadata string +#[pyclass(extends = Metadata, subclass)] +#[pyo3(text_signature = "(context, string)")] +pub(crate) struct MetadataString; + +#[pymethods] +impl MetadataString { + /// Creates a metadata string + /// + /// :param context: The LLVM context. + /// :param string: the value of the metadata string to create + #[new] + pub(crate) unsafe fn new( + py: Python, + context: Py, + string: &str, + ) -> PyResult> { + let owner = context.clone_ref(py).into(); + let c_string = CString::new(string).unwrap(); + let context = context.borrow(py).as_ptr(); + let md = unsafe { LLVMMDStringInContext2(context, c_string.as_ptr(), string.len()) }; + unsafe { MetadataString::from_raw(py, owner, md) } + } + + /// The underlying metadata string value. + /// + /// :type: str + #[getter] + fn value(slf: PyRef, py: Python) -> String { + let mut len = 0; + unsafe { + let slf = slf.into_super(); + let context = slf.owner.context(py).borrow(py).as_ptr(); + let value = LLVMMetadataAsValue(context, slf.as_ptr()); + let mds = LLVMGetMDString(value, &mut len); + str::from_utf8(slice::from_raw_parts(mds.cast(), len as usize)) + .unwrap() + .to_string() + } + } +} + +impl MetadataString { + unsafe fn from_raw( + py: Python, + owner: Owner, + value: LLVMMetadataRef, + ) -> PyResult> { + let value = NonNull::new(value).expect("Value is null."); + let context = owner.context(py).borrow(py).as_ptr(); + let valueref = LLVMMetadataAsValue(context, value.as_ptr()); + if LLVMIsAMDString(valueref) == valueref { + Ok(PyClassInitializer::from(Metadata { value, owner }).add_subclass(MetadataString)) + } else { + Err(PyValueError::new_err("Value is not a metadata string.")) + } + } +} + +/// A metadata constant value. +#[pyclass(extends = Metadata, subclass)] +pub(crate) struct ConstantAsMetadata; + +#[pymethods] +impl ConstantAsMetadata { + /// The value. + /// + /// :type: Constant + #[getter] + fn value(slf: PyRef, py: Python) -> PyResult { + let slf = slf.into_super(); + let context = slf.owner.context(py).borrow(py).as_ptr(); + let valueref = unsafe { LLVMMetadataAsValue(context, slf.as_ptr()) }; + + if let Some(value) = unsafe { qirlib::metadata::extract_constant(valueref) } { + return unsafe { Constant::from_raw(py, slf.owner().clone_ref(py), value) }; + } + panic!("Could not extract constant value from metadata") + } +} + +impl ConstantAsMetadata { + unsafe fn from_raw(py: Python, owner: Owner, value: LLVMMetadataRef) -> PyResult { + let value = NonNull::new(value).expect("Value is null."); + let context = owner.context(py).borrow(py).as_ptr(); + let valueref = LLVMMetadataAsValue(context, value.as_ptr()); + if qirlib::metadata::extract_constant(valueref).is_some() { + let initializer = + PyClassInitializer::from(Metadata { value, owner }).add_subclass(Self); + Ok(Py::new(py, initializer)?.to_object(py)) + } else { + Err(PyValueError::new_err("Could not extract constant.")) + } + } +} diff --git a/pyqir/src/module.rs b/pyqir/src/module.rs index b767f39c..8a00c0a8 100644 --- a/pyqir/src/module.rs +++ b/pyqir/src/module.rs @@ -6,7 +6,8 @@ use crate::{ core::Context, core::{MemoryBuffer, Message}, - values::Value, + metadata::Metadata, + values::{Constant, Owner, Value}, }; use core::slice; #[allow(clippy::wildcard_imports, deprecated)] @@ -19,6 +20,7 @@ use llvm_sys::{ LLVMLinkage, LLVMModule, }; use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; +use qirlib::module::FlagBehavior; use std::{ ffi::CString, ops::Deref, @@ -31,7 +33,7 @@ use std::{ /// :param Context context: The LLVM context. /// :param str name: The module name. #[pyclass(unsendable)] -#[pyo3(text_signature = "(context, str)")] +#[pyo3(text_signature = "(context, name)")] pub(crate) struct Module { module: NonNull, context: Py, @@ -183,6 +185,54 @@ impl Module { &self.context } + /// Adds a flag to the llvm.module.flags metadata + /// + /// See https://llvm.org/docs/LangRef.html#module-flags-metadata + /// + /// :param ModuleFlagBehavior behavior: flag specifying the behavior when two (or more) modules are merged together + /// :param str id: string that is a unique ID for the metadata. + /// :param Union[Metadata, Value] flag: value of the flag + #[pyo3(text_signature = "(behavior, id, flag)")] + pub(crate) fn add_flag( + &self, + py: Python, + behavior: ModuleFlagBehavior, + id: &str, + flag: Flag, + ) -> PyResult<()> { + let context = self.context().clone_ref(py); + let _owner = Owner::merge(py, [Owner::Context(context), flag.owner().clone_ref(py)])?; + let md = match flag { + Flag::Constant(v) => unsafe { LLVMValueAsMetadata(v.into_super().as_ptr()) }, + Flag::Metadata(m) => m.as_ptr(), + }; + unsafe { + qirlib::module::add_flag(self.module.as_ptr(), behavior.into(), id, md); + } + Ok(()) + } + + /// Gets the flag value from the llvm.module.flags metadata for a given id + /// + /// See https://llvm.org/docs/LangRef.html#module-flags-metadata + /// + /// :param str id: metadata string that is a unique ID for the metadata. + /// :returns: value of the flag if found, otherwise None + /// :rtype: typing.Optional[Metadata] + #[pyo3(text_signature = "(id)")] + pub(crate) fn get_flag(slf: Py, py: Python, id: &str) -> Option { + let module = slf.borrow(py).module.as_ptr(); + let flag = unsafe { LLVMGetModuleFlag(module, id.as_ptr().cast(), id.len()) }; + + if flag.is_null() { + return None; + } + + let owner = slf.into(); + let value = unsafe { Metadata::from_raw(py, owner, flag) }; + value.ok() + } + /// Verifies that this module is valid. /// /// :returns: An error description if this module is invalid or `None` if this module is valid. @@ -282,3 +332,66 @@ impl From for LLVMLinkage { } } } + +/// Module flag behavior choices +#[pyclass] +#[derive(Clone)] +pub(crate) enum ModuleFlagBehavior { + #[pyo3(name = "ERROR")] + Error, + #[pyo3(name = "WARNING")] + Warning, + #[pyo3(name = "REQUIRE")] + Require, + #[pyo3(name = "OVERRIDE")] + Override, + #[pyo3(name = "APPEND")] + Append, + #[pyo3(name = "APPEND_UNIQUE")] + AppendUnique, + #[pyo3(name = "MAX")] + Max, +} + +impl From for ModuleFlagBehavior { + fn from(flag: FlagBehavior) -> Self { + match flag { + FlagBehavior::Error => ModuleFlagBehavior::Error, + FlagBehavior::Warning => ModuleFlagBehavior::Warning, + FlagBehavior::Require => ModuleFlagBehavior::Require, + FlagBehavior::Override => ModuleFlagBehavior::Override, + FlagBehavior::Append => ModuleFlagBehavior::Append, + FlagBehavior::AppendUnique => ModuleFlagBehavior::AppendUnique, + FlagBehavior::Max => ModuleFlagBehavior::Max, + } + } +} + +impl From for FlagBehavior { + fn from(flag: ModuleFlagBehavior) -> Self { + match flag { + ModuleFlagBehavior::Error => FlagBehavior::Error, + ModuleFlagBehavior::Warning => FlagBehavior::Warning, + ModuleFlagBehavior::Require => FlagBehavior::Require, + ModuleFlagBehavior::Override => FlagBehavior::Override, + ModuleFlagBehavior::Append => FlagBehavior::Append, + ModuleFlagBehavior::AppendUnique => FlagBehavior::AppendUnique, + ModuleFlagBehavior::Max => FlagBehavior::Max, + } + } +} + +#[derive(FromPyObject)] +pub(crate) enum Flag<'py> { + Constant(PyRef<'py, Constant>), + Metadata(PyRef<'py, Metadata>), +} + +impl Flag<'_> { + fn owner(&self) -> &Owner { + match self { + Flag::Constant(v) => v.as_ref().owner(), + Flag::Metadata(m) => m.owner(), + } + } +} diff --git a/pyqir/src/python.rs b/pyqir/src/python.rs index dd3a56f6..427ccc2e 100644 --- a/pyqir/src/python.rs +++ b/pyqir/src/python.rs @@ -7,7 +7,8 @@ use crate::{ instructions::{ Call, FCmp, FloatPredicate, ICmp, Instruction, IntPredicate, Opcode, Phi, Switch, }, - module::{Linkage, Module}, + metadata::{ConstantAsMetadata, Metadata, MetadataString}, + module::{Linkage, Module, ModuleFlagBehavior}, qis::{ barrier, ccx, cx, cz, h, if_result, mz, reset, rx, ry, rz, s, s_adj, swap, t, t_adj, x, y, z, @@ -36,6 +37,7 @@ fn _native(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; @@ -48,7 +50,10 @@ fn _native(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/pyqir/src/values.rs b/pyqir/src/values.rs index 302a75f4..ed668f6e 100644 --- a/pyqir/src/values.rs +++ b/pyqir/src/values.rs @@ -332,7 +332,11 @@ impl Constant { } impl Constant { - unsafe fn from_raw(py: Python, owner: Owner, value: LLVMValueRef) -> PyResult { + pub(crate) unsafe fn from_raw( + py: Python, + owner: Owner, + value: LLVMValueRef, + ) -> PyResult { let value = NonNull::new(value).expect("Value is null."); if LLVMIsConstant(value.as_ptr()) == 0 { Err(PyValueError::new_err("Value is not constant.")) @@ -682,16 +686,22 @@ pub(crate) fn result_id(value: &Value) -> Option { /// :param str name: The entry point name. /// :param int required_num_qubits: The number of qubits required by the entry point. /// :param int required_num_results: The number of results required by the entry point. +/// :param str qir_profiles: Value identifying the profile the entry point has been compiled for. Use base_profile when QIR is compliant. +/// :param str output_labeling_schema: An arbitrary string value that identifies the schema used by a compiler frontend that produced the IR to label the recorded output /// :returns: An entry point. /// :rtype: Function #[pyfunction] -#[pyo3(text_signature = "(module, name, required_num_qubits, required_num_results)")] +#[pyo3( + text_signature = "(module, name, required_num_qubits, required_num_results, qir_profiles, output_labeling_schema)" +)] pub(crate) fn entry_point( py: Python, module: Py, name: &str, required_num_qubits: u64, required_num_results: u64, + qir_profiles: Option<&str>, + output_labeling_schema: Option<&str>, ) -> PyResult { let name = CString::new(name).unwrap(); unsafe { @@ -700,6 +710,8 @@ pub(crate) fn entry_point( name.as_c_str(), required_num_qubits, required_num_results, + qir_profiles.unwrap_or("custom"), + output_labeling_schema.unwrap_or(""), ); Value::from_raw(py, module.into(), entry_point) } diff --git a/pyqir/tests/resources/test_empty_false_block.ll b/pyqir/tests/resources/test_empty_false_block.ll index b821f306..e1c079af 100644 --- a/pyqir/tests/resources/test_empty_false_block.ll +++ b/pyqir/tests/resources/test_empty_false_block.ll @@ -24,4 +24,11 @@ declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/pyqir/tests/resources/test_empty_true_block.ll b/pyqir/tests/resources/test_empty_true_block.ll index e9062297..0f6f2ab7 100644 --- a/pyqir/tests/resources/test_empty_true_block.ll +++ b/pyqir/tests/resources/test_empty_true_block.ll @@ -24,4 +24,11 @@ declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/pyqir/tests/resources/test_if_empty_blocks.ll b/pyqir/tests/resources/test_if_empty_blocks.ll index 1aaeb6fa..0c86dc99 100644 --- a/pyqir/tests/resources/test_if_empty_blocks.ll +++ b/pyqir/tests/resources/test_if_empty_blocks.ll @@ -20,4 +20,11 @@ continue: ; preds = %else, %then declare i1 @__quantum__qis__read_result__body(%Result*) -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/pyqir/tests/resources/test_nested_blocks.ll b/pyqir/tests/resources/test_nested_blocks.ll index f6e6c1f6..5f45c936 100644 --- a/pyqir/tests/resources/test_nested_blocks.ll +++ b/pyqir/tests/resources/test_nested_blocks.ll @@ -53,4 +53,11 @@ declare void @__quantum__qis__z__body(%Qubit*) declare void @__quantum__qis__t__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="3" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="3" "output_labeling_schema" "qir_profiles"="custom" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/pyqir/tests/teleportchain.baseprofile.ll.reference b/pyqir/tests/teleportchain.baseprofile.ll.reference index d2fec52f..4ab2dd17 100644 --- a/pyqir/tests/teleportchain.baseprofile.ll.reference +++ b/pyqir/tests/teleportchain.baseprofile.ll.reference @@ -103,7 +103,7 @@ declare void @__quantum__rt__message(%String*) local_unnamed_addr declare void @__quantum__rt__string_update_reference_count(%String*, i32) local_unnamed_addr -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) declare i1 @__quantum__qir__read_result(%Result*) diff --git a/pyqir/tests/test_module_attributes.py b/pyqir/tests/test_module_attributes.py new file mode 100644 index 00000000..1225163e --- /dev/null +++ b/pyqir/tests/test_module_attributes.py @@ -0,0 +1,91 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +import pyqir +from pyqir import ( + IntType, + ModuleFlagBehavior, +) +import pytest + + +def test_getting_non_existing_metadata_returns_none() -> None: + mod = pyqir.Module(pyqir.Context(), "test") + assert mod.get_flag("not found") is None + + +def test_value_metadata_can_added() -> None: + mod = pyqir.Module(pyqir.Context(), "test") + i32 = IntType(mod.context, 32) + value = pyqir.const(i32, 42) + mod.add_flag(ModuleFlagBehavior.ERROR, "expected", value) + ir = str(mod) + print(ir) + assert "!llvm.module.flags = !{!0}" in ir + assert '!0 = !{i32 1, !"expected", i32 42}' in ir + + +def test_i32_value_metadata_can_retrieved() -> None: + mod = pyqir.Module(pyqir.Context(), "test") + i32 = IntType(mod.context, 32) + value = pyqir.const(i32, 42) + mod.add_flag(ModuleFlagBehavior.ERROR, "expected", value) + flag = mod.get_flag("expected") + assert flag is not None + assert isinstance(flag, pyqir.ConstantAsMetadata) + assert isinstance(flag.value, pyqir.IntConstant) + assert flag.value.value == 42 + assert str(flag) == "i32 42" + + +def test_bool_value_metadata_can_retrieved() -> None: + mod = pyqir.Module(pyqir.Context(), "test") + i1 = IntType(mod.context, 1) + false_value = pyqir.const(i1, False) + mod.add_flag(ModuleFlagBehavior.ERROR, "id_f", false_value) + + true_value = pyqir.const(i1, True) + mod.add_flag(ModuleFlagBehavior.ERROR, "id_t", true_value) + false_flag = mod.get_flag("id_f") + assert false_flag is not None + assert isinstance(false_flag, pyqir.ConstantAsMetadata) + assert str(false_flag) == "i1 false" + assert isinstance(false_flag.value, pyqir.IntConstant) + assert false_flag.value.value == False + true_flag = mod.get_flag("id_t") + assert true_flag is not None + assert isinstance(true_flag, pyqir.ConstantAsMetadata) + assert str(true_flag) == "i1 true" + assert isinstance(true_flag.value, pyqir.IntConstant) + assert true_flag.value.value == True + + +def test_metadata_string_value_metadata_can_retrieved() -> None: + context = pyqir.Context() + source = "md string" + id = "md_id" + expected = f'!"{source}"' + mds = pyqir.MetadataString(context, source) + mod = pyqir.Module(context, "test") + + mod.add_flag(ModuleFlagBehavior.ERROR, id, mds) + flag = mod.get_flag(id) + assert flag is not None + assert isinstance(flag, pyqir.MetadataString) + assert expected == str(flag) + assert flag.value == source + + +def test_add_metadata_flag_raises_with_wrong_ownership() -> None: + mod = pyqir.Module(pyqir.Context(), "") + md = pyqir.MetadataString(pyqir.Context(), "value") + with pytest.raises(ValueError): + mod.add_flag(ModuleFlagBehavior.ERROR, "", md) + + +def test_add_value_flag_raises_with_wrong_ownership() -> None: + i32 = IntType(pyqir.Context(), 32) + value = pyqir.const(i32, 42) + mod = pyqir.Module(pyqir.Context(), "") + with pytest.raises(ValueError): + mod.add_flag(ModuleFlagBehavior.ERROR, "", value) diff --git a/pyqir/tests/test_simplemodule.py b/pyqir/tests/test_simplemodule.py new file mode 100644 index 00000000..a5e1c864 --- /dev/null +++ b/pyqir/tests/test_simplemodule.py @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +import pyqir +from pyqir import ( + required_num_qubits, + required_num_results, + is_entry_point, +) + + +def test_default_attributes_are_set() -> None: + simple = pyqir.SimpleModule("test", 2, 5) + mod = pyqir.Module.from_bitcode(pyqir.Context(), simple.bitcode()) + + entry = next(filter(is_entry_point, mod.functions)) + assert required_num_qubits(entry) == 2 + assert required_num_results(entry) == 5 + + assert mod.get_flag("qir_major_version") is not None + assert str(mod.get_flag("qir_major_version")) == "i32 1" + + assert mod.get_flag("qir_minor_version") is not None + assert str(mod.get_flag("qir_minor_version")) == "i32 0" + + assert mod.get_flag("dynamic_qubit_management") is not None + assert str(mod.get_flag("dynamic_qubit_management")) == "i1 false" + + assert mod.get_flag("dynamic_result_management") is not None + assert str(mod.get_flag("dynamic_result_management")) == "i1 false" diff --git a/qirlib/build.rs b/qirlib/build.rs index 5a88e09f..c1ddd431 100644 --- a/qirlib/build.rs +++ b/qirlib/build.rs @@ -96,6 +96,9 @@ fn main() -> Result<(), Box> { // llvm-sys components println!("cargo:rerun-if-changed=external.rs"); println!("cargo:rerun-if-changed=target.c"); + println!("cargo:rerun-if-changed=llvm-wrapper/LLVMWrapper.h"); + println!("cargo:rerun-if-changed=llvm-wrapper/MetadataWrapper.cpp"); + println!("cargo:rerun-if-changed=llvm-wrapper/ModuleWrapper.cpp"); // Download vars passed to cmake println!("cargo:rerun-if-env-changed=QIRLIB_DOWNLOAD_LLVM"); @@ -125,12 +128,15 @@ fn main() -> Result<(), Box> { println!("Linking llvm"); link_llvm(); let build_dir = get_build_dir()?; - compile_target_wrappers(&build_dir); + compile_target_wrappers(&build_dir)?; } else if cfg!(feature = "external-llvm-linking") { println!("LLVM_SYS_{{}}_PREFIX will provide the LLVM linking"); } else { println!("No LLVM linking"); } + if !cfg!(feature = "no-llvm-linking") { + compile_llvm_wrapper()?; + } Ok(()) } @@ -245,10 +251,30 @@ fn link_llvm() { } } -fn compile_target_wrappers(build_dir: &Path) { - let target_c = build_dir.join("target.c"); +fn compile_target_wrappers(build_dir: &Path) -> Result<(), Box> { + let target_c = build_dir.join("target.c").canonicalize()?; env::set_var("CFLAGS", llvm_sys::get_llvm_cflags()); Build::new().file(target_c).compile("targetwrappers"); + Ok(()) +} + +fn compile_llvm_wrapper() -> Result<(), Box> { + let mut cfg = cc::Build::new(); + cfg.warnings(false); + let cxxflags = llvm_sys::get_llvm_cxxflags(); + for flag in cxxflags.split_whitespace() { + if flag.starts_with("-flto") { + continue; + } + cfg.flag(flag); + } + cfg.cpp(true) + .cpp_link_stdlib(None) + .static_crt(true) + .file("llvm-wrapper/MetadataWrapper.cpp") + .file("llvm-wrapper/ModuleWrapper.cpp") + .compile("llvm-wrapper"); + Ok(()) } fn get_package_file_name() -> Result> { @@ -313,14 +339,30 @@ fn get_llvm_install_dir() -> PathBuf { } fn locate_llvm_config() -> Option { - let dir = get_llvm_install_dir(); - let prefix = dir.join("bin"); - let binary_name = llvm_config_name(); - let binary_path = prefix.join(binary_name); - if binary_path.as_path().exists() { - Some(binary_path) + let major = if cfg!(feature = "llvm11-0") { + "11" + } else if cfg!(feature = "llvm12-0") { + "12" + } else if cfg!(feature = "llvm13-0") { + "13" + } else if cfg!(feature = "llvm14-0") { + "14" + } else { + "unknown" + }; + if let Ok(path) = env::var(format!("DEP_LLVM_{major}_CONFIG_PATH")) { + Some(PathBuf::from(path)) } else { - None + let dir = get_llvm_install_dir(); + println!("Looking in {:?}", dir); + let prefix = dir.join("bin"); + let binary_name = llvm_config_name(); + let binary_path = prefix.join(binary_name); + if binary_path.as_path().exists() { + Some(binary_path) + } else { + None + } } } diff --git a/qirlib/external.rs b/qirlib/external.rs index e2afe5e9..5c10f09c 100644 --- a/qirlib/external.rs +++ b/qirlib/external.rs @@ -79,6 +79,22 @@ pub mod llvm_sys { .join(" ") } + pub fn get_llvm_cxxflags() -> String { + let output = llvm_config("--cxxflags"); + if target_env_is("msvc") { + // MSVC doesn't accept -W... options, so don't try to strip them and + // possibly strip something that should be retained. Also do nothing if + // the user requests it. + return output; + } + + llvm_config("--cxxflags") + .split(&[' ', '\n'][..]) + .filter(|word| !word.starts_with("-W")) + .collect::>() + .join(" ") + } + pub fn target_env_is(name: &str) -> bool { match env::var_os("CARGO_CFG_TARGET_ENV") { Some(s) => s == name, diff --git a/qirlib/llvm-wrapper/LLVMWrapper.h b/qirlib/llvm-wrapper/LLVMWrapper.h new file mode 100644 index 00000000..8ce0343a --- /dev/null +++ b/qirlib/llvm-wrapper/LLVMWrapper.h @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "llvm-c/Core.h" +#include "llvm/Support/CBindingWrapping.h" + +#define LLVM_VERSION_GE(major, minor) \ + (LLVM_VERSION_MAJOR > (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) diff --git a/qirlib/llvm-wrapper/MetadataWrapper.cpp b/qirlib/llvm-wrapper/MetadataWrapper.cpp new file mode 100644 index 00000000..ddd4e6a9 --- /dev/null +++ b/qirlib/llvm-wrapper/MetadataWrapper.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "LLVMWrapper.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Metadata.h" + +#ifdef _WIN32 +#define QIR_SHARED_API __declspec(dllexport) +#else +#define QIR_SHARED_API +#endif + +using namespace llvm; + +extern "C" +{ + QIR_SHARED_API LLVMValueRef LLVMRustExtractMDConstant(LLVMValueRef Val) + { + if (auto *MD = dyn_cast_or_null(unwrap(Val))) + if (isa(MD->getMetadata())) + if (auto *CMD = dyn_cast_or_null(MD->getMetadata())) + return wrap(CMD->getValue()); + return nullptr; + } + +} // extern "C" diff --git a/qirlib/llvm-wrapper/ModuleWrapper.cpp b/qirlib/llvm-wrapper/ModuleWrapper.cpp new file mode 100644 index 00000000..2a203bba --- /dev/null +++ b/qirlib/llvm-wrapper/ModuleWrapper.cpp @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "LLVMWrapper.h" + +#include "llvm/IR/Module.h" + +#ifdef _WIN32 +#define QIR_SHARED_API __declspec(dllexport) +#else +#define QIR_SHARED_API +#endif + +using namespace llvm; + +extern "C" +{ + enum LLVMRustModFlagBehavior + { + Error = 1, + Warning = 2, + Require = 3, + Override = 4, + Append = 5, + AppendUnique = 6, + Max = 7, +#if LLVM_VERSION_GE(15, 0) + Min = 8, +#endif + + // Markers: + ModFlagBehaviorFirstVal = LLVMRustModFlagBehavior::Error, +#if LLVM_VERSION_GE(15, 0) + ModFlagBehaviorLastVal = Min +#else + ModFlagBehaviorLastVal = Max +#endif + }; + + static llvm::Module::ModFlagBehavior + map_to_llvmRustModFlagBehavior(LLVMRustModFlagBehavior Behavior) + { + switch (Behavior) + { + case LLVMRustModFlagBehavior::Error: + return llvm::Module::ModFlagBehavior::Error; + case LLVMRustModFlagBehavior::Warning: + return llvm::Module::ModFlagBehavior::Warning; + case LLVMRustModFlagBehavior::Require: + return llvm::Module::ModFlagBehavior::Require; + case LLVMRustModFlagBehavior::Override: + return llvm::Module::ModFlagBehavior::Override; + case LLVMRustModFlagBehavior::Append: + return llvm::Module::ModFlagBehavior::Append; + case LLVMRustModFlagBehavior::AppendUnique: + return llvm::Module::ModFlagBehavior::AppendUnique; + case LLVMRustModFlagBehavior::Max: + return llvm::Module::ModFlagBehavior::Max; +#if LLVM_VERSION_GE(15, 0) + case LLVMRustModFlagBehavior::Min: + return llvm::Module::ModFlagBehavior::Min; +#endif + } + llvm_unreachable("Unknown LLVMRustModFlagBehavior"); + } + + QIR_SHARED_API void LLVMRustAddModuleFlag(LLVMModuleRef M, LLVMRustModFlagBehavior Behavior, + const char *Key, size_t KeyLen, + LLVMMetadataRef Val) + { + + llvm::unwrap(M)->addModuleFlag(map_to_llvmRustModFlagBehavior(Behavior), {Key, KeyLen}, llvm::unwrap(Val)); + } + +} // extern "C" diff --git a/qirlib/resources/tests/module/many_required_qubits_results.ll b/qirlib/resources/tests/module/many_required_qubits_results.ll index 19034029..e0dd4358 100644 --- a/qirlib/resources/tests/module/many_required_qubits_results.ll +++ b/qirlib/resources/tests/module/many_required_qubits_results.ll @@ -5,4 +5,4 @@ define void @main() #0 { ret void } -attributes #0 = { "EntryPoint" "requiredQubits"="5" "requiredResults"="7" } +attributes #0 = { "entry_point" "num_required_qubits"="5" "num_required_results"="7" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/module/one_required_qubit.ll b/qirlib/resources/tests/module/one_required_qubit.ll index b31b4caa..23215bbd 100644 --- a/qirlib/resources/tests/module/one_required_qubit.ll +++ b/qirlib/resources/tests/module/one_required_qubit.ll @@ -5,4 +5,4 @@ define void @main() #0 { ret void } -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/module/one_required_result.ll b/qirlib/resources/tests/module/one_required_result.ll index d30f915f..5c0cbaa9 100644 --- a/qirlib/resources/tests/module/one_required_result.ll +++ b/qirlib/resources/tests/module/one_required_result.ll @@ -5,4 +5,4 @@ define void @main() #0 { ret void } -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/module/zero_required_qubits_results.ll b/qirlib/resources/tests/module/zero_required_qubits_results.ll index bafd0f9c..dd085881 100644 --- a/qirlib/resources/tests/module/zero_required_qubits_results.ll +++ b/qirlib/resources/tests/module/zero_required_qubits_results.ll @@ -5,4 +5,4 @@ define void @main() #0 { ret void } -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/barrier.ll b/qirlib/resources/tests/qis/barrier.ll index 47f436fa..c088618f 100644 --- a/qirlib/resources/tests/qis/barrier.ll +++ b/qirlib/resources/tests/qis/barrier.ll @@ -8,4 +8,4 @@ define void @main() #0 { declare void @__quantum__qis__barrier__body() -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/ccx.ll b/qirlib/resources/tests/qis/ccx.ll index 448680af..b1d58887 100644 --- a/qirlib/resources/tests/qis/ccx.ll +++ b/qirlib/resources/tests/qis/ccx.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="3" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="3" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/cx.ll b/qirlib/resources/tests/qis/cx.ll index 3635c434..a10a6f86 100644 --- a/qirlib/resources/tests/qis/cx.ll +++ b/qirlib/resources/tests/qis/cx.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="2" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="2" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/cz.ll b/qirlib/resources/tests/qis/cz.ll index 61afbc7c..3407dd4c 100644 --- a/qirlib/resources/tests/qis/cz.ll +++ b/qirlib/resources/tests/qis/cz.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="2" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="2" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/empty_if.ll b/qirlib/resources/tests/qis/empty_if.ll index aaa7f94f..e6f0c9cc 100644 --- a/qirlib/resources/tests/qis/empty_if.ll +++ b/qirlib/resources/tests/qis/empty_if.ll @@ -19,8 +19,9 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/h.ll b/qirlib/resources/tests/qis/h.ll index fe45c122..09b52d1b 100644 --- a/qirlib/resources/tests/qis/h.ll +++ b/qirlib/resources/tests/qis/h.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__h__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/if_else.ll b/qirlib/resources/tests/qis/if_else.ll index 25b07185..7b1934d0 100644 --- a/qirlib/resources/tests/qis/if_else.ll +++ b/qirlib/resources/tests/qis/if_else.ll @@ -20,10 +20,11 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_else_continue.ll b/qirlib/resources/tests/qis/if_else_continue.ll index 1db2a6f5..1737d457 100644 --- a/qirlib/resources/tests/qis/if_else_continue.ll +++ b/qirlib/resources/tests/qis/if_else_continue.ll @@ -21,7 +21,7 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) @@ -29,4 +29,5 @@ declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__h__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_else_else.ll b/qirlib/resources/tests/qis/if_else_else.ll index eb387f83..f3e7d094 100644 --- a/qirlib/resources/tests/qis/if_else_else.ll +++ b/qirlib/resources/tests/qis/if_else_else.ll @@ -31,10 +31,11 @@ continue3: ; preds = %else2, %then1 br label %continue } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="2" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="2" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_else_then.ll b/qirlib/resources/tests/qis/if_else_then.ll index 05f02e51..3668f607 100644 --- a/qirlib/resources/tests/qis/if_else_then.ll +++ b/qirlib/resources/tests/qis/if_else_then.ll @@ -31,10 +31,11 @@ continue3: ; preds = %else2, %then1 br label %continue } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="2" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="2" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_then.ll b/qirlib/resources/tests/qis/if_then.ll index 88f3d548..fe113e19 100644 --- a/qirlib/resources/tests/qis/if_then.ll +++ b/qirlib/resources/tests/qis/if_then.ll @@ -20,10 +20,11 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_then_continue.ll b/qirlib/resources/tests/qis/if_then_continue.ll index b083c5ec..4f8d8f1f 100644 --- a/qirlib/resources/tests/qis/if_then_continue.ll +++ b/qirlib/resources/tests/qis/if_then_continue.ll @@ -21,7 +21,7 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) @@ -29,4 +29,5 @@ declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__h__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_then_else.ll b/qirlib/resources/tests/qis/if_then_else.ll index 60b8c2cf..6bbf105a 100644 --- a/qirlib/resources/tests/qis/if_then_else.ll +++ b/qirlib/resources/tests/qis/if_then_else.ll @@ -31,10 +31,11 @@ continue3: ; preds = %else2, %then1 br label %continue } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="2" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="2" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_then_else_continue.ll b/qirlib/resources/tests/qis/if_then_else_continue.ll index e990141b..8928256d 100644 --- a/qirlib/resources/tests/qis/if_then_else_continue.ll +++ b/qirlib/resources/tests/qis/if_then_else_continue.ll @@ -22,7 +22,7 @@ continue: ; preds = %else, %then ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) @@ -32,4 +32,5 @@ declare void @__quantum__qis__y__body(%Qubit*) declare void @__quantum__qis__h__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_then_then.ll b/qirlib/resources/tests/qis/if_then_then.ll index 8d70f8fb..8fa4453d 100644 --- a/qirlib/resources/tests/qis/if_then_then.ll +++ b/qirlib/resources/tests/qis/if_then_then.ll @@ -31,10 +31,11 @@ continue3: ; preds = %else2, %then1 br label %continue } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare i1 @__quantum__qis__read_result__body(%Result*) declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="2" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="2" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/if_unmeasured_result.ll b/qirlib/resources/tests/qis/if_unmeasured_result.ll index bba80979..30ac1228 100644 --- a/qirlib/resources/tests/qis/if_unmeasured_result.ll +++ b/qirlib/resources/tests/qis/if_unmeasured_result.ll @@ -26,4 +26,4 @@ declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__h__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/mz.ll b/qirlib/resources/tests/qis/mz.ll index 2ff884f9..592620ad 100644 --- a/qirlib/resources/tests/qis/mz.ll +++ b/qirlib/resources/tests/qis/mz.ll @@ -9,6 +9,7 @@ define void @main() #0 { ret void } -declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } diff --git a/qirlib/resources/tests/qis/read_result.ll b/qirlib/resources/tests/qis/read_result.ll index d4fb87d9..c11accfb 100644 --- a/qirlib/resources/tests/qis/read_result.ll +++ b/qirlib/resources/tests/qis/read_result.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare i1 @__quantum__qis__read_result__body(%Result*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/reset.ll b/qirlib/resources/tests/qis/reset.ll index 4530487c..19f35ec2 100644 --- a/qirlib/resources/tests/qis/reset.ll +++ b/qirlib/resources/tests/qis/reset.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__reset__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/rx.ll b/qirlib/resources/tests/qis/rx.ll index 2fd52620..0774883a 100644 --- a/qirlib/resources/tests/qis/rx.ll +++ b/qirlib/resources/tests/qis/rx.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__rx__body(double, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/ry.ll b/qirlib/resources/tests/qis/ry.ll index 6623fdd7..e0d30499 100644 --- a/qirlib/resources/tests/qis/ry.ll +++ b/qirlib/resources/tests/qis/ry.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__ry__body(double, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/rz.ll b/qirlib/resources/tests/qis/rz.ll index eafe35c4..8a27c8cf 100644 --- a/qirlib/resources/tests/qis/rz.ll +++ b/qirlib/resources/tests/qis/rz.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__rz__body(double, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/s.ll b/qirlib/resources/tests/qis/s.ll index 0971b015..d9059629 100644 --- a/qirlib/resources/tests/qis/s.ll +++ b/qirlib/resources/tests/qis/s.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__s__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/s_adj.ll b/qirlib/resources/tests/qis/s_adj.ll index ec334adc..e26ee221 100644 --- a/qirlib/resources/tests/qis/s_adj.ll +++ b/qirlib/resources/tests/qis/s_adj.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__s__adj(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/swap.ll b/qirlib/resources/tests/qis/swap.ll index ab0d6096..b75af900 100644 --- a/qirlib/resources/tests/qis/swap.ll +++ b/qirlib/resources/tests/qis/swap.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="2" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="2" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/t.ll b/qirlib/resources/tests/qis/t.ll index bda3b047..4b383595 100644 --- a/qirlib/resources/tests/qis/t.ll +++ b/qirlib/resources/tests/qis/t.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__t__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/t_adj.ll b/qirlib/resources/tests/qis/t_adj.ll index a65947b9..5abef9cf 100644 --- a/qirlib/resources/tests/qis/t_adj.ll +++ b/qirlib/resources/tests/qis/t_adj.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__t__adj(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/x.ll b/qirlib/resources/tests/qis/x.ll index dcde67f8..659a402e 100644 --- a/qirlib/resources/tests/qis/x.ll +++ b/qirlib/resources/tests/qis/x.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__x__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/y.ll b/qirlib/resources/tests/qis/y.ll index 6eafa53a..aa19a27c 100644 --- a/qirlib/resources/tests/qis/y.ll +++ b/qirlib/resources/tests/qis/y.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__y__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/qis/z.ll b/qirlib/resources/tests/qis/z.ll index bf2b7030..a378734b 100644 --- a/qirlib/resources/tests/qis/z.ll +++ b/qirlib/resources/tests/qis/z.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__qis__z__body(%Qubit*) -attributes #0 = { "EntryPoint" "requiredQubits"="1" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/rt/array_record_output.ll b/qirlib/resources/tests/rt/array_record_output.ll index 25ed1547..1c3d9553 100644 --- a/qirlib/resources/tests/rt/array_record_output.ll +++ b/qirlib/resources/tests/rt/array_record_output.ll @@ -8,4 +8,4 @@ define void @main() #0 { declare void @__quantum__rt__array_record_output(i64, i8*) -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/rt/initialize.ll b/qirlib/resources/tests/rt/initialize.ll index d3ae7678..5a88b41f 100644 --- a/qirlib/resources/tests/rt/initialize.ll +++ b/qirlib/resources/tests/rt/initialize.ll @@ -8,4 +8,4 @@ define void @main() #0 { declare void @__quantum__rt__initialize(i8*) -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/rt/result_record_output.ll b/qirlib/resources/tests/rt/result_record_output.ll index 2fe7dc74..34bc4a08 100644 --- a/qirlib/resources/tests/rt/result_record_output.ll +++ b/qirlib/resources/tests/rt/result_record_output.ll @@ -10,4 +10,4 @@ define void @main() #0 { declare void @__quantum__rt__result_record_output(%Result*, i8*) -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="1" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="1" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/rt/tuple_record_output.ll b/qirlib/resources/tests/rt/tuple_record_output.ll index 4a1cf7e3..8c277866 100644 --- a/qirlib/resources/tests/rt/tuple_record_output.ll +++ b/qirlib/resources/tests/rt/tuple_record_output.ll @@ -8,4 +8,4 @@ define void @main() #0 { declare void @__quantum__rt__tuple_record_output(i64, i8*) -attributes #0 = { "EntryPoint" "requiredQubits"="0" "requiredResults"="0" } +attributes #0 = { "entry_point" "num_required_qubits"="0" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/resources/tests/test_unknown_external_func.ll b/qirlib/resources/tests/test_unknown_external_func.ll deleted file mode 100644 index 4e73df08..00000000 --- a/qirlib/resources/tests/test_unknown_external_func.ll +++ /dev/null @@ -1,14 +0,0 @@ -; ModuleID = 'test_unknown_external_func' -source_filename = "test_unknown_external_func" - -%String = type opaque - -declare %String* @__quantum__rt__bool_to_string(i1) - -define void @main() #1 { -entry: - call %String* @__quantum__rt__bool_to_string(i1 1) - ret void -} - -attributes #1 = { "EntryPoint" } diff --git a/qirlib/src/lib.rs b/qirlib/src/lib.rs index dedc49eb..5711e787 100644 --- a/qirlib/src/lib.rs +++ b/qirlib/src/lib.rs @@ -20,6 +20,12 @@ extern crate llvm_sys_140 as llvm_sys; #[cfg(not(feature = "no-llvm-linking"))] pub mod builder; #[cfg(not(feature = "no-llvm-linking"))] +pub(crate) mod llvm_wrapper; +#[cfg(not(feature = "no-llvm-linking"))] +pub mod metadata; +#[cfg(not(feature = "no-llvm-linking"))] +pub mod module; +#[cfg(not(feature = "no-llvm-linking"))] pub mod qis; #[cfg(not(feature = "no-llvm-linking"))] pub mod rt; diff --git a/qirlib/src/llvm_wrapper.rs b/qirlib/src/llvm_wrapper.rs new file mode 100644 index 00000000..cbe85997 --- /dev/null +++ b/qirlib/src/llvm_wrapper.rs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use llvm_sys::prelude::{LLVMMetadataRef, LLVMModuleRef, LLVMValueRef}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum LLVMRustModFlagBehavior { + Error = 1, + Warning = 2, + Require = 3, + Override = 4, + Append = 5, + AppendUnique = 6, + Max = 7, + #[cfg(feature = "llvm15-0")] + Min = 8, +} + +extern "C" { + /// Add a module-level flag to the module-level flags metadata if it doesn't already exist. + pub fn LLVMRustAddModuleFlag( + M: LLVMModuleRef, + Behavior: LLVMRustModFlagBehavior, + Key: *const std::ffi::c_char, + KeyLen: std::ffi::c_uint, + Val: LLVMMetadataRef, + ); + pub fn LLVMRustExtractMDConstant(Val: LLVMValueRef) -> LLVMValueRef; +} diff --git a/qirlib/src/metadata.rs b/qirlib/src/metadata.rs new file mode 100644 index 00000000..077c012d --- /dev/null +++ b/qirlib/src/metadata.rs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use llvm_sys::prelude::LLVMValueRef; + +use crate::llvm_wrapper::LLVMRustExtractMDConstant; + +pub unsafe fn extract_constant(value: LLVMValueRef) -> Option { + let constant_value = unsafe { LLVMRustExtractMDConstant(value) }; + if constant_value.is_null() { + None + } else { + Some(constant_value) + } +} diff --git a/qirlib/src/module.rs b/qirlib/src/module.rs new file mode 100644 index 00000000..342a5817 --- /dev/null +++ b/qirlib/src/module.rs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use llvm_sys::prelude::{LLVMMetadataRef, LLVMModuleRef}; + +use crate::llvm_wrapper::{LLVMRustAddModuleFlag, LLVMRustModFlagBehavior}; + +pub enum FlagBehavior { + Error, + Warning, + Require, + Override, + Append, + AppendUnique, + Max, +} + +impl From for FlagBehavior { + fn from(flag: LLVMRustModFlagBehavior) -> Self { + match flag { + LLVMRustModFlagBehavior::Error => FlagBehavior::Error, + LLVMRustModFlagBehavior::Warning => FlagBehavior::Warning, + LLVMRustModFlagBehavior::Require => FlagBehavior::Require, + LLVMRustModFlagBehavior::Override => FlagBehavior::Override, + LLVMRustModFlagBehavior::Append => FlagBehavior::Append, + LLVMRustModFlagBehavior::AppendUnique => FlagBehavior::AppendUnique, + LLVMRustModFlagBehavior::Max => FlagBehavior::Max, + } + } +} + +impl From for LLVMRustModFlagBehavior { + fn from(flag: FlagBehavior) -> Self { + match flag { + FlagBehavior::Error => LLVMRustModFlagBehavior::Error, + FlagBehavior::Warning => LLVMRustModFlagBehavior::Warning, + FlagBehavior::Require => LLVMRustModFlagBehavior::Require, + FlagBehavior::Override => LLVMRustModFlagBehavior::Override, + FlagBehavior::Append => LLVMRustModFlagBehavior::Append, + FlagBehavior::AppendUnique => LLVMRustModFlagBehavior::AppendUnique, + FlagBehavior::Max => LLVMRustModFlagBehavior::Max, + } + } +} + +pub unsafe fn add_flag( + module: LLVMModuleRef, + behavior: FlagBehavior, + id: &str, + md: LLVMMetadataRef, +) { + unsafe { + LLVMRustAddModuleFlag( + module, + behavior + .try_into() + .expect("Could not convert behavior for the current version of LLVM"), + id.as_ptr() as *mut std::ffi::c_char, + id.len().try_into().unwrap(), + md, + ); + } +} diff --git a/qirlib/src/qis.rs b/qirlib/src/qis.rs index aa0839a4..83e7e7fe 100644 --- a/qirlib/src/qis.rs +++ b/qirlib/src/qis.rs @@ -10,6 +10,8 @@ use crate::{ }, }; +use llvm_sys::LLVMAttributeFunctionIndex; + #[allow(clippy::wildcard_imports)] use llvm_sys::{core::*, prelude::*}; @@ -184,11 +186,34 @@ unsafe fn build_read_result(builder: LLVMBuilderRef, result: LLVMValueRef) -> LL unsafe fn mz(module: LLVMModuleRef) -> LLVMValueRef { let context = LLVMGetModuleContext(module); + let result_type = types::result(context); let ty = function_type( LLVMVoidTypeInContext(context), - &mut [types::qubit(context), types::result(context)], + &mut [types::qubit(context), result_type], + ); + + let function = declare_qis(module, "mz", Functor::Body, ty); + let attr_name = "writeonly"; + let kind_id = LLVMGetEnumAttributeKindForName(attr_name.as_ptr().cast::(), attr_name.len()); + let attr = LLVMCreateEnumAttribute(context, kind_id, 0); + let result_param_index = 2; // indices are 1 based. + LLVMAddAttributeAtIndex(function, result_param_index, attr); + + add_irreversible_attr(context, function); + function +} + +#[allow(clippy::cast_possible_truncation)] +unsafe fn add_irreversible_attr(context: LLVMContextRef, function: LLVMValueRef) { + let irreversable = "irreversible"; + let irreversable_attr = LLVMCreateStringAttribute( + context, + irreversable.as_ptr().cast::(), + irreversable.len() as u32, + "".as_ptr().cast::(), + 0, ); - declare_qis(module, "mz", Functor::Body, ty) + LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex, irreversable_attr); } unsafe fn read_result(module: LLVMModuleRef) -> LLVMValueRef { diff --git a/qirlib/src/tests.rs b/qirlib/src/tests.rs index b6ba470a..f791db2d 100644 --- a/qirlib/src/tests.rs +++ b/qirlib/src/tests.rs @@ -178,6 +178,8 @@ fn build_ir( cstr!("main"), required_num_qubits, required_num_results, + "custom", + "", ); let builder = Builder::new(&context); diff --git a/qirlib/src/values.rs b/qirlib/src/values.rs index d2e0393e..1660cb72 100644 --- a/qirlib/src/values.rs +++ b/qirlib/src/values.rs @@ -44,30 +44,46 @@ pub unsafe fn entry_point( name: &CStr, required_num_qubits: u64, required_num_results: u64, + qir_profiles: &str, + output_labeling_schema: &str, ) -> LLVMValueRef { let context = LLVMGetModuleContext(module); let void = LLVMVoidTypeInContext(context); let ty = LLVMFunctionType(void, [].as_mut_ptr(), 0, 0); let function = LLVMAddFunction(module, name.as_ptr(), ty); - add_string_attribute(function, b"EntryPoint", b""); + add_string_attribute(function, b"entry_point", b""); add_string_attribute( function, - b"requiredQubits", + b"num_required_qubits", required_num_qubits.to_string().as_bytes(), ); add_string_attribute( function, - b"requiredResults", + b"num_required_results", required_num_results.to_string().as_bytes(), ); + add_string_attribute(function, b"qir_profiles", qir_profiles.as_bytes()); + + add_string_attribute( + function, + b"output_labeling_schema", + output_labeling_schema.as_bytes(), + ); + function } pub unsafe fn is_entry_point(function: LLVMValueRef) -> bool { LLVMGetValueKind(function) == LLVMValueKind::LLVMFunctionValueKind - && get_string_attribute(function, LLVMAttributeFunctionIndex, b"EntryPoint").is_some() + && (get_string_attribute(function, LLVMAttributeFunctionIndex, b"entry_point").is_some() + || get_string_attribute(function, LLVMAttributeFunctionIndex, b"EntryPoint").is_some()) +} + +pub unsafe fn is_irreversible(function: LLVMValueRef) -> bool { + LLVMGetValueKind(function) == LLVMValueKind::LLVMFunctionValueKind + && get_string_attribute(function, LLVMAttributeFunctionIndex, b"irreversible").is_some() } pub unsafe fn is_interop_friendly(function: LLVMValueRef) -> bool { @@ -78,7 +94,11 @@ pub unsafe fn is_interop_friendly(function: LLVMValueRef) -> bool { pub unsafe fn required_num_qubits(function: LLVMValueRef) -> Option { if LLVMGetValueKind(function) == LLVMValueKind::LLVMFunctionValueKind { let required_qubits = - get_string_attribute(function, LLVMAttributeFunctionIndex, b"requiredQubits")?; + get_string_attribute(function, LLVMAttributeFunctionIndex, b"num_required_qubits") + .or_else(|| { + get_string_attribute(function, LLVMAttributeFunctionIndex, b"requiredQubits") + })?; + let mut len = 0; let value = LLVMGetStringAttributeValue(required_qubits.as_ptr(), &mut len); let value = slice::from_raw_parts(value.cast(), len.try_into().unwrap()); @@ -90,10 +110,16 @@ pub unsafe fn required_num_qubits(function: LLVMValueRef) -> Option { pub unsafe fn required_num_results(function: LLVMValueRef) -> Option { if LLVMGetValueKind(function) == LLVMValueKind::LLVMFunctionValueKind { - let required_qubits = - get_string_attribute(function, LLVMAttributeFunctionIndex, b"requiredResults")?; + let required_results = get_string_attribute( + function, + LLVMAttributeFunctionIndex, + b"num_required_results", + ) + .or_else(|| { + get_string_attribute(function, LLVMAttributeFunctionIndex, b"requiredResults") + })?; let mut len = 0; - let value = LLVMGetStringAttributeValue(required_qubits.as_ptr(), &mut len); + let value = LLVMGetStringAttributeValue(required_results.as_ptr(), &mut len); let value = slice::from_raw_parts(value.cast(), len.try_into().unwrap()); str::from_utf8(value).ok()?.parse().ok() } else {