public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value) { Debug.Assert(value.Type == OperandType.I32); if (rd == RegisterAlias.Aarch32Pc && setFlags) { if (setFlags) { // TODO: Load SPSR etc. Operand isThumb = GetFlag(PState.TFlag); Operand lblThumb = Label(); context.BranchIfTrue(lblThumb, isThumb); // Make this count as a call, the translator will ignore the low bit for the address. context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(value, Const(~3)), Const(1)))); context.MarkLabel(lblThumb); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(value, Const(1)))); } else { EmitAluWritePc(context, value); } } else { SetIntA32(context, rd, value); } }
private static void EmitReadInt(ArmEmitterContext context, Operand address, int rt, int size) { Operand lblSlowPath = Label(); Operand lblEnd = Label(); Operand isUnalignedAddr = EmitAddressCheck(context, address, size); context.BranchIfTrue(lblSlowPath, isUnalignedAddr); Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false); Operand value = null; switch (size) { case 0: value = context.Load8(physAddr); break; case 1: value = context.Load16(physAddr); break; case 2: value = context.Load(OperandType.I32, physAddr); break; case 3: value = context.Load(OperandType.I64, physAddr); break; } SetInt(context, rt, value); context.Branch(lblEnd); context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold); EmitReadIntFallback(context, address, rt, size); context.MarkLabel(lblEnd); }
private static void EmitAluStore(ArmEmitterContext context, Operand value) { IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; if (op.Rd == RegisterAlias.Aarch32Pc) { if (op.SetFlags) { // TODO: Load SPSR etc. Operand isThumb = GetFlag(PState.TFlag); Operand lblThumb = Label(); context.BranchIfTrue(lblThumb, isThumb); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~3)))); context.MarkLabel(lblThumb); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1)))); } else { EmitAluWritePc(context, value); } } else { SetIntA32(context, op.Rd, value); } }
public static void Fcsel_S(ArmEmitterContext context) { OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; Operand lblTrue = Label(); Operand lblEnd = Label(); Operand isTrue = InstEmitFlowHelper.GetCondTrue(context, op.Cond); context.BranchIfTrue(lblTrue, isTrue); OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64; Operand me = context.VectorExtract(type, GetVec(op.Rm), 0); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), me, 0)); context.Branch(lblEnd); context.MarkLabel(lblTrue); Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), ne, 0)); context.MarkLabel(lblEnd); }
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); } }
private static void EmitBranch(ArmEmitterContext context, Operand value, bool onNotZero) { OpCodeBImm op = (OpCodeBImm)context.CurrOp; if (context.CurrBlock.Branch != null) { Operand lblTarget = context.GetLabel((ulong)op.Immediate); if (onNotZero) { context.BranchIfTrue(lblTarget, value); } else { context.BranchIfFalse(lblTarget, value); } if (context.CurrBlock.Next == null) { context.Return(Const(op.Address + 4)); } } else { Operand lblTaken = Label(); if (onNotZero) { context.BranchIfTrue(lblTaken, value); } else { context.BranchIfFalse(lblTaken, value); } context.Return(Const(op.Address + 4)); context.MarkLabel(lblTaken); context.Return(Const(op.Immediate)); } }
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond) { if (cond != Condition.Al) { context.BranchIfTrue(target, GetCondTrue(context, cond)); } else { context.Branch(target); } }
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); }
private static void EmitBranch(ArmEmitterContext context, Operand value, bool onNotZero) { OpCodeBImm op = (OpCodeBImm)context.CurrOp; Operand lblTarget = context.GetLabel((ulong)op.Immediate); if (onNotZero) { context.BranchIfTrue(lblTarget, value); } else { context.BranchIfFalse(lblTarget, value); } }
private static void EmitCb(ArmEmitterContext context, bool onNotZero) { OpCodeT16BImmCmp op = (OpCodeT16BImmCmp)context.CurrOp; Operand value = GetIntA32(context, op.Rn); Operand lblTarget = context.GetLabel((ulong)op.Immediate); if (onNotZero) { context.BranchIfTrue(lblTarget, value); } else { context.BranchIfFalse(lblTarget, value); } }
public static void EmitBxWritePc(ArmEmitterContext context, Operand pc) { Operand mode = context.BitwiseAnd(pc, Const(1)); SetFlag(context, PState.TFlag, mode); Operand lblArmMode = Label(); context.BranchIfTrue(lblArmMode, mode); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~1)))); context.MarkLabel(lblArmMode); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~3)))); }
private static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblFallbackPath) { Operand pte = Const(context.Memory.PageTable.ToInt64()); int bit = MemoryManager.PageBits; do { Operand addrPart = context.ShiftRightUI(address, Const(bit)); bit += context.Memory.PtLevelBits; if (bit < context.Memory.AddressSpaceBits) { addrPart = context.BitwiseAnd(addrPart, Const(addrPart.Type, context.Memory.PtLevelMask)); } Operand pteOffset = context.ShiftLeft(addrPart, Const(3)); if (pteOffset.Type == OperandType.I32) { pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset); } Operand pteAddress = context.Add(pte, pteOffset); pte = context.Load(OperandType.I64, pteAddress); }while (bit < context.Memory.AddressSpaceBits); if (!context.Memory.HasWriteWatchSupport) { Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask)); context.BranchIfTrue(lblFallbackPath, hasFlagSet); } Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, MemoryManager.PageMask)); if (pageOffset.Type == OperandType.I32) { pageOffset = context.ZeroExtend32(OperandType.I64, pageOffset); } Operand physAddr = context.Add(pte, pageOffset); return(physAddr); }
private static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath) { int ptLevelBits = context.Memory.AddressSpaceBits - 12; // 12 = Number of page bits. int ptLevelSize = 1 << ptLevelBits; int ptLevelMask = ptLevelSize - 1; Operand pte = Ptc.State == PtcState.Disabled ? Const(context.Memory.PageTablePointer.ToInt64()) : Const(context.Memory.PageTablePointer.ToInt64(), true, Ptc.PageTablePointerIndex); int bit = PageBits; do { Operand addrPart = context.ShiftRightUI(address, Const(bit)); bit += ptLevelBits; if (bit < context.Memory.AddressSpaceBits) { addrPart = context.BitwiseAnd(addrPart, Const(addrPart.Type, ptLevelMask)); } Operand pteOffset = context.ShiftLeft(addrPart, Const(3)); if (pteOffset.Type == OperandType.I32) { pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset); } Operand pteAddress = context.Add(pte, pteOffset); pte = context.Load(OperandType.I64, pteAddress); }while (bit < context.Memory.AddressSpaceBits); context.BranchIfTrue(lblSlowPath, context.ICompareLess(pte, Const(0L))); Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, PageMask)); if (pageOffset.Type == OperandType.I32) { pageOffset = context.ZeroExtend32(OperandType.I64, pageOffset); } return(context.Add(pte, pageOffset)); }
public static void EmitBxWritePc(ArmEmitterContext context, Operand pc) { Operand mode = context.BitwiseAnd(pc, Const(1)); SetFlag(context, PState.TFlag, mode); Operand lblArmMode = Label(); context.BranchIfTrue(lblArmMode, mode); // Make this count as a call, the translator will ignore the low bit for the address. context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(pc, Const((int)InstEmitFlowHelper.CallFlag)))); context.MarkLabel(lblArmMode); context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(pc, Const(~3)), Const((int)InstEmitFlowHelper.CallFlag)))); }
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true) { Debug.Assert(boolValue.Type == OperandType.I32); Operand endLabel = Label(); if (expected) { context.BranchIfFalse(endLabel, boolValue); } else { context.BranchIfTrue(endLabel, boolValue); } action(); context.MarkLabel(endLabel); }
private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; Operand lblTrue = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond)); EmitSetNzcv(context, op.Nzcv); context.Branch(lblEnd); context.MarkLabel(lblTrue); EmitFcmpOrFcmpe(context, signalNaNs); context.MarkLabel(lblEnd); }
private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD) { int vecQ = vecD >> 1; int vecSElem = (vecD & 1) << 1; Operand lblBigEndian = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2); EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2); context.Branch(lblEnd); context.MarkLabel(lblBigEndian); EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2); EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2); context.MarkLabel(lblEnd); }
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)); } }
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 void EmitStoreExclusive( ArmEmitterContext context, Operand address, Operand value, bool exclusive, int size, int rs, bool a32) { if (size < 3) { value = context.ConvertI64ToI32(value); } if (exclusive) { // We overwrite one of the register (Rs), // keep a copy of the values to ensure we are working with the correct values. address = context.Copy(address); value = context.Copy(value); void SetRs(Operand value) { if (a32) { SetIntA32(context, rs, value); } else { SetIntOrZR(context, rs, value); } } Operand arg0 = context.LoadArgument(OperandType.I64, 0); Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset())); Operand exAddr = context.Load(address.Type, exAddrPtr); // STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store. Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())); Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress); Operand lblExit = Label(); SetRs(Const(1)); context.BranchIfTrue(lblExit, exFailed); // STEP 2: We have exclusive access and the address is valid, attempt the store using CAS. Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true, size); Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset())); Operand exValue = size switch { 0 => context.Load8(exValuePtr), 1 => context.Load16(exValuePtr), 2 => context.Load(OperandType.I32, exValuePtr), 3 => context.Load(OperandType.I64, exValuePtr), _ => context.Load(OperandType.V128, exValuePtr) }; Operand currValue = size switch { 0 => context.CompareAndSwap8(physAddr, exValue, value), 1 => context.CompareAndSwap16(physAddr, exValue, value), _ => context.CompareAndSwap(physAddr, exValue, value) }; // STEP 3: Check if we succeeded by comparing expected and in-memory values. Operand storeFailed; if (size == 4) { Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0); Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1); Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0); Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1); storeFailed = context.BitwiseOr( context.ICompareNotEqual(currValueLow, exValueLow), context.ICompareNotEqual(currValueHigh, exValueHigh)); } else { storeFailed = context.ICompareNotEqual(currValue, exValue); } SetRs(storeFailed); context.MarkLabel(lblExit); } else { InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size); } }
public static void EmitStoreExclusive( ArmEmitterContext context, Operand address, Operand value, bool exclusive, int size, int rs, bool a32) { if (size < 3) { value = context.ConvertI64ToI32(value); } if (exclusive) { void SetRs(Operand value) { if (a32) { SetIntA32(context, rs, value); } else { SetIntOrZR(context, rs, value); } } Operand arg0 = context.LoadArgument(OperandType.I64, 0); Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset())); Operand exAddr = context.Load(address.Type, exAddrPtr); // STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store. Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())); Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress); Operand lblExit = Label(); SetRs(exFailed); context.BranchIfTrue(lblExit, exFailed); // STEP 2: We have exclusive access, make sure that the address is valid. Operand isUnalignedAddr = InstEmitMemoryHelper.EmitAddressCheck(context, address, size); Operand lblFastPath = Label(); context.BranchIfFalse(lblFastPath, isUnalignedAddr); // The call is not expected to return (it should throw). context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address); // STEP 3: We have exclusive access and the address is valid, attempt the store using CAS. context.MarkLabel(lblFastPath); Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true); Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset())); Operand exValue = size switch { 0 => context.Load8(exValuePtr), 1 => context.Load16(exValuePtr), 2 => context.Load(OperandType.I32, exValuePtr), 3 => context.Load(OperandType.I64, exValuePtr), _ => context.Load(OperandType.V128, exValuePtr) }; Operand currValue = size switch { 0 => context.CompareAndSwap8(physAddr, exValue, value), 1 => context.CompareAndSwap16(physAddr, exValue, value), _ => context.CompareAndSwap(physAddr, exValue, value) }; // STEP 4: Check if we succeeded by comparing expected and in-memory values. Operand storeFailed; if (size == 4) { Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0); Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1); Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0); Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1); storeFailed = context.BitwiseOr( context.ICompareNotEqual(currValueLow, exValueLow), context.ICompareNotEqual(currValueHigh, exValueHigh)); } else { storeFailed = context.ICompareNotEqual(currValue, exValue); } SetRs(storeFailed); context.MarkLabel(lblExit); } else { InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size); } }
private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType) { IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp; Operand address = context.Copy(GetIntA32(context, op.Rn)); var exclusive = (accType & AccessType.Exclusive) != 0; var ordered = (accType & AccessType.Ordered) != 0; if ((accType & AccessType.Load) != 0) { if (ordered) { EmitBarrier(context); } if (size == DWordSizeLog2) { // Keep loads atomic - make the call to get the whole region and then decompose it into parts // for the registers. Operand value = EmitLoadExclusive(context, address, exclusive, size); Operand valueLow = context.ConvertI64ToI32(value); valueLow = context.ZeroExtend32(OperandType.I64, valueLow); Operand valueHigh = context.ShiftRightUI(value, Const(32)); Operand lblBigEndian = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); SetIntA32(context, op.Rt, valueLow); SetIntA32(context, op.Rt | 1, valueHigh); context.Branch(lblEnd); context.MarkLabel(lblBigEndian); SetIntA32(context, op.Rt | 1, valueLow); SetIntA32(context, op.Rt, valueHigh); context.MarkLabel(lblEnd); } else { SetIntA32(context, op.Rt, EmitLoadExclusive(context, address, exclusive, size)); } } else { if (size == DWordSizeLog2) { // Split the result into 2 words (based on endianness) Operand lo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt)); Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt | 1)); Operand lblBigEndian = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); Operand leResult = context.BitwiseOr(lo, context.ShiftLeft(hi, Const(32))); EmitStoreExclusive(context, address, leResult, exclusive, size, op.Rd, a32: true); context.Branch(lblEnd); context.MarkLabel(lblBigEndian); Operand beResult = context.BitwiseOr(hi, context.ShiftLeft(lo, Const(32))); EmitStoreExclusive(context, address, beResult, exclusive, size, op.Rd, a32: true); context.MarkLabel(lblEnd); } else { Operand value = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt)); EmitStoreExclusive(context, address, value, exclusive, size, op.Rd, a32: true); } if (ordered) { EmitBarrier(context); } } }
private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType) { OpCode32Mem op = (OpCode32Mem)context.CurrOp; Operand n = context.Copy(GetIntA32(context, op.Rn)); Operand temp = null; if (op.Index || op.WBack) { temp = op.Add ? context.Add(n, Const(op.Immediate)) : context.Subtract(n, Const(op.Immediate)); } if (op.WBack) { SetIntA32(context, op.Rn, temp); } Operand address; if (op.Index) { address = temp; } else { address = n; } if ((accType & AccessType.Load) != 0) { void Load(int rt, int offs, int loadSize) { Operand addr = context.Add(address, Const(offs)); if ((accType & AccessType.Signed) != 0) { EmitLoadSx32(context, addr, rt, loadSize); } else { EmitLoadZx(context, addr, rt, loadSize); } } if (size == DWordSizeLog2) { Operand lblBigEndian = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); Load(op.Rt, 0, WordSizeLog2); Load(op.Rt | 1, 4, WordSizeLog2); context.Branch(lblEnd); context.MarkLabel(lblBigEndian); Load(op.Rt | 1, 0, WordSizeLog2); Load(op.Rt, 4, WordSizeLog2); context.MarkLabel(lblEnd); } else { Load(op.Rt, 0, size); } } else { void Store(int rt, int offs, int storeSize) { Operand addr = context.Add(address, Const(offs)); EmitStore(context, addr, rt, storeSize); } if (size == DWordSizeLog2) { Operand lblBigEndian = Label(); Operand lblEnd = Label(); context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); Store(op.Rt, 0, WordSizeLog2); Store(op.Rt | 1, 4, WordSizeLog2); context.Branch(lblEnd); context.MarkLabel(lblBigEndian); Store(op.Rt | 1, 0, WordSizeLog2); Store(op.Rt, 4, WordSizeLog2); context.MarkLabel(lblEnd); } else { Store(op.Rt, 0, size); } } }