public static void EmitDynamicTableCall(ArmEmitterContext context, Operand tableAddress, Operand address, bool isJump) { // Loop over elements of the dynamic table. Unrolled loop. Operand endLabel = Label(); Operand fallbackLabel = Label(); void EmitTableEntry(Operand entrySkipLabel) { // Try to take this entry in the table if its guest address equals 0. Operand gotResult = context.CompareAndSwap(tableAddress, Const(0L), address); // Is the address ours? (either taken via CompareAndSwap (0), or what was already here) context.BranchIfFalse(entrySkipLabel, context.BitwiseOr( context.ICompareEqual(gotResult, address), context.ICompareEqual(gotResult, Const(0L))) ); // It's ours, so what function is it pointing to? Operand targetFunctionPtr = context.Add(tableAddress, Const(8L)); Operand targetFunction = context.Load(OperandType.I64, targetFunctionPtr); // Call the function. // We pass in the entry address as the guest address, as the entry may need to be updated by the // indirect call stub. EmitNativeCallWithGuestAddress(context, targetFunction, tableAddress, isJump); context.Branch(endLabel); } // Currently this uses a size of 1, as higher values inflate code size for no real benefit. for (int i = 0; i < JumpTable.DynamicTableElems; i++) { if (i == JumpTable.DynamicTableElems - 1) { // If this is the last entry, avoid emitting the additional label and add. EmitTableEntry(fallbackLabel); } else { Operand nextLabel = Label(); EmitTableEntry(nextLabel); context.MarkLabel(nextLabel); // Move to the next table entry. tableAddress = context.Add(tableAddress, Const((long)JumpTable.JumpTableStride)); } } context.MarkLabel(fallbackLabel); EmitBranchFallback(context, address, isJump); context.MarkLabel(endLabel); }
private static void EmitDiv(ArmEmitterContext context, bool unsigned) { OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp; // If Rm == 0, Rd = 0 (division by zero). Operand n = GetIntOrZR(context, op.Rn); Operand m = GetIntOrZR(context, op.Rm); Operand divisorIsZero = context.ICompareEqual(m, Const(m.Type, 0)); Operand lblBadDiv = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBadDiv, divisorIsZero); if (!unsigned) { // If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). bool is32Bits = op.RegisterSize == RegisterSize.Int32; Operand intMin = is32Bits ? Const(int.MinValue) : Const(long.MinValue); Operand minus1 = is32Bits ? Const(-1) : Const(-1L); Operand nIsIntMin = context.ICompareEqual(n, intMin); Operand mIsMinus1 = context.ICompareEqual(m, minus1); Operand lblGoodDiv = Label(); context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1)); SetAluDOrZR(context, intMin); context.Branch(lblEnd); context.MarkLabel(lblGoodDiv); } Operand d = unsigned ? context.DivideUI(n, m) : context.Divide(n, m); SetAluDOrZR(context, d); context.Branch(lblEnd); context.MarkLabel(lblBadDiv); SetAluDOrZR(context, Const(op.GetOperandType(), 0)); context.MarkLabel(lblEnd); }
public static void EmitDiv(ArmEmitterContext context, bool unsigned) { Operand n = GetAluN(context); Operand m = GetAluM(context); Operand zero = Const(m.Type, 0); Operand divisorIsZero = context.ICompareEqual(m, zero); Operand lblBadDiv = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBadDiv, divisorIsZero); if (!unsigned) { // ARM64 behaviour: If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). // TODO: tests to ensure A32 works the same Operand intMin = Const(int.MinValue); Operand minus1 = Const(-1); Operand nIsIntMin = context.ICompareEqual(n, intMin); Operand mIsMinus1 = context.ICompareEqual(m, minus1); Operand lblGoodDiv = Label(); context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1)); EmitAluStore(context, intMin); context.Branch(lblEnd); context.MarkLabel(lblGoodDiv); } Operand res = unsigned ? context.DivideUI(n, m) : context.Divide(n, m); EmitAluStore(context, res); context.Branch(lblEnd); context.MarkLabel(lblBadDiv); EmitAluStore(context, zero); context.MarkLabel(lblEnd); }
public static void Cmeq_V(ArmEmitterContext context) { if (Optimizations.UseSse41) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; Operand n = GetVec(op.Rn); Operand m; if (op is OpCodeSimdReg binOp) { m = GetVec(binOp.Rm); } else { m = context.VectorZero(); } Intrinsic cmpInst = X86PcmpeqInstruction[op.Size]; Operand res = context.AddIntrinsic(cmpInst, n, m); if (op.RegisterSize == RegisterSize.Simd64) { res = context.VectorZeroUpper64(res); } context.Copy(GetVec(op.Rd), res); } else { EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: false); } }
private static void EmitNativeCall(ArmEmitterContext context, Operand nativeContextPtr, Operand funcAddr, bool isJump = false) { context.StoreToContext(); if (isJump) { context.Tailcall(funcAddr, nativeContextPtr); } else { OpCode op = context.CurrOp; Operand returnAddress = context.Call(funcAddr, OperandType.I64, nativeContextPtr); context.LoadFromContext(); // Note: The return value of a translated function is always an Int64 with the // address execution has returned to. We expect this address to be immediately after the // current instruction, if it isn't we keep returning until we reach the dispatcher. Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes); // Try to continue within this block. // If the return address isn't to our next instruction, we need to return so the JIT can figure out what to do. Operand lblContinue = context.GetLabel(nextAddr.Value); // We need to clear out the call flag for the return address before comparing it. context.BranchIfTrue(lblContinue, context.ICompareEqual(context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr)); context.Return(returnAddress); } }
public static void Vtst(ArmEmitterContext context) { EmitVectorBinaryOpZx32(context, (op1, op2) => { Operand isZero = context.ICompareEqual(context.BitwiseAnd(op1, op2), Const(0)); return(context.ConditionalSelect(isZero, Const(0), Const(-1))); }); }
public static void EmitSbcsCCheck(ArmEmitterContext context, Operand n, Operand m) { // C = (Rn == Rm && CIn) || Rn > Rm Operand cIn = GetFlag(PState.CFlag); Operand cOut = context.BitwiseAnd(context.ICompareEqual(n, m), cIn); cOut = context.BitwiseOr(cOut, context.ICompareGreaterUI(n, m)); SetFlag(context, PState.CFlag, cOut); }
public static void EmitAdcsCCheck(ArmEmitterContext context, Operand n, Operand d) { // C = (Rd == Rn && CIn) || Rd < Rn Operand cIn = GetFlag(PState.CFlag); Operand cOut = context.BitwiseAnd(context.ICompareEqual(d, n), cIn); cOut = context.BitwiseOr(cOut, context.ICompareLessUI(d, n)); SetFlag(context, PState.CFlag, cOut); }
private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand retVal) { // Note: The return value of the called method will be placed // at the Stack, the return value is always a Int64 with the // return address of the function. We check if the address is // correct, if it isn't we keep returning until we reach the dispatcher. ulong nextAddr = GetNextOpAddress(context.CurrOp); if (context.CurrBlock.Next != null) { Operand lblContinue = Label(); context.BranchIfTrue(lblContinue, context.ICompareEqual(retVal, Const(nextAddr))); context.Return(Const(nextAddr)); context.MarkLabel(lblContinue); } else { context.Return(Const(nextAddr)); } }
public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry) { Operand m = GetIntA32(context, op.Rm); Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs)); Operand shiftIsZero = context.ICompareEqual(s, Const(0)); Operand zeroResult = m; Operand shiftResult = m; setCarry &= ShouldSetFlags(context); switch (op.ShiftType) { case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break; case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break; case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break; case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break; } return(context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult)); }
private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand returnAddress) { // Note: The return value of a translated function is always an Int64 with the // address execution has returned to. We expect this address to be immediately after the // current instruction, if it isn't we keep returning until we reach the dispatcher. Operand nextAddr = Const(GetNextOpAddress(context.CurrOp)); // Try to continue within this block. // If the return address isn't to our next instruction, we need to return so the JIT can figure out what to do. Operand lblContinue = Label(); // We need to clear out the call flag for the return address before comparing it. context.BranchIfTrue(lblContinue, context.ICompareEqual(context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr)); context.Return(returnAddress); context.MarkLabel(lblContinue); if (context.CurrBlock.Next == null) { // No code following this instruction, try and find the next block and jump to it. EmitTailContinue(context, nextAddr); } }
public static Operand GetCondTrue(ArmEmitterContext context, Condition condition) { Operand cmpResult = context.TryGetComparisonResult(condition); if (cmpResult != null) { return(cmpResult); } Operand value = Const(1); Operand Inverse(Operand val) { return(context.BitwiseExclusiveOr(val, Const(1))); } switch (condition) { case Condition.Eq: value = GetFlag(PState.ZFlag); break; case Condition.Ne: value = Inverse(GetFlag(PState.ZFlag)); break; case Condition.GeUn: value = GetFlag(PState.CFlag); break; case Condition.LtUn: value = Inverse(GetFlag(PState.CFlag)); break; case Condition.Mi: value = GetFlag(PState.NFlag); break; case Condition.Pl: value = Inverse(GetFlag(PState.NFlag)); break; case Condition.Vs: value = GetFlag(PState.VFlag); break; case Condition.Vc: value = Inverse(GetFlag(PState.VFlag)); break; case Condition.GtUn: { Operand c = GetFlag(PState.CFlag); Operand z = GetFlag(PState.ZFlag); value = context.BitwiseAnd(c, Inverse(z)); break; } case Condition.LeUn: { Operand c = GetFlag(PState.CFlag); Operand z = GetFlag(PState.ZFlag); value = context.BitwiseOr(Inverse(c), z); break; } case Condition.Ge: { Operand n = GetFlag(PState.NFlag); Operand v = GetFlag(PState.VFlag); value = context.ICompareEqual(n, v); break; } case Condition.Lt: { Operand n = GetFlag(PState.NFlag); Operand v = GetFlag(PState.VFlag); value = context.ICompareNotEqual(n, v); break; } case Condition.Gt: { Operand n = GetFlag(PState.NFlag); Operand z = GetFlag(PState.ZFlag); Operand v = GetFlag(PState.VFlag); value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v)); break; } case Condition.Le: { Operand n = GetFlag(PState.NFlag); Operand z = GetFlag(PState.ZFlag); Operand v = GetFlag(PState.VFlag); value = context.BitwiseOr(z, context.ICompareNotEqual(n, v)); break; } } return(value); }
public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d) { SetFlag(context, PState.NFlag, context.ICompareLess(d, Const(d.Type, 0))); SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0))); }
public static void Cmeq_S(ArmEmitterContext context) { EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: true); }