ArmEmitterContext.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using ARMeilleure.CodeGen.Linking;
  2. using ARMeilleure.Common;
  3. using ARMeilleure.Decoders;
  4. using ARMeilleure.Diagnostics;
  5. using ARMeilleure.Instructions;
  6. using ARMeilleure.IntermediateRepresentation;
  7. using ARMeilleure.Memory;
  8. using ARMeilleure.State;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Reflection;
  12. using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  13. namespace ARMeilleure.Translation
  14. {
  15. class ArmEmitterContext : EmitterContext
  16. {
  17. private readonly Dictionary<ulong, Operand> _labels;
  18. private OpCode _optOpLastCompare;
  19. private OpCode _optOpLastFlagSet;
  20. private Operand _optCmpTempN;
  21. private Operand _optCmpTempM;
  22. private Block _currBlock;
  23. public Block CurrBlock
  24. {
  25. get
  26. {
  27. return _currBlock;
  28. }
  29. set
  30. {
  31. _currBlock = value;
  32. ResetBlockState();
  33. }
  34. }
  35. private bool _pendingQcFlagSync;
  36. public OpCode CurrOp { get; set; }
  37. public IMemoryManager Memory { get; }
  38. public EntryTable<uint> CountTable { get; }
  39. public AddressTable<ulong> FunctionTable { get; }
  40. public TranslatorStubs Stubs { get; }
  41. public ulong EntryAddress { get; }
  42. public bool HighCq { get; }
  43. public bool HasPtc { get; }
  44. public Aarch32Mode Mode { get; }
  45. private int _ifThenBlockStateIndex = 0;
  46. private Condition[] _ifThenBlockState = { };
  47. public bool IsInIfThenBlock => _ifThenBlockStateIndex < _ifThenBlockState.Length;
  48. public Condition CurrentIfThenBlockCond => _ifThenBlockState[_ifThenBlockStateIndex];
  49. public ArmEmitterContext(
  50. IMemoryManager memory,
  51. EntryTable<uint> countTable,
  52. AddressTable<ulong> funcTable,
  53. TranslatorStubs stubs,
  54. ulong entryAddress,
  55. bool highCq,
  56. bool hasPtc,
  57. Aarch32Mode mode)
  58. {
  59. Memory = memory;
  60. CountTable = countTable;
  61. FunctionTable = funcTable;
  62. Stubs = stubs;
  63. EntryAddress = entryAddress;
  64. HighCq = highCq;
  65. HasPtc = hasPtc;
  66. Mode = mode;
  67. _labels = new Dictionary<ulong, Operand>();
  68. }
  69. public override Operand Call(MethodInfo info, params Operand[] callArgs)
  70. {
  71. SyncQcFlag();
  72. if (!HasPtc)
  73. {
  74. return base.Call(info, callArgs);
  75. }
  76. else
  77. {
  78. int index = Delegates.GetDelegateIndex(info);
  79. IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
  80. OperandType returnType = GetOperandType(info.ReturnType);
  81. Symbol symbol = new Symbol(SymbolType.DelegateTable, (ulong)index);
  82. Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
  83. return Call(Const(funcPtr.ToInt64(), symbol), returnType, callArgs);
  84. }
  85. }
  86. public Operand GetLabel(ulong address)
  87. {
  88. if (!_labels.TryGetValue(address, out Operand label))
  89. {
  90. label = Label();
  91. _labels.Add(address, label);
  92. }
  93. return label;
  94. }
  95. public void MarkComparison(Operand n, Operand m)
  96. {
  97. _optOpLastCompare = CurrOp;
  98. _optCmpTempN = Copy(n);
  99. _optCmpTempM = Copy(m);
  100. }
  101. public void MarkFlagSet(PState stateFlag)
  102. {
  103. // Set this only if any of the NZCV flag bits were modified.
  104. // This is used to ensure that when emiting a direct IL branch
  105. // instruction for compare + branch sequences, we're not expecting
  106. // to use comparison values from an old instruction, when in fact
  107. // the flags were already overwritten by another instruction further along.
  108. if (stateFlag >= PState.VFlag)
  109. {
  110. _optOpLastFlagSet = CurrOp;
  111. }
  112. }
  113. private void ResetBlockState()
  114. {
  115. _optOpLastCompare = null;
  116. _optOpLastFlagSet = null;
  117. }
  118. public void SetPendingQcFlagSync()
  119. {
  120. _pendingQcFlagSync = true;
  121. }
  122. public void SyncQcFlag()
  123. {
  124. if (_pendingQcFlagSync)
  125. {
  126. if (Optimizations.UseAdvSimd)
  127. {
  128. Operand fpsr = AddIntrinsicInt(Intrinsic.Arm64MrsFpsr);
  129. uint qcFlagMask = (uint)FPSR.Qc;
  130. Operand qcClearLabel = Label();
  131. BranchIfFalse(qcClearLabel, BitwiseAnd(fpsr, Const(qcFlagMask)));
  132. AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0));
  133. InstEmitHelper.SetFpFlag(this, FPState.QcFlag, Const(1));
  134. MarkLabel(qcClearLabel);
  135. }
  136. _pendingQcFlagSync = false;
  137. }
  138. }
  139. public void ClearQcFlag()
  140. {
  141. if (Optimizations.UseAdvSimd)
  142. {
  143. AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0));
  144. }
  145. }
  146. public void ClearQcFlagIfModified()
  147. {
  148. if (_pendingQcFlagSync && Optimizations.UseAdvSimd)
  149. {
  150. AddIntrinsicNoRet(Intrinsic.Arm64MsrFpsr, Const(0));
  151. }
  152. }
  153. public void EnterArmFpMode()
  154. {
  155. InstEmitSimdHelper.EnterArmFpMode(this, InstEmitHelper.GetFpFlag);
  156. }
  157. public void UpdateArmFpMode()
  158. {
  159. EnterArmFpMode();
  160. }
  161. public void ExitArmFpMode()
  162. {
  163. InstEmitSimdHelper.ExitArmFpMode(this, (flag, value) => InstEmitHelper.SetFpFlag(this, flag, value));
  164. }
  165. public Operand TryGetComparisonResult(Condition condition)
  166. {
  167. if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
  168. {
  169. return default;
  170. }
  171. Operand n = _optCmpTempN;
  172. Operand m = _optCmpTempM;
  173. InstName cmpName = _optOpLastCompare.Instruction.Name;
  174. if (cmpName == InstName.Subs)
  175. {
  176. switch (condition)
  177. {
  178. case Condition.Eq: return ICompareEqual (n, m);
  179. case Condition.Ne: return ICompareNotEqual (n, m);
  180. case Condition.GeUn: return ICompareGreaterOrEqualUI(n, m);
  181. case Condition.LtUn: return ICompareLessUI (n, m);
  182. case Condition.GtUn: return ICompareGreaterUI (n, m);
  183. case Condition.LeUn: return ICompareLessOrEqualUI (n, m);
  184. case Condition.Ge: return ICompareGreaterOrEqual (n, m);
  185. case Condition.Lt: return ICompareLess (n, m);
  186. case Condition.Gt: return ICompareGreater (n, m);
  187. case Condition.Le: return ICompareLessOrEqual (n, m);
  188. }
  189. }
  190. else if (cmpName == InstName.Adds && _optOpLastCompare is IOpCodeAluImm op)
  191. {
  192. // There are several limitations that needs to be taken into account for CMN comparisons:
  193. // - The unsigned comparisons are not valid, as they depend on the
  194. // carry flag value, and they will have different values for addition and
  195. // subtraction. For addition, it's carry, and for subtraction, it's borrow.
  196. // So, we need to make sure we're not doing a unsigned compare for the CMN case.
  197. // - We can only do the optimization for the immediate variants,
  198. // because when the second operand value is exactly INT_MIN, we can't
  199. // negate the value as theres no positive counterpart.
  200. // Such invalid values can't be encoded on the immediate encodings.
  201. if (op.RegisterSize == RegisterSize.Int32)
  202. {
  203. m = Const((int)-op.Immediate);
  204. }
  205. else
  206. {
  207. m = Const(-op.Immediate);
  208. }
  209. switch (condition)
  210. {
  211. case Condition.Eq: return ICompareEqual (n, m);
  212. case Condition.Ne: return ICompareNotEqual (n, m);
  213. case Condition.Ge: return ICompareGreaterOrEqual(n, m);
  214. case Condition.Lt: return ICompareLess (n, m);
  215. case Condition.Gt: return ICompareGreater (n, m);
  216. case Condition.Le: return ICompareLessOrEqual (n, m);
  217. }
  218. }
  219. return default;
  220. }
  221. public void SetIfThenBlockState(Condition[] state)
  222. {
  223. _ifThenBlockState = state;
  224. _ifThenBlockStateIndex = 0;
  225. }
  226. public void AdvanceIfThenBlockState()
  227. {
  228. if (IsInIfThenBlock)
  229. {
  230. _ifThenBlockStateIndex++;
  231. }
  232. }
  233. }
  234. }