diff --git a/src/Qir/Execution/Tools/EntryPointOperationLoader.cs b/src/Qir/Execution/Tools/EntryPointOperationLoader.cs new file mode 100644 index 00000000000..7713ff0d03f --- /dev/null +++ b/src/Qir/Execution/Tools/EntryPointOperationLoader.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Quantum.Qir.Serialization; +using Microsoft.Quantum.QsCompiler; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.QsCompiler.QIR; + +namespace Microsoft.Quantum.Qir.Tools +{ + using QsTypeKind = QsTypeKind; + + internal static class EntryPointLoader + { + /// + /// Loads the entry point operations found in the syntax tree included as a resource from . + /// + /// The file info of a .NET DLL from which to load entry point operations from. + /// + /// A list of entry point operation objects representing the QIR entry point operations. + /// + /// does not exist. + /// does not contain a Q# syntax tree. + /// Encounters invalid parameters for an entry point. + public static IList LoadEntryPointOperations(FileInfo assemblyFileInfo) + { + if (!AssemblyLoader.LoadReferencedAssembly(assemblyFileInfo.FullName, out var compilation)) + { + throw new ArgumentException("Unable to read the Q# syntax tree from the given DLL."); + } + + return GenerateEntryPointOperations(compilation); + } + + private static IList GenerateEntryPointOperations(QsCompilation compilation) + { + var globals = compilation.Namespaces.GlobalCallableResolutions(); + + return compilation.EntryPoints.Select(ep => new EntryPointOperation() + { + Name = NameGeneration.InteropFriendlyWrapperName(ep), + Parameters = GetParams(globals[ep]), + }).ToList(); + } + + private static List GetParams(QsCallable callable) + { + return SyntaxGenerator.ExtractItems(callable.ArgumentTuple) + .Where(sym => !sym.Type.Resolution.IsUnitType) + .Select(sym => MakeParameter(sym)) + .ToList(); + } + + private static Parameter MakeParameter(LocalVariableDeclaration parameter) + { + var type = MapResolvedTypeToDataType(parameter.Type); + var arrayType = parameter.Type.Resolution is QsTypeKind.ArrayType innerType + ? (DataType?)MapResolvedTypeToDataType(innerType.Item) + : null; + + if (arrayType == DataType.ArrayType) + { + throw new ArgumentException("Multi-dimensional arrays are not supported types of entry point parameters."); + } + + return new Parameter() + { + Name = parameter.VariableName is QsLocalSymbol.ValidName name + ? name.Item + : throw new ArgumentException("Encountered invalid name for parameter."), + Type = type, + ArrayType = arrayType, + }; + } + + private static DataType MapResolvedTypeToDataType(ResolvedType rt) => rt.Resolution.Tag switch + { + QsTypeKind.Tags.Bool => DataType.BoolType, + QsTypeKind.Tags.Int => DataType.IntegerType, + QsTypeKind.Tags.Double => DataType.DoubleType, + QsTypeKind.Tags.Pauli => DataType.PauliType, + QsTypeKind.Tags.Range => DataType.RangeType, + QsTypeKind.Tags.Result => DataType.ResultType, + QsTypeKind.Tags.String => DataType.StringType, + QsTypeKind.Tags.ArrayType => DataType.ArrayType, + _ => throw new ArgumentException($"Invalid type ({rt.Resolution.Tag}) for entry point parameter"), + }; + } +} diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index a962aa46a76..118fef8ed9c 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.Qir.Tools.Executable public abstract class QirExecutable : IQirExecutable { private const string DriverFileName = "driver"; - private const string BytecodeFileName = "qir.bc"; + private const string BitcodeFileName = "qir.bc"; public virtual string SourceDirectoryPath => "src"; public abstract string DriverFileExtension { get; } @@ -27,15 +27,15 @@ public abstract class QirExecutable : IQirExecutable protected FileInfo ExecutableFile { get; } - private readonly byte[] qirBytecode; + private readonly byte[] qirBitcode; private readonly ILogger logger; private readonly IQuantumExecutableRunner runner; private readonly IQirDriverGenerator driverGenerator; private readonly IQirExecutableGenerator executableGenerator; - public QirExecutable(FileInfo executableFile, byte[] qirBytecode, IQirDriverGenerator driverGenerator, ILogger logger = null) + public QirExecutable(FileInfo executableFile, byte[] qirBitcode, IQirDriverGenerator driverGenerator, ILogger logger = null) : this(executableFile, - qirBytecode, + qirBitcode, logger ?? new Logger(new Clock()), driverGenerator, new QirExecutableGenerator(new ClangClient(logger), logger), @@ -43,10 +43,10 @@ public QirExecutable(FileInfo executableFile, byte[] qirBytecode, IQirDriverGene { } - internal QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) + internal QirExecutable(FileInfo executableFile, byte[] qirBitcode, ILogger logger, IQirDriverGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner runner) { ExecutableFile = executableFile; - this.qirBytecode = qirBytecode; + this.qirBitcode = qirBitcode; this.logger = logger; this.runner = runner; this.driverGenerator = driverGenerator; @@ -73,13 +73,13 @@ public async Task BuildAsync(EntryPointOperation entryPoint, IList LibraryDirectories { get; } = new List(); - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logger = null) + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBitcode, ILogger logger = null) : base(executableFile, - qirBytecode, + qirBitcode, new QirFullStateDriverGenerator(), logger) { diff --git a/src/Qir/Execution/Tools/QirTools.cs b/src/Qir/Execution/Tools/QirTools.cs index b79c73072bb..1b1990c3f95 100644 --- a/src/Qir/Execution/Tools/QirTools.cs +++ b/src/Qir/Execution/Tools/QirTools.cs @@ -31,9 +31,9 @@ public static async Task BuildFromQSharpDll( DirectoryInfo executablesDirectory) { using var qirContentStream = new MemoryStream(); - if (!AssemblyLoader.LoadQirByteCode(qsharpDll, qirContentStream)) + if (!AssemblyLoader.LoadQirBitcode(qsharpDll, qirContentStream)) { - throw new ArgumentException("The given DLL does not contain QIR byte code."); + throw new ArgumentException("The given DLL does not contain QIR bitcode."); } if (!executablesDirectory.Exists) @@ -41,7 +41,7 @@ public static async Task BuildFromQSharpDll( executablesDirectory.Create(); } - var tasks = EntryPointOperationLoader.LoadEntryPointOperations(qsharpDll).Select(entryPointOp => + var tasks = EntryPointLoader.LoadEntryPointOperations(qsharpDll).Select(entryPointOp => { var exeFileInfo = new FileInfo(Path.Combine(executablesDirectory.FullName, $"{entryPointOp.Name}.exe")); var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray());