InstEmitFlowHelper.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using ARMeilleure.CodeGen.Linking;
  2. using ARMeilleure.Common;
  3. using ARMeilleure.Decoders;
  4. using ARMeilleure.IntermediateRepresentation;
  5. using ARMeilleure.State;
  6. using ARMeilleure.Translation;
  7. using ARMeilleure.Translation.PTC;
  8. using static ARMeilleure.Instructions.InstEmitHelper;
  9. using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  10. namespace ARMeilleure.Instructions
  11. {
  12. static class InstEmitFlowHelper
  13. {
  14. public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
  15. {
  16. if (cond != Condition.Al)
  17. {
  18. context.BranchIfTrue(target, GetCondTrue(context, cond));
  19. }
  20. else
  21. {
  22. context.Branch(target);
  23. }
  24. }
  25. public static Operand GetCondTrue(ArmEmitterContext context, Condition condition)
  26. {
  27. Operand cmpResult = context.TryGetComparisonResult(condition);
  28. if (cmpResult != default)
  29. {
  30. return cmpResult;
  31. }
  32. Operand value = Const(1);
  33. Operand Inverse(Operand val)
  34. {
  35. return context.BitwiseExclusiveOr(val, Const(1));
  36. }
  37. switch (condition)
  38. {
  39. case Condition.Eq:
  40. value = GetFlag(PState.ZFlag);
  41. break;
  42. case Condition.Ne:
  43. value = Inverse(GetFlag(PState.ZFlag));
  44. break;
  45. case Condition.GeUn:
  46. value = GetFlag(PState.CFlag);
  47. break;
  48. case Condition.LtUn:
  49. value = Inverse(GetFlag(PState.CFlag));
  50. break;
  51. case Condition.Mi:
  52. value = GetFlag(PState.NFlag);
  53. break;
  54. case Condition.Pl:
  55. value = Inverse(GetFlag(PState.NFlag));
  56. break;
  57. case Condition.Vs:
  58. value = GetFlag(PState.VFlag);
  59. break;
  60. case Condition.Vc:
  61. value = Inverse(GetFlag(PState.VFlag));
  62. break;
  63. case Condition.GtUn:
  64. {
  65. Operand c = GetFlag(PState.CFlag);
  66. Operand z = GetFlag(PState.ZFlag);
  67. value = context.BitwiseAnd(c, Inverse(z));
  68. break;
  69. }
  70. case Condition.LeUn:
  71. {
  72. Operand c = GetFlag(PState.CFlag);
  73. Operand z = GetFlag(PState.ZFlag);
  74. value = context.BitwiseOr(Inverse(c), z);
  75. break;
  76. }
  77. case Condition.Ge:
  78. {
  79. Operand n = GetFlag(PState.NFlag);
  80. Operand v = GetFlag(PState.VFlag);
  81. value = context.ICompareEqual(n, v);
  82. break;
  83. }
  84. case Condition.Lt:
  85. {
  86. Operand n = GetFlag(PState.NFlag);
  87. Operand v = GetFlag(PState.VFlag);
  88. value = context.ICompareNotEqual(n, v);
  89. break;
  90. }
  91. case Condition.Gt:
  92. {
  93. Operand n = GetFlag(PState.NFlag);
  94. Operand z = GetFlag(PState.ZFlag);
  95. Operand v = GetFlag(PState.VFlag);
  96. value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v));
  97. break;
  98. }
  99. case Condition.Le:
  100. {
  101. Operand n = GetFlag(PState.NFlag);
  102. Operand z = GetFlag(PState.ZFlag);
  103. Operand v = GetFlag(PState.VFlag);
  104. value = context.BitwiseOr(z, context.ICompareNotEqual(n, v));
  105. break;
  106. }
  107. }
  108. return value;
  109. }
  110. public static void EmitCall(ArmEmitterContext context, ulong immediate)
  111. {
  112. bool isRecursive = immediate == context.EntryAddress;
  113. if (isRecursive)
  114. {
  115. context.Branch(context.GetLabel(immediate));
  116. }
  117. else
  118. {
  119. EmitTableBranch(context, Const(immediate), isJump: false);
  120. }
  121. }
  122. public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
  123. {
  124. EmitTableBranch(context, target, isJump: false);
  125. }
  126. public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
  127. {
  128. if (isReturn)
  129. {
  130. if (target.Type == OperandType.I32)
  131. {
  132. target = context.ZeroExtend32(OperandType.I64, target);
  133. }
  134. context.Return(target);
  135. }
  136. else
  137. {
  138. EmitTableBranch(context, target, isJump: true);
  139. }
  140. }
  141. private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
  142. {
  143. context.StoreToContext();
  144. if (guestAddress.Type == OperandType.I32)
  145. {
  146. guestAddress = context.ZeroExtend32(OperandType.I64, guestAddress);
  147. }
  148. // Store the target guest address into the native context. The stubs uses this address to dispatch into the
  149. // next translation.
  150. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  151. Operand dispAddressAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
  152. context.Store(dispAddressAddr, guestAddress);
  153. Operand hostAddress;
  154. IAddressTable<ulong> table = context.FunctionTable;
  155. // If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
  156. // onto the dispatch stub.
  157. if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
  158. {
  159. Operand hostAddressAddr = !context.HasPtc ?
  160. Const(ref context.FunctionTable.GetValue(guestAddress.Value)) :
  161. Const(ref context.FunctionTable.GetValue(guestAddress.Value), new Symbol(SymbolType.FunctionTable, guestAddress.Value));
  162. hostAddress = context.Load(OperandType.I64, hostAddressAddr);
  163. }
  164. else if (table.Sparse)
  165. {
  166. // Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
  167. // Deliberately attempts to avoid branches.
  168. Operand tableBase = !context.HasPtc ?
  169. Const(table.Base) :
  170. Const(table.Base, Ptc.FunctionTableSymbol);
  171. hostAddress = tableBase;
  172. for (int i = 0; i < table.Levels.Length; i++)
  173. {
  174. AddressTableLevel level = table.Levels[i];
  175. int clearBits = 64 - (level.Index + level.Length);
  176. Operand index = context.ShiftLeft(
  177. context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
  178. Const(3)
  179. );
  180. hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
  181. }
  182. }
  183. else
  184. {
  185. hostAddress = !context.HasPtc ?
  186. Const((long)context.Stubs.DispatchStub) :
  187. Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
  188. }
  189. if (isJump)
  190. {
  191. context.Tailcall(hostAddress, nativeContext);
  192. }
  193. else
  194. {
  195. OpCode op = context.CurrOp;
  196. Operand returnAddress = context.Call(hostAddress, OperandType.I64, nativeContext);
  197. context.LoadFromContext();
  198. // Note: The return value of a translated function is always an Int64 with the address execution has
  199. // returned to. We expect this address to be immediately after the current instruction, if it isn't we
  200. // keep returning until we reach the dispatcher.
  201. Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes);
  202. // Try to continue within this block.
  203. // If the return address isn't to our next instruction, we need to return so the JIT can figure out
  204. // what to do.
  205. Operand lblContinue = context.GetLabel(nextAddr.Value);
  206. context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
  207. context.Return(returnAddress);
  208. }
  209. }
  210. }
  211. }