Module.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. using System.Collections.Generic;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using static Spv.Specification;
  5. namespace Spv.Generator
  6. {
  7. public partial class Module
  8. {
  9. // TODO: Register on SPIR-V registry.
  10. private const int GeneratorId = 0;
  11. private readonly uint _version;
  12. private uint _bound;
  13. // Follow spec order here while keeping it as simple as possible.
  14. private readonly List<Capability> _capabilities;
  15. private readonly List<string> _extensions;
  16. private readonly Dictionary<DeterministicStringKey, Instruction> _extInstImports;
  17. private AddressingModel _addressingModel;
  18. private MemoryModel _memoryModel;
  19. private readonly List<Instruction> _entrypoints;
  20. private readonly List<Instruction> _executionModes;
  21. private readonly List<Instruction> _debug;
  22. private readonly List<Instruction> _annotations;
  23. // In the declaration block.
  24. private readonly Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations;
  25. private readonly List<Instruction> _typeDeclarationsList;
  26. // In the declaration block.
  27. private readonly List<Instruction> _globals;
  28. // In the declaration block.
  29. private readonly Dictionary<ConstantKey, Instruction> _constants;
  30. // In the declaration block, for function that aren't defined in the module.
  31. private readonly List<Instruction> _functionsDeclarations;
  32. private readonly List<Instruction> _functionsDefinitions;
  33. private readonly GeneratorPool<Instruction> _instPool;
  34. private readonly GeneratorPool<LiteralInteger> _integerPool;
  35. public Module(uint version, GeneratorPool<Instruction> instPool = null, GeneratorPool<LiteralInteger> integerPool = null)
  36. {
  37. _version = version;
  38. _bound = 1;
  39. _capabilities = new List<Capability>();
  40. _extensions = new List<string>();
  41. _extInstImports = new Dictionary<DeterministicStringKey, Instruction>();
  42. _addressingModel = AddressingModel.Logical;
  43. _memoryModel = MemoryModel.Simple;
  44. _entrypoints = new List<Instruction>();
  45. _executionModes = new List<Instruction>();
  46. _debug = new List<Instruction>();
  47. _annotations = new List<Instruction>();
  48. _typeDeclarations = new Dictionary<TypeDeclarationKey, Instruction>();
  49. _typeDeclarationsList = new List<Instruction>();
  50. _constants = new Dictionary<ConstantKey, Instruction>();
  51. _globals = new List<Instruction>();
  52. _functionsDeclarations = new List<Instruction>();
  53. _functionsDefinitions = new List<Instruction>();
  54. _instPool = instPool ?? new GeneratorPool<Instruction>();
  55. _integerPool = integerPool ?? new GeneratorPool<LiteralInteger>();
  56. LiteralInteger.RegisterPool(_integerPool);
  57. }
  58. private uint GetNewId()
  59. {
  60. return _bound++;
  61. }
  62. public void AddCapability(Capability capability)
  63. {
  64. _capabilities.Add(capability);
  65. }
  66. public void AddExtension(string extension)
  67. {
  68. _extensions.Add(extension);
  69. }
  70. public Instruction NewInstruction(Op opcode, uint id = Instruction.InvalidId, Instruction resultType = null)
  71. {
  72. var result = _instPool.Allocate();
  73. result.Set(opcode, id, resultType);
  74. return result;
  75. }
  76. public Instruction AddExtInstImport(string import)
  77. {
  78. var key = new DeterministicStringKey(import);
  79. if (_extInstImports.TryGetValue(key, out Instruction extInstImport))
  80. {
  81. // Update the duplicate instance to use the good id so it ends up being encoded correctly.
  82. return extInstImport;
  83. }
  84. Instruction instruction = NewInstruction(Op.OpExtInstImport);
  85. instruction.AddOperand(import);
  86. instruction.SetId(GetNewId());
  87. _extInstImports.Add(key, instruction);
  88. return instruction;
  89. }
  90. private void AddTypeDeclaration(Instruction instruction, bool forceIdAllocation)
  91. {
  92. var key = new TypeDeclarationKey(instruction);
  93. if (!forceIdAllocation)
  94. {
  95. if (_typeDeclarations.TryGetValue(key, out Instruction typeDeclaration))
  96. {
  97. // Update the duplicate instance to use the good id so it ends up being encoded correctly.
  98. instruction.SetId(typeDeclaration.Id);
  99. return;
  100. }
  101. }
  102. instruction.SetId(GetNewId());
  103. _typeDeclarations[key] = instruction;
  104. _typeDeclarationsList.Add(instruction);
  105. }
  106. public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params Instruction[] interfaces)
  107. {
  108. Debug.Assert(function.Opcode == Op.OpFunction);
  109. Instruction entryPoint = NewInstruction(Op.OpEntryPoint);
  110. entryPoint.AddOperand(executionModel);
  111. entryPoint.AddOperand(function);
  112. entryPoint.AddOperand(name);
  113. entryPoint.AddOperand(interfaces);
  114. _entrypoints.Add(entryPoint);
  115. }
  116. public void AddExecutionMode(Instruction function, ExecutionMode mode, params IOperand[] parameters)
  117. {
  118. Debug.Assert(function.Opcode == Op.OpFunction);
  119. Instruction executionModeInstruction = NewInstruction(Op.OpExecutionMode);
  120. executionModeInstruction.AddOperand(function);
  121. executionModeInstruction.AddOperand(mode);
  122. executionModeInstruction.AddOperand(parameters);
  123. _executionModes.Add(executionModeInstruction);
  124. }
  125. private void AddToFunctionDefinitions(Instruction instruction)
  126. {
  127. Debug.Assert(instruction.Opcode != Op.OpTypeInt);
  128. _functionsDefinitions.Add(instruction);
  129. }
  130. private void AddAnnotation(Instruction annotation)
  131. {
  132. _annotations.Add(annotation);
  133. }
  134. private void AddDebug(Instruction debug)
  135. {
  136. _debug.Add(debug);
  137. }
  138. public void AddLabel(Instruction label)
  139. {
  140. Debug.Assert(label.Opcode == Op.OpLabel);
  141. label.SetId(GetNewId());
  142. AddToFunctionDefinitions(label);
  143. }
  144. public void AddLocalVariable(Instruction variable)
  145. {
  146. // TODO: Ensure it has the local modifier.
  147. Debug.Assert(variable.Opcode == Op.OpVariable);
  148. variable.SetId(GetNewId());
  149. AddToFunctionDefinitions(variable);
  150. }
  151. public void AddGlobalVariable(Instruction variable)
  152. {
  153. // TODO: Ensure it has the global modifier.
  154. // TODO: All constants opcodes (OpSpecXXX and the rest of the OpConstantXXX).
  155. Debug.Assert(variable.Opcode == Op.OpVariable);
  156. variable.SetId(GetNewId());
  157. _globals.Add(variable);
  158. }
  159. private void AddConstant(Instruction constant)
  160. {
  161. Debug.Assert(constant.Opcode == Op.OpConstant ||
  162. constant.Opcode == Op.OpConstantFalse ||
  163. constant.Opcode == Op.OpConstantTrue ||
  164. constant.Opcode == Op.OpConstantNull ||
  165. constant.Opcode == Op.OpConstantComposite);
  166. var key = new ConstantKey(constant);
  167. if (_constants.TryGetValue(key, out Instruction global))
  168. {
  169. // Update the duplicate instance to use the good id so it ends up being encoded correctly.
  170. constant.SetId(global.Id);
  171. return;
  172. }
  173. constant.SetId(GetNewId());
  174. _constants.Add(key, constant);
  175. }
  176. public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params IOperand[] parameters)
  177. {
  178. Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType);
  179. result.AddOperand(set);
  180. result.AddOperand(instruction);
  181. result.AddOperand(parameters);
  182. AddToFunctionDefinitions(result);
  183. return result;
  184. }
  185. public void SetMemoryModel(AddressingModel addressingModel, MemoryModel memoryModel)
  186. {
  187. _addressingModel = addressingModel;
  188. _memoryModel = memoryModel;
  189. }
  190. // TODO: Find a way to make the auto generate one used.
  191. public Instruction OpenClPrintf(Instruction resultType, Instruction format, params Instruction[] additionalarguments)
  192. {
  193. Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType);
  194. result.AddOperand(AddExtInstImport("OpenCL.std"));
  195. result.AddOperand((LiteralInteger)184);
  196. result.AddOperand(format);
  197. result.AddOperand(additionalarguments);
  198. AddToFunctionDefinitions(result);
  199. return result;
  200. }
  201. public byte[] Generate()
  202. {
  203. // Estimate the size needed for the generated code, to avoid expanding the MemoryStream.
  204. int sizeEstimate = 1024 + _functionsDefinitions.Count * 32;
  205. using MemoryStream stream = new(sizeEstimate);
  206. BinaryWriter writer = new(stream, System.Text.Encoding.ASCII);
  207. // Header
  208. writer.Write(MagicNumber);
  209. writer.Write(_version);
  210. writer.Write(GeneratorId);
  211. writer.Write(_bound);
  212. writer.Write(0u);
  213. // 1.
  214. foreach (Capability capability in _capabilities)
  215. {
  216. Instruction capabilityInstruction = NewInstruction(Op.OpCapability);
  217. capabilityInstruction.AddOperand(capability);
  218. capabilityInstruction.Write(writer);
  219. }
  220. // 2.
  221. foreach (string extension in _extensions)
  222. {
  223. Instruction extensionInstruction = NewInstruction(Op.OpExtension);
  224. extensionInstruction.AddOperand(extension);
  225. extensionInstruction.Write(writer);
  226. }
  227. // 3.
  228. foreach (Instruction extInstImport in _extInstImports.Values)
  229. {
  230. extInstImport.Write(writer);
  231. }
  232. // 4.
  233. Instruction memoryModelInstruction = NewInstruction(Op.OpMemoryModel);
  234. memoryModelInstruction.AddOperand(_addressingModel);
  235. memoryModelInstruction.AddOperand(_memoryModel);
  236. memoryModelInstruction.Write(writer);
  237. // 5.
  238. foreach (Instruction entrypoint in _entrypoints)
  239. {
  240. entrypoint.Write(writer);
  241. }
  242. // 6.
  243. foreach (Instruction executionMode in _executionModes)
  244. {
  245. executionMode.Write(writer);
  246. }
  247. // 7.
  248. // TODO: Order debug information correctly.
  249. foreach (Instruction debug in _debug)
  250. {
  251. debug.Write(writer);
  252. }
  253. // 8.
  254. foreach (Instruction annotation in _annotations)
  255. {
  256. annotation.Write(writer);
  257. }
  258. // Ensure that everything is in the right order in the declarations section.
  259. List<Instruction> declarations = new();
  260. declarations.AddRange(_typeDeclarationsList);
  261. declarations.AddRange(_globals);
  262. declarations.AddRange(_constants.Values);
  263. declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id));
  264. // 9.
  265. foreach (Instruction declaration in declarations)
  266. {
  267. declaration.Write(writer);
  268. }
  269. // 10.
  270. foreach (Instruction functionDeclaration in _functionsDeclarations)
  271. {
  272. functionDeclaration.Write(writer);
  273. }
  274. // 11.
  275. foreach (Instruction functionDefinition in _functionsDefinitions)
  276. {
  277. functionDefinition.Write(writer);
  278. }
  279. _instPool.Clear();
  280. _integerPool.Clear();
  281. LiteralInteger.UnregisterPool();
  282. return stream.ToArray();
  283. }
  284. }
  285. }