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