/ src / ARMeilleure / Translation / EmitterContext.cs
EmitterContext.cs
  1  using ARMeilleure.Diagnostics;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using System;
  5  using System.Collections.Generic;
  6  using System.Reflection;
  7  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  8  
  9  namespace ARMeilleure.Translation
 10  {
 11      class EmitterContext
 12      {
 13          private int _localsCount;
 14  
 15          private readonly Dictionary<Operand, BasicBlock> _irLabels;
 16          private readonly IntrusiveList<BasicBlock> _irBlocks;
 17  
 18          private BasicBlock _irBlock;
 19          private BasicBlock _ifBlock;
 20  
 21          private bool _needsNewBlock;
 22          private BasicBlockFrequency _nextBlockFreq;
 23  
 24          public EmitterContext()
 25          {
 26              _localsCount = 0;
 27  
 28              _irLabels = new Dictionary<Operand, BasicBlock>();
 29              _irBlocks = new IntrusiveList<BasicBlock>();
 30  
 31              _needsNewBlock = true;
 32              _nextBlockFreq = BasicBlockFrequency.Default;
 33          }
 34  
 35          public Operand AllocateLocal(OperandType type)
 36          {
 37              Operand local = Local(type);
 38  
 39              local.NumberLocal(++_localsCount);
 40  
 41              return local;
 42          }
 43  
 44          public Operand Add(Operand op1, Operand op2)
 45          {
 46              return Add(Instruction.Add, Local(op1.Type), op1, op2);
 47          }
 48  
 49          public Operand BitwiseAnd(Operand op1, Operand op2)
 50          {
 51              return Add(Instruction.BitwiseAnd, Local(op1.Type), op1, op2);
 52          }
 53  
 54          public Operand BitwiseExclusiveOr(Operand op1, Operand op2)
 55          {
 56              return Add(Instruction.BitwiseExclusiveOr, Local(op1.Type), op1, op2);
 57          }
 58  
 59          public Operand BitwiseNot(Operand op1)
 60          {
 61              return Add(Instruction.BitwiseNot, Local(op1.Type), op1);
 62          }
 63  
 64          public Operand BitwiseOr(Operand op1, Operand op2)
 65          {
 66              return Add(Instruction.BitwiseOr, Local(op1.Type), op1, op2);
 67          }
 68  
 69          public void Branch(Operand label)
 70          {
 71              NewNextBlockIfNeeded();
 72  
 73              BranchToLabel(label, uncond: true, BasicBlockFrequency.Default);
 74          }
 75  
 76          public void BranchIf(Operand label, Operand op1, Operand op2, Comparison comp, BasicBlockFrequency falseFreq = default)
 77          {
 78              Add(Instruction.BranchIf, default, op1, op2, Const((int)comp));
 79  
 80              BranchToLabel(label, uncond: false, falseFreq);
 81          }
 82  
 83          public void BranchIfFalse(Operand label, Operand op1, BasicBlockFrequency falseFreq = default)
 84          {
 85              BranchIf(label, op1, Const(op1.Type, 0), Comparison.Equal, falseFreq);
 86          }
 87  
 88          public void BranchIfTrue(Operand label, Operand op1, BasicBlockFrequency falseFreq = default)
 89          {
 90              BranchIf(label, op1, Const(op1.Type, 0), Comparison.NotEqual, falseFreq);
 91          }
 92  
 93          public Operand ByteSwap(Operand op1)
 94          {
 95              return Add(Instruction.ByteSwap, Local(op1.Type), op1);
 96          }
 97  
 98          public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
 99          {
100              IntPtr funcPtr = Delegates.GetDelegateFuncPtr(info);
101  
102              OperandType returnType = GetOperandType(info.ReturnType);
103  
104              Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
105  
106              return Call(Const(funcPtr.ToInt64()), returnType, callArgs);
107          }
108  
109          protected static OperandType GetOperandType(Type type)
110          {
111              if (type == typeof(bool) || type == typeof(byte) ||
112                  type == typeof(char) || type == typeof(short) ||
113                  type == typeof(int) || type == typeof(sbyte) ||
114                  type == typeof(ushort) || type == typeof(uint))
115              {
116                  return OperandType.I32;
117              }
118              else if (type == typeof(long) || type == typeof(ulong))
119              {
120                  return OperandType.I64;
121              }
122              else if (type == typeof(double))
123              {
124                  return OperandType.FP64;
125              }
126              else if (type == typeof(float))
127              {
128                  return OperandType.FP32;
129              }
130              else if (type == typeof(V128))
131              {
132                  return OperandType.V128;
133              }
134              else if (type == typeof(void))
135              {
136                  return OperandType.None;
137              }
138              else
139              {
140                  throw new ArgumentException($"Invalid type \"{type.Name}\".");
141              }
142          }
143  
144          public Operand Call(Operand address, OperandType returnType, params Operand[] callArgs)
145          {
146              Operand[] args = new Operand[callArgs.Length + 1];
147  
148              args[0] = address;
149  
150              Array.Copy(callArgs, 0, args, 1, callArgs.Length);
151  
152              if (returnType != OperandType.None)
153              {
154                  return Add(Instruction.Call, Local(returnType), args);
155              }
156              else
157              {
158                  return Add(Instruction.Call, default, args);
159              }
160          }
161  
162          public void Tailcall(Operand address, params Operand[] callArgs)
163          {
164              Operand[] args = new Operand[callArgs.Length + 1];
165  
166              args[0] = address;
167  
168              Array.Copy(callArgs, 0, args, 1, callArgs.Length);
169  
170              Add(Instruction.Tailcall, default, args);
171  
172              _needsNewBlock = true;
173          }
174  
175          public Operand CompareAndSwap(Operand address, Operand expected, Operand desired)
176          {
177              return Add(Instruction.CompareAndSwap, Local(desired.Type), address, expected, desired);
178          }
179  
180          public Operand CompareAndSwap16(Operand address, Operand expected, Operand desired)
181          {
182              return Add(Instruction.CompareAndSwap16, Local(OperandType.I32), address, expected, desired);
183          }
184  
185          public Operand CompareAndSwap8(Operand address, Operand expected, Operand desired)
186          {
187              return Add(Instruction.CompareAndSwap8, Local(OperandType.I32), address, expected, desired);
188          }
189  
190          public Operand ConditionalSelect(Operand op1, Operand op2, Operand op3)
191          {
192              return Add(Instruction.ConditionalSelect, Local(op2.Type), op1, op2, op3);
193          }
194  
195          public Operand ConvertI64ToI32(Operand op1)
196          {
197              if (op1.Type != OperandType.I64)
198              {
199                  throw new ArgumentException($"Invalid operand type \"{op1.Type}\".");
200              }
201  
202              return Add(Instruction.ConvertI64ToI32, Local(OperandType.I32), op1);
203          }
204  
205          public Operand ConvertToFP(OperandType type, Operand op1)
206          {
207              return Add(Instruction.ConvertToFP, Local(type), op1);
208          }
209  
210          public Operand ConvertToFPUI(OperandType type, Operand op1)
211          {
212              return Add(Instruction.ConvertToFPUI, Local(type), op1);
213          }
214  
215          public Operand Copy(Operand op1)
216          {
217              return Add(Instruction.Copy, Local(op1.Type), op1);
218          }
219  
220          public Operand Copy(Operand dest, Operand op1)
221          {
222              if (dest.Kind != OperandKind.Register &&
223                  (dest.Kind != OperandKind.LocalVariable || dest.GetLocalNumber() == 0))
224              {
225                  throw new ArgumentException($"Destination operand must be a Register or a numbered LocalVariable.");
226              }
227  
228              return Add(Instruction.Copy, dest, op1);
229          }
230  
231          public Operand CountLeadingZeros(Operand op1)
232          {
233              return Add(Instruction.CountLeadingZeros, Local(op1.Type), op1);
234          }
235  
236          public Operand Divide(Operand op1, Operand op2)
237          {
238              return Add(Instruction.Divide, Local(op1.Type), op1, op2);
239          }
240  
241          public Operand DivideUI(Operand op1, Operand op2)
242          {
243              return Add(Instruction.DivideUI, Local(op1.Type), op1, op2);
244          }
245  
246          public Operand ICompare(Operand op1, Operand op2, Comparison comp)
247          {
248              return Add(Instruction.Compare, Local(OperandType.I32), op1, op2, Const((int)comp));
249          }
250  
251          public Operand ICompareEqual(Operand op1, Operand op2)
252          {
253              return ICompare(op1, op2, Comparison.Equal);
254          }
255  
256          public Operand ICompareGreater(Operand op1, Operand op2)
257          {
258              return ICompare(op1, op2, Comparison.Greater);
259          }
260  
261          public Operand ICompareGreaterOrEqual(Operand op1, Operand op2)
262          {
263              return ICompare(op1, op2, Comparison.GreaterOrEqual);
264          }
265  
266          public Operand ICompareGreaterOrEqualUI(Operand op1, Operand op2)
267          {
268              return ICompare(op1, op2, Comparison.GreaterOrEqualUI);
269          }
270  
271          public Operand ICompareGreaterUI(Operand op1, Operand op2)
272          {
273              return ICompare(op1, op2, Comparison.GreaterUI);
274          }
275  
276          public Operand ICompareLess(Operand op1, Operand op2)
277          {
278              return ICompare(op1, op2, Comparison.Less);
279          }
280  
281          public Operand ICompareLessOrEqual(Operand op1, Operand op2)
282          {
283              return ICompare(op1, op2, Comparison.LessOrEqual);
284          }
285  
286          public Operand ICompareLessOrEqualUI(Operand op1, Operand op2)
287          {
288              return ICompare(op1, op2, Comparison.LessOrEqualUI);
289          }
290  
291          public Operand ICompareLessUI(Operand op1, Operand op2)
292          {
293              return ICompare(op1, op2, Comparison.LessUI);
294          }
295  
296          public Operand ICompareNotEqual(Operand op1, Operand op2)
297          {
298              return ICompare(op1, op2, Comparison.NotEqual);
299          }
300  
301          public Operand Load(OperandType type, Operand address)
302          {
303              return Add(Instruction.Load, Local(type), address);
304          }
305  
306          public Operand Load16(Operand address)
307          {
308              return Add(Instruction.Load16, Local(OperandType.I32), address);
309          }
310  
311          public Operand Load8(Operand address)
312          {
313              return Add(Instruction.Load8, Local(OperandType.I32), address);
314          }
315  
316          public Operand LoadArgument(OperandType type, int index)
317          {
318              return Add(Instruction.LoadArgument, Local(type), Const(index));
319          }
320  
321          public void LoadFromContext()
322          {
323              _needsNewBlock = true;
324  
325              Add(Instruction.LoadFromContext);
326          }
327  
328          public void MemoryBarrier()
329          {
330              Add(Instruction.MemoryBarrier);
331          }
332  
333          public Operand Multiply(Operand op1, Operand op2)
334          {
335              return Add(Instruction.Multiply, Local(op1.Type), op1, op2);
336          }
337  
338          public Operand Multiply64HighSI(Operand op1, Operand op2)
339          {
340              return Add(Instruction.Multiply64HighSI, Local(OperandType.I64), op1, op2);
341          }
342  
343          public Operand Multiply64HighUI(Operand op1, Operand op2)
344          {
345              return Add(Instruction.Multiply64HighUI, Local(OperandType.I64), op1, op2);
346          }
347  
348          public Operand Negate(Operand op1)
349          {
350              return Add(Instruction.Negate, Local(op1.Type), op1);
351          }
352  
353          public void Return()
354          {
355              Add(Instruction.Return);
356  
357              _needsNewBlock = true;
358          }
359  
360          public void Return(Operand op1)
361          {
362              Add(Instruction.Return, default, op1);
363  
364              _needsNewBlock = true;
365          }
366  
367          public Operand RotateRight(Operand op1, Operand op2)
368          {
369              return Add(Instruction.RotateRight, Local(op1.Type), op1, op2);
370          }
371  
372          public Operand ShiftLeft(Operand op1, Operand op2)
373          {
374              return Add(Instruction.ShiftLeft, Local(op1.Type), op1, op2);
375          }
376  
377          public Operand ShiftRightSI(Operand op1, Operand op2)
378          {
379              return Add(Instruction.ShiftRightSI, Local(op1.Type), op1, op2);
380          }
381  
382          public Operand ShiftRightUI(Operand op1, Operand op2)
383          {
384              return Add(Instruction.ShiftRightUI, Local(op1.Type), op1, op2);
385          }
386  
387          public Operand SignExtend16(OperandType type, Operand op1)
388          {
389              return Add(Instruction.SignExtend16, Local(type), op1);
390          }
391  
392          public Operand SignExtend32(OperandType type, Operand op1)
393          {
394              return Add(Instruction.SignExtend32, Local(type), op1);
395          }
396  
397          public Operand SignExtend8(OperandType type, Operand op1)
398          {
399              return Add(Instruction.SignExtend8, Local(type), op1);
400          }
401  
402          public void Store(Operand address, Operand value)
403          {
404              Add(Instruction.Store, default, address, value);
405          }
406  
407          public void Store16(Operand address, Operand value)
408          {
409              Add(Instruction.Store16, default, address, value);
410          }
411  
412          public void Store8(Operand address, Operand value)
413          {
414              Add(Instruction.Store8, default, address, value);
415          }
416  
417          public void StoreToContext()
418          {
419              Add(Instruction.StoreToContext);
420  
421              _needsNewBlock = true;
422          }
423  
424          public Operand Subtract(Operand op1, Operand op2)
425          {
426              return Add(Instruction.Subtract, Local(op1.Type), op1, op2);
427          }
428  
429          public Operand VectorCreateScalar(Operand value)
430          {
431              return Add(Instruction.VectorCreateScalar, Local(OperandType.V128), value);
432          }
433  
434          public Operand VectorExtract(OperandType type, Operand vector, int index)
435          {
436              return Add(Instruction.VectorExtract, Local(type), vector, Const(index));
437          }
438  
439          public Operand VectorExtract16(Operand vector, int index)
440          {
441              return Add(Instruction.VectorExtract16, Local(OperandType.I32), vector, Const(index));
442          }
443  
444          public Operand VectorExtract8(Operand vector, int index)
445          {
446              return Add(Instruction.VectorExtract8, Local(OperandType.I32), vector, Const(index));
447          }
448  
449          public Operand VectorInsert(Operand vector, Operand value, int index)
450          {
451              return Add(Instruction.VectorInsert, Local(OperandType.V128), vector, value, Const(index));
452          }
453  
454          public Operand VectorInsert16(Operand vector, Operand value, int index)
455          {
456              return Add(Instruction.VectorInsert16, Local(OperandType.V128), vector, value, Const(index));
457          }
458  
459          public Operand VectorInsert8(Operand vector, Operand value, int index)
460          {
461              return Add(Instruction.VectorInsert8, Local(OperandType.V128), vector, value, Const(index));
462          }
463  
464          public Operand VectorOne()
465          {
466              return Add(Instruction.VectorOne, Local(OperandType.V128));
467          }
468  
469          public Operand VectorZero()
470          {
471              return Add(Instruction.VectorZero, Local(OperandType.V128));
472          }
473  
474          public Operand VectorZeroUpper64(Operand vector)
475          {
476              return Add(Instruction.VectorZeroUpper64, Local(OperandType.V128), vector);
477          }
478  
479          public Operand VectorZeroUpper96(Operand vector)
480          {
481              return Add(Instruction.VectorZeroUpper96, Local(OperandType.V128), vector);
482          }
483  
484          public Operand ZeroExtend16(OperandType type, Operand op1)
485          {
486              return Add(Instruction.ZeroExtend16, Local(type), op1);
487          }
488  
489          public Operand ZeroExtend32(OperandType type, Operand op1)
490          {
491              return Add(Instruction.ZeroExtend32, Local(type), op1);
492          }
493  
494          public Operand ZeroExtend8(OperandType type, Operand op1)
495          {
496              return Add(Instruction.ZeroExtend8, Local(type), op1);
497          }
498  
499          private void NewNextBlockIfNeeded()
500          {
501              if (_needsNewBlock)
502              {
503                  NewNextBlock();
504              }
505          }
506  
507          private Operand Add(Instruction inst, Operand dest = default)
508          {
509              NewNextBlockIfNeeded();
510  
511              Operation operation = Operation.Factory.Operation(inst, dest);
512  
513              _irBlock.Operations.AddLast(operation);
514  
515              return dest;
516          }
517  
518          private Operand Add(Instruction inst, Operand dest, Operand[] sources)
519          {
520              NewNextBlockIfNeeded();
521  
522              Operation operation = Operation.Factory.Operation(inst, dest, sources);
523  
524              _irBlock.Operations.AddLast(operation);
525  
526              return dest;
527          }
528  
529          private Operand Add(Instruction inst, Operand dest, Operand source0)
530          {
531              NewNextBlockIfNeeded();
532  
533              Operation operation = Operation.Factory.Operation(inst, dest, source0);
534  
535              _irBlock.Operations.AddLast(operation);
536  
537              return dest;
538          }
539  
540          private Operand Add(Instruction inst, Operand dest, Operand source0, Operand source1)
541          {
542              NewNextBlockIfNeeded();
543  
544              Operation operation = Operation.Factory.Operation(inst, dest, source0, source1);
545  
546              _irBlock.Operations.AddLast(operation);
547  
548              return dest;
549          }
550  
551          private Operand Add(Instruction inst, Operand dest, Operand source0, Operand source1, Operand source2)
552          {
553              NewNextBlockIfNeeded();
554  
555              Operation operation = Operation.Factory.Operation(inst, dest, source0, source1, source2);
556  
557              _irBlock.Operations.AddLast(operation);
558  
559              return dest;
560          }
561  
562          public Operand AddIntrinsic(Intrinsic intrin, params Operand[] args)
563          {
564              return Add(intrin, Local(OperandType.V128), args);
565          }
566  
567          public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args)
568          {
569              return Add(intrin, Local(OperandType.I32), args);
570          }
571  
572          public Operand AddIntrinsicLong(Intrinsic intrin, params Operand[] args)
573          {
574              return Add(intrin, Local(OperandType.I64), args);
575          }
576  
577          public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args)
578          {
579              Add(intrin, default, args);
580          }
581  
582          private Operand Add(Intrinsic intrin, Operand dest, params Operand[] sources)
583          {
584              NewNextBlockIfNeeded();
585  
586              Operation operation = Operation.Factory.Operation(intrin, dest, sources);
587  
588              _irBlock.Operations.AddLast(operation);
589  
590              return dest;
591          }
592  
593          private void BranchToLabel(Operand label, bool uncond, BasicBlockFrequency nextFreq)
594          {
595              if (!_irLabels.TryGetValue(label, out BasicBlock branchBlock))
596              {
597                  branchBlock = new BasicBlock();
598  
599                  _irLabels.Add(label, branchBlock);
600              }
601  
602              if (uncond)
603              {
604                  _irBlock.AddSuccessor(branchBlock);
605              }
606              else
607              {
608                  // Defer registration of successor to _irBlock so that the order of successors is correct.
609                  _ifBlock = branchBlock;
610              }
611  
612              _needsNewBlock = true;
613              _nextBlockFreq = nextFreq;
614          }
615  
616          public void MarkLabel(Operand label, BasicBlockFrequency nextFreq = default)
617          {
618              _nextBlockFreq = nextFreq;
619  
620              if (_irLabels.TryGetValue(label, out BasicBlock nextBlock))
621              {
622                  nextBlock.Index = _irBlocks.Count;
623  
624                  _irBlocks.AddLast(nextBlock);
625  
626                  NextBlock(nextBlock);
627              }
628              else
629              {
630                  NewNextBlock();
631  
632                  _irLabels.Add(label, _irBlock);
633              }
634          }
635  
636          private void NewNextBlock()
637          {
638              BasicBlock block = new(_irBlocks.Count);
639  
640              _irBlocks.AddLast(block);
641  
642              NextBlock(block);
643          }
644  
645          private void NextBlock(BasicBlock nextBlock)
646          {
647              if (_irBlock?.SuccessorsCount == 0 && !EndsWithUnconditional(_irBlock))
648              {
649                  _irBlock.AddSuccessor(nextBlock);
650  
651                  if (_ifBlock != null)
652                  {
653                      _irBlock.AddSuccessor(_ifBlock);
654  
655                      _ifBlock = null;
656                  }
657              }
658  
659              _irBlock = nextBlock;
660              _irBlock.Frequency = _nextBlockFreq;
661  
662              _needsNewBlock = false;
663              _nextBlockFreq = BasicBlockFrequency.Default;
664          }
665  
666          private static bool EndsWithUnconditional(BasicBlock block)
667          {
668              Operation last = block.Operations.Last;
669  
670              return last != default &&
671                 (last.Instruction == Instruction.Return ||
672                  last.Instruction == Instruction.Tailcall);
673          }
674  
675          public ControlFlowGraph GetControlFlowGraph()
676          {
677              return new ControlFlowGraph(_irBlocks.First, _irBlocks, _localsCount);
678          }
679      }
680  }