InstEmitFlowHelper.cs 7.6 KB

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