aboutsummaryrefslogtreecommitdiff
path: root/Spv.Generator/Module.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Spv.Generator/Module.cs')
-rw-r--r--Spv.Generator/Module.cs365
1 files changed, 365 insertions, 0 deletions
diff --git a/Spv.Generator/Module.cs b/Spv.Generator/Module.cs
new file mode 100644
index 00000000..34ad6036
--- /dev/null
+++ b/Spv.Generator/Module.cs
@@ -0,0 +1,365 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using static Spv.Specification;
+
+namespace Spv.Generator
+{
+ public partial class Module
+ {
+ // TODO: Register on SPIR-V registry.
+ private const int GeneratorId = 0;
+
+ private readonly uint _version;
+
+ private uint _bound;
+
+ // Follow spec order here while keeping it as simple as possible.
+ private List<Capability> _capabilities;
+ private List<string> _extensions;
+ private Dictionary<DeterministicStringKey, Instruction> _extInstImports;
+ private AddressingModel _addressingModel;
+ private MemoryModel _memoryModel;
+
+ private List<Instruction> _entrypoints;
+ private List<Instruction> _executionModes;
+ private List<Instruction> _debug;
+ private List<Instruction> _annotations;
+
+ // In the declaration block.
+ private Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations;
+ // In the declaration block.
+ private List<Instruction> _globals;
+ // In the declaration block.
+ private Dictionary<ConstantKey, Instruction> _constants;
+ // In the declaration block, for function that aren't defined in the module.
+ private List<Instruction> _functionsDeclarations;
+
+ private List<Instruction> _functionsDefinitions;
+
+ private GeneratorPool<Instruction> _instPool;
+ private GeneratorPool<LiteralInteger> _integerPool;
+
+ public Module(uint version, GeneratorPool<Instruction> instPool = null, GeneratorPool<LiteralInteger> integerPool = null)
+ {
+ _version = version;
+ _bound = 1;
+ _capabilities = new List<Capability>();
+ _extensions = new List<string>();
+ _extInstImports = new Dictionary<DeterministicStringKey, Instruction>();
+ _addressingModel = AddressingModel.Logical;
+ _memoryModel = MemoryModel.Simple;
+ _entrypoints = new List<Instruction>();
+ _executionModes = new List<Instruction>();
+ _debug = new List<Instruction>();
+ _annotations = new List<Instruction>();
+ _typeDeclarations = new Dictionary<TypeDeclarationKey, Instruction>();
+ _constants = new Dictionary<ConstantKey, Instruction>();
+ _globals = new List<Instruction>();
+ _functionsDeclarations = new List<Instruction>();
+ _functionsDefinitions = new List<Instruction>();
+
+ _instPool = instPool ?? new GeneratorPool<Instruction>();
+ _integerPool = integerPool ?? new GeneratorPool<LiteralInteger>();
+
+ LiteralInteger.RegisterPool(_integerPool);
+ }
+
+ private uint GetNewId()
+ {
+ return _bound++;
+ }
+
+ public void AddCapability(Capability capability)
+ {
+ _capabilities.Add(capability);
+ }
+
+ public void AddExtension(string extension)
+ {
+ _extensions.Add(extension);
+ }
+
+ public Instruction NewInstruction(Op opcode, uint id = Instruction.InvalidId, Instruction resultType = null)
+ {
+ var result = _instPool.Allocate();
+ result.Set(opcode, id, resultType);
+
+ return result;
+ }
+
+ public Instruction AddExtInstImport(string import)
+ {
+ var key = new DeterministicStringKey(import);
+
+ if (_extInstImports.TryGetValue(key, out Instruction extInstImport))
+ {
+ // Update the duplicate instance to use the good id so it ends up being encoded correctly.
+ return extInstImport;
+ }
+
+ Instruction instruction = NewInstruction(Op.OpExtInstImport);
+ instruction.AddOperand(import);
+
+ instruction.SetId(GetNewId());
+
+ _extInstImports.Add(key, instruction);
+
+ return instruction;
+ }
+
+ private void AddTypeDeclaration(Instruction instruction, bool forceIdAllocation)
+ {
+ var key = new TypeDeclarationKey(instruction);
+
+ if (!forceIdAllocation)
+ {
+ if (_typeDeclarations.TryGetValue(key, out Instruction typeDeclaration))
+ {
+ // Update the duplicate instance to use the good id so it ends up being encoded correctly.
+
+ instruction.SetId(typeDeclaration.Id);
+
+ return;
+ }
+ }
+
+ instruction.SetId(GetNewId());
+
+ _typeDeclarations.Add(key, instruction);
+ }
+
+ public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params Instruction[] interfaces)
+ {
+ Debug.Assert(function.Opcode == Op.OpFunction);
+
+ Instruction entryPoint = NewInstruction(Op.OpEntryPoint);
+
+ entryPoint.AddOperand(executionModel);
+ entryPoint.AddOperand(function);
+ entryPoint.AddOperand(name);
+ entryPoint.AddOperand(interfaces);
+
+ _entrypoints.Add(entryPoint);
+ }
+
+ public void AddExecutionMode(Instruction function, ExecutionMode mode, params Operand[] parameters)
+ {
+ Debug.Assert(function.Opcode == Op.OpFunction);
+
+ Instruction executionModeInstruction = NewInstruction(Op.OpExecutionMode);
+
+ executionModeInstruction.AddOperand(function);
+ executionModeInstruction.AddOperand(mode);
+ executionModeInstruction.AddOperand(parameters);
+
+ _executionModes.Add(executionModeInstruction);
+ }
+
+ private void AddToFunctionDefinitions(Instruction instruction)
+ {
+ Debug.Assert(instruction.Opcode != Op.OpTypeInt);
+ _functionsDefinitions.Add(instruction);
+ }
+
+ private void AddAnnotation(Instruction annotation)
+ {
+ _annotations.Add(annotation);
+ }
+
+ private void AddDebug(Instruction debug)
+ {
+ _debug.Add(debug);
+ }
+
+ public void AddLabel(Instruction label)
+ {
+ Debug.Assert(label.Opcode == Op.OpLabel);
+
+ label.SetId(GetNewId());
+
+ AddToFunctionDefinitions(label);
+ }
+
+ public void AddLocalVariable(Instruction variable)
+ {
+ // TODO: Ensure it has the local modifier.
+ Debug.Assert(variable.Opcode == Op.OpVariable);
+
+ variable.SetId(GetNewId());
+
+ AddToFunctionDefinitions(variable);
+ }
+
+ public void AddGlobalVariable(Instruction variable)
+ {
+ // TODO: Ensure it has the global modifier.
+ // TODO: All constants opcodes (OpSpecXXX and the rest of the OpConstantXXX).
+ Debug.Assert(variable.Opcode == Op.OpVariable);
+
+ variable.SetId(GetNewId());
+
+ _globals.Add(variable);
+ }
+
+ private void AddConstant(Instruction constant)
+ {
+ Debug.Assert(constant.Opcode == Op.OpConstant ||
+ constant.Opcode == Op.OpConstantFalse ||
+ constant.Opcode == Op.OpConstantTrue ||
+ constant.Opcode == Op.OpConstantNull ||
+ constant.Opcode == Op.OpConstantComposite);
+
+ var key = new ConstantKey(constant);
+
+ if (_constants.TryGetValue(key, out Instruction global))
+ {
+ // Update the duplicate instance to use the good id so it ends up being encoded correctly.
+ constant.SetId(global.Id);
+
+ return;
+ }
+
+ constant.SetId(GetNewId());
+
+ _constants.Add(key, constant);
+ }
+
+ public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params Operand[] parameters)
+ {
+ Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType);
+
+ result.AddOperand(set);
+ result.AddOperand(instruction);
+ result.AddOperand(parameters);
+ AddToFunctionDefinitions(result);
+
+ return result;
+ }
+
+ public void SetMemoryModel(AddressingModel addressingModel, MemoryModel memoryModel)
+ {
+ _addressingModel = addressingModel;
+ _memoryModel = memoryModel;
+ }
+
+ // TODO: Find a way to make the auto generate one used.
+ public Instruction OpenClPrintf(Instruction resultType, Instruction format, params Instruction[] additionalarguments)
+ {
+ Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType);
+
+ result.AddOperand(AddExtInstImport("OpenCL.std"));
+ result.AddOperand((LiteralInteger)184);
+ result.AddOperand(format);
+ result.AddOperand(additionalarguments);
+ AddToFunctionDefinitions(result);
+
+ return result;
+ }
+
+ public byte[] Generate()
+ {
+ // Estimate the size needed for the generated code, to avoid expanding the MemoryStream.
+ int sizeEstimate = 1024 + _functionsDefinitions.Count * 32;
+
+ using (MemoryStream stream = new MemoryStream(sizeEstimate))
+ {
+ BinaryWriter writer = new BinaryWriter(stream, System.Text.Encoding.ASCII);
+
+ // Header
+ writer.Write(MagicNumber);
+ writer.Write(_version);
+ writer.Write(GeneratorId);
+ writer.Write(_bound);
+ writer.Write(0u);
+
+ // 1.
+ foreach (Capability capability in _capabilities)
+ {
+ Instruction capabilityInstruction = NewInstruction(Op.OpCapability);
+
+ capabilityInstruction.AddOperand(capability);
+ capabilityInstruction.Write(writer);
+ }
+
+ // 2.
+ foreach (string extension in _extensions)
+ {
+ Instruction extensionInstruction = NewInstruction(Op.OpExtension);
+
+ extensionInstruction.AddOperand(extension);
+ extensionInstruction.Write(writer);
+ }
+
+ // 3.
+ foreach (Instruction extInstImport in _extInstImports.Values)
+ {
+ extInstImport.Write(writer);
+ }
+
+ // 4.
+ Instruction memoryModelInstruction = NewInstruction(Op.OpMemoryModel);
+ memoryModelInstruction.AddOperand(_addressingModel);
+ memoryModelInstruction.AddOperand(_memoryModel);
+ memoryModelInstruction.Write(writer);
+
+ // 5.
+ foreach (Instruction entrypoint in _entrypoints)
+ {
+ entrypoint.Write(writer);
+ }
+
+ // 6.
+ foreach (Instruction executionMode in _executionModes)
+ {
+ executionMode.Write(writer);
+ }
+
+ // 7.
+ // TODO: Order debug information correctly.
+ foreach (Instruction debug in _debug)
+ {
+ debug.Write(writer);
+ }
+
+ // 8.
+ foreach (Instruction annotation in _annotations)
+ {
+ annotation.Write(writer);
+ }
+
+ // Ensure that everything is in the right order in the declarations section.
+ List<Instruction> declarations = new List<Instruction>();
+ declarations.AddRange(_typeDeclarations.Values);
+ declarations.AddRange(_globals);
+ declarations.AddRange(_constants.Values);
+ declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id));
+
+ // 9.
+ foreach (Instruction declaration in declarations)
+ {
+ declaration.Write(writer);
+ }
+
+ // 10.
+ foreach (Instruction functionDeclaration in _functionsDeclarations)
+ {
+ functionDeclaration.Write(writer);
+ }
+
+ // 11.
+ foreach (Instruction functionDefinition in _functionsDefinitions)
+ {
+ functionDefinition.Write(writer);
+ }
+
+ _instPool.Clear();
+ _integerPool.Clear();
+
+ LiteralInteger.UnregisterPool();
+
+ return stream.ToArray();
+ }
+ }
+ }
+}