aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/IntermediateRepresentation
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/ARMeilleure/IntermediateRepresentation
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/ARMeilleure/IntermediateRepresentation')
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs159
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs8
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Comparison.cs24
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs8
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Instruction.cs72
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs636
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs208
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs54
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Multiplier.cs11
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Operand.cs594
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/OperandKind.cs13
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/OperandType.cs65
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Operation.cs376
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs37
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/Register.cs43
-rw-r--r--src/ARMeilleure/IntermediateRepresentation/RegisterType.cs10
16 files changed, 2318 insertions, 0 deletions
diff --git a/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs
new file mode 100644
index 00000000..07bd8b67
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/BasicBlock.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ class BasicBlock : IEquatable<BasicBlock>, IIntrusiveListNode<BasicBlock>
+ {
+ private const uint MaxSuccessors = 2;
+
+ private int _succCount;
+ private BasicBlock _succ0;
+ private BasicBlock _succ1;
+ private HashSet<BasicBlock> _domFrontiers;
+
+ public int Index { get; set; }
+ public BasicBlockFrequency Frequency { get; set; }
+ public BasicBlock ListPrevious { get; set; }
+ public BasicBlock ListNext { get; set; }
+ public IntrusiveList<Operation> Operations { get; }
+ public List<BasicBlock> Predecessors { get; }
+ public BasicBlock ImmediateDominator { get; set; }
+
+ public int SuccessorsCount => _succCount;
+
+ public HashSet<BasicBlock> DominanceFrontiers
+ {
+ get
+ {
+ if (_domFrontiers == null)
+ {
+ _domFrontiers = new HashSet<BasicBlock>();
+ }
+
+ return _domFrontiers;
+ }
+ }
+
+ public BasicBlock() : this(index: -1) { }
+
+ public BasicBlock(int index)
+ {
+ Operations = new IntrusiveList<Operation>();
+ Predecessors = new List<BasicBlock>();
+
+ Index = index;
+ }
+
+ public void AddSuccessor(BasicBlock block)
+ {
+ ArgumentNullException.ThrowIfNull(block);
+
+ if ((uint)_succCount + 1 > MaxSuccessors)
+ {
+ ThrowSuccessorOverflow();
+ }
+
+ block.Predecessors.Add(this);
+
+ GetSuccessorUnsafe(_succCount++) = block;
+ }
+
+ public void RemoveSuccessor(int index)
+ {
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
+
+ oldBlock.Predecessors.Remove(this);
+ oldBlock = null;
+
+ if (index == 0)
+ {
+ _succ0 = _succ1;
+ }
+
+ _succCount--;
+ }
+
+ public BasicBlock GetSuccessor(int index)
+ {
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ return GetSuccessorUnsafe(index);
+ }
+
+ private ref BasicBlock GetSuccessorUnsafe(int index)
+ {
+ return ref Unsafe.Add(ref _succ0, index);
+ }
+
+ public void SetSuccessor(int index, BasicBlock block)
+ {
+ ArgumentNullException.ThrowIfNull(block);
+
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
+
+ oldBlock.Predecessors.Remove(this);
+ block.Predecessors.Add(this);
+
+ oldBlock = block;
+ }
+
+ public void Append(Operation node)
+ {
+ Operation last = Operations.Last;
+
+ // Append node before terminal or to end if no terminal.
+ if (last == default)
+ {
+ Operations.AddLast(node);
+
+ return;
+ }
+
+ switch (last.Instruction)
+ {
+ case Instruction.Return:
+ case Instruction.Tailcall:
+ case Instruction.BranchIf:
+ Operations.AddBefore(last, node);
+ break;
+
+ default:
+ Operations.AddLast(node);
+ break;
+ }
+ }
+
+ private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
+ private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
+
+ public bool Equals(BasicBlock other)
+ {
+ return other == this;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as BasicBlock);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs b/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs
new file mode 100644
index 00000000..96cfee35
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/BasicBlockFrequency.cs
@@ -0,0 +1,8 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum BasicBlockFrequency
+ {
+ Default,
+ Cold
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/Comparison.cs b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs
new file mode 100644
index 00000000..628ce105
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Comparison.cs
@@ -0,0 +1,24 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum Comparison
+ {
+ Equal = 0,
+ NotEqual = 1,
+ Greater = 2,
+ LessOrEqual = 3,
+ GreaterUI = 4,
+ LessOrEqualUI = 5,
+ GreaterOrEqual = 6,
+ Less = 7,
+ GreaterOrEqualUI = 8,
+ LessUI = 9
+ }
+
+ static class ComparisonExtensions
+ {
+ public static Comparison Invert(this Comparison comp)
+ {
+ return (Comparison)((int)comp ^ 1);
+ }
+ }
+}
diff --git a/src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs b/src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs
new file mode 100644
index 00000000..caa9b83f
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs
@@ -0,0 +1,8 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ interface IIntrusiveListNode<T>
+ {
+ T ListPrevious { get; set; }
+ T ListNext { get; set; }
+ }
+}
diff --git a/src/ARMeilleure/IntermediateRepresentation/Instruction.cs b/src/ARMeilleure/IntermediateRepresentation/Instruction.cs
new file mode 100644
index 00000000..b55fe1da
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Instruction.cs
@@ -0,0 +1,72 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum Instruction : ushort
+ {
+ Add,
+ BitwiseAnd,
+ BitwiseExclusiveOr,
+ BitwiseNot,
+ BitwiseOr,
+ BranchIf,
+ ByteSwap,
+ Call,
+ Compare,
+ CompareAndSwap,
+ CompareAndSwap16,
+ CompareAndSwap8,
+ ConditionalSelect,
+ ConvertI64ToI32,
+ ConvertToFP,
+ ConvertToFPUI,
+ Copy,
+ CountLeadingZeros,
+ Divide,
+ DivideUI,
+ Load,
+ Load16,
+ Load8,
+ LoadArgument,
+ MemoryBarrier,
+ Multiply,
+ Multiply64HighSI,
+ Multiply64HighUI,
+ Negate,
+ Return,
+ RotateRight,
+ ShiftLeft,
+ ShiftRightSI,
+ ShiftRightUI,
+ SignExtend16,
+ SignExtend32,
+ SignExtend8,
+ StackAlloc,
+ Store,
+ Store16,
+ Store8,
+ Subtract,
+ Tailcall,
+ VectorCreateScalar,
+ VectorExtract,
+ VectorExtract16,
+ VectorExtract8,
+ VectorInsert,
+ VectorInsert16,
+ VectorInsert8,
+ VectorOne,
+ VectorZero,
+ VectorZeroUpper64,
+ VectorZeroUpper96,
+ ZeroExtend16,
+ ZeroExtend32,
+ ZeroExtend8,
+
+ Clobber,
+ Extended,
+ Fill,
+ LoadFromContext,
+ Phi,
+ Spill,
+ SpillArg,
+ StoreToContext
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
new file mode 100644
index 00000000..f5a776fa
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
@@ -0,0 +1,636 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum Intrinsic : ushort
+ {
+ // X86 (SSE and AVX)
+
+ X86Addpd,
+ X86Addps,
+ X86Addsd,
+ X86Addss,
+ X86Aesdec,
+ X86Aesdeclast,
+ X86Aesenc,
+ X86Aesenclast,
+ X86Aesimc,
+ X86Andnpd,
+ X86Andnps,
+ X86Andpd,
+ X86Andps,
+ X86Blendvpd,
+ X86Blendvps,
+ X86Cmppd,
+ X86Cmpps,
+ X86Cmpsd,
+ X86Cmpss,
+ X86Comisdeq,
+ X86Comisdge,
+ X86Comisdlt,
+ X86Comisseq,
+ X86Comissge,
+ X86Comisslt,
+ X86Crc32,
+ X86Crc32_16,
+ X86Crc32_8,
+ X86Cvtdq2pd,
+ X86Cvtdq2ps,
+ X86Cvtpd2dq,
+ X86Cvtpd2ps,
+ X86Cvtps2dq,
+ X86Cvtps2pd,
+ X86Cvtsd2si,
+ X86Cvtsd2ss,
+ X86Cvtsi2sd,
+ X86Cvtsi2si,
+ X86Cvtsi2ss,
+ X86Cvtss2sd,
+ X86Cvtss2si,
+ X86Divpd,
+ X86Divps,
+ X86Divsd,
+ X86Divss,
+ X86Gf2p8affineqb,
+ X86Haddpd,
+ X86Haddps,
+ X86Insertps,
+ X86Ldmxcsr,
+ X86Maxpd,
+ X86Maxps,
+ X86Maxsd,
+ X86Maxss,
+ X86Minpd,
+ X86Minps,
+ X86Minsd,
+ X86Minss,
+ X86Movhlps,
+ X86Movlhps,
+ X86Movss,
+ X86Mulpd,
+ X86Mulps,
+ X86Mulsd,
+ X86Mulss,
+ X86Paddb,
+ X86Paddd,
+ X86Paddq,
+ X86Paddw,
+ X86Palignr,
+ X86Pand,
+ X86Pandn,
+ X86Pavgb,
+ X86Pavgw,
+ X86Pblendvb,
+ X86Pclmulqdq,
+ X86Pcmpeqb,
+ X86Pcmpeqd,
+ X86Pcmpeqq,
+ X86Pcmpeqw,
+ X86Pcmpgtb,
+ X86Pcmpgtd,
+ X86Pcmpgtq,
+ X86Pcmpgtw,
+ X86Pmaxsb,
+ X86Pmaxsd,
+ X86Pmaxsw,
+ X86Pmaxub,
+ X86Pmaxud,
+ X86Pmaxuw,
+ X86Pminsb,
+ X86Pminsd,
+ X86Pminsw,
+ X86Pminub,
+ X86Pminud,
+ X86Pminuw,
+ X86Pmovsxbw,
+ X86Pmovsxdq,
+ X86Pmovsxwd,
+ X86Pmovzxbw,
+ X86Pmovzxdq,
+ X86Pmovzxwd,
+ X86Pmulld,
+ X86Pmullw,
+ X86Popcnt,
+ X86Por,
+ X86Pshufb,
+ X86Pshufd,
+ X86Pslld,
+ X86Pslldq,
+ X86Psllq,
+ X86Psllw,
+ X86Psrad,
+ X86Psraw,
+ X86Psrld,
+ X86Psrlq,
+ X86Psrldq,
+ X86Psrlw,
+ X86Psubb,
+ X86Psubd,
+ X86Psubq,
+ X86Psubw,
+ X86Punpckhbw,
+ X86Punpckhdq,
+ X86Punpckhqdq,
+ X86Punpckhwd,
+ X86Punpcklbw,
+ X86Punpckldq,
+ X86Punpcklqdq,
+ X86Punpcklwd,
+ X86Pxor,
+ X86Rcpps,
+ X86Rcpss,
+ X86Roundpd,
+ X86Roundps,
+ X86Roundsd,
+ X86Roundss,
+ X86Rsqrtps,
+ X86Rsqrtss,
+ X86Sha256Msg1,
+ X86Sha256Msg2,
+ X86Sha256Rnds2,
+ X86Shufpd,
+ X86Shufps,
+ X86Sqrtpd,
+ X86Sqrtps,
+ X86Sqrtsd,
+ X86Sqrtss,
+ X86Stmxcsr,
+ X86Subpd,
+ X86Subps,
+ X86Subsd,
+ X86Subss,
+ X86Unpckhpd,
+ X86Unpckhps,
+ X86Unpcklpd,
+ X86Unpcklps,
+ X86Vcvtph2ps,
+ X86Vcvtps2ph,
+ X86Vfmadd231pd,
+ X86Vfmadd231ps,
+ X86Vfmadd231sd,
+ X86Vfmadd231ss,
+ X86Vfmsub231sd,
+ X86Vfmsub231ss,
+ X86Vfnmadd231pd,
+ X86Vfnmadd231ps,
+ X86Vfnmadd231sd,
+ X86Vfnmadd231ss,
+ X86Vfnmsub231sd,
+ X86Vfnmsub231ss,
+ X86Vpternlogd,
+ X86Xorpd,
+ X86Xorps,
+
+ // Arm64 (FP and Advanced SIMD)
+
+ Arm64AbsS,
+ Arm64AbsV,
+ Arm64AddhnV,
+ Arm64AddpS,
+ Arm64AddpV,
+ Arm64AddvV,
+ Arm64AddS,
+ Arm64AddV,
+ Arm64AesdV,
+ Arm64AeseV,
+ Arm64AesimcV,
+ Arm64AesmcV,
+ Arm64AndV,
+ Arm64BicVi,
+ Arm64BicV,
+ Arm64BifV,
+ Arm64BitV,
+ Arm64BslV,
+ Arm64ClsV,
+ Arm64ClzV,
+ Arm64CmeqS,
+ Arm64CmeqV,
+ Arm64CmeqSz,
+ Arm64CmeqVz,
+ Arm64CmgeS,
+ Arm64CmgeV,
+ Arm64CmgeSz,
+ Arm64CmgeVz,
+ Arm64CmgtS,
+ Arm64CmgtV,
+ Arm64CmgtSz,
+ Arm64CmgtVz,
+ Arm64CmhiS,
+ Arm64CmhiV,
+ Arm64CmhsS,
+ Arm64CmhsV,
+ Arm64CmleSz,
+ Arm64CmleVz,
+ Arm64CmltSz,
+ Arm64CmltVz,
+ Arm64CmtstS,
+ Arm64CmtstV,
+ Arm64CntV,
+ Arm64DupSe,
+ Arm64DupVe,
+ Arm64DupGp,
+ Arm64EorV,
+ Arm64ExtV,
+ Arm64FabdS,
+ Arm64FabdV,
+ Arm64FabsV,
+ Arm64FabsS,
+ Arm64FacgeS,
+ Arm64FacgeV,
+ Arm64FacgtS,
+ Arm64FacgtV,
+ Arm64FaddpS,
+ Arm64FaddpV,
+ Arm64FaddV,
+ Arm64FaddS,
+ Arm64FccmpeS,
+ Arm64FccmpS,
+ Arm64FcmeqS,
+ Arm64FcmeqV,
+ Arm64FcmeqSz,
+ Arm64FcmeqVz,
+ Arm64FcmgeS,
+ Arm64FcmgeV,
+ Arm64FcmgeSz,
+ Arm64FcmgeVz,
+ Arm64FcmgtS,
+ Arm64FcmgtV,
+ Arm64FcmgtSz,
+ Arm64FcmgtVz,
+ Arm64FcmleSz,
+ Arm64FcmleVz,
+ Arm64FcmltSz,
+ Arm64FcmltVz,
+ Arm64FcmpeS,
+ Arm64FcmpS,
+ Arm64FcselS,
+ Arm64FcvtasS,
+ Arm64FcvtasV,
+ Arm64FcvtasGp,
+ Arm64FcvtauS,
+ Arm64FcvtauV,
+ Arm64FcvtauGp,
+ Arm64FcvtlV,
+ Arm64FcvtmsS,
+ Arm64FcvtmsV,
+ Arm64FcvtmsGp,
+ Arm64FcvtmuS,
+ Arm64FcvtmuV,
+ Arm64FcvtmuGp,
+ Arm64FcvtnsS,
+ Arm64FcvtnsV,
+ Arm64FcvtnsGp,
+ Arm64FcvtnuS,
+ Arm64FcvtnuV,
+ Arm64FcvtnuGp,
+ Arm64FcvtnV,
+ Arm64FcvtpsS,
+ Arm64FcvtpsV,
+ Arm64FcvtpsGp,
+ Arm64FcvtpuS,
+ Arm64FcvtpuV,
+ Arm64FcvtpuGp,
+ Arm64FcvtxnS,
+ Arm64FcvtxnV,
+ Arm64FcvtzsSFixed,
+ Arm64FcvtzsVFixed,
+ Arm64FcvtzsS,
+ Arm64FcvtzsV,
+ Arm64FcvtzsGpFixed,
+ Arm64FcvtzsGp,
+ Arm64FcvtzuSFixed,
+ Arm64FcvtzuVFixed,
+ Arm64FcvtzuS,
+ Arm64FcvtzuV,
+ Arm64FcvtzuGpFixed,
+ Arm64FcvtzuGp,
+ Arm64FcvtS,
+ Arm64FdivV,
+ Arm64FdivS,
+ Arm64FmaddS,
+ Arm64FmaxnmpS,
+ Arm64FmaxnmpV,
+ Arm64FmaxnmvV,
+ Arm64FmaxnmV,
+ Arm64FmaxnmS,
+ Arm64FmaxpS,
+ Arm64FmaxpV,
+ Arm64FmaxvV,
+ Arm64FmaxV,
+ Arm64FmaxS,
+ Arm64FminnmpS,
+ Arm64FminnmpV,
+ Arm64FminnmvV,
+ Arm64FminnmV,
+ Arm64FminnmS,
+ Arm64FminpS,
+ Arm64FminpV,
+ Arm64FminvV,
+ Arm64FminV,
+ Arm64FminS,
+ Arm64FmlaSe,
+ Arm64FmlaVe,
+ Arm64FmlaV,
+ Arm64FmlsSe,
+ Arm64FmlsVe,
+ Arm64FmlsV,
+ Arm64FmovVi,
+ Arm64FmovS,
+ Arm64FmovGp,
+ Arm64FmovSi,
+ Arm64FmsubS,
+ Arm64FmulxSe,
+ Arm64FmulxVe,
+ Arm64FmulxS,
+ Arm64FmulxV,
+ Arm64FmulSe,
+ Arm64FmulVe,
+ Arm64FmulV,
+ Arm64FmulS,
+ Arm64FnegV,
+ Arm64FnegS,
+ Arm64FnmaddS,
+ Arm64FnmsubS,
+ Arm64FnmulS,
+ Arm64FrecpeS,
+ Arm64FrecpeV,
+ Arm64FrecpsS,
+ Arm64FrecpsV,
+ Arm64FrecpxS,
+ Arm64FrintaV,
+ Arm64FrintaS,
+ Arm64FrintiV,
+ Arm64FrintiS,
+ Arm64FrintmV,
+ Arm64FrintmS,
+ Arm64FrintnV,
+ Arm64FrintnS,
+ Arm64FrintpV,
+ Arm64FrintpS,
+ Arm64FrintxV,
+ Arm64FrintxS,
+ Arm64FrintzV,
+ Arm64FrintzS,
+ Arm64FrsqrteS,
+ Arm64FrsqrteV,
+ Arm64FrsqrtsS,
+ Arm64FrsqrtsV,
+ Arm64FsqrtV,
+ Arm64FsqrtS,
+ Arm64FsubV,
+ Arm64FsubS,
+ Arm64InsVe,
+ Arm64InsGp,
+ Arm64Ld1rV,
+ Arm64Ld1Vms,
+ Arm64Ld1Vss,
+ Arm64Ld2rV,
+ Arm64Ld2Vms,
+ Arm64Ld2Vss,
+ Arm64Ld3rV,
+ Arm64Ld3Vms,
+ Arm64Ld3Vss,
+ Arm64Ld4rV,
+ Arm64Ld4Vms,
+ Arm64Ld4Vss,
+ Arm64MlaVe,
+ Arm64MlaV,
+ Arm64MlsVe,
+ Arm64MlsV,
+ Arm64MoviV,
+ Arm64MrsFpcr,
+ Arm64MsrFpcr,
+ Arm64MrsFpsr,
+ Arm64MsrFpsr,
+ Arm64MulVe,
+ Arm64MulV,
+ Arm64MvniV,
+ Arm64NegS,
+ Arm64NegV,
+ Arm64NotV,
+ Arm64OrnV,
+ Arm64OrrVi,
+ Arm64OrrV,
+ Arm64PmullV,
+ Arm64PmulV,
+ Arm64RaddhnV,
+ Arm64RbitV,
+ Arm64Rev16V,
+ Arm64Rev32V,
+ Arm64Rev64V,
+ Arm64RshrnV,
+ Arm64RsubhnV,
+ Arm64SabalV,
+ Arm64SabaV,
+ Arm64SabdlV,
+ Arm64SabdV,
+ Arm64SadalpV,
+ Arm64SaddlpV,
+ Arm64SaddlvV,
+ Arm64SaddlV,
+ Arm64SaddwV,
+ Arm64ScvtfSFixed,
+ Arm64ScvtfVFixed,
+ Arm64ScvtfS,
+ Arm64ScvtfV,
+ Arm64ScvtfGpFixed,
+ Arm64ScvtfGp,
+ Arm64Sha1cV,
+ Arm64Sha1hV,
+ Arm64Sha1mV,
+ Arm64Sha1pV,
+ Arm64Sha1su0V,
+ Arm64Sha1su1V,
+ Arm64Sha256h2V,
+ Arm64Sha256hV,
+ Arm64Sha256su0V,
+ Arm64Sha256su1V,
+ Arm64ShaddV,
+ Arm64ShllV,
+ Arm64ShlS,
+ Arm64ShlV,
+ Arm64ShrnV,
+ Arm64ShsubV,
+ Arm64SliS,
+ Arm64SliV,
+ Arm64SmaxpV,
+ Arm64SmaxvV,
+ Arm64SmaxV,
+ Arm64SminpV,
+ Arm64SminvV,
+ Arm64SminV,
+ Arm64SmlalVe,
+ Arm64SmlalV,
+ Arm64SmlslVe,
+ Arm64SmlslV,
+ Arm64SmovV,
+ Arm64SmullVe,
+ Arm64SmullV,
+ Arm64SqabsS,
+ Arm64SqabsV,
+ Arm64SqaddS,
+ Arm64SqaddV,
+ Arm64SqdmlalSe,
+ Arm64SqdmlalVe,
+ Arm64SqdmlalS,
+ Arm64SqdmlalV,
+ Arm64SqdmlslSe,
+ Arm64SqdmlslVe,
+ Arm64SqdmlslS,
+ Arm64SqdmlslV,
+ Arm64SqdmulhSe,
+ Arm64SqdmulhVe,
+ Arm64SqdmulhS,
+ Arm64SqdmulhV,
+ Arm64SqdmullSe,
+ Arm64SqdmullVe,
+ Arm64SqdmullS,
+ Arm64SqdmullV,
+ Arm64SqnegS,
+ Arm64SqnegV,
+ Arm64SqrdmulhSe,
+ Arm64SqrdmulhVe,
+ Arm64SqrdmulhS,
+ Arm64SqrdmulhV,
+ Arm64SqrshlS,
+ Arm64SqrshlV,
+ Arm64SqrshrnS,
+ Arm64SqrshrnV,
+ Arm64SqrshrunS,
+ Arm64SqrshrunV,
+ Arm64SqshluS,
+ Arm64SqshluV,
+ Arm64SqshlSi,
+ Arm64SqshlVi,
+ Arm64SqshlS,
+ Arm64SqshlV,
+ Arm64SqshrnS,
+ Arm64SqshrnV,
+ Arm64SqshrunS,
+ Arm64SqshrunV,
+ Arm64SqsubS,
+ Arm64SqsubV,
+ Arm64SqxtnS,
+ Arm64SqxtnV,
+ Arm64SqxtunS,
+ Arm64SqxtunV,
+ Arm64SrhaddV,
+ Arm64SriS,
+ Arm64SriV,
+ Arm64SrshlS,
+ Arm64SrshlV,
+ Arm64SrshrS,
+ Arm64SrshrV,
+ Arm64SrsraS,
+ Arm64SrsraV,
+ Arm64SshllV,
+ Arm64SshlS,
+ Arm64SshlV,
+ Arm64SshrS,
+ Arm64SshrV,
+ Arm64SsraS,
+ Arm64SsraV,
+ Arm64SsublV,
+ Arm64SsubwV,
+ Arm64St1Vms,
+ Arm64St1Vss,
+ Arm64St2Vms,
+ Arm64St2Vss,
+ Arm64St3Vms,
+ Arm64St3Vss,
+ Arm64St4Vms,
+ Arm64St4Vss,
+ Arm64SubhnV,
+ Arm64SubS,
+ Arm64SubV,
+ Arm64SuqaddS,
+ Arm64SuqaddV,
+ Arm64TblV,
+ Arm64TbxV,
+ Arm64Trn1V,
+ Arm64Trn2V,
+ Arm64UabalV,
+ Arm64UabaV,
+ Arm64UabdlV,
+ Arm64UabdV,
+ Arm64UadalpV,
+ Arm64UaddlpV,
+ Arm64UaddlvV,
+ Arm64UaddlV,
+ Arm64UaddwV,
+ Arm64UcvtfSFixed,
+ Arm64UcvtfVFixed,
+ Arm64UcvtfS,
+ Arm64UcvtfV,
+ Arm64UcvtfGpFixed,
+ Arm64UcvtfGp,
+ Arm64UhaddV,
+ Arm64UhsubV,
+ Arm64UmaxpV,
+ Arm64UmaxvV,
+ Arm64UmaxV,
+ Arm64UminpV,
+ Arm64UminvV,
+ Arm64UminV,
+ Arm64UmlalVe,
+ Arm64UmlalV,
+ Arm64UmlslVe,
+ Arm64UmlslV,
+ Arm64UmovV,
+ Arm64UmullVe,
+ Arm64UmullV,
+ Arm64UqaddS,
+ Arm64UqaddV,
+ Arm64UqrshlS,
+ Arm64UqrshlV,
+ Arm64UqrshrnS,
+ Arm64UqrshrnV,
+ Arm64UqshlSi,
+ Arm64UqshlVi,
+ Arm64UqshlS,
+ Arm64UqshlV,
+ Arm64UqshrnS,
+ Arm64UqshrnV,
+ Arm64UqsubS,
+ Arm64UqsubV,
+ Arm64UqxtnS,
+ Arm64UqxtnV,
+ Arm64UrecpeV,
+ Arm64UrhaddV,
+ Arm64UrshlS,
+ Arm64UrshlV,
+ Arm64UrshrS,
+ Arm64UrshrV,
+ Arm64UrsqrteV,
+ Arm64UrsraS,
+ Arm64UrsraV,
+ Arm64UshllV,
+ Arm64UshlS,
+ Arm64UshlV,
+ Arm64UshrS,
+ Arm64UshrV,
+ Arm64UsqaddS,
+ Arm64UsqaddV,
+ Arm64UsraS,
+ Arm64UsraV,
+ Arm64UsublV,
+ Arm64UsubwV,
+ Arm64Uzp1V,
+ Arm64Uzp2V,
+ Arm64XtnV,
+ Arm64Zip1V,
+ Arm64Zip2V,
+
+ Arm64VTypeShift = 13,
+ Arm64VTypeMask = 1 << Arm64VTypeShift,
+ Arm64V64 = 0 << Arm64VTypeShift,
+ Arm64V128 = 1 << Arm64VTypeShift,
+
+ Arm64VSizeShift = 14,
+ Arm64VSizeMask = 3 << Arm64VSizeShift,
+ Arm64VFloat = 0 << Arm64VSizeShift,
+ Arm64VDouble = 1 << Arm64VSizeShift,
+ Arm64VByte = 0 << Arm64VSizeShift,
+ Arm64VHWord = 1 << Arm64VSizeShift,
+ Arm64VWord = 2 << Arm64VSizeShift,
+ Arm64VDWord = 3 << Arm64VSizeShift
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs b/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs
new file mode 100644
index 00000000..184df87c
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ /// <summary>
+ /// Represents a efficient linked list that stores the pointer on the object directly and does not allocate.
+ /// </summary>
+ /// <typeparam name="T">Type of the list items</typeparam>
+ class IntrusiveList<T> where T : IEquatable<T>, IIntrusiveListNode<T>
+ {
+ /// <summary>
+ /// First item of the list, or null if empty.
+ /// </summary>
+ public T First { get; private set; }
+
+ /// <summary>
+ /// Last item of the list, or null if empty.
+ /// </summary>
+ public T Last { get; private set; }
+
+ /// <summary>
+ /// Total number of items on the list.
+ /// </summary>
+ public int Count { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IntrusiveList{T}"/> class.
+ /// </summary>
+ /// <exception cref="ArgumentException"><typeparamref name="T"/> is not pointer sized.</exception>
+ public IntrusiveList()
+ {
+ if (Unsafe.SizeOf<T>() != IntPtr.Size)
+ {
+ throw new ArgumentException("T must be a reference type or a pointer sized struct.");
+ }
+ }
+
+ /// <summary>
+ /// Adds a item as the first item of the list.
+ /// </summary>
+ /// <param name="newNode">Item to be added</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T AddFirst(T newNode)
+ {
+ if (!EqualsNull(First))
+ {
+ return AddBefore(First, newNode);
+ }
+ else
+ {
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+ Debug.Assert(EqualsNull(Last));
+
+ First = newNode;
+ Last = newNode;
+
+ Debug.Assert(Count == 0);
+
+ Count = 1;
+
+ return newNode;
+ }
+ }
+
+ /// <summary>
+ /// Adds a item as the last item of the list.
+ /// </summary>
+ /// <param name="newNode">Item to be added</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T AddLast(T newNode)
+ {
+ if (!EqualsNull(Last))
+ {
+ return AddAfter(Last, newNode);
+ }
+ else
+ {
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+ Debug.Assert(EqualsNull(First));
+
+ First = newNode;
+ Last = newNode;
+
+ Debug.Assert(Count == 0);
+
+ Count = 1;
+
+ return newNode;
+ }
+ }
+
+ /// <summary>
+ /// Adds a item before a existing item on the list.
+ /// </summary>
+ /// <param name="node">Item on the list that will succeed the new item</param>
+ /// <param name="newNode">Item to be added</param>
+ /// <returns>New item</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T AddBefore(T node, T newNode)
+ {
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+
+ newNode.ListPrevious = node.ListPrevious;
+ newNode.ListNext = node;
+
+ node.ListPrevious = newNode;
+
+ if (!EqualsNull(newNode.ListPrevious))
+ {
+ newNode.ListPrevious.ListNext = newNode;
+ }
+
+ if (Equals(First, node))
+ {
+ First = newNode;
+ }
+
+ Count++;
+
+ return newNode;
+ }
+
+ /// <summary>
+ /// Adds a item after a existing item on the list.
+ /// </summary>
+ /// <param name="node">Item on the list that will preceed the new item</param>
+ /// <param name="newNode">Item to be added</param>
+ /// <returns>New item</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T AddAfter(T node, T newNode)
+ {
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+
+ newNode.ListPrevious = node;
+ newNode.ListNext = node.ListNext;
+
+ node.ListNext = newNode;
+
+ if (!EqualsNull(newNode.ListNext))
+ {
+ newNode.ListNext.ListPrevious = newNode;
+ }
+
+ if (Equals(Last, node))
+ {
+ Last = newNode;
+ }
+
+ Count++;
+
+ return newNode;
+ }
+
+ /// <summary>
+ /// Removes a item from the list.
+ /// </summary>
+ /// <param name="node">The item to be removed</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Remove(T node)
+ {
+ if (!EqualsNull(node.ListPrevious))
+ {
+ node.ListPrevious.ListNext = node.ListNext;
+ }
+ else
+ {
+ Debug.Assert(Equals(First, node));
+
+ First = node.ListNext;
+ }
+
+ if (!EqualsNull(node.ListNext))
+ {
+ node.ListNext.ListPrevious = node.ListPrevious;
+ }
+ else
+ {
+ Debug.Assert(Equals(Last, node));
+
+ Last = node.ListPrevious;
+ }
+
+ node.ListPrevious = default;
+ node.ListNext = default;
+
+ Count--;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool EqualsNull(T a)
+ {
+ return EqualityComparer<T>.Default.Equals(a, default);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool Equals(T a, T b)
+ {
+ return EqualityComparer<T>.Default.Equals(a, b);
+ }
+ }
+}
diff --git a/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs
new file mode 100644
index 00000000..07d2633b
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ unsafe struct MemoryOperand
+ {
+ private struct Data
+ {
+#pragma warning disable CS0649
+ public byte Kind;
+ public byte Type;
+#pragma warning restore CS0649
+ public byte Scale;
+ public Operand BaseAddress;
+ public Operand Index;
+ public int Displacement;
+ }
+
+ private Data* _data;
+
+ public MemoryOperand(Operand operand)
+ {
+ Debug.Assert(operand.Kind == OperandKind.Memory);
+
+ _data = (Data*)Unsafe.As<Operand, IntPtr>(ref operand);
+ }
+
+ public Operand BaseAddress
+ {
+ get => _data->BaseAddress;
+ set => _data->BaseAddress = value;
+ }
+
+ public Operand Index
+ {
+ get => _data->Index;
+ set => _data->Index = value;
+ }
+
+ public Multiplier Scale
+ {
+ get => (Multiplier)_data->Scale;
+ set => _data->Scale = (byte)value;
+ }
+
+ public int Displacement
+ {
+ get => _data->Displacement;
+ set => _data->Displacement = value;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs b/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs
new file mode 100644
index 00000000..d6bc7d99
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Multiplier.cs
@@ -0,0 +1,11 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum Multiplier
+ {
+ x1 = 0,
+ x2 = 1,
+ x4 = 2,
+ x8 = 3,
+ x16 = 4
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/Operand.cs b/src/ARMeilleure/IntermediateRepresentation/Operand.cs
new file mode 100644
index 00000000..9e8de3ba
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Operand.cs
@@ -0,0 +1,594 @@
+using ARMeilleure.CodeGen.Linking;
+using ARMeilleure.Common;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ unsafe struct Operand : IEquatable<Operand>
+ {
+ internal struct Data
+ {
+ public byte Kind;
+ public byte Type;
+ public byte SymbolType;
+ public byte Padding; // Unused space.
+ public ushort AssignmentsCount;
+ public ushort AssignmentsCapacity;
+ public uint UsesCount;
+ public uint UsesCapacity;
+ public Operation* Assignments;
+ public Operation* Uses;
+ public ulong Value;
+ public ulong SymbolValue;
+ }
+
+ private Data* _data;
+
+ public OperandKind Kind
+ {
+ get => (OperandKind)_data->Kind;
+ private set => _data->Kind = (byte)value;
+ }
+
+ public OperandType Type
+ {
+ get => (OperandType)_data->Type;
+ private set => _data->Type = (byte)value;
+ }
+
+ public ulong Value
+ {
+ get => _data->Value;
+ private set => _data->Value = value;
+ }
+
+ public Symbol Symbol
+ {
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
+
+ return new Symbol((SymbolType)_data->SymbolType, _data->SymbolValue);
+ }
+ private set
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
+
+ if (value.Type == SymbolType.None)
+ {
+ _data->SymbolType = (byte)SymbolType.None;
+ }
+ else
+ {
+ _data->SymbolType = (byte)value.Type;
+ _data->SymbolValue = value.Value;
+ }
+ }
+ }
+
+ public ReadOnlySpan<Operation> Assignments
+ {
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
+
+ return new ReadOnlySpan<Operation>(_data->Assignments, _data->AssignmentsCount);
+ }
+ }
+
+ public ReadOnlySpan<Operation> Uses
+ {
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
+
+ return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
+ }
+ }
+
+ public int UsesCount => (int)_data->UsesCount;
+ public int AssignmentsCount => _data->AssignmentsCount;
+
+ public bool Relocatable => Symbol.Type != SymbolType.None;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Register GetRegister()
+ {
+ Debug.Assert(Kind == OperandKind.Register);
+
+ return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public MemoryOperand GetMemory()
+ {
+ Debug.Assert(Kind == OperandKind.Memory);
+
+ return new MemoryOperand(this);
+ }
+
+ public int GetLocalNumber()
+ {
+ Debug.Assert(Kind == OperandKind.LocalVariable);
+
+ return (int)Value;
+ }
+
+ public byte AsByte()
+ {
+ return (byte)Value;
+ }
+
+ public short AsInt16()
+ {
+ return (short)Value;
+ }
+
+ public int AsInt32()
+ {
+ return (int)Value;
+ }
+
+ public long AsInt64()
+ {
+ return (long)Value;
+ }
+
+ public float AsFloat()
+ {
+ return BitConverter.Int32BitsToSingle((int)Value);
+ }
+
+ public double AsDouble()
+ {
+ return BitConverter.Int64BitsToDouble((long)Value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ref ulong GetValueUnsafe()
+ {
+ return ref _data->Value;
+ }
+
+ internal void NumberLocal(int number)
+ {
+ if (Kind != OperandKind.LocalVariable)
+ {
+ throw new InvalidOperationException("The operand is not a local variable.");
+ }
+
+ Value = (ulong)number;
+ }
+
+ public void AddAssignment(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Add(operation, ref _data->Assignments, ref _data->AssignmentsCount, ref _data->AssignmentsCapacity);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
+ }
+
+ if (index != default)
+ {
+ Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
+ }
+ }
+ }
+
+ public void RemoveAssignment(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Remove(operation, ref _data->Assignments, ref _data->AssignmentsCount);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Remove(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount);
+ }
+
+ if (index != default)
+ {
+ Remove(operation, ref index._data->Assignments, ref index._data->AssignmentsCount);
+ }
+ }
+ }
+
+ public void AddUse(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Add(operation, ref _data->Uses, ref _data->UsesCount, ref _data->UsesCapacity);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Add(operation, ref addr._data->Uses, ref addr._data->UsesCount, ref addr._data->UsesCapacity);
+ }
+
+ if (index != default)
+ {
+ Add(operation, ref index._data->Uses, ref index._data->UsesCount, ref index._data->UsesCapacity);
+ }
+ }
+ }
+
+ public void RemoveUse(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Remove(operation, ref _data->Uses, ref _data->UsesCount);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Remove(operation, ref addr._data->Uses, ref addr._data->UsesCount);
+ }
+
+ if (index != default)
+ {
+ Remove(operation, ref index._data->Uses, ref index._data->UsesCount);
+ }
+ }
+ }
+
+ public Span<Operation> GetUses(ref Span<Operation> buffer)
+ {
+ ReadOnlySpan<Operation> uses = Uses;
+
+ if (buffer.Length < uses.Length)
+ {
+ buffer = Allocators.Default.AllocateSpan<Operation>((uint)uses.Length);
+ }
+
+ uses.CopyTo(buffer);
+
+ return buffer.Slice(0, uses.Length);
+ }
+
+ private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged
+ {
+ count = 0;
+ capacity = initialCapacity;
+ data = Allocators.References.Allocate<T>(initialCapacity);
+ }
+
+ private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
+ {
+ count = 0;
+ capacity = initialCapacity;
+ data = Allocators.References.Allocate<T>(initialCapacity);
+ }
+
+ private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
+ {
+ if (count < capacity)
+ {
+ data[(uint)count++] = item;
+
+ return;
+ }
+
+ // Could not add item in the fast path, fallback onto the slow path.
+ ExpandAdd(item, ref data, ref count, ref capacity);
+
+ static void ExpandAdd(T item, ref T* data, ref ushort count, ref ushort capacity)
+ {
+ ushort newCount = checked((ushort)(count + 1));
+ ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
+
+ var oldSpan = new Span<T>(data, count);
+
+ capacity = newCapacity;
+ data = Allocators.References.Allocate<T>(capacity);
+
+ oldSpan.CopyTo(new Span<T>(data, count));
+
+ data[count] = item;
+ count = newCount;
+ }
+ }
+
+ private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
+ {
+ if (count < capacity)
+ {
+ data[count++] = item;
+
+ return;
+ }
+
+ // Could not add item in the fast path, fallback onto the slow path.
+ ExpandAdd(item, ref data, ref count, ref capacity);
+
+ static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
+ {
+ uint newCount = checked(count + 1);
+ uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
+
+ if (newCapacity <= capacity)
+ {
+ throw new OverflowException();
+ }
+
+ var oldSpan = new Span<T>(data, (int)count);
+
+ capacity = newCapacity;
+ data = Allocators.References.Allocate<T>(capacity);
+
+ oldSpan.CopyTo(new Span<T>(data, (int)count));
+
+ data[count] = item;
+ count = newCount;
+ }
+ }
+
+ private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
+ {
+ var span = new Span<T>(data, count);
+
+ for (int i = 0; i < span.Length; i++)
+ {
+ if (EqualityComparer<T>.Default.Equals(span[i], item))
+ {
+ if (i + 1 < count)
+ {
+ span.Slice(i + 1).CopyTo(span.Slice(i));
+ }
+
+ count--;
+
+ return;
+ }
+ }
+ }
+
+ private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
+ {
+ var span = new Span<T>(data, (int)count);
+
+ for (int i = 0; i < span.Length; i++)
+ {
+ if (EqualityComparer<T>.Default.Equals(span[i], item))
+ {
+ if (i + 1 < count)
+ {
+ span.Slice(i + 1).CopyTo(span.Slice(i));
+ }
+
+ count--;
+
+ return;
+ }
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return ((ulong)_data).GetHashCode();
+ }
+
+ public bool Equals(Operand operand)
+ {
+ return operand._data == _data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Operand operand && Equals(operand);
+ }
+
+ public static bool operator ==(Operand a, Operand b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(Operand a, Operand b)
+ {
+ return !a.Equals(b);
+ }
+
+ public static class Factory
+ {
+ private const int InternTableSize = 256;
+ private const int InternTableProbeLength = 8;
+
+ [ThreadStatic]
+ private static Data* _internTable;
+
+ private static Data* InternTable
+ {
+ get
+ {
+ if (_internTable == null)
+ {
+ _internTable = (Data*)NativeAllocator.Instance.Allocate((uint)sizeof(Data) * InternTableSize);
+
+ // Make sure the table is zeroed.
+ new Span<Data>(_internTable, InternTableSize).Clear();
+ }
+
+ return _internTable;
+ }
+ }
+
+ private static Operand Make(OperandKind kind, OperandType type, ulong value, Symbol symbol = default)
+ {
+ Debug.Assert(kind != OperandKind.None);
+
+ Data* data = null;
+
+ // If constant or register, then try to look up in the intern table before allocating.
+ if (kind == OperandKind.Constant || kind == OperandKind.Register)
+ {
+ uint hash = (uint)HashCode.Combine(kind, type, value);
+
+ // Look in the next InternTableProbeLength slots for a match.
+ for (uint i = 0; i < InternTableProbeLength; i++)
+ {
+ Operand interned = new();
+ interned._data = &InternTable[(hash + i) % InternTableSize];
+
+ // If slot matches the allocation request then return that slot.
+ if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol)
+ {
+ return interned;
+ }
+ // Otherwise if the slot is not occupied, we store in that slot.
+ else if (interned.Kind == OperandKind.None)
+ {
+ data = interned._data;
+
+ break;
+ }
+ }
+ }
+
+ // If we could not get a slot from the intern table, we allocate somewhere else and store there.
+ if (data == null)
+ {
+ data = Allocators.Operands.Allocate<Data>();
+ }
+
+ *data = default;
+
+ Operand result = new();
+ result._data = data;
+ result.Value = value;
+ result.Kind = kind;
+ result.Type = type;
+
+ if (kind != OperandKind.Memory)
+ {
+ result.Symbol = symbol;
+ }
+
+ // If local variable, then the use and def list is initialized with default sizes.
+ if (kind == OperandKind.LocalVariable)
+ {
+ New(ref result._data->Assignments, ref result._data->AssignmentsCount, ref result._data->AssignmentsCapacity, 1);
+ New(ref result._data->Uses, ref result._data->UsesCount, ref result._data->UsesCapacity, 4);
+ }
+
+ return result;
+ }
+
+ public static Operand Const(OperandType type, long value)
+ {
+ Debug.Assert(type is OperandType.I32 or OperandType.I64);
+
+ return type == OperandType.I32 ? Const((int)value) : Const(value);
+ }
+
+ public static Operand Const(bool value)
+ {
+ return Const(value ? 1 : 0);
+ }
+
+ public static Operand Const(int value)
+ {
+ return Const((uint)value);
+ }
+
+ public static Operand Const(uint value)
+ {
+ return Make(OperandKind.Constant, OperandType.I32, value);
+ }
+
+ public static Operand Const(long value)
+ {
+ return Const(value, symbol: default);
+ }
+
+ public static Operand Const<T>(ref T reference, Symbol symbol = default)
+ {
+ return Const((long)Unsafe.AsPointer(ref reference), symbol);
+ }
+
+ public static Operand Const(long value, Symbol symbol)
+ {
+ return Make(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
+ }
+
+ public static Operand Const(ulong value)
+ {
+ return Make(OperandKind.Constant, OperandType.I64, value);
+ }
+
+ public static Operand ConstF(float value)
+ {
+ return Make(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
+ }
+
+ public static Operand ConstF(double value)
+ {
+ return Make(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
+ }
+
+ public static Operand Label()
+ {
+ return Make(OperandKind.Label, OperandType.None, 0);
+ }
+
+ public static Operand Local(OperandType type)
+ {
+ return Make(OperandKind.LocalVariable, type, 0);
+ }
+
+ public static Operand Register(int index, RegisterType regType, OperandType type)
+ {
+ return Make(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
+ }
+
+ public static Operand Undef()
+ {
+ return Make(OperandKind.Undefined, OperandType.None, 0);
+ }
+
+ public static Operand MemoryOp(
+ OperandType type,
+ Operand baseAddress,
+ Operand index = default,
+ Multiplier scale = Multiplier.x1,
+ int displacement = 0)
+ {
+ Operand result = Make(OperandKind.Memory, type, 0);
+
+ MemoryOperand memory = result.GetMemory();
+ memory.BaseAddress = baseAddress;
+ memory.Index = index;
+ memory.Scale = scale;
+ memory.Displacement = displacement;
+
+ return result;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs b/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs
new file mode 100644
index 00000000..adb83561
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/OperandKind.cs
@@ -0,0 +1,13 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum OperandKind
+ {
+ None,
+ Constant,
+ Label,
+ LocalVariable,
+ Memory,
+ Register,
+ Undefined
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/OperandType.cs b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs
new file mode 100644
index 00000000..81b22cf5
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/OperandType.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum OperandType
+ {
+ None,
+ I32,
+ I64,
+ FP32,
+ FP64,
+ V128
+ }
+
+ static class OperandTypeExtensions
+ {
+ public static bool IsInteger(this OperandType type)
+ {
+ return type == OperandType.I32 ||
+ type == OperandType.I64;
+ }
+
+ public static RegisterType ToRegisterType(this OperandType type)
+ {
+ switch (type)
+ {
+ case OperandType.FP32: return RegisterType.Vector;
+ case OperandType.FP64: return RegisterType.Vector;
+ case OperandType.I32: return RegisterType.Integer;
+ case OperandType.I64: return RegisterType.Integer;
+ case OperandType.V128: return RegisterType.Vector;
+ }
+
+ throw new InvalidOperationException($"Invalid operand type \"{type}\".");
+ }
+
+ public static int GetSizeInBytes(this OperandType type)
+ {
+ switch (type)
+ {
+ case OperandType.FP32: return 4;
+ case OperandType.FP64: return 8;
+ case OperandType.I32: return 4;
+ case OperandType.I64: return 8;
+ case OperandType.V128: return 16;
+ }
+
+ throw new InvalidOperationException($"Invalid operand type \"{type}\".");
+ }
+
+ public static int GetSizeInBytesLog2(this OperandType type)
+ {
+ switch (type)
+ {
+ case OperandType.FP32: return 2;
+ case OperandType.FP64: return 3;
+ case OperandType.I32: return 2;
+ case OperandType.I64: return 3;
+ case OperandType.V128: return 4;
+ }
+
+ throw new InvalidOperationException($"Invalid operand type \"{type}\".");
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/Operation.cs b/src/ARMeilleure/IntermediateRepresentation/Operation.cs
new file mode 100644
index 00000000..c71e143c
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Operation.cs
@@ -0,0 +1,376 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ unsafe struct Operation : IEquatable<Operation>, IIntrusiveListNode<Operation>
+ {
+ internal struct Data
+ {
+ public ushort Instruction;
+ public ushort Intrinsic;
+ public ushort SourcesCount;
+ public ushort DestinationsCount;
+ public Operation ListPrevious;
+ public Operation ListNext;
+ public Operand* Destinations;
+ public Operand* Sources;
+ }
+
+ private Data* _data;
+
+ public Instruction Instruction
+ {
+ get => (Instruction)_data->Instruction;
+ private set => _data->Instruction = (ushort)value;
+ }
+
+ public Intrinsic Intrinsic
+ {
+ get => (Intrinsic)_data->Intrinsic;
+ private set => _data->Intrinsic = (ushort)value;
+ }
+
+ public Operation ListPrevious
+ {
+ get => _data->ListPrevious;
+ set => _data->ListPrevious = value;
+ }
+
+ public Operation ListNext
+ {
+ get => _data->ListNext;
+ set => _data->ListNext = value;
+ }
+
+ public Operand Destination
+ {
+ get => _data->DestinationsCount != 0 ? GetDestination(0) : default;
+ set => SetDestination(value);
+ }
+
+ public int DestinationsCount => _data->DestinationsCount;
+ public int SourcesCount => _data->SourcesCount;
+
+ internal Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount);
+ internal Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount);
+
+ public PhiOperation AsPhi()
+ {
+ Debug.Assert(Instruction == Instruction.Phi);
+
+ return new PhiOperation(this);
+ }
+
+ public Operand GetDestination(int index)
+ {
+ return DestinationsUnsafe[index];
+ }
+
+ public Operand GetSource(int index)
+ {
+ return SourcesUnsafe[index];
+ }
+
+ public void SetDestination(int index, Operand dest)
+ {
+ ref Operand curDest = ref DestinationsUnsafe[index];
+
+ RemoveAssignment(curDest);
+ AddAssignment(dest);
+
+ curDest = dest;
+ }
+
+ public void SetSource(int index, Operand src)
+ {
+ ref Operand curSrc = ref SourcesUnsafe[index];
+
+ RemoveUse(curSrc);
+ AddUse(src);
+
+ curSrc = src;
+ }
+
+ private void RemoveOldDestinations()
+ {
+ for (int i = 0; i < _data->DestinationsCount; i++)
+ {
+ RemoveAssignment(_data->Destinations[i]);
+ }
+ }
+
+ public void SetDestination(Operand dest)
+ {
+ RemoveOldDestinations();
+
+ if (dest == default)
+ {
+ _data->DestinationsCount = 0;
+ }
+ else
+ {
+ EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, 1);
+
+ _data->Destinations[0] = dest;
+
+ AddAssignment(dest);
+ }
+ }
+
+ public void SetDestinations(Operand[] dests)
+ {
+ RemoveOldDestinations();
+
+ EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, dests.Length);
+
+ for (int index = 0; index < dests.Length; index++)
+ {
+ Operand newOp = dests[index];
+
+ _data->Destinations[index] = newOp;
+
+ AddAssignment(newOp);
+ }
+ }
+
+ private void RemoveOldSources()
+ {
+ for (int index = 0; index < _data->SourcesCount; index++)
+ {
+ RemoveUse(_data->Sources[index]);
+ }
+ }
+
+ public void SetSource(Operand src)
+ {
+ RemoveOldSources();
+
+ if (src == default)
+ {
+ _data->SourcesCount = 0;
+ }
+ else
+ {
+ EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, 1);
+
+ _data->Sources[0] = src;
+
+ AddUse(src);
+ }
+ }
+
+ public void SetSources(Operand[] srcs)
+ {
+ RemoveOldSources();
+
+ EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, srcs.Length);
+
+ for (int index = 0; index < srcs.Length; index++)
+ {
+ Operand newOp = srcs[index];
+
+ _data->Sources[index] = newOp;
+
+ AddUse(newOp);
+ }
+ }
+
+ public void TurnIntoCopy(Operand source)
+ {
+ Instruction = Instruction.Copy;
+
+ SetSource(source);
+ }
+
+ private void AddAssignment(Operand op)
+ {
+ if (op != default)
+ {
+ op.AddAssignment(this);
+ }
+ }
+
+ private void RemoveAssignment(Operand op)
+ {
+ if (op != default)
+ {
+ op.RemoveAssignment(this);
+ }
+ }
+
+ private void AddUse(Operand op)
+ {
+ if (op != default)
+ {
+ op.AddUse(this);
+ }
+ }
+
+ private void RemoveUse(Operand op)
+ {
+ if (op != default)
+ {
+ op.RemoveUse(this);
+ }
+ }
+
+ public bool Equals(Operation operation)
+ {
+ return operation._data == _data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Operation operation && Equals(operation);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((IntPtr)_data);
+ }
+
+ public static bool operator ==(Operation a, Operation b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(Operation a, Operation b)
+ {
+ return !a.Equals(b);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void EnsureCapacity(ref Operand* list, ref ushort capacity, int newCapacity)
+ {
+ if (newCapacity > ushort.MaxValue)
+ {
+ ThrowOverflow(newCapacity);
+ }
+ // We only need to allocate a new buffer if we're increasing the size.
+ else if (newCapacity > capacity)
+ {
+ list = Allocators.References.Allocate<Operand>((uint)newCapacity);
+ }
+
+ capacity = (ushort)newCapacity;
+ }
+
+ private static void ThrowOverflow(int count) =>
+ throw new OverflowException($"Exceeded maximum size for Source or Destinations. Required {count}.");
+
+ public static class Factory
+ {
+ private static Operation Make(Instruction inst, int destCount, int srcCount)
+ {
+ Data* data = Allocators.Operations.Allocate<Data>();
+ *data = default;
+
+ Operation result = new();
+ result._data = data;
+ result.Instruction = inst;
+
+ EnsureCapacity(ref result._data->Destinations, ref result._data->DestinationsCount, destCount);
+ EnsureCapacity(ref result._data->Sources, ref result._data->SourcesCount, srcCount);
+
+ result.DestinationsUnsafe.Clear();
+ result.SourcesUnsafe.Clear();
+
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest)
+ {
+ Operation result = Make(inst, 0, 0);
+ result.SetDestination(dest);
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest, Operand src0)
+ {
+ Operation result = Make(inst, 0, 1);
+ result.SetDestination(dest);
+ result.SetSource(0, src0);
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1)
+ {
+ Operation result = Make(inst, 0, 2);
+ result.SetDestination(dest);
+ result.SetSource(0, src0);
+ result.SetSource(1, src1);
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1, Operand src2)
+ {
+ Operation result = Make(inst, 0, 3);
+ result.SetDestination(dest);
+ result.SetSource(0, src0);
+ result.SetSource(1, src1);
+ result.SetSource(2, src2);
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest, int srcCount)
+ {
+ Operation result = Make(inst, 0, srcCount);
+ result.SetDestination(dest);
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand dest, Operand[] srcs)
+ {
+ Operation result = Make(inst, 0, srcs.Length);
+
+ result.SetDestination(dest);
+
+ for (int index = 0; index < srcs.Length; index++)
+ {
+ result.SetSource(index, srcs[index]);
+ }
+
+ return result;
+ }
+
+ public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs)
+ {
+ Operation result = Make(Instruction.Extended, 0, srcs.Length);
+
+ result.Intrinsic = intrin;
+ result.SetDestination(dest);
+
+ for (int index = 0; index < srcs.Length; index++)
+ {
+ result.SetSource(index, srcs[index]);
+ }
+
+ return result;
+ }
+
+ public static Operation Operation(Instruction inst, Operand[] dests, Operand[] srcs)
+ {
+ Operation result = Make(inst, dests.Length, srcs.Length);
+
+ for (int index = 0; index < dests.Length; index++)
+ {
+ result.SetDestination(index, dests[index]);
+ }
+
+ for (int index = 0; index < srcs.Length; index++)
+ {
+ result.SetSource(index, srcs[index]);
+ }
+
+ return result;
+ }
+
+ public static Operation PhiOperation(Operand dest, int srcCount)
+ {
+ return Operation(Instruction.Phi, dest, srcCount * 2);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs b/src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
new file mode 100644
index 00000000..d2a3cf21
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
@@ -0,0 +1,37 @@
+using ARMeilleure.Translation;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ readonly struct PhiOperation
+ {
+ private readonly Operation _operation;
+
+ public PhiOperation(Operation operation)
+ {
+ _operation = operation;
+ }
+
+ public int SourcesCount => _operation.SourcesCount / 2;
+
+ public BasicBlock GetBlock(ControlFlowGraph cfg, int index)
+ {
+ return cfg.PostOrderBlocks[cfg.PostOrderMap[_operation.GetSource(index * 2).AsInt32()]];
+ }
+
+ public void SetBlock(int index, BasicBlock block)
+ {
+ _operation.SetSource(index * 2, Const(block.Index));
+ }
+
+ public Operand GetSource(int index)
+ {
+ return _operation.GetSource(index * 2 + 1);
+ }
+
+ public void SetSource(int index, Operand operand)
+ {
+ _operation.SetSource(index * 2 + 1, operand);
+ }
+ }
+}
diff --git a/src/ARMeilleure/IntermediateRepresentation/Register.cs b/src/ARMeilleure/IntermediateRepresentation/Register.cs
new file mode 100644
index 00000000..241e4d13
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/Register.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ readonly struct Register : IEquatable<Register>
+ {
+ public int Index { get; }
+
+ public RegisterType Type { get; }
+
+ public Register(int index, RegisterType type)
+ {
+ Index = index;
+ Type = type;
+ }
+
+ public override int GetHashCode()
+ {
+ return (ushort)Index | ((int)Type << 16);
+ }
+
+ public static bool operator ==(Register x, Register y)
+ {
+ return x.Equals(y);
+ }
+
+ public static bool operator !=(Register x, Register y)
+ {
+ return !x.Equals(y);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Register reg && Equals(reg);
+ }
+
+ public bool Equals(Register other)
+ {
+ return other.Index == Index &&
+ other.Type == Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs b/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs
new file mode 100644
index 00000000..88ac6c12
--- /dev/null
+++ b/src/ARMeilleure/IntermediateRepresentation/RegisterType.cs
@@ -0,0 +1,10 @@
+namespace ARMeilleure.IntermediateRepresentation
+{
+ enum RegisterType
+ {
+ Integer,
+ Vector,
+ Flag,
+ FpFlag
+ }
+} \ No newline at end of file