/ src / ARMeilleure / State / NativeContext.cs
NativeContext.cs
  1  using ARMeilleure.IntermediateRepresentation;
  2  using ARMeilleure.Memory;
  3  using System;
  4  using System.Runtime.CompilerServices;
  5  
  6  namespace ARMeilleure.State
  7  {
  8      class NativeContext : IDisposable
  9      {
 10          private unsafe struct NativeCtxStorage
 11          {
 12              public fixed ulong X[RegisterConsts.IntRegsCount];
 13              public fixed ulong V[RegisterConsts.VecRegsCount * 2];
 14              public fixed uint Flags[RegisterConsts.FlagsCount];
 15              public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
 16              public long TpidrEl0;
 17              public long TpidrroEl0;
 18              public int Counter;
 19              public ulong DispatchAddress;
 20              public ulong ExclusiveAddress;
 21              public ulong ExclusiveValueLow;
 22              public ulong ExclusiveValueHigh;
 23              public int Running;
 24          }
 25  
 26          private static NativeCtxStorage _dummyStorage = new();
 27  
 28          private readonly IJitMemoryBlock _block;
 29  
 30          public IntPtr BasePtr => _block.Pointer;
 31  
 32          public NativeContext(IJitMemoryAllocator allocator)
 33          {
 34              _block = allocator.Allocate((ulong)Unsafe.SizeOf<NativeCtxStorage>());
 35  
 36              GetStorage().ExclusiveAddress = ulong.MaxValue;
 37          }
 38  
 39          public ulong GetPc()
 40          {
 41              // TODO: More precise tracking of PC value.
 42              return GetStorage().DispatchAddress;
 43          }
 44  
 45          public unsafe ulong GetX(int index)
 46          {
 47              if ((uint)index >= RegisterConsts.IntRegsCount)
 48              {
 49                  throw new ArgumentOutOfRangeException(nameof(index));
 50              }
 51  
 52              return GetStorage().X[index];
 53          }
 54  
 55          public unsafe void SetX(int index, ulong value)
 56          {
 57              if ((uint)index >= RegisterConsts.IntRegsCount)
 58              {
 59                  throw new ArgumentOutOfRangeException(nameof(index));
 60              }
 61  
 62              GetStorage().X[index] = value;
 63          }
 64  
 65          public unsafe V128 GetV(int index)
 66          {
 67              if ((uint)index >= RegisterConsts.VecRegsCount)
 68              {
 69                  throw new ArgumentOutOfRangeException(nameof(index));
 70              }
 71  
 72              return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]);
 73          }
 74  
 75          public unsafe void SetV(int index, V128 value)
 76          {
 77              if ((uint)index >= RegisterConsts.VecRegsCount)
 78              {
 79                  throw new ArgumentOutOfRangeException(nameof(index));
 80              }
 81  
 82              GetStorage().V[index * 2 + 0] = value.Extract<ulong>(0);
 83              GetStorage().V[index * 2 + 1] = value.Extract<ulong>(1);
 84          }
 85  
 86          public unsafe bool GetPstateFlag(PState flag)
 87          {
 88              if ((uint)flag >= RegisterConsts.FlagsCount)
 89              {
 90                  throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
 91              }
 92  
 93              return GetStorage().Flags[(int)flag] != 0;
 94          }
 95  
 96          public unsafe void SetPstateFlag(PState flag, bool value)
 97          {
 98              if ((uint)flag >= RegisterConsts.FlagsCount)
 99              {
100                  throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
101              }
102  
103              GetStorage().Flags[(int)flag] = value ? 1u : 0u;
104          }
105  
106          public unsafe uint GetPstate()
107          {
108              uint value = 0;
109              for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
110              {
111                  value |= GetStorage().Flags[flag] != 0 ? 1u << flag : 0u;
112              }
113              return value;
114          }
115  
116          public unsafe void SetPstate(uint value)
117          {
118              for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
119              {
120                  uint bit = 1u << flag;
121                  GetStorage().Flags[flag] = (value & bit) == bit ? 1u : 0u;
122              }
123          }
124  
125          public unsafe bool GetFPStateFlag(FPState flag)
126          {
127              if ((uint)flag >= RegisterConsts.FpFlagsCount)
128              {
129                  throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
130              }
131  
132              return GetStorage().FpFlags[(int)flag] != 0;
133          }
134  
135          public unsafe void SetFPStateFlag(FPState flag, bool value)
136          {
137              if ((uint)flag >= RegisterConsts.FpFlagsCount)
138              {
139                  throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
140              }
141  
142              GetStorage().FpFlags[(int)flag] = value ? 1u : 0u;
143          }
144  
145          public unsafe uint GetFPState(uint mask = uint.MaxValue)
146          {
147              uint value = 0;
148              for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
149              {
150                  uint bit = 1u << flag;
151  
152                  if ((mask & bit) == bit)
153                  {
154                      value |= GetStorage().FpFlags[flag] != 0 ? bit : 0u;
155                  }
156              }
157              return value;
158          }
159  
160          public unsafe void SetFPState(uint value, uint mask = uint.MaxValue)
161          {
162              for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
163              {
164                  uint bit = 1u << flag;
165  
166                  if ((mask & bit) == bit)
167                  {
168                      GetStorage().FpFlags[flag] = (value & bit) == bit ? 1u : 0u;
169                  }
170              }
171          }
172  
173          public long GetTpidrEl0() => GetStorage().TpidrEl0;
174          public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
175  
176          public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
177          public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
178  
179          public int GetCounter() => GetStorage().Counter;
180          public void SetCounter(int value) => GetStorage().Counter = value;
181  
182          public bool GetRunning() => GetStorage().Running != 0;
183          public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
184  
185          public unsafe static int GetRegisterOffset(Register reg)
186          {
187              if (reg.Type == RegisterType.Integer)
188              {
189                  if ((uint)reg.Index >= RegisterConsts.IntRegsCount)
190                  {
191                      throw new ArgumentException("Invalid register.");
192                  }
193  
194                  return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[reg.Index]);
195              }
196              else if (reg.Type == RegisterType.Vector)
197              {
198                  if ((uint)reg.Index >= RegisterConsts.VecRegsCount)
199                  {
200                      throw new ArgumentException("Invalid register.");
201                  }
202  
203                  return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[reg.Index * 2]);
204              }
205              else if (reg.Type == RegisterType.Flag)
206              {
207                  if ((uint)reg.Index >= RegisterConsts.FlagsCount)
208                  {
209                      throw new ArgumentException("Invalid register.");
210                  }
211  
212                  return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags[reg.Index]);
213              }
214              else /* if (reg.Type == RegisterType.FpFlag) */
215              {
216                  if ((uint)reg.Index >= RegisterConsts.FpFlagsCount)
217                  {
218                      throw new ArgumentException("Invalid register.");
219                  }
220  
221                  return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags[reg.Index]);
222              }
223          }
224  
225          public static int GetTpidrEl0Offset()
226          {
227              return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
228          }
229  
230          public static int GetTpidrroEl0Offset()
231          {
232              return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
233          }
234  
235          public static int GetCounterOffset()
236          {
237              return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
238          }
239  
240          public static int GetDispatchAddressOffset()
241          {
242              return StorageOffset(ref _dummyStorage, ref _dummyStorage.DispatchAddress);
243          }
244  
245          public static int GetExclusiveAddressOffset()
246          {
247              return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveAddress);
248          }
249  
250          public static int GetExclusiveValueOffset()
251          {
252              return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveValueLow);
253          }
254  
255          public static int GetRunningOffset()
256          {
257              return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
258          }
259  
260          private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
261          {
262              return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
263          }
264  
265          private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
266  
267          public void Dispose() => _block.Dispose();
268      }
269  }