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 }