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); }
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); }
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); } }
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); }
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); }
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)); } }
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)); }
void iContent.render() { try { render(); } catch (Exception ex) { NativeContext.cacheException(ex); throw; } }
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)); } }
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); } }
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; } }
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; } }
/// <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); }
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; } }
/// <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)); }
/// <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)); }
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); } }
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); } }
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); } }