diff --git a/src/QsCompiler/Compiler/RewriteSteps/QirGeneration.cs b/src/QsCompiler/Compiler/RewriteSteps/QirGeneration.cs index 656a66dc16..9c573fb8de 100644 --- a/src/QsCompiler/Compiler/RewriteSteps/QirGeneration.cs +++ b/src/QsCompiler/Compiler/RewriteSteps/QirGeneration.cs @@ -68,7 +68,7 @@ public bool PreconditionVerification(QsCompilation compilation) /// public bool Transformation(QsCompilation compilation, out QsCompilation transformed) { - var generator = new Generator(compilation, new Configuration()); + var generator = new Generator(compilation); generator.Apply(); generator.Emit(this.outputFile); transformed = compilation; diff --git a/src/QsCompiler/Core/SyntaxGenerator.fs b/src/QsCompiler/Core/SyntaxGenerator.fs index f6361bc0ee..d067611f58 100644 --- a/src/QsCompiler/Core/SyntaxGenerator.fs +++ b/src/QsCompiler/Core/SyntaxGenerator.fs @@ -341,6 +341,17 @@ module SyntaxGenerator = let QubitArrayType = Qubit |> ResolvedType.New |> ArrayType |> ResolvedType.New + /// Recursively extracts and returns all tuple items in the given tuple. + let ExtractInnerItems (this: ITuple) = + let rec extractAll = + function + | Tuple items -> items |> Seq.collect extractAll + | Item item -> seq { yield item } + | Missing -> ArgumentException "missing item in tuple" |> raise + | _ -> ArgumentException "invalid item in tuple" |> raise + + this |> extractAll + /// Given a QsTuple, recursively extracts and returns all of its items. let ExtractItems (this: QsTuple<_>) = this.Items.ToImmutableArray() diff --git a/src/QsCompiler/QirGeneration/Configuration.cs b/src/QsCompiler/QirGeneration/Configuration.cs deleted file mode 100644 index 0ea10ab488..0000000000 --- a/src/QsCompiler/QirGeneration/Configuration.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.Quantum.QIR; - -namespace Microsoft.Quantum.QsCompiler.QIR -{ - /// - /// Class that contains all configurable settings for the QIR emission. - /// - public class Configuration - { - private static readonly ImmutableDictionary ClangInteropTypeMapping = - ImmutableDictionary.CreateRange(new Dictionary - { - [TypeNames.Result] = "class.RESULT", - [TypeNames.Array] = "struct.quantum::Array", - [TypeNames.Callable] = "struct.quantum::Callable", - [TypeNames.Tuple] = "struct.quantum::TupleHeader", - [TypeNames.Qubit] = "class.QUBIT" - }); - - internal readonly ImmutableDictionary InteropTypeMapping; - - /// - /// Constructs a class instance storing the configurable settings for QIR emission. - /// - /// - /// Optional parameter that maps the name of a QIR type to the name of the corresponding interop type. - /// The mapping specifies with which type names the QIR types are replaced with - /// when generating the interop wrappers and entry point(s). - /// - public Configuration(Dictionary? interopTypeMapping = null) - { - this.InteropTypeMapping = interopTypeMapping != null - ? interopTypeMapping.ToImmutableDictionary() - : ClangInteropTypeMapping; - } - } -} diff --git a/src/QsCompiler/QirGeneration/Context.cs b/src/QsCompiler/QirGeneration/Context.cs index 1ace79e8e4..add969def2 100644 --- a/src/QsCompiler/QirGeneration/Context.cs +++ b/src/QsCompiler/QirGeneration/Context.cs @@ -41,43 +41,8 @@ static GenerationContext() LibContext.RegisterTarget(CodeGenTarget.Native); } - /// - /// This type is used to map Q# types to interop-friendly types. - /// - private class ArgMapping - { - internal readonly string BaseName; - - /// - /// The first item contains the array element type, and the second item contains the array count name. - /// If is not null, then is null. - /// - private readonly (ResolvedType, string)? arrayInfo; - - internal bool IsArray => this.arrayInfo != null; - internal ResolvedType ArrayElementType => this.arrayInfo?.Item1 ?? throw new InvalidOperationException("not an array"); - internal string ArrayCountName => this.arrayInfo?.Item2 ?? throw new InvalidOperationException("not an array"); - - private ArgMapping(string baseName, (ResolvedType, string)? arrayInfo = null) - { - this.BaseName = baseName; - this.arrayInfo = arrayInfo; - } - - internal static ArgMapping Create(string baseName) => - new ArgMapping(baseName); - - internal ArgMapping WithArrayInfo(ResolvedType arrayType, string arrayCountName) => - new ArgMapping(this.BaseName, arrayInfo: (arrayType, arrayCountName)); - } - #region Member variables - /// - /// The configuration for QIR generation. - /// - public readonly Configuration Config; - /// /// The context used for QIR generation. /// @@ -91,12 +56,6 @@ internal ArgMapping WithArrayInfo(ResolvedType arrayType, string arrayCountName) /// public readonly BitcodeModule Module; - /// - /// The module used for constructing functions to facilitate interoperability. - /// - /// - public readonly BitcodeModule InteropModule; - /// /// The used QIR types. /// @@ -151,7 +110,6 @@ internal ArgMapping WithArrayInfo(ResolvedType arrayType, string arrayCountName) private readonly Dictionary uniqueLocalNames = new Dictionary(); private readonly Dictionary uniqueGlobalNames = new Dictionary(); - private readonly Dictionary interopType = new Dictionary(); private readonly List<(IrFunction, Action>)> liftedPartialApplications = new List<(IrFunction, Action>)>(); private readonly Dictionary callableTables = new Dictionary(); private readonly Dictionary memoryManagementTables = new Dictionary(); @@ -187,14 +145,11 @@ internal void EndBranch() => /// 2.) the runtime library needs to be initialized by calling , and /// 3.) the quantum instructions set needs to be registered by calling . /// - /// The compilation unit for which QIR is generated. - /// The configuration for QIR generation. - internal GenerationContext(IEnumerable syntaxTree, Configuration config) + /// The syntax tree for which QIR is generated. + internal GenerationContext(IEnumerable syntaxTree) { - this.Config = config; this.Context = new Context(); this.Module = this.Context.CreateBitcodeModule(); - this.InteropModule = this.Context.CreateBitcodeModule("bridge"); this.Types = new Types(this.Context); this.Constants = new Constants(this.Context, this.Module, this.Types); @@ -279,6 +234,7 @@ public static string FunctionName(QsQualifiedName fullName, QsSpecializationKind /// Generates a mangled name for a callable specialization wrapper. /// Wrapper names are the mangled specialization name followed by double underscore and "wrapper". /// + /// The callable's qualified name /// The specialization kind. /// The mangled name for the wrapper. public static string FunctionWrapperName(QsQualifiedName fullName, QsSpecializationKind kind) => @@ -322,8 +278,8 @@ internal static bool TryGetTargetInstructionName(QsCallable callable, [MaybeNull /// public void InitializeRuntimeLibrary() { - // int library functions - this.runtimeLibrary.AddFunction(RuntimeLibrary.IntPower, this.Types.Int, this.Types.Int, this.Context.Int32Type); + // Q# specific helpers + this.runtimeLibrary.AddFunction(RuntimeLibrary.HeapAllocate, this.Context.Int8Type.CreatePointerType(), this.Context.Int64Type); // result library functions this.runtimeLibrary.AddFunction(RuntimeLibrary.ResultUpdateReferenceCount, this.Context.VoidType, this.Types.Result, this.Types.Int); @@ -331,6 +287,8 @@ public void InitializeRuntimeLibrary() // string library functions this.runtimeLibrary.AddFunction(RuntimeLibrary.StringCreate, this.Types.String, this.Context.Int32Type, this.Types.DataArrayPointer); + this.runtimeLibrary.AddFunction(RuntimeLibrary.StringGetLength, this.Context.Int32Type, this.Types.String); + this.runtimeLibrary.AddFunction(RuntimeLibrary.StringGetData, this.Types.DataArrayPointer, this.Types.String); this.runtimeLibrary.AddFunction(RuntimeLibrary.StringUpdateReferenceCount, this.Context.VoidType, this.Types.String, this.Types.Int); this.runtimeLibrary.AddFunction(RuntimeLibrary.StringConcatenate, this.Types.String, this.Types.String, this.Types.String); this.runtimeLibrary.AddFunction(RuntimeLibrary.StringEqual, this.Context.BoolType, this.Types.String, this.Types.String); @@ -373,16 +331,12 @@ public void InitializeRuntimeLibrary() this.runtimeLibrary.AddFunction(RuntimeLibrary.TupleCopy, this.Types.Tuple, this.Types.Tuple, this.Context.BoolType); // array library functions - this.runtimeLibrary.AddVarArgsFunction(RuntimeLibrary.ArrayCreate, this.Types.Array, this.Context.Int32Type, this.Context.Int32Type); - this.runtimeLibrary.AddVarArgsFunction(RuntimeLibrary.ArrayGetElementPtr, this.Context.Int8Type.CreatePointerType(), this.Types.Array); - // TODO: figure out how to call a varargs function and get rid of these two functions this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayCreate1d, this.Types.Array, this.Context.Int32Type, this.Context.Int64Type); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayGetElementPtr1d, this.Context.Int8Type.CreatePointerType(), this.Types.Array, this.Context.Int64Type); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayUpdateAliasCount, this.Context.VoidType, this.Types.Array, this.Types.Int); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayUpdateReferenceCount, this.Context.VoidType, this.Types.Array, this.Types.Int); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayCopy, this.Types.Array, this.Types.Array, this.Context.BoolType); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayConcatenate, this.Types.Array, this.Types.Array, this.Types.Array); - this.runtimeLibrary.AddFunction(RuntimeLibrary.ArraySlice, this.Types.Array, this.Context.Int32Type, this.Types.Array, this.Types.Range); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArraySlice1d, this.Types.Array, this.Types.Array, this.Types.Range, this.Context.BoolType); this.runtimeLibrary.AddFunction(RuntimeLibrary.ArrayGetSize1d, this.Context.Int64Type, this.Types.Array); @@ -435,214 +389,29 @@ public void RegisterQuantumInstructionSet() } /// - /// Writes the current content to the output file. - /// - public void Emit(string fileName, bool overwrite = true, bool generateInteropWrappers = false) - { - var bridgeFile = Path.Combine( - Path.GetDirectoryName(fileName), - Path.GetFileNameWithoutExtension(fileName) + "_bridge.ll"); - var existing = new[] - { - File.Exists(fileName) ? fileName : null, - generateInteropWrappers && File.Exists(bridgeFile) ? bridgeFile : null, - }; - - if (!overwrite && existing.Any(s => s != null)) - { - var argStr = string.Join(", ", existing.Where(s => s == null)); - throw new ArgumentException($"The following file(s) already exist(s): {argStr}"); - } - - this.GenerateRequiredFunctions(); - - if (!this.Module.Verify(out string validationErrors)) - { - File.WriteAllText(fileName, $"LLVM errors:{Environment.NewLine}{validationErrors}"); - } - - if (!this.Module.WriteToTextFile(fileName, out string errorMessage)) - { - throw new IOException(errorMessage); - } - - var bitcodeFileName = Path.ChangeExtension(fileName, "bc"); - this.Module.WriteToFile(bitcodeFileName); - - // Generate the wrappers for the runtime library that were used, if requested - if (generateInteropWrappers) - { - foreach (var kvp in this.runtimeLibrary) - { - this.GenerateInterop(kvp.Value, kvp.Key); - } - - foreach (var c in this.globalCallables.Values) - { - if (TryGetTargetInstructionName(c, out var name)) - { - var func = this.quantumInstructionSet.GetOrCreateFunction(name); - this.GenerateInterop(func, name); - } - } - - if (!this.InteropModule.Verify(out string bridgeValidationErrors)) - { - File.WriteAllText(bridgeFile, $"LLVM errors:{Environment.NewLine}{bridgeValidationErrors}"); - } - else if (!this.InteropModule.WriteToTextFile(bridgeFile, out string bridgeError)) - { - throw new IOException(bridgeError); - } - - var bitcodeBridgeFile = Path.ChangeExtension(bridgeFile, "bc"); - this.InteropModule.WriteToFile(bitcodeBridgeFile); - } - } - - #endregion - - #region Interop utils - - /// - /// Generates an interop-friendly wrapper around the Q# entry point using the configured type mapping. + /// Generates an interop-friendly wrapper around the callable with the given name such that it can be invoked + /// from within native code without relying on the QIR runtime or adhering to the QIR specification. + /// See and + /// for more detail. + ///
+ /// If an attribute name is specified, adds an attribute with the given name to the created wrapper. + /// If no wrapper needed to be created because the signature of the callable is interop-friendly, + /// adds the attribute to the existing function. ///
- /// The namespace-qualified name of the Q# entry point - public void GenerateEntryPoint(QsQualifiedName qualifiedName) + /// The function name to give the wrapper. + /// The fully qualified name of the Q# callable to create a wrapper for. + /// Optionally the name of the attribute to attach. + /// No callable with the given name exists in the compilation. + public void CreateInteropWrapper(string wrapperName, QsQualifiedName qualifiedName, string? attributeName = null) { - // Unfortunately this is different enough from all of the other type mapping we do to require - // an actual different routine. Blech... - IEnumerable MapType(ArgumentTuple t, List nameList, List mappingList) - { - if (t is ArgumentTuple.QsTuple tuple) - { - foreach (var inner in tuple.Item) - { - foreach (var mapped in MapType(inner, nameList, mappingList)) - { - yield return mapped; - } - } - } - else if (t is ArgumentTuple.QsTupleItem item && item.Item.VariableName is QsLocalSymbol.ValidName varName) - { - var baseName = varName.Item; - var map = ArgMapping.Create(baseName); - switch (item.Item.Type.Resolution) - { - case ResolvedTypeKind.ArrayType elementType: - yield return this.Context.Int64Type; - var elementTypeRef = this.LlvmTypeFromQsharpType(elementType.Item); - yield return elementTypeRef.CreatePointerType(); - var arrayCountName = $"{baseName}__count"; - nameList.Add(arrayCountName); - nameList.Add(baseName); - map = map.WithArrayInfo(elementType.Item, arrayCountName); - mappingList.Add(map); - break; - default: - yield return this.LlvmTypeFromQsharpType(item.Item.Type); - nameList.Add(baseName); - mappingList.Add(map); - break; - } - } - } - if (this.TryGetFunction(qualifiedName, QsSpecializationKind.QsBody, out IrFunction? func) && this.TryGetGlobalCallable(qualifiedName, out QsCallable? callable)) { - var entryPointName = EntryPointName(qualifiedName); - var entryPointAttribute = this.Context.CreateAttribute(AttributeNames.EntryPoint); - - // Check to see if the arg list needs mapping to interop-friendly types - var mappedNameList = new List(); - var mappingList = new List(); - var mappedResultType = this.MapToInteropType(func.ReturnType); - var mappedArgList = MapType(callable.ArgumentTuple, mappedNameList, mappingList); - - var entryPointType = this.Context.GetFunctionType(mappedResultType, mappedArgList); - if (!entryPointType.Equals(func.Signature)) + var wrapper = Interop.GenerateWrapper(this, wrapperName, callable.ArgumentTuple, callable.Signature.ReturnType, func); + if (attributeName != null) { - this.StartFunction(); - this.CurrentFunction = this.Module.CreateFunction(entryPointName, entryPointType); - var namedValues = new Dictionary(); - for (var i = 0; i < mappedNameList.Count; i++) - { - this.CurrentFunction.Parameters[i].Name = mappedNameList[i]; - namedValues[this.CurrentFunction.Parameters[i].Name] = this.CurrentFunction.Parameters[i]; - } - var entryBlock = this.CurrentFunction.AppendBasicBlock("entry"); - this.SetCurrentBlock(entryBlock); - - // Build the argument list for the inner function - var argValueList = new List(); - foreach (var mapping in mappingList) - { - if (mapping.IsArray) - { - var length = namedValues[mapping.ArrayCountName]; - ArrayValue array = this.Values.CreateArray(length, mapping.ArrayElementType); - argValueList.Add(array.OpaquePointer); - - // Fill in the array if the length is >0. - // Since the QIR array is new, we assume we can use memcpy. // FIXME: THIS ASSUMPTION IS NOT VALID EITHER...! - var copyBlock = this.CurrentFunction.AppendBasicBlock("copy"); - var nextBlock = this.CurrentFunction.AppendBasicBlock("next"); - var cond = this.CurrentBuilder.Compare(IntPredicate.SignedGreaterThan, length, this.Context.CreateConstant(0L)); - this.CurrentBuilder.Branch(cond, copyBlock, nextBlock); - - this.SetCurrentBlock(copyBlock); - var getArrayItem = this.GetOrCreateRuntimeFunction(RuntimeLibrary.ArrayGetElementPtr1d); - - var givenArray = namedValues[mapping.BaseName]; - var givenArrayPtr = this.CurrentBuilder.PointerToInt(givenArray, this.Context.Int64Type); - var givenArrElementType = Types.PointerElementType(givenArray); - var givenArrElementSize = this.ComputeSizeForType(givenArrElementType); - - //// - // We need to populate the array - var start = this.Context.CreateConstant(0L); - var end = this.CurrentBuilder.Sub(array.Length, this.Context.CreateConstant(1L)); - void PopulateItem(Value index) - { - // We need to make sure that the reference count for the built item is increased by 1. - var arrayElementType = this.LlvmTypeFromQsharpType(mapping.ArrayElementType); - var offset = this.CurrentBuilder.Mul(index, givenArrElementSize); - var elementPointer = this.CurrentBuilder.Add(givenArrayPtr, offset); - - var loaded = this.CurrentBuilder.Load(arrayElementType, this.CurrentBuilder.IntToPointer(elementPointer, (IPointerType)givenArray.NativeType)); - var element = this.Values.From(loaded, mapping.ArrayElementType); // FIXME: THIS IS NOT CORRECT IF WE HAVE ARRAY OF ARRAY - array.GetArrayElementPointer(index).StoreValue(element); - } - this.IterateThroughRange(start, null, end, PopulateItem); - //// - - this.CurrentBuilder.Branch(nextBlock); - this.SetCurrentBlock(nextBlock); - } - else - { - argValueList.Add(namedValues[mapping.BaseName]); - } - } - - Value result = this.CurrentBuilder.Call(func, argValueList); - var mappedResult = mappedResultType.IsVoid || mappedResultType == func.ReturnType - ? this.Values.From(result, callable.Signature.ReturnType) - // FIME: WHY WOULD THAT BE OK; WE NEED TO DO THE SAME TRANSLATION AS FOR THE INPUT... - : this.Values.From(this.CurrentBuilder.BitCast(result, mappedResultType), callable.Signature.ReturnType); // FIXME: NOT CORRECT - this.AddReturn(mappedResult, mappedResultType.IsVoid); - this.EndFunction(); - - // Mark the function as an entry point - this.CurrentFunction.AddAttributeAtIndex(FunctionAttributeIndex.Function, entryPointAttribute); - } - else - { - this.Module.AddAlias(func, entryPointName).Linkage = Linkage.External; - // Mark the function as an entry point - func.AddAttributeAtIndex(FunctionAttributeIndex.Function, entryPointAttribute); + var attribute = this.Context.CreateAttribute(attributeName); + wrapper.AddAttributeAtIndex(FunctionAttributeIndex.Function, attribute); } } else @@ -652,128 +421,25 @@ void PopulateItem(Value index) } /// - /// Generates a stub implementation for a runtime function or quantum instruction using the specified type - /// mappings for interoperability. Note that the create functions go into a separate module from the other QIR code. + /// Writes the current content to the output file. /// - /// The function to generate a stub for - /// The function that the stub should call - private void GenerateInterop(IrFunction func, string baseName) + public void Emit(string fileName, bool overwrite = true) { - // TODO: why do we need both GenerateEntryPoint and GenerateInterop? - - func = this.InteropModule.CreateFunction(func.Name, func.Signature); - - var mappedResultType = this.MapToInteropType(func.ReturnType); - var argTypes = func.Parameters.Select(p => p.NativeType).ToArray(); - var mappedArgTypes = argTypes.Select(this.MapToInteropType).ToArray(); - - var interopFunction = this.InteropModule.CreateFunction( - baseName, - this.Context.GetFunctionType(mappedResultType, mappedArgTypes)); - - for (var i = 0; i < func.Parameters.Count; i++) + if (!overwrite && File.Exists(fileName)) { - func.Parameters[i].Name = $"arg{i + 1}"; + throw new ArgumentException($"The file \"{fileName}\" already exist(s)."); } - var builder = new InstructionBuilder(func.AppendBasicBlock("entry")); - var implArgs = Enumerable.Range(0, argTypes.Length) - .Select(index => argTypes[index] == mappedArgTypes[index] - ? func.Parameters[index] - : builder.BitCast(func.Parameters[index], mappedArgTypes[index])) - .ToArray(); - var interopReturnValue = builder.Call(interopFunction, implArgs); - if (func.ReturnType == this.Context.VoidType) - { - builder.Return(); - } - else if (func.ReturnType == mappedResultType) - { - builder.Return(interopReturnValue); - } - else - { - var returnValue = builder.BitCast(interopReturnValue, func.ReturnType); - builder.Return(returnValue); - } - } + this.GenerateRequiredFunctions(); - /// - /// Maps a QIR type to a more interop-friendly type using the specified type mapping for interoperability. - /// - /// The type to map - /// The mapped type - private ITypeRef MapToInteropType(ITypeRef t) - { - string typeName = ""; - if (t == this.Types.Result) - { - typeName = TypeNames.Result; - } - else if (t == this.Types.Array) - { - typeName = TypeNames.Array; - } - else if (t == this.Types.Pauli) - { - typeName = TypeNames.Pauli; - } - // we use 32 bit ints in some cases, e.g. for exponents - else if (t == this.Types.Int || t == this.Context.Int32Type) - { - typeName = TypeNames.Int; - } - else if (t == this.Types.Double) - { - typeName = TypeNames.Double; - } - else if (t == this.Types.Bool) - { - typeName = TypeNames.Bool; - } - else if (t == this.Types.BigInt) - { - typeName = TypeNames.BigInt; - } - else if (t == this.Types.String) - { - typeName = TypeNames.String; - } - else if (t == this.Types.Qubit) - { - typeName = TypeNames.Qubit; - } - else if (t == this.Types.Callable) - { - typeName = TypeNames.Callable; - } - else if (t == this.Types.Range) - { - typeName = TypeNames.Range; - } - else if (t == this.Types.Tuple) + if (!this.Module.Verify(out string validationErrors)) { - typeName = TypeNames.Tuple; + File.WriteAllText(fileName, $"LLVM errors:{Environment.NewLine}{validationErrors}"); } - // todo: Currently, e.g. void (this.Context.VoidType) is not covered, - // and for some reason we end up with i8* here as well. It would be good to cover everything and throw if something is not covered. - if ((typeName != string.Empty) && this.Config.InteropTypeMapping.TryGetValue(typeName, out string replacementName)) - { - if (this.interopType.TryGetValue(typeName, out ITypeRef interopType)) - { - return interopType; - } - else - { - var newType = this.Context.CreateStructType(replacementName).CreatePointerType(); - this.interopType[typeName] = newType; - return newType; - } - } - else + if (!this.Module.WriteToTextFile(fileName, out string errorMessage)) { - return t; + throw new IOException(errorMessage); } } @@ -802,6 +468,7 @@ internal IrFunction GetOrCreateTargetInstruction(string name) => /// /// Tries to find a global Q# callable in the current compilation. /// + /// The callable's qualified name /// The Q# callable, if found /// true if the callable is found, false if not internal bool TryGetGlobalCallable(QsQualifiedName fullName, [MaybeNullWhen(false)] out QsCallable callable) => @@ -810,7 +477,8 @@ internal bool TryGetGlobalCallable(QsQualifiedName fullName, [MaybeNullWhen(fals /// /// Tries to find a Q# user-defined type in the current compilation. /// - /// The Q# UDT< if found + /// The UDT's qualified name + /// The Q# UDT, if found /// true if the UDT is found, false if not internal bool TryGetCustomType(QsQualifiedName fullName, [MaybeNullWhen(false)] out QsCustomType udt) => this.globalTypes.TryGetValue(fullName, out udt); @@ -1203,6 +871,36 @@ internal Constant GetOrCreateCallableMemoryManagementTable(TupleValue? capture) return table; } + /// + /// Sets the current function to the given one and sets the parameter names to the given names. + /// Populates the body of the given function by invoking the given action with the function parameters. + /// If the current block after the invokation is not terminated, adds a void return. + /// + internal void GenerateFunction(IrFunction func, string?[] argNames, Action> executeBody) + { + this.StartFunction(); + this.CurrentFunction = func; + for (var i = 0; i < argNames.Length; ++i) + { + var name = argNames[i]; + if (name != null) + { + this.CurrentFunction.Parameters[i].Name = name; + } + } + this.SetCurrentBlock(this.CurrentFunction.AppendBasicBlock("entry")); + + this.ScopeMgr.OpenScope(); + executeBody(this.CurrentFunction.Parameters); + var isTerminated = this.CurrentBlock?.Terminator != null; + this.ScopeMgr.CloseScope(isTerminated); + if (!isTerminated) + { + this.CurrentBuilder.Return(); + } + this.EndFunction(); + } + /// /// Callables within QIR are bundles of four functions. When callables are assigned to variable or /// passed around, which one of these functions is ultimately invoked is strictly runtime information. @@ -1288,22 +986,6 @@ Value GenerateBaseMethodCall(QsCallable callable, QsSpecializationKind specKind, } } - void GenerateFunction(IrFunction func, string[] argNames, Action> executeBody) - { - this.CurrentFunction = func; - for (var i = 0; i < argNames.Length; ++i) - { - this.CurrentFunction.Parameters[i].Name = argNames[i]; - } - this.CurrentBlock = this.CurrentFunction.AppendBasicBlock("entry"); - this.CurrentBuilder = new InstructionBuilder(this.CurrentBlock); - - this.ScopeMgr.OpenScope(); - executeBody(this.CurrentFunction.Parameters); - this.ScopeMgr.CloseScope(isTerminated: false); - this.CurrentBuilder.Return(); - } - foreach (var (callable, _) in this.callableTables.Values) { foreach (var spec in callable.Specializations) @@ -1312,7 +994,7 @@ void GenerateFunction(IrFunction func, string[] argNames, Action + this.GenerateFunction(func, new[] { "capture-tuple", "arg-tuple", "result-tuple" }, parameters => { var argTuple = GetArgumentTuple(spec.Signature.ArgumentType, parameters[1]); var args = spec.Signature.ArgumentType.Resolution.IsUserDefinedType @@ -1331,7 +1013,7 @@ void GenerateFunction(IrFunction func, string[] argNames, Action + this.GenerateFunction(func, new[] { "capture-tuple", "count-change" }, parameters => { var capture = GetArgumentTuple(type, parameters[0]); var countChange = this.Values.FromSimpleValue(parameters[1], ResolvedType.New(ResolvedTypeKind.Int)); @@ -1365,6 +1047,16 @@ void GenerateFunction(IrFunction func, string[] argNames, ActionA range with the given start, step and end. + internal IValue CreateRange(Value start, Value step, Value end) + { + Value constant = this.CurrentBuilder.Load(this.Types.Range, this.Constants.EmptyRange); + constant = this.CurrentBuilder.InsertValue(constant, start, 0u); + constant = this.CurrentBuilder.InsertValue(constant, step, 1u); + constant = this.CurrentBuilder.InsertValue(constant, end, 2u); + return this.Values.From(constant, ResolvedType.New(ResolvedTypeKind.Range)); + } + /// /// Creates a for-loop that breaks based on a condition. /// diff --git a/src/QsCompiler/QirGeneration/Generator.cs b/src/QsCompiler/QirGeneration/Generator.cs index b34de216bc..c2e784e675 100644 --- a/src/QsCompiler/QirGeneration/Generator.cs +++ b/src/QsCompiler/QirGeneration/Generator.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Quantum.QIR; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.Core; @@ -11,11 +12,6 @@ namespace Microsoft.Quantum.QsCompiler.QIR /// public class Generator : SyntaxTreeTransformation { - /// - /// The configuration used for QIR emission. - /// - public readonly Configuration Config; - /// /// The compilation unit for which QIR is generated. /// @@ -39,11 +35,9 @@ public class Generator : SyntaxTreeTransformation /// Instantiates a transformation capable of emitting QIR for the given compilation. /// /// The compilation for which to generate QIR - /// The configuration for the QIR generation - public Generator(QsCompilation compilation, Configuration config) - : base(new GenerationContext(compilation.Namespaces, config), TransformationOptions.NoRebuild) + public Generator(QsCompilation compilation) + : base(new GenerationContext(compilation.Namespaces), TransformationOptions.NoRebuild) { - this.Config = config; this.Compilation = compilation; this.Namespaces = new QirNamespaceTransformation(this, TransformationOptions.NoRebuild); @@ -59,7 +53,7 @@ public Generator(QsCompilation compilation, Configuration config) } /// - /// Constructs the QIR for the compilation. + /// Constructs the QIR for the compilation, including interop-friendly functions for entry points. /// Does not emit anything; use to output the constructed QIR. /// public void Apply() @@ -71,7 +65,8 @@ public void Apply() foreach (var epName in this.Compilation.EntryPoints) { - this.SharedState.GenerateEntryPoint(epName); + var wrapperName = GenerationContext.EntryPointName(epName); + this.SharedState.CreateInteropWrapper(wrapperName, epName, AttributeNames.EntryPoint); } } diff --git a/src/QsCompiler/QirGeneration/Interop.cs b/src/QsCompiler/QirGeneration/Interop.cs new file mode 100644 index 0000000000..f2c854cce1 --- /dev/null +++ b/src/QsCompiler/QirGeneration/Interop.cs @@ -0,0 +1,500 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Quantum.QIR; +using Microsoft.Quantum.QIR.Emission; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Ubiquity.NET.Llvm.Instructions; +using Ubiquity.NET.Llvm.Types; +using Ubiquity.NET.Llvm.Values; + +namespace Microsoft.Quantum.QsCompiler.QIR +{ + using ArgumentTuple = QsTuple>; + using ResolvedTypeKind = QsTypeKind; + + /// + /// This class contains utils for facilitating interoperability and entry point handling. + /// + internal sealed class Interop + { + private readonly GenerationContext sharedState; + + private Interop(GenerationContext sharedState) => + this.sharedState = sharedState; + + // public static methods calling into private methods + + /// + public static IrFunction GenerateWrapper(GenerationContext sharedState, string wrapperName, ArgumentTuple argumentTuple, ResolvedType returnType, IrFunction implementation) => + new Interop(sharedState).GenerateWrapper(wrapperName, argumentTuple, returnType, implementation); + + // private methods + + /// + /// Creates a suitable array of values to access the item at a given index for a pointer to a struct. + /// + private Value[] PointerIndex(int index) => new[] + { + this.sharedState.Context.CreateConstant(0L), + this.sharedState.Context.CreateConstant(index) + }; + + /// + /// Applies the map function to the given items, filters all items that are null, + /// and returns the mapped non-null values as array. + /// + /// The type of the items in the given sequence. + /// The type of the items in the returned array. + /// The function to apply to each item in the sequence. + /// The sequence of items to map. + private static TOut[] WithoutNullValues(Func map, IEnumerable items) + where TOut : class => + items.Select(map).Where(i => i != null).Select(i => i!).ToArray(); + + /// + /// Bitcasts the given value to the expected type if needed. + /// Does nothing if the native type of the value already matches the expected type. + /// + private Value CastToType(Value value, ITypeRef expectedType) => + value.NativeType.Equals(expectedType) + ? value + : this.sharedState.CurrentBuilder.BitCast(value, expectedType); + + /// + private ITypeRef? MapToInteropType(ResolvedType type) => + this.MapToInteropType(this.sharedState.LlvmTypeFromQsharpType(type)); + + /// + /// Maps the given Q#/QIR type to a more interop-friendly type. + /// Returns null only if the given type is Unit. Strips items of type Unit inside tuples. + /// + /// + /// The given type is a pointer to a non-struct type, + /// or the given type is not specified in the QIR format. + /// + private ITypeRef? MapToInteropType(ITypeRef t) + { + // Range, Tuple (typed and untyped), Array, Result, String, BigInt, Callable, and Qubit + // are all structs or struct pointers. + t = t.IsPointer ? Types.StructFromPointer(t) : t; + var typeName = (t as IStructType)?.Name; + + var bytePtrType = this.sharedState.Context.Int8Type.CreatePointerType(); + + if (typeName == TypeNames.Array || typeName == TypeNames.BigInt) + { + return this.sharedState.Context.CreateStructType( + packed: false, + this.sharedState.Context.Int64Type, + this.sharedState.Types.DataArrayPointer) + .CreatePointerType(); + } + else if (typeName == TypeNames.String) + { + return this.sharedState.Types.DataArrayPointer; + } + else if (typeName == TypeNames.Callable || typeName == TypeNames.Qubit) + { + return bytePtrType; + } + else if (typeName == TypeNames.Result) + { + return this.sharedState.Context.Int8Type; + } + else if (t is IStructType st) + { + var itemTypes = WithoutNullValues(this.MapToInteropType, st.Members); + return itemTypes.Length > 0 + ? this.sharedState.Context.CreateStructType(packed: false, itemTypes).CreatePointerType() + : null; + } + if (t.IsInteger) + { + // covers Int, Bool, Pauli + var nrBytes = 1 + ((t.IntegerBitWidth - 1) / 8); + return this.sharedState.Context.GetIntType(8 * nrBytes); + } + else if (t.IsFloatingPoint) + { + return this.sharedState.Context.DoubleType; + } + else + { + throw new ArgumentException("Unrecognized type"); + } + } + + /// + /// Assuming the given parameters are defined by flattening the given argument tuple and stripping + /// all values of type Unit, constructs the argument(s) to the QIR function that matches the argument tuple. + /// The arguments of the current function are assumed to be given as interop friendly types + /// defined by . + /// This method generates suitable calls to the QIR runtime functions and other necessary + /// conversions and casts to construct the arguments for the QIR function; + /// i.e. this method implements the mapping "interop-friendly function arguments -> QIR function arguments". + /// + /// The array of arguments with which to invoke the QIR function with the given argument tuple. + /// The current function is null. + private Value[] ProcessArguments(ArgumentTuple arg, IReadOnlyList parameters) + { + if (this.sharedState.CurrentFunction == null) + { + throw new InvalidOperationException("the current function is null"); + } + + (Value Length, Value DataArray) LoadSizedArray(Value value) + { + var lengthPtr = this.sharedState.CurrentBuilder.GetElementPtr(Types.PointerElementType(value), value, this.PointerIndex(0)); + var dataArrPtr = this.sharedState.CurrentBuilder.GetElementPtr(Types.PointerElementType(value), value, this.PointerIndex(1)); + var length = this.sharedState.CurrentBuilder.Load(this.sharedState.Types.Int, lengthPtr); + var dataArr = this.sharedState.CurrentBuilder.Load(this.sharedState.Types.DataArrayPointer, dataArrPtr); + return (length, dataArr); + } + + IValue[] GetStructItems(Value value, IEnumerable itemTypes) + { + var itemIndex = 0; + Value NextTupleItem() + { + var itemPtr = this.sharedState.CurrentBuilder.GetElementPtr(Types.PointerElementType(value), value, this.PointerIndex(itemIndex++)); + return this.sharedState.CurrentBuilder.Load(Types.PointerElementType(itemPtr), itemPtr); + } + return itemTypes.Select(arg => ProcessGivenValue(arg, NextTupleItem)).ToArray(); + } + + IValue ProcessGivenValue(ResolvedType type, Func next) + { + if (type.Resolution.IsUnitType) + { + return this.sharedState.Values.Unit; + } + + Value givenValue = next(); + if (type.Resolution is ResolvedTypeKind.ArrayType arrItemType) + { + var (length, dataArr) = LoadSizedArray(givenValue); + ArrayValue array = this.sharedState.Values.CreateArray(length, arrItemType.Item); + + var dataArrStart = this.sharedState.CurrentBuilder.PointerToInt(dataArr, this.sharedState.Context.Int64Type); + var givenArrElementType = this.MapToInteropType(array.LlvmElementType) ?? this.sharedState.Values.Unit.LlvmType; + var givenArrElementSize = this.sharedState.ComputeSizeForType(givenArrElementType); + + void PopulateItem(Value index) + { + var element = ProcessGivenValue(array.QSharpElementType, () => + { + var offset = this.sharedState.CurrentBuilder.Mul(index, givenArrElementSize); + var elementPointer = this.sharedState.CurrentBuilder.IntToPointer( + this.sharedState.CurrentBuilder.Add(dataArrStart, offset), + givenArrElementType.CreatePointerType()); + return this.sharedState.CurrentBuilder.Load(givenArrElementType, elementPointer); + }); + array.GetArrayElementPointer(index).StoreValue(element); + } + + var start = this.sharedState.Context.CreateConstant(0L); + var end = this.sharedState.CurrentBuilder.Sub(array.Length, this.sharedState.Context.CreateConstant(1L)); + this.sharedState.IterateThroughRange(start, null, end, PopulateItem); + return array; + } + else if (type.Resolution is ResolvedTypeKind.TupleType items) + { + var tupleItems = GetStructItems(givenValue, items.Item); + return this.sharedState.Values.CreateTuple(tupleItems); + } + else if (type.Resolution.IsBigInt) + { + var createBigInt = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.BigIntCreateArray); + var (length, dataArr) = LoadSizedArray(givenValue); + var argValue = this.sharedState.CurrentBuilder.Call(createBigInt, length, dataArr); + var value = this.sharedState.Values.From(argValue, type); + this.sharedState.ScopeMgr.RegisterValue(value); + return value; + } + else if (type.Resolution.IsString) + { + var createString = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringCreate); + var argValue = this.sharedState.CurrentBuilder.Call(createString, this.sharedState.Context.CreateConstant(0), givenValue); + var value = this.sharedState.Values.From(argValue, type); + this.sharedState.ScopeMgr.RegisterValue(value); + return value; + } + else if (type.Resolution.IsResult) + { + var zero = this.sharedState.CurrentBuilder.Load(this.sharedState.Types.Result, this.sharedState.Constants.ResultZero); + var one = this.sharedState.CurrentBuilder.Load(this.sharedState.Types.Result, this.sharedState.Constants.ResultOne); + var cond = this.sharedState.CurrentBuilder.Compare( + IntPredicate.Equal, + givenValue, + this.sharedState.Context.CreateConstant(givenValue.NativeType, 0u, false)); + var argValue = this.sharedState.CurrentBuilder.Select(cond, zero, one); + return this.sharedState.Values.From(argValue, type); + } + else if (type.Resolution.IsRange) + { + var itemTypes = Enumerable.Repeat(ResolvedType.New(ResolvedTypeKind.Int), 3); + var rangeItems = GetStructItems(givenValue, itemTypes); + return this.sharedState.CreateRange(rangeItems[0].Value, rangeItems[1].Value, rangeItems[2].Value); + } + else if (givenValue.NativeType.IsInteger) + { + var expectedType = this.sharedState.LlvmTypeFromQsharpType(type); + var argValue = this.sharedState.CurrentBuilder.IntCast(givenValue, expectedType, type.Resolution.IsInt); + return this.sharedState.Values.FromSimpleValue(argValue, type); + } + else if (givenValue.NativeType.IsFloatingPoint) + { + return this.sharedState.Values.FromSimpleValue(givenValue, type); + } + else + { + // bitcast to the correct type and return + var expectedArgType = this.sharedState.LlvmTypeFromQsharpType(type); + var argValue = this.CastToType(givenValue, expectedArgType); + return this.sharedState.Values.From(argValue, type); + } + } + + IValue ProcessArgumentTupleItem(ArgumentTuple item, Func nextArgument) + { + if (item is ArgumentTuple.QsTuple innerTuple) + { + var tupleItems = innerTuple.Item.Select(arg => ProcessArgumentTupleItem(arg, nextArgument)).ToArray(); + return this.sharedState.Values.CreateTuple(tupleItems); + } + else + { + return item is ArgumentTuple.QsTupleItem innerItem + ? ProcessGivenValue(innerItem.Item.Type, nextArgument) + : throw new NotSupportedException("unknown item in argument tuple"); + } + } + + var currentFunctionArgIndex = 0; + Value NextArgument() => parameters[currentFunctionArgIndex++]; + var args = arg is ArgumentTuple.QsTuple argTuple ? argTuple.Item : ImmutableArray.Create(arg); + return args.Select(item => ProcessArgumentTupleItem(item, NextArgument).Value).ToArray(); + } + + /// + /// This method generates suitable calls, conversions and casts to map the given return value + /// of a QIR function to an interop-friendly value; i.e. this method implements the mapping + /// "QIR value -> interop friendly value". It strips all inner tuple items of type Unit, + /// and returns null only if the given value represents a value of type Unit. + ///

+ /// The memory for the returned value is allocated on the heap using the corresponding runtime + /// function and will not be freed by the QIR runtime. + /// It is the responsibility of the code calling into the QIR entry point wrapper to free that memory. + ///
+ /// The interop-friendly value for the given value obtained by invoking a QIR function. + /// The current function is null. + private Value? ProcessReturnValue(IValue res) + { + if (this.sharedState.CurrentFunction == null) + { + throw new InvalidOperationException("the current function is null"); + } + + Value CopyToMemory(IPointerType targetType, Value size, Value? sourcePtr = null) + { + var malloc = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.HeapAllocate); + var allocated = this.sharedState.CurrentBuilder.Call(malloc, size); + if (sourcePtr != null) + { + this.sharedState.CurrentBuilder.MemCpy(allocated, sourcePtr, size, false); + } + return this.sharedState.CurrentBuilder.BitCast(allocated, targetType); + } + + Value PopulateStruct(IPointerType mappedType, Value[] tupleItems) + { + var mappedStructType = Types.StructFromPointer(mappedType); + var size = this.sharedState.ComputeSizeForType(mappedStructType); + var mappedTuple = CopyToMemory(mappedType, size); + + for (var itemIdx = 0; itemIdx < mappedStructType.Members.Count; ++itemIdx) + { + var itemPtr = this.sharedState.CurrentBuilder.GetElementPtr(mappedStructType, mappedTuple, this.PointerIndex(itemIdx)); + var tupleItem = this.CastToType(tupleItems[itemIdx], mappedStructType.Members[itemIdx]); + this.sharedState.CurrentBuilder.Store(tupleItem, itemPtr); + } + return mappedTuple; + } + + if (res.QSharpType.Resolution.IsUnitType) + { + return null; + } + + if (res is ArrayValue array) + { + var malloc = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.HeapAllocate); + var dataArrElementType = this.MapToInteropType(array.QSharpElementType) ?? this.sharedState.Values.Unit.LlvmType; + var sizePerElement = this.sharedState.ComputeSizeForType(dataArrElementType); + var dataArr = this.sharedState.CurrentBuilder.Call(malloc, this.sharedState.CurrentBuilder.Mul(array.Length, sizePerElement)); + + var dataArrStart = this.sharedState.CurrentBuilder.PointerToInt(dataArr, this.sharedState.Context.Int64Type); + void PopulateItem(Value index) + { + var offset = this.sharedState.CurrentBuilder.Mul(index, sizePerElement); + var elementPointer = this.sharedState.CurrentBuilder.IntToPointer( + this.sharedState.CurrentBuilder.Add(dataArrStart, offset), + dataArrElementType.CreatePointerType()); + var element = this.ProcessReturnValue(array.GetArrayElement(index)) ?? this.sharedState.Values.Unit.Value; + this.sharedState.CurrentBuilder.Store(element, elementPointer); + } + + var start = this.sharedState.Context.CreateConstant(0L); + var end = this.sharedState.CurrentBuilder.Sub(array.Length, this.sharedState.Context.CreateConstant(1L)); + this.sharedState.IterateThroughRange(start, null, end, PopulateItem); + + var tupleItems = new[] { array.Length, dataArr }; // FIXME: CAST DATA ARR TO THE RIGHT TYPE IF NEEDED + var mappedType = this.MapToInteropType(array.QSharpType)!; + return PopulateStruct((IPointerType)mappedType, tupleItems); + } + else if (res is TupleValue tuple) + { + var mappedType = this.MapToInteropType(tuple.QSharpType); + if (mappedType == null) + { + return null; + } + else if (tuple.LlvmType.Equals(mappedType)) + { + var size = this.sharedState.ComputeSizeForType(tuple.StructType); + return CopyToMemory((IPointerType)mappedType, size, tuple.TypedPointer); + } + + var tupleItems = WithoutNullValues(this.ProcessReturnValue, tuple.GetTupleElements()); + return PopulateStruct((IPointerType)mappedType, tupleItems); + } + else if (res.QSharpType.Resolution.IsBigInt) + { + // TODO: We can't know the length of the big int without runtime support. + // We may also need functions to access the data array for both string and big int. + throw new NotImplementedException("returning values of type BigInt is not yet supported"); + } + else if (res.QSharpType.Resolution.IsString) + { + var getLength = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringGetLength); + var strLength = this.sharedState.CurrentBuilder.Call(getLength, res.Value); + var size = this.sharedState.CurrentBuilder.IntCast(strLength, this.sharedState.Context.Int64Type, true); + + var getData = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringGetData); + var strData = this.sharedState.CurrentBuilder.Call(getData, res.Value); + + var expectedType = this.MapToInteropType(res.LlvmType)!; + return CopyToMemory((IPointerType)expectedType, size, strData); // not sure if it is better to avoid the copy and increase the ref count + } + else if (res.QSharpType.Resolution.IsResult) + { + var zero = this.sharedState.CurrentBuilder.Load(this.sharedState.Types.Result, this.sharedState.Constants.ResultZero); + var resType = this.MapToInteropType(this.sharedState.Types.Result)!; + var zeroValue = this.sharedState.Context.CreateConstant(resType, 0u, false); + var oneValue = this.sharedState.Context.CreateConstant(resType, ~0u, false); + + var equals = this.sharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.ResultEqual); + var cond = this.sharedState.CurrentBuilder.Call(equals, res.Value, zero); + return this.sharedState.CurrentBuilder.Select(cond, zeroValue, oneValue); + } + else if (res.QSharpType.Resolution.IsRange) + { + var start = this.sharedState.CurrentBuilder.ExtractValue(res.Value, 0u); + var step = this.sharedState.CurrentBuilder.ExtractValue(res.Value, 1u); + var end = this.sharedState.CurrentBuilder.ExtractValue(res.Value, 2u); + + var expectedType = this.MapToInteropType(res.LlvmType)!; + return PopulateStruct((IPointerType)expectedType, new[] { start, step, end }); + } + else if (res.LlvmType.IsInteger) + { + var expectedType = this.MapToInteropType(res.LlvmType)!; + return this.sharedState.CurrentBuilder.IntCast(res.Value, expectedType, res.QSharpType.Resolution.IsInt); + } + else if (res.LlvmType.IsFloatingPoint) + { + return res.Value; + } + else + { + // callables and qubits + var expectedType = this.MapToInteropType(res.LlvmType)!; + this.sharedState.ScopeMgr.IncreaseReferenceCount(res); + return this.CastToType(res.Value, expectedType); + } + } + + /// + /// Generates an interop-friendly wrapper around a QIR function that can be invoked from within + /// native code without relying on the QIR runtime or adhering to the QIR specification. + /// See and + /// for more detail. + ///
+ /// Creates an alias with the given name instead of a wrapper function + /// if the wrapper signature and signature of the QIR implementation match. + ///
+ /// The function name to give the wrapper. + /// The argument tuple of the callable that the wrapper should invoke. + /// The return type of the callable that the wrapper should invoke. + /// The QIR function that implements the body of the function that should be invoked. + /// The created wrapper function or the implementation if no wrapper function has been created. + /// No callable with the given name exists in the compilation. + private IrFunction GenerateWrapper(string wrapperName, ArgumentTuple argumentTuple, ResolvedType returnType, IrFunction implementation) + { + var argItems = SyntaxGenerator.ExtractItems(argumentTuple) + .Where(sym => !sym.Type.Resolution.IsUnitType) + .ToArray(); + + ITypeRef[] wrapperArgsTypes = argItems + .Select(sym => this.MapToInteropType(sym.Type)!) + .ToArray(); + var wrapperReturnType = this.MapToInteropType(returnType); + var wrapperSignature = this.sharedState.Context.GetFunctionType( + wrapperReturnType ?? this.sharedState.Context.VoidType, + wrapperArgsTypes); + + if (wrapperSignature.Equals(implementation.Signature)) + { + this.sharedState.Module.AddAlias(implementation, wrapperName).Linkage = Linkage.External; + return implementation; + } + else + { + var wrapperFunc = this.sharedState.Module.CreateFunction(wrapperName, wrapperSignature); + var argNames = argItems.Select(arg => arg.VariableName is QsLocalSymbol.ValidName name ? name.Item : null).ToArray(); + + this.sharedState.GenerateFunction(wrapperFunc, argNames, parameters => + { + var argValueList = this.ProcessArguments(argumentTuple, parameters); + var evaluatedValue = this.sharedState.CurrentBuilder.Call(implementation, argValueList); + var result = this.sharedState.Values.From(evaluatedValue, returnType); + this.sharedState.ScopeMgr.RegisterValue(result); + + if (wrapperSignature.ReturnType.IsVoid) + { + this.sharedState.AddReturn(this.sharedState.Values.Unit, true); + } + else if (wrapperSignature.ReturnType.Equals(result.LlvmType)) + { + this.sharedState.AddReturn(result, false); + } + else + { + // ProcessReturnValue makes sure the memory for the returned value isn't freed + var returnValue = this.ProcessReturnValue(result)!; + this.sharedState.ScopeMgr.ExitFunction(this.sharedState.Values.Unit); + this.sharedState.CurrentBuilder.Return(returnValue); + } + }); + + return wrapperFunc; + } + } + } +} diff --git a/src/QsCompiler/QirGeneration/QIR/DataStructures.cs b/src/QsCompiler/QirGeneration/QIR/DataStructures.cs index be167cb382..b837a8df3d 100644 --- a/src/QsCompiler/QirGeneration/QIR/DataStructures.cs +++ b/src/QsCompiler/QirGeneration/QIR/DataStructures.cs @@ -281,7 +281,7 @@ internal TupleValue(ImmutableArray elementTypes, GenerationContext internal TupleValue(UserDefinedType? type, Value tuple, ImmutableArray elementTypes, GenerationContext context) { var isTypedTuple = Types.IsTypedTuple(tuple.NativeType); - var isOpaqueTuple = Types.IsTuple(tuple.NativeType); + var isOpaqueTuple = !tuple.IsNull && Types.IsTupleOrUnit(tuple.NativeType); if (!isTypedTuple && !isOpaqueTuple) { throw new ArgumentException("expecting either an opaque or a typed tuple"); diff --git a/src/QsCompiler/QirGeneration/QIR/Functions.cs b/src/QsCompiler/QirGeneration/QIR/Functions.cs index 7746250852..09e35dd000 100644 --- a/src/QsCompiler/QirGeneration/QIR/Functions.cs +++ b/src/QsCompiler/QirGeneration/QIR/Functions.cs @@ -67,7 +67,7 @@ public Functions(GenerationContext sharedState) /// The mangled names are a double underscore, "quantum", and another double underscore, followed by /// "rt" or "qis", another double underscore, and then the base name. /// - /// The component that is expected to provide the function + /// The component that is expected to provide the function /// The name of the function without the component prefix /// The mangled function name /// No naming convention is defined for the given component. @@ -80,7 +80,6 @@ public Functions(GenerationContext sharedState) // public and internal methods - /// The generation context in which to emit the instructions /// The range expression for which to create the access functions /// /// Three functions to access the start, step, and end of a range. @@ -112,9 +111,9 @@ public Functions(GenerationContext sharedState) else { var range = this.sharedState.EvaluateSubexpression(rangeEx).Value; - startValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 0); - stepValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 1); - endValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 2); + startValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 0u); + stepValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 1u); + endValue = () => this.sharedState.CurrentBuilder.ExtractValue(range, 2u); } return (startValue, stepValue, endValue); } @@ -202,13 +201,7 @@ private IValue RangeReverse(TypedExpression arg) step, this.sharedState.CurrentBuilder.SDiv( this.sharedState.CurrentBuilder.Sub(end, start), step))); - Value reversed = this.sharedState.CurrentBuilder.Load( - this.sharedState.Types.Range, - this.sharedState.Constants.EmptyRange); - reversed = this.sharedState.CurrentBuilder.InsertValue(reversed, newStart, 0u); - reversed = this.sharedState.CurrentBuilder.InsertValue(reversed, this.sharedState.CurrentBuilder.Neg(step), 1u); - reversed = this.sharedState.CurrentBuilder.InsertValue(reversed, start, 2u); - return this.sharedState.Values.From(reversed, Range); + return this.sharedState.CreateRange(newStart, this.sharedState.CurrentBuilder.Neg(step), start); } } } diff --git a/src/QsCompiler/QirGeneration/QIR/RuntimeLibrary.cs b/src/QsCompiler/QirGeneration/QIR/RuntimeLibrary.cs index 0c5d4d2216..e2e97001c6 100644 --- a/src/QsCompiler/QirGeneration/QIR/RuntimeLibrary.cs +++ b/src/QsCompiler/QirGeneration/QIR/RuntimeLibrary.cs @@ -8,8 +8,8 @@ namespace Microsoft.Quantum.QIR /// public static class RuntimeLibrary { - // int functions - public const string IntPower = "int_power"; + // Q# specific helpers + internal const string HeapAllocate = "heap_alloc"; // result functions public const string ResultUpdateReferenceCount = "result_update_reference_count"; @@ -17,6 +17,8 @@ public static class RuntimeLibrary // string functions public const string StringCreate = "string_create"; + public const string StringGetData = "string_get_data"; + public const string StringGetLength = "string_get_length"; public const string StringUpdateReferenceCount = "string_update_reference_count"; public const string StringConcatenate = "string_concatenate"; public const string StringEqual = "string_equal"; @@ -59,15 +61,12 @@ public static class RuntimeLibrary public const string TupleCopy = "tuple_copy"; // array functions - public const string ArrayCreate = "array_create"; - public const string ArrayGetElementPtr = "array_get_element_ptr"; public const string ArrayCreate1d = "array_create_1d"; public const string ArrayGetElementPtr1d = "array_get_element_ptr_1d"; public const string ArrayUpdateAliasCount = "array_update_alias_count"; public const string ArrayUpdateReferenceCount = "array_update_reference_count"; public const string ArrayCopy = "array_copy"; public const string ArrayConcatenate = "array_concatenate"; - public const string ArraySlice = "array_slice"; public const string ArraySlice1d = "array_slice_1d"; public const string ArrayGetSize1d = "array_get_size_1d"; diff --git a/src/QsCompiler/QirGeneration/QIR/Types.cs b/src/QsCompiler/QirGeneration/QIR/Types.cs index 2a464d541f..541e1d981c 100644 --- a/src/QsCompiler/QirGeneration/QIR/Types.cs +++ b/src/QsCompiler/QirGeneration/QIR/Types.cs @@ -137,13 +137,6 @@ internal Types(Context context) // internal helpers to simplify common code - /// - /// Type by which data allocated as global constant array is passed to the runtime. - /// String and big integers for example are instantiated with a data array. - /// - internal IPointerType DataArrayPointer => - this.context.Int8Type.CreatePointerType(); - /// /// Given the type of a pointer to a struct, returns the type of the struct. /// This method thus is the inverse mapping of CreatePointerType. @@ -161,6 +154,13 @@ internal static IStructType StructFromPointer(ITypeRef pointer) => internal static ITypeRef PointerElementType(Value pointer) => ((IPointerType)pointer.NativeType).ElementType; + /// + /// Type by which data allocated as global constant array is passed to the runtime. + /// String and big integers for example are instantiated with a data array. + /// + internal IPointerType DataArrayPointer => + this.context.Int8Type.CreatePointerType(); + // public members /// @@ -193,7 +193,7 @@ t is IPointerType pt /// /// Determines whether an LLVM type is a pointer to an opaque tuple. /// - public static bool IsTuple(ITypeRef t) => + public static bool IsTupleOrUnit(ITypeRef t) => t is IPointerType pt && pt.ElementType is IStructType st && st.Name == TypeNames.Tuple; @@ -245,6 +245,13 @@ public static bool IsString(ITypeRef t) => t is IPointerType pt && pt.ElementType is IStructType st && st.Name == TypeNames.String; + + /// + /// Determines whether an LLVM type is a struct type representing a range of integers. + /// + public static bool IsRange(ITypeRef t) => + t is IStructType st + && st.Name == TypeNames.Range; } /// diff --git a/src/QsCompiler/QirGeneration/Subtransformations/ExpressionKindTransformation.cs b/src/QsCompiler/QirGeneration/Subtransformations/ExpressionKindTransformation.cs index cb930bc4d2..f50f8551ee 100644 --- a/src/QsCompiler/QirGeneration/Subtransformations/ExpressionKindTransformation.cs +++ b/src/QsCompiler/QirGeneration/Subtransformations/ExpressionKindTransformation.cs @@ -1548,10 +1548,8 @@ IValue DefaultValue(ResolvedType type) } else if (type.Resolution.IsString) { - var value = this.SharedState.CurrentBuilder.Call( - this.SharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringCreate), - this.SharedState.Context.CreateConstant(0), - this.SharedState.Types.DataArrayPointer.GetNullValue()); + var create = this.SharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringCreate); + var value = this.SharedState.CurrentBuilder.Call(create, this.SharedState.Context.CreateConstant(0), this.SharedState.Types.DataArrayPointer.GetNullValue()); var built = this.SharedState.Values.From(value, type); this.SharedState.ScopeMgr.RegisterValue(built); return built; @@ -1876,15 +1874,7 @@ public override ResolvedExpressionKind OnRangeLiteral(TypedExpression lhs, Typed break; } Value end = this.SharedState.EvaluateSubexpression(rhs).Value; - - Value rangePtr = this.SharedState.Constants.EmptyRange; - Value constant = this.SharedState.CurrentBuilder.Load(this.SharedState.Types.Range, rangePtr); - constant = this.SharedState.CurrentBuilder.InsertValue(constant, start, 0); - constant = this.SharedState.CurrentBuilder.InsertValue(constant, step, 1); - constant = this.SharedState.CurrentBuilder.InsertValue(constant, end, 2); - - var exType = this.SharedState.CurrentExpressionType(); - var value = this.SharedState.Values.From(constant, exType); + var value = this.SharedState.CreateRange(start, step, end); this.SharedState.ValueStack.Push(value); return ResolvedExpressionKind.InvalidExpr; @@ -1980,9 +1970,8 @@ Value CreateConstantString(string s) constantArray, this.SharedState.Types.DataArrayPointer); - var n = this.SharedState.Context.CreateConstant(cleanStr.Length); var createString = this.SharedState.GetOrCreateRuntimeFunction(RuntimeLibrary.StringCreate); - return this.SharedState.CurrentBuilder.Call(createString, n, zeroLengthString); + return this.SharedState.CurrentBuilder.Call(createString, this.SharedState.Context.CreateConstant(0), zeroLengthString); } // Creates a string value that needs to be queued for unreferencing. diff --git a/src/QsCompiler/QirGeneration/Subtransformations/StatementKindTransformation.cs b/src/QsCompiler/QirGeneration/Subtransformations/StatementKindTransformation.cs index 2adefacb88..173c8fc4cd 100644 --- a/src/QsCompiler/QirGeneration/Subtransformations/StatementKindTransformation.cs +++ b/src/QsCompiler/QirGeneration/Subtransformations/StatementKindTransformation.cs @@ -65,8 +65,8 @@ private void CreateVariable(string varName, IValue value, bool mutable = false) /// to a Value. /// /// The symbols to bind - /// The action to invoke to bind each symbol /// + /// The action to invoke to bind each symbol /// The Q# expression that defines the value to bind the symbols to; it will be deconstructed if necessary /// private void BindSymbolTuple(SymbolTuple symbols, TypedExpression ex, Action bindVariable) diff --git a/examples/QIR/QirCore.qs b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirCore.qs similarity index 99% rename from examples/QIR/QirCore.qs rename to src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirCore.qs index de169cab59..e63359e95a 100644 --- a/examples/QIR/QirCore.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirCore.qs @@ -27,4 +27,4 @@ namespace Microsoft.Quantum.Targeting { @Attribute() newtype TargetInstruction = String; -} \ No newline at end of file +} diff --git a/examples/QIR/QirTarget.qs b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirTarget.qs similarity index 97% rename from examples/QIR/QirTarget.qs rename to src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirTarget.qs index fb2add8c10..70fb2da71c 100644 --- a/examples/QIR/QirTarget.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/QirTarget.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.Quantum.Instructions { @@ -132,4 +132,4 @@ namespace Microsoft.Quantum.Intrinsic { CNOT(ctls[0], qb); } } -} \ No newline at end of file +} diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate1.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate1.ll index 724c09ea64..aa28f0a8d6 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate1.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate1.ll @@ -56,7 +56,7 @@ body__3: ; preds = %header__3 br i1 %15, label %condTrue__1, label %condFalse__1 condTrue__1: ; preds = %body__3 - %16 = call %String* @__quantum__rt__string_create(i32 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) + %16 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) br label %condContinue__1 condFalse__1: ; preds = %body__3 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate2.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate2.ll index 48fa43f5c2..485c22fc10 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate2.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate2.ll @@ -39,7 +39,7 @@ body__2: ; preds = %header__2 br i1 %10, label %condTrue__1, label %condFalse__1 condTrue__1: ; preds = %body__2 - %11 = call %String* @__quantum__rt__string_create(i32 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @1, i32 0, i32 0)) + %11 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @1, i32 0, i32 0)) br label %condContinue__1 condFalse__1: ; preds = %body__2 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate3.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate3.ll index d5fe597606..4f6f36c5ce 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate3.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate3.ll @@ -48,7 +48,7 @@ condContinue__1: ; preds = %condFalse__1, %exit call void @__quantum__rt__array_update_alias_count(%Array* %8, i64 -1) %13 = call %Array* @__quantum__rt__array_copy(%Array* %8, i1 false) %14 = icmp ne %Array* %8, %13 - %15 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0)) + %15 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0)) %16 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %13, i64 1) %17 = bitcast i8* %16 to %String** call void @__quantum__rt__string_update_reference_count(%String* %15, i64 1) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate4.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate4.ll index a6b4a23fbf..ef81ad3c86 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate4.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestArrayUpdate4.ll @@ -1,7 +1,7 @@ define %Array* @Microsoft__Quantum__Testing__QIR__TestArrayUpdate4__body(%Array* %array) { entry: call void @__quantum__rt__array_update_alias_count(%Array* %array, i64 1) - %item = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i32 0, i32 0)) + %item = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i32 0, i32 0)) %0 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 0) %arr = alloca %Array*, align 8 store %Array* %0, %Array** %arr, align 8 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestConditional2.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestConditional2.ll index 81d8f09743..984c79cbef 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestConditional2.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestConditional2.ll @@ -7,8 +7,8 @@ entry: %3 = bitcast i8* %2 to %String** %4 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %arr, i64 2) %5 = bitcast i8* %4 to %String** - %6 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) - %7 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) + %6 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) + %7 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) %8 = call %String* @__quantum__rt__string_create(i32 0, i8* null) store %String* %6, %String** %1, align 8 store %String* %7, %String** %3, align 8 @@ -22,7 +22,7 @@ condTrue__1: ; preds = %entry condFalse__1: ; preds = %entry %9 = call %Array* @__quantum__rt__array_copy(%Array* %arr, i1 false) %10 = icmp ne %Array* %arr, %9 - %11 = call %String* @__quantum__rt__string_create(i32 1, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0)) + %11 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @2, i32 0, i32 0)) %12 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %9, i64 2) %13 = bitcast i8* %12 to %String** br i1 %10, label %condContinue__2, label %condFalse__2 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.ll index 3643e4627d..30823f5e8c 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.ll @@ -1,38 +1,153 @@ -define double @Microsoft__Quantum__Testing__QIR__TestEntryPoint(i64 %a__count, double* %a, i1 %b) #0 { +define { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* @Microsoft__Quantum__Testing__QIR__TestEntryPoint({ i64, i8* }* %arr, i8* %str, i8 %res, { i64, i64, i64 }* %range, i64 %cnt, i8 %b) #0 { entry: - %0 = call %Array* @__quantum__rt__array_create_1d(i32 8, i64 %a__count) - %1 = icmp sgt i64 %a__count, 0 - br i1 %1, label %copy, label %next - -copy: ; preds = %entry - %2 = ptrtoint double* %a to i64 - %3 = sub i64 %a__count, 1 + %0 = getelementptr { i64, i8* }, { i64, i8* }* %arr, i64 0, i32 0 + %1 = getelementptr { i64, i8* }, { i64, i8* }* %arr, i64 0, i32 1 + %2 = load i64, i64* %0, align 4 + %3 = load i8*, i8** %1, align 8 + %4 = call %Array* @__quantum__rt__array_create_1d(i32 1, i64 %2) + %5 = ptrtoint i8* %3 to i64 + %6 = sub i64 %2, 1 br label %header__1 -next: ; preds = %exit__1, %entry - %4 = call double @Microsoft__Quantum__Testing__QIR__TestEntryPoint__body(%Array* %0, i1 %b) - call void @__quantum__rt__array_update_reference_count(%Array* %0, i64 -1) - ret double %4 - -header__1: ; preds = %exiting__1, %copy - %5 = phi i64 [ 0, %copy ], [ %13, %exiting__1 ] - %6 = icmp sle i64 %5, %3 - br i1 %6, label %body__1, label %exit__1 +header__1: ; preds = %exiting__1, %entry + %7 = phi i64 [ 0, %entry ], [ %16, %exiting__1 ] + %8 = icmp sle i64 %7, %6 + br i1 %8, label %body__1, label %exit__1 body__1: ; preds = %header__1 - %7 = mul i64 %5, 8 - %8 = add i64 %2, %7 - %9 = inttoptr i64 %8 to double* - %10 = load double, double* %9, align 8 - %11 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %0, i64 %5) - %12 = bitcast i8* %11 to double* - store double %10, double* %12, align 8 + %9 = mul i64 %7, 1 + %10 = add i64 %5, %9 + %11 = inttoptr i64 %10 to i8* + %12 = load i8, i8* %11, align 1 + %13 = trunc i8 %12 to i2 + %14 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %4, i64 %7) + %15 = bitcast i8* %14 to i2* + store i2 %13, i2* %15, align 1 br label %exiting__1 exiting__1: ; preds = %body__1 - %13 = add i64 %5, 1 + %16 = add i64 %7, 1 br label %header__1 exit__1: ; preds = %header__1 - br label %next + %17 = call %String* @__quantum__rt__string_create(i32 0, i8* %str) + %18 = load %Result*, %Result** @ResultZero, align 8 + %19 = load %Result*, %Result** @ResultOne, align 8 + %20 = icmp eq i8 %res, 0 + %21 = select i1 %20, %Result* %18, %Result* %19 + %22 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %range, i64 0, i32 0 + %23 = load i64, i64* %22, align 4 + %24 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %range, i64 0, i32 1 + %25 = load i64, i64* %24, align 4 + %26 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %range, i64 0, i32 2 + %27 = load i64, i64* %26, align 4 + %28 = load %Range, %Range* @EmptyRange, align 4 + %29 = insertvalue %Range %28, i64 %23, 0 + %30 = insertvalue %Range %29, i64 %25, 1 + %31 = insertvalue %Range %30, i64 %27, 2 + %32 = trunc i8 %b to i1 + %33 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ i64, i1 }* getelementptr ({ i64, i1 }, { i64, i1 }* null, i32 1) to i64)) + %34 = bitcast %Tuple* %33 to { i64, i1 }* + %35 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %34, i32 0, i32 0 + %36 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %34, i32 0, i32 1 + store i64 %cnt, i64* %35, align 4 + store i1 %32, i1* %36, align 1 + %37 = call { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* @Microsoft__Quantum__Testing__QIR__TestEntryPoint__body(%Array* %4, %String* %17, %Result* %21, %Range %31, { i64, i1 }* %34) + %38 = getelementptr inbounds { %Array*, %String*, %Result*, %Range, { i64, i1 }* }, { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37, i32 0, i32 0 + %39 = getelementptr inbounds { %Array*, %String*, %Result*, %Range, { i64, i1 }* }, { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37, i32 0, i32 1 + %40 = getelementptr inbounds { %Array*, %String*, %Result*, %Range, { i64, i1 }* }, { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37, i32 0, i32 2 + %41 = getelementptr inbounds { %Array*, %String*, %Result*, %Range, { i64, i1 }* }, { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37, i32 0, i32 3 + %42 = getelementptr inbounds { %Array*, %String*, %Result*, %Range, { i64, i1 }* }, { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37, i32 0, i32 4 + %43 = load %Array*, %Array** %38, align 8 + %44 = load %String*, %String** %39, align 8 + %45 = load %Result*, %Result** %40, align 8 + %46 = load %Range, %Range* %41, align 4 + %47 = load { i64, i1 }*, { i64, i1 }** %42, align 8 + %48 = call i64 @__quantum__rt__array_get_size_1d(%Array* %43) + %49 = mul i64 %48, 1 + %50 = call i8* @__quantum__rt__heap_alloc(i64 %49) + %51 = ptrtoint i8* %50 to i64 + %52 = sub i64 %48, 1 + br label %header__2 + +header__2: ; preds = %exiting__2, %exit__1 + %53 = phi i64 [ 0, %exit__1 ], [ %62, %exiting__2 ] + %54 = icmp sle i64 %53, %52 + br i1 %54, label %body__2, label %exit__2 + +body__2: ; preds = %header__2 + %55 = mul i64 %53, 1 + %56 = add i64 %51, %55 + %57 = inttoptr i64 %56 to i8* + %58 = call i8* @__quantum__rt__array_get_element_ptr_1d(%Array* %43, i64 %53) + %59 = bitcast i8* %58 to i2* + %60 = load i2, i2* %59, align 1 + %61 = sext i2 %60 to i8 + store i8 %61, i8* %57, align 1 + br label %exiting__2 + +exiting__2: ; preds = %body__2 + %62 = add i64 %53, 1 + br label %header__2 + +exit__2: ; preds = %header__2 + %63 = call i8* @__quantum__rt__heap_alloc(i64 ptrtoint ({ i64, i8* }* getelementptr ({ i64, i8* }, { i64, i8* }* null, i32 1) to i64)) + %64 = bitcast i8* %63 to { i64, i8* }* + %65 = getelementptr { i64, i8* }, { i64, i8* }* %64, i64 0, i32 0 + store i64 %48, i64* %65, align 4 + %66 = getelementptr { i64, i8* }, { i64, i8* }* %64, i64 0, i32 1 + store i8* %50, i8** %66, align 8 + %67 = call i32 @__quantum__rt__string_get_length(%String* %44) + %68 = sext i32 %67 to i64 + %69 = call i8* @__quantum__rt__string_get_data(%String* %44) + %70 = call i8* @__quantum__rt__heap_alloc(i64 %68) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %70, i8* %69, i64 %68, i1 false) + %71 = load %Result*, %Result** @ResultZero, align 8 + %72 = call i1 @__quantum__rt__result_equal(%Result* %45, %Result* %71) + %73 = select i1 %72, i8 0, i8 -1 + %74 = extractvalue %Range %46, 0 + %75 = extractvalue %Range %46, 1 + %76 = extractvalue %Range %46, 2 + %77 = call i8* @__quantum__rt__heap_alloc(i64 mul nuw (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 3)) + %78 = bitcast i8* %77 to { i64, i64, i64 }* + %79 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %78, i64 0, i32 0 + store i64 %74, i64* %79, align 4 + %80 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %78, i64 0, i32 1 + store i64 %75, i64* %80, align 4 + %81 = getelementptr { i64, i64, i64 }, { i64, i64, i64 }* %78, i64 0, i32 2 + store i64 %76, i64* %81, align 4 + %82 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %47, i32 0, i32 0 + %83 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %47, i32 0, i32 1 + %84 = load i64, i64* %82, align 4 + %85 = load i1, i1* %83, align 1 + %86 = sext i1 %85 to i8 + %87 = call i8* @__quantum__rt__heap_alloc(i64 ptrtoint ({ i64, i8 }* getelementptr ({ i64, i8 }, { i64, i8 }* null, i32 1) to i64)) + %88 = bitcast i8* %87 to { i64, i8 }* + %89 = getelementptr { i64, i8 }, { i64, i8 }* %88, i64 0, i32 0 + store i64 %84, i64* %89, align 4 + %90 = getelementptr { i64, i8 }, { i64, i8 }* %88, i64 0, i32 1 + store i8 %86, i8* %90, align 1 + %91 = call i8* @__quantum__rt__heap_alloc(i64 ptrtoint ({ { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* getelementptr ({ { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* null, i32 1) to i64)) + %92 = bitcast i8* %91 to { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* + %93 = getelementptr { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92, i64 0, i32 0 + store { i64, i8* }* %64, { i64, i8* }** %93, align 8 + %94 = getelementptr { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92, i64 0, i32 1 + store i8* %70, i8** %94, align 8 + %95 = getelementptr { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92, i64 0, i32 2 + store i8 %73, i8* %95, align 1 + %96 = getelementptr { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92, i64 0, i32 3 + store { i64, i64, i64 }* %78, { i64, i64, i64 }** %96, align 8 + %97 = getelementptr { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }, { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92, i64 0, i32 4 + store { i64, i8 }* %88, { i64, i8 }** %97, align 8 + call void @__quantum__rt__array_update_reference_count(%Array* %4, i64 -1) + call void @__quantum__rt__string_update_reference_count(%String* %17, i64 -1) + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %33, i64 -1) + call void @__quantum__rt__array_update_reference_count(%Array* %43, i64 -1) + call void @__quantum__rt__string_update_reference_count(%String* %44, i64 -1) + call void @__quantum__rt__result_update_reference_count(%Result* %45, i64 -1) + %98 = bitcast { i64, i1 }* %47 to %Tuple* + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %98, i64 -1) + %99 = bitcast { %Array*, %String*, %Result*, %Range, { i64, i1 }* }* %37 to %Tuple* + call void @__quantum__rt__tuple_update_reference_count(%Tuple* %99, i64 -1) + ret { { i64, i8* }*, i8*, i8, { i64, i64, i64 }*, { i64, i8 }* }* %92 } diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.qs b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.qs index 37947d5eac..15f78d61c2 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestEntryPoint.qs @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -namespace Microsoft.Quantum.Testing.QIR -{ - //open Microsoft.Quantum.Intrinsic; +namespace Microsoft.Quantum.Testing.QIR { @EntryPoint() - operation TestEntryPoint(a : Double[], b : Bool) : Double - { + operation TestEntryPoint( + arr : Pauli[], str : String, res : Result, range : Range, (cnt : Int, b : Bool)) + : (Pauli[], String, Result, Range, (Int, Bool)) { + mutable sum = 0.0; mutable flag = b; - for (i in a) + for pauli in arr { - set sum = sum + (flag ? i | -i); + let value = pauli == PauliI ? 0. | 1.; + set sum = sum + (flag ? value | -value); set flag = not flag; } - return sum; + + return (arr, str, res, range, (cnt, b)); } } diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestForLoop.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestForLoop.ll index e00d808705..f53eb66522 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestForLoop.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestForLoop.ll @@ -1,6 +1,6 @@ define { double, %String* }* @Microsoft__Quantum__Testing__QIR__TestNestedLoops__body() { entry: - %name = call %String* @__quantum__rt__string_create(i32 6, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i32 0, i32 0)) + %name = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i32 0, i32 0)) %0 = call %String* @__quantum__rt__string_create(i32 0, i8* null) %1 = call { double, %String* }* @Microsoft__Quantum__Testing__QIR__Energy__body(double 0.000000e+00, %String* %0) %res = alloca { double, %String* }*, align 8 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestOpArgument.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestOpArgument.ll index 04becbf097..c67ef8d759 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestOpArgument.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestOpArgument.ll @@ -1,4 +1,4 @@ -define %String* @Microsoft__Quantum__Testing__QIR__TestOpArgument__body() #0 { +define %String* @Microsoft__Quantum__Testing__QIR__TestOpArgument__body() { entry: %q1 = call %Qubit* @__quantum__rt__qubit_allocate() %q2 = call %Qubit* @__quantum__rt__qubit_allocate() diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestPartials1.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestPartials1.ll index afa12b5f5a..6f64b6c225 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestPartials1.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestPartials1.ll @@ -1,4 +1,4 @@ -define i1 @Microsoft__Quantum__Testing__QIR__TestPartials__body(i64 %a, double %b) #0 { +define i1 @Microsoft__Quantum__Testing__QIR__TestPartials__body(i64 %a, double %b) { entry: %0 = call %Tuple* @__quantum__rt__tuple_create(i64 ptrtoint ({ %Callable*, double }* getelementptr ({ %Callable*, double }, { %Callable*, double }* null, i32 1) to i64)) %1 = bitcast %Tuple* %0 to { %Callable*, double }* diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestRepeat.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestRepeat.ll index a51c37012c..102dcfecc1 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestRepeat.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestRepeat.ll @@ -9,7 +9,7 @@ repeat__1: ; preds = %condContinue__2, %e call void @__quantum__qis__x__body(%Qubit* %q) call void @__quantum__qis__t__adj(%Qubit* %q) call void @__quantum__qis__h__body(%Qubit* %q) - %name = call %String* @__quantum__rt__string_create(i32 6, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i32 0, i32 0)) + %name = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i32 0, i32 0)) %0 = call %String* @__quantum__rt__string_create(i32 0, i8* null) %1 = call { double, %String* }* @Microsoft__Quantum__Testing__QIR__Energy__body(double 0.000000e+00, %String* %0) %res = alloca { double, %String* }*, align 8 @@ -57,7 +57,7 @@ fixup__1: ; preds = %until__1 br i1 %15, label %then0__1, label %continue__1 then0__1: ; preds = %fixup__1 - %16 = call %String* @__quantum__rt__string_create(i32 19, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @1, i32 0, i32 0)) + %16 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @1, i32 0, i32 0)) %17 = load %String*, %String** %3, align 8 %18 = load %String*, %String** %8, align 8 %19 = load { double, %String* }*, { double, %String* }** %res, align 8 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestStrings.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestStrings.ll index dcb3bf3816..9b253102fa 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestStrings.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestStrings.ll @@ -1,27 +1,27 @@ define %String* @Microsoft__Quantum__Testing__QIR__TestStrings__body(i64 %a, i64 %b) { entry: - %0 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) + %0 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) %1 = call %String* @__quantum__rt__int_to_string(i64 %a) %2 = call %String* @__quantum__rt__string_concatenate(%String* %0, %String* %1) call void @__quantum__rt__string_update_reference_count(%String* %0, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %1, i64 -1) - %3 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) + %3 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) %x = call %String* @__quantum__rt__string_concatenate(%String* %2, %String* %3) call void @__quantum__rt__string_update_reference_count(%String* %2, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %3, i64 -1) - %4 = call %String* @__quantum__rt__string_create(i32 7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i32 0, i32 0)) + %4 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i32 0, i32 0)) %5 = add i64 %a, %b %6 = call %String* @__quantum__rt__int_to_string(i64 %5) %y = call %String* @__quantum__rt__string_concatenate(%String* %4, %String* %6) call void @__quantum__rt__string_update_reference_count(%String* %4, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %6, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %y, i64 1) - %7 = call %String* @__quantum__rt__string_create(i32 16, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @3, i32 0, i32 0)) + %7 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @3, i32 0, i32 0)) %8 = call %String* @__quantum__rt__double_to_string(double 1.200000e+00) %9 = call %String* @__quantum__rt__string_concatenate(%String* %7, %String* %8) call void @__quantum__rt__string_update_reference_count(%String* %7, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %8, i64 -1) - %10 = call %String* @__quantum__rt__string_create(i32 6, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @4, i32 0, i32 0)) + %10 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @4, i32 0, i32 0)) %11 = call %String* @__quantum__rt__string_concatenate(%String* %9, %String* %10) call void @__quantum__rt__string_update_reference_count(%String* %9, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %10, i64 -1) @@ -29,7 +29,7 @@ entry: %13 = call %String* @__quantum__rt__string_concatenate(%String* %11, %String* %12) call void @__quantum__rt__string_update_reference_count(%String* %11, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %12, i64 -1) - %14 = call %String* @__quantum__rt__string_create(i32 7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @5, i32 0, i32 0)) + %14 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @5, i32 0, i32 0)) %15 = call %String* @__quantum__rt__string_concatenate(%String* %13, %String* %14) call void @__quantum__rt__string_update_reference_count(%String* %13, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %14, i64 -1) @@ -38,7 +38,7 @@ entry: %18 = call %String* @__quantum__rt__string_concatenate(%String* %15, %String* %17) call void @__quantum__rt__string_update_reference_count(%String* %15, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %17, i64 -1) - %19 = call %String* @__quantum__rt__string_create(i32 8, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @6, i32 0, i32 0)) + %19 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @6, i32 0, i32 0)) %20 = call %String* @__quantum__rt__string_concatenate(%String* %18, %String* %19) call void @__quantum__rt__string_update_reference_count(%String* %18, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %19, i64 -1) @@ -47,7 +47,7 @@ entry: %23 = call %String* @__quantum__rt__string_concatenate(%String* %20, %String* %22) call void @__quantum__rt__string_update_reference_count(%String* %20, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %22, i64 -1) - %24 = call %String* @__quantum__rt__string_create(i32 8, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @7, i32 0, i32 0)) + %24 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @7, i32 0, i32 0)) %25 = call %String* @__quantum__rt__string_concatenate(%String* %23, %String* %24) call void @__quantum__rt__string_update_reference_count(%String* %23, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %24, i64 -1) @@ -56,7 +56,7 @@ entry: %28 = call %String* @__quantum__rt__string_concatenate(%String* %25, %String* %27) call void @__quantum__rt__string_update_reference_count(%String* %25, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %27, i64 -1) - %29 = call %String* @__quantum__rt__string_create(i32 7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @8, i32 0, i32 0)) + %29 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @8, i32 0, i32 0)) %30 = call %String* @__quantum__rt__string_concatenate(%String* %28, %String* %29) call void @__quantum__rt__string_update_reference_count(%String* %28, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %29, i64 -1) @@ -68,7 +68,7 @@ entry: %i = call %String* @__quantum__rt__string_concatenate(%String* %30, %String* %35) call void @__quantum__rt__string_update_reference_count(%String* %30, i64 -1) call void @__quantum__rt__string_update_reference_count(%String* %35, i64 -1) - %36 = call %String* @__quantum__rt__string_create(i32 7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @9, i32 0, i32 0)) + %36 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @9, i32 0, i32 0)) call void @__quantum__rt__string_update_reference_count(%String* %x, i64 1) %37 = call %String* @__quantum__rt__string_concatenate(%String* %36, %String* %x) call void @__quantum__rt__string_update_reference_count(%String* %36, i64 -1) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtArgument.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtArgument.ll index 7f67653db0..ec4ca41e52 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtArgument.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtArgument.ll @@ -1,4 +1,4 @@ -define { i64, { i2, i64 }* }* @Microsoft__Quantum__Testing__QIR__TestUdtArgument__body() #0 { +define { i64, { i2, i64 }* }* @Microsoft__Quantum__Testing__QIR__TestUdtArgument__body() { entry: %0 = call %Callable* @__quantum__rt__callable_create([4 x void (%Tuple*, %Tuple*, %Tuple*)*]* @Microsoft__Quantum__Testing__QIR__TestType1, [2 x void (%Tuple*, i64)*]* null, %Tuple* null) %udt1 = call { i64 }* @Microsoft__Quantum__Testing__QIR_____GUID___Build__body(%Callable* %0) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate1.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate1.ll index 596a2ef762..f2600307aa 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate1.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate1.ll @@ -43,7 +43,7 @@ condFalse__1: ; preds = %entry condContinue__1: ; preds = %condFalse__1, %entry store { double, %String* }* %19, { double, %String* }** %14, align 8 %20 = getelementptr inbounds { double, %String* }, { double, %String* }* %19, i32 0, i32 1 - %21 = call %String* @__quantum__rt__string_create(i32 5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) + %21 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) call void @__quantum__rt__string_update_reference_count(%String* %21, i64 1) %22 = load %String*, %String** %20, align 8 br i1 %12, label %condContinue__2, label %condFalse__2 diff --git a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate2.ll b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate2.ll index fd92802fa4..831081ddc0 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate2.ll +++ b/src/QsCompiler/Tests.Compiler/TestCases/QirTests/TestUdtUpdate2.ll @@ -21,7 +21,7 @@ entry: call void @__quantum__rt__tuple_update_reference_count(%Tuple* %2, i64 1) call void @__quantum__rt__tuple_update_reference_count(%Tuple* %5, i64 1) call void @__quantum__rt__tuple_update_reference_count(%Tuple* %6, i64 1) - %9 = call %String* @__quantum__rt__string_create(i32 4, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0)) + %9 = call %String* @__quantum__rt__string_create(i32 0, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0)) %10 = call i1 @__quantum__rt__string_equal(%String* %8, %String* %9) br i1 %10, label %then0__1, label %continue__1 diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 182557169e..9a5ddf6695 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -21,11 +21,11 @@ - - Always + + Always - - Always + + Always Always