Esempio n. 1
0
        private static void EmitSynchronization(EmitterContext context)
        {
            long countOffs = NativeContext.GetCounterOffset();

            Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));

            Operand count = context.Load(OperandType.I32, countAddr);

            Operand lblNonZero = Label();
            Operand lblExit    = Label();

            context.BranchIfTrue(lblNonZero, count);

            Operand running = context.Call(new _Bool(NativeInterface.CheckSynchronization));

            context.BranchIfTrue(lblExit, running);

            context.Return(Const(0L));

            context.Branch(lblExit);

            context.MarkLabel(lblNonZero);

            count = context.Subtract(count, Const(1));

            context.Store(countAddr, count);

            context.MarkLabel(lblExit);
        }
Esempio n. 2
0
        private static void LoadLocals(BasicBlock block, long inputs, RegisterType baseType)
        {
            Operand arg0 = Local(OperandType.I64);

            for (int bit = 63; bit >= 0; bit--)
            {
                long mask = 1L << bit;

                if ((inputs & mask) == 0)
                {
                    continue;
                }

                Operand dest = GetRegFromBit(bit, baseType);

                long offset = NativeContext.GetRegisterOffset(dest.GetRegister());

                Operand addr = Local(OperandType.I64);

                Operation loadOp = new Operation(Instruction.Load, dest, addr);

                block.Operations.AddFirst(loadOp);

                Operation calcOffsOp = new Operation(Instruction.Add, addr, arg0, Const(offset));

                block.Operations.AddFirst(calcOffsOp);
            }

            Operation loadArg0 = new Operation(Instruction.LoadArgument, arg0, Const(0));

            block.Operations.AddFirst(loadArg0);
        }
Esempio n. 3
0
        private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType, ExecutionMode mode)
        {
            Operand arg0 = Local(OperandType.I64);

            Operation loadArg0 = Operation(Instruction.LoadArgument, arg0, Const(0));

            block.Append(loadArg0);

            for (int bit = 0; bit < 64; bit++)
            {
                long mask = 1L << bit;

                if ((outputs & mask) == 0)
                {
                    continue;
                }

                Operand source = GetRegFromBit(bit, baseType, mode);

                long offset = NativeContext.GetRegisterOffset(source.GetRegister());

                Operand addr = Local(OperandType.I64);

                Operation calcOffsOp = Operation(Instruction.Add, addr, arg0, Const(offset));

                block.Append(calcOffsOp);

                Operation storeOp = Operation(Instruction.Store, null, addr, source);

                block.Append(storeOp);
            }
        }
Esempio n. 4
0
        internal static void EmitSynchronization(EmitterContext context)
        {
            long countOffs = NativeContext.GetCounterOffset();

            Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));

            Operand count = context.Load(OperandType.I32, countAddr);

            Operand lblNonZero = Label();
            Operand lblExit    = Label();

            context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);

            Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));

            context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);

            context.Return(Const(0L));

            context.MarkLabel(lblNonZero);

            count = context.Subtract(count, Const(1));

            context.Store(countAddr, count);

            context.MarkLabel(lblExit);
        }
Esempio n. 5
0
        private static void EmitNativeCallWithGuestAddress(ArmEmitterContext context, Operand funcAddr, Operand guestAddress, bool isJump)
        {
            Operand nativeContextPtr = context.LoadArgument(OperandType.I64, 0);

            context.Store(context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())), guestAddress);

            EmitNativeCall(context, nativeContextPtr, funcAddr, isJump);
        }
Esempio n. 6
0
        public static Operand EmitLoadExclusive(ArmEmitterContext context, Operand address, bool exclusive, int size)
        {
            if (exclusive)
            {
                Operand value;

                if (size == 4)
                {
                    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);

                    context.MarkLabel(lblFastPath);

                    // Only 128-bit CAS is guaranteed to have a atomic load.
                    Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: false);

                    Operand zero = context.VectorZero();

                    value = context.CompareAndSwap(physAddr, zero, zero);
                }
                else
                {
                    value = InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
                }

                Operand arg0 = context.LoadArgument(OperandType.I64, 0);

                Operand exAddrPtr  = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
                Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));

                context.Store(exAddrPtr, context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())));

                // Make sure the unused higher bits of the value are cleared.
                if (size < 3)
                {
                    context.Store(exValuePtr, Const(0UL));
                }
                if (size < 4)
                {
                    context.Store(context.Add(exValuePtr, Const(exValuePtr.Type, 8L)), Const(0UL));
                }

                // Store the new exclusive value.
                context.Store(exValuePtr, value);

                return(value);
            }
            else
            {
                return(InstEmitMemoryHelper.EmitReadIntAligned(context, address, size));
            }
        }
Esempio n. 7
0
        public static void EmitClearExclusive(ArmEmitterContext context)
        {
            Operand arg0 = context.LoadArgument(OperandType.I64, 0);

            Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));

            // We store ULONG max to force any exclusive address checks to fail,
            // since this value is not aligned to the ERG mask.
            context.Store(exAddrPtr, Const(ulong.MaxValue));
        }
Esempio n. 8
0
 void iContent.render()
 {
     try
     {
         render();
     }
     catch (Exception ex)
     {
         NativeContext.cacheException(ex);
         throw;
     }
 }
Esempio n. 9
0
        public static Operand EmitLoadExclusive(ArmEmitterContext context, Operand address, bool exclusive, int size)
        {
            if (exclusive)
            {
                Operand value;

                if (size == 4)
                {
                    // Only 128-bit CAS is guaranteed to have a atomic load.
                    Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: false, 4);

                    Operand zero = context.VectorZero();

                    value = context.CompareAndSwap(physAddr, zero, zero);
                }
                else
                {
                    value = InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
                }

                Operand arg0 = context.LoadArgument(OperandType.I64, 0);

                Operand exAddrPtr  = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
                Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));

                context.Store(exAddrPtr, context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())));

                // Make sure the unused higher bits of the value are cleared.
                if (size < 3)
                {
                    context.Store(exValuePtr, Const(0UL));
                }
                if (size < 4)
                {
                    context.Store(context.Add(exValuePtr, Const(exValuePtr.Type, 8L)), Const(0UL));
                }

                // Store the new exclusive value.
                context.Store(exValuePtr, value);

                return(value);
            }
            else
            {
                return(InstEmitMemoryHelper.EmitReadIntAligned(context, address, size));
            }
        }
Esempio n. 10
0
        private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType, bool isCompleteFunction)
        {
            if (Optimizations.AssumeStrictAbiCompliance && isCompleteFunction)
            {
                if (baseType == RegisterType.Integer || baseType == RegisterType.Flag)
                {
                    outputs = ClearCallerSavedIntRegs(outputs);
                }
                else /* if (baseType == RegisterType.Vector) */
                {
                    outputs = ClearCallerSavedVecRegs(outputs);
                }
            }

            Operand arg0 = Local(OperandType.I64);

            Operation loadArg0 = new Operation(Instruction.LoadArgument, arg0, Const(0));

            block.Append(loadArg0);

            for (int bit = 0; bit < 64; bit++)
            {
                long mask = 1L << bit;

                if ((outputs & mask) == 0)
                {
                    continue;
                }

                Operand source = GetRegFromBit(bit, baseType);

                long offset = NativeContext.GetRegisterOffset(source.GetRegister());

                Operand addr = Local(OperandType.I64);

                Operation calcOffsOp = new Operation(Instruction.Add, addr, arg0, Const(offset));

                block.Append(calcOffsOp);

                Operation storeOp = new Operation(Instruction.Store, null, addr, source);

                block.Append(storeOp);
            }
        }
Esempio n. 11
0
        void iContent.releaseCachedResources(eReleaseResources what)
        {
            try
            {
                if (null == timersHardPause)
                {
                    timersHardPause = animation.timers.hardPause();
                }

                foreach (var rr in releaseResources)
                {
                    rr(what);
                }

                ComUtils.clear(ref cachedRtv);
                ComUtils.clear(ref cachedDsv);
                context?.SetRenderTargets(0, null, null, ResourceStateTransitionMode.None);

                if (what == eReleaseResources.Buffers)
                {
                    return;
                }

                context?.Dispose();
                context = null;
                if (what == eReleaseResources.Context)
                {
                    return;
                }

                swapChain?.Dispose();
                swapChain = null;
            }
            catch (Exception ex)
            {
                NativeContext.cacheException(ex);
                throw;
            }
        }
Esempio n. 12
0
        void iContent.swapChainRecreated(iDiligentWindow window)
        {
            try
            {
                swapChain = window.swapChain;
                context   = window.context;

                timersHardPause?.Dispose();
                timersHardPause = null;

                var scDesc = swapChain.GetDesc();
                swapChainSize = new CSize(scDesc.Width, scDesc.Height);
                byte samples = swapChainFormats.sampleCount;
                swapChainFormats      = new SwapChainFormats(scDesc.ColorBufferFormat, scDesc.DepthBufferFormat, samples);
                swapChainBuffersCount = scDesc.BufferCount;
            }
            catch (Exception ex)
            {
                NativeContext.cacheException(ex);
                throw;
            }
        }
Esempio n. 13
0
        /// <summary>
        ///   Generates the native executable.
        /// </summary>
        /// <param name = "directory">The directory.</param>
        /// <returns></returns>
        public String Generate(String directory)
        {
            NativeContext nativeContext = new NativeContext(this.TargetOSVersion, this.TargetArchitecture);

            if (this.NativeCompiler == null)
            {
                this.NativeCompiler = nativeContext.Compiler;
            }

            // We embed:
            // - the assemblies (with their configuration)
            // - the machine configuration
            //
            // For every embedded assembly, we store:
            // - the name of the assembly
            // - the pretty name of the assembly (for inclusion in source code)
            // - the path to the static library that embed the assembly
            // - the path to the static library that embed the assembly configuration file
            List <Dictionary <String, String> > assemblies     = new List <Dictionary <string, string> >();
            List <Dictionary <String, String> > configurations = new List <Dictionary <string, string> >();

            // Create an instance of creator
            DataLibraryCreator creator = new DataLibraryCreator();

            creator.Logger            = this.Logger;
            creator.ArchitectureFlags = nativeContext.ArchitectureFlags;
            creator.OutputDirectory   = directory;

            this.Logger.LogInfo("Creating static libraries...");

            // For each assembly and its config, generate a native library);
            foreach (String assembly in this.Assemblies)
            {
                String name = Path.GetFileName(assembly);

                this.Logger.LogInfo("Processing static library " + name);

                Dictionary <string, string> bundle = CreateBundle(creator, assembly, false);
                assemblies.Add(bundle);

                String config = assembly + ".config";
                if (File.Exists(config))
                {
                    this.Logger.LogInfo("Processing configuration for " + name);

                    bundle = CreateBundle(creator, config, true);
                    bundle[KEY_ASSEMBLY] = name;
                    configurations.Add(bundle);
                }
            }

            // Store the main image
            String mainImage = assemblies[0][KEY_NAME];

            // Sort all the symbols
            assemblies.Sort((D1, D2) => D1[KEY_SYMBOL].CompareTo(D2[KEY_SYMBOL]));
            configurations.Sort((D1, D2) => D1[KEY_SYMBOL].CompareTo(D2[KEY_SYMBOL]));

            // Generate a native library for the machine configuration
            Dictionary <String, String> machineConfig = null;

            if (this.MachineConfiguration != null)
            {
                machineConfig = CreateBundle(creator, this.MachineConfiguration, true);
            }

            this.Logger.LogInfo("Creating source file...");

            // Generate the main source file
            String mainSource = Path.Combine(directory, "main.c");

            this.GenerateMainSource(mainSource, mainImage, assemblies, configurations, machineConfig);

            // Dump the header file
            nativeContext.WriteHeader(directory);

            // Dump the library file
            nativeContext.WriteLibrary(directory);

            // Stage 1: Preparation of common properties
            String mainObject     = Path.Combine(directory, "main.o");
            String executableFile = Path.Combine(directory, Path.GetFileNameWithoutExtension(mainImage));
            String nativeOptions  = String.Format(" {0} {1} ", nativeContext.ArchitectureFlags, nativeContext.SDKFlags);

            // Stage 2: Compilation
            this.Compile(directory, mainSource, mainObject, nativeOptions);

            // Stage 3: Linkage
            this.Link(directory, mainObject, executableFile, nativeOptions, assemblies, configurations, machineConfig);

            this.Logger.LogInfo("Embedding done");

            return(executableFile);
        }
Esempio n. 14
0
        void iContent.resized(ref sWindowPosition position)
        {
            // ConsoleLogger.logDebug( "{0}", position );
            try
            {
                // No need to waste resources rendering frames while the window is minimized.
                if (position.show == eShowWindow.Minimized)
                {
                    if (null == timersHardPause)
                    {
                        animation.timers.pause();
                    }
                    Dispatcher.currentDispatcher.nativeDispatcher.runPolicy = eDispatcherRunPolicy.EnvironmentFriendly;
                }
                else
                {
                    if (null == timersHardPause && animation.any)
                    {
                        animation.timers.resume();
                        Dispatcher.currentDispatcher.nativeDispatcher.runPolicy = eDispatcherRunPolicy.GameStyle;
                    }
                }

                ComUtils.clear(ref cachedRtv);
                ComUtils.clear(ref cachedDsv);
                context?.SetRenderTargets(0, null, null, ResourceStateTransitionMode.None);

                windowState = position.show;

                if (dpiScalingFactor != position.dpiScaling)
                {
                    dpiScalingFactor = position.dpiScaling;
                    var evt = m_dpiChanged;
                    if (null != evt)
                    {
                        foreach (var sub in evt)
                        {
                            sub(dpiScalingFactor);
                        }
                    }
                }

                if (position.show != eShowWindow.Minimized)
                {
                    foreach (var rr in releaseResources)
                    {
                        rr(eReleaseResources.Buffers);
                    }

                    swapChain?.Resize(position.size.cx, position.size.cy);
                    swapChainSize = position.size;
                    foreach (var sub in swapChainResized)
                    {
                        sub(position.size, position.dpiScaling);
                    }
                }
            }
            catch (Exception ex)
            {
                // This is to marshal that exception across C++ code on the stack. C++ can't deliver .NET exceptions, it only knows about HRESULT codes.
                // cacheException call makes it so the complete .NET exception is delivered to the caller, not just the HRESULT code of it.
                NativeContext.cacheException(ex);
                throw;
            }
        }
Esempio n. 15
0
        /// <summary>
        /// Generates a stub that is used to find function addresses. Used for direct calls when their jump table does not have the host address yet.
        /// Takes a NativeContext like a translated guest function, and extracts the target address from the NativeContext.
        /// When the target function is compiled in highCq, all table entries are updated to point to that function instead of this stub by the translator.
        /// </summary>
        private static GuestFunction GenerateDirectCallStub(bool tailCall)
        {
            EmitterContext context = new EmitterContext();

            Operand nativeContextPtr = context.LoadArgument(OperandType.I64, 0);

            Operand address = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())));

            Operand functionAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address);

            EmitCall(context, functionAddr, tailCall);

            ControlFlowGraph cfg = context.GetControlFlowGraph();

            OperandType[] argTypes = new OperandType[] { OperandType.I64 };

            return(Compiler.Compile <GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq));
        }
Esempio n. 16
0
        /// <summary>
        /// Generates a stub that is used to find function addresses and add them to an indirect table.
        /// Used for indirect calls entries (already claimed) when their jump table does not have the host address yet.
        /// Takes a NativeContext like a translated guest function, and extracts the target indirect table entry from the NativeContext.
        /// If the function we find is highCq, the entry in the table is updated to point to that function rather than this stub.
        /// </summary>
        private static GuestFunction GenerateIndirectCallStub(bool tailCall)
        {
            EmitterContext context = new EmitterContext();

            Operand nativeContextPtr = context.LoadArgument(OperandType.I64, 0);

            Operand entryAddress = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())));
            Operand address      = context.Load(OperandType.I64, entryAddress);

            // We need to find the missing function. If the function is HighCq, then it replaces this stub in the indirect table.
            // Either way, we call it afterwards.
            Operand functionAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetIndirectFunctionAddress)), address, entryAddress);

            // Call and save the function.
            EmitCall(context, functionAddr, tailCall);

            ControlFlowGraph cfg = context.GetControlFlowGraph();

            OperandType[] argTypes = new OperandType[] { OperandType.I64 };

            return(Compiler.Compile <GuestFunction>(cfg, argTypes, OperandType.I64, CompilerOptions.HighCq));
        }
Esempio n. 17
0
        private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
        {
            context.StoreToContext();

            if (guestAddress.Type == OperandType.I32)
            {
                guestAddress = context.ZeroExtend32(OperandType.I64, guestAddress);
            }

            // Store the target guest address into the native context. The stubs uses this address to dispatch into the
            // next translation.
            Operand nativeContext   = context.LoadArgument(OperandType.I64, 0);
            Operand dispAddressAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));

            context.Store(dispAddressAddr, guestAddress);

            Operand hostAddress;

            // If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
            // onto the dispatch stub.
            if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
            {
                Operand hostAddressAddr = !context.HasPtc ?
                                          Const(ref context.FunctionTable.GetValue(guestAddress.Value)) :
                                          Const(ref context.FunctionTable.GetValue(guestAddress.Value), new Symbol(SymbolType.FunctionTable, guestAddress.Value));

                hostAddress = context.Load(OperandType.I64, hostAddressAddr);
            }
            else
            {
                hostAddress = !context.HasPtc ?
                              Const((long)context.Stubs.DispatchStub) :
                              Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
            }

            if (isJump)
            {
                context.Tailcall(hostAddress, nativeContext);
            }
            else
            {
                OpCode op = context.CurrOp;

                Operand returnAddress = context.Call(hostAddress, OperandType.I64, nativeContext);

                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);
                context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);

                context.Return(returnAddress);
            }
        }
Esempio n. 18
0
        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);
            }
        }
Esempio n. 19
0
        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);
            }
        }