private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value) { RegisterMask oldValue = masks[blkIndex]; masks[blkIndex] = value; return(oldValue != value); }
internal FunctionDeclaration(CallingConvention callingConvention, VariableType[] arguments, VariableType @return) { Used = new RegisterMask(); Passed = new RegisterMask(); Preserved = new RegisterMask(); CallingConvention = callingConvention == CallingConvention.Default ? Constants.DefaultCallingConvention : callingConvention; _returns = new FunctionInOut[2]; _arguments = new FunctionInOut[arguments.Length]; Init(arguments, @return); }
public FunctionNode(LabelNode entry, LabelNode exit, VariableType[] arguments, VariableType @return, CallingConvention callingConvention) : base(CodeNodeType.Function) { Entry = entry; Exit = exit; End = new SentinelNode(); FunctionDeclaration = new FunctionDeclaration(callingConvention, arguments, @return); _arguments = new VariableData[arguments.Length]; StackFrameCopyGpIndex = new int[6].InitializeWith(() => RegisterIndex.Invalid); SaveRestoreRegs = new RegisterMask(); StackFrameRegIndex = RegisterIndex.Invalid; _hints = Utils.Mask((int)FuncionNodeHints.Naked); }
private void EmitStoreContext(ILGenerator generator, BasicBlock block) { RegisterMask outputs = _regUsage.GetOutputs(block); long intOutputs = outputs.IntMask; long vecOutputs = outputs.VecMask; if (Optimizations.AssumeStrictAbiCompliance && _isComplete) { intOutputs = RegisterUsage.ClearCallerSavedIntRegs(intOutputs, _mode); vecOutputs = RegisterUsage.ClearCallerSavedVecRegs(vecOutputs, _mode); } StoreLocals(generator, intOutputs, RegisterType.Int); StoreLocals(generator, vecOutputs, RegisterType.Vector); }
private void SetPrototype(VariableType[] arguments, VariableType returnValue) { if (arguments == null) throw new ArgumentNullException("arguments"); if (arguments.Length > 32) throw new ArgumentException(); Contract.EndContractBlock(); int i; int posGP = 0; int posXMM = 0; int stackOffset = 0; _returnValue = returnValue; if (arguments.Length == 0) { _arguments = new Argument[0]; return; } ArgumentData[] argumentData; argumentData = Array.ConvertAll(arguments, a => new ArgumentData(a, RegIndex.Invalid, InvalidValue)); // -------------------------------------------------------------------------- // [X86 Calling Conventions (32-bit)] // -------------------------------------------------------------------------- if (Util.IsX86) { // Register arguments (Integer), always left-to-right. for (i = 0; i != arguments.Length; i++) { ArgumentData a = argumentData[i]; if (VariableInfo.IsVariableInteger(a._variableType) && posGP < 16 && CallingConventionInfo.ArgumentsGPList[posGP] != RegIndex.Invalid) { a._registerIndex = CallingConventionInfo.ArgumentsGPList[posGP++]; _passedGP |= RegisterMask.FromIndex(a._registerIndex); } } // Stack arguments. bool ltr = CallingConventionInfo.ArgumentsDirection == ArgumentsDirection.LeftToRight; int istart = ltr ? 0 : arguments.Length - 1; int iend = ltr ? arguments.Length : -1; int istep = ltr ? 1 : -1; for (i = istart; i != iend; i += istep) { ArgumentData a = argumentData[i]; if (a._registerIndex != RegIndex.Invalid) continue; if (VariableInfo.IsVariableInteger(a._variableType)) { stackOffset -= 4; a._stackOffset = stackOffset; } else if (VariableInfo.IsVariableFloat(a._variableType)) { int size = VariableInfo.GetVariableInfo(a._variableType).Size; stackOffset -= size; a._stackOffset = stackOffset; } } } // -------------------------------------------------------------------------- // [X64 Calling Conventions (64-bit)] // -------------------------------------------------------------------------- if (Util.IsX64) { // Windows 64-bit specific. if (_callingConvention == CallingConvention.X64W) { int max = Math.Min(arguments.Length, 4); // Register arguments (Integer / FP), always left to right. for (i = 0; i != max; i++) { ArgumentData a = argumentData[i]; if (VariableInfo.IsVariableInteger(a._variableType)) { a._registerIndex = CallingConventionInfo.ArgumentsGPList[i]; _passedGP |= RegisterMask.FromIndex(a._registerIndex); } else if (VariableInfo.IsVariableFloat(a._variableType)) { a._registerIndex = CallingConventionInfo.ArgumentsXMMList[i]; _passedXMM |= RegisterMask.FromIndex(a._registerIndex); } } // Stack arguments (always right-to-left). for (i = arguments.Length - 1; i != -1; i--) { ArgumentData a = argumentData[i]; if (a.IsAssigned) continue; if (VariableInfo.IsVariableInteger(a._variableType)) { stackOffset -= 8; // Always 8 bytes. a._stackOffset = stackOffset; } else if (VariableInfo.IsVariableFloat(a._variableType)) { int size = VariableInfo.GetVariableInfo(a._variableType).Size; stackOffset -= size; a._stackOffset = stackOffset; } } // 32 bytes shadow space (X64W calling convention specific). stackOffset -= 4 * 8; } // Linux/Unix 64-bit (AMD64 calling convention). else { // Register arguments (Integer), always left to right. for (i = 0; i != arguments.Length; i++) { ArgumentData a = argumentData[i]; if (VariableInfo.IsVariableInteger(a._variableType) && posGP < 32 && CallingConventionInfo.ArgumentsGPList[posGP] != RegIndex.Invalid) { a._registerIndex = CallingConventionInfo.ArgumentsGPList[posGP++]; _passedGP |= RegisterMask.FromIndex(a._registerIndex); } } // Register arguments (FP), always left to right. for (i = 0; i != arguments.Length; i++) { ArgumentData a = argumentData[i]; if (VariableInfo.IsVariableFloat(a._variableType)) { a._registerIndex = CallingConventionInfo.ArgumentsXMMList[posXMM++]; _passedXMM |= RegisterMask.FromIndex(a._registerIndex); } } // Stack arguments. for (i = arguments.Length - 1; i != -1; i--) { ArgumentData a = argumentData[i]; if (a.IsAssigned) continue; if (VariableInfo.IsVariableInteger(a._variableType)) { stackOffset -= 8; a._stackOffset = stackOffset; } else if (VariableInfo.IsVariableFloat(a._variableType)) { int size = VariableInfo.GetVariableInfo(a._variableType).Size; stackOffset -= size; a._stackOffset = stackOffset; } } } } // Modify stack offset (all function parameters will be in positive stack // offset that is never zero). for (i = 0; i < arguments.Length; i++) { if (argumentData[i]._registerIndex == RegIndex.Invalid) argumentData[i]._stackOffset += IntPtr.Size - stackOffset; } _argumentsStackSize = -stackOffset; _arguments = Array.ConvertAll(argumentData, data => new Argument(data._variableType, data._registerIndex, data._stackOffset)); }
internal void PreparePrologEpilog(CompilerContext cc) { Contract.Requires(cc != null); _pePushPop = false; _emitEMMS = false; _emitSFence = false; _emitLFence = false; _isAssumed16ByteAlignment = false; _isPerformed16ByteAlignment = false; uint accessibleMemoryBelowStack = 0; if (_functionPrototype.CallingConvention == CallingConvention.X64U) accessibleMemoryBelowStack = 128; if (_isCaller && (cc.MemBytesTotal > 0 || _isAssumed16ByteAlignment)) _isEspAdjusted = true; if (cc.MemBytesTotal > accessibleMemoryBelowStack) _isEspAdjusted = true; _isAssumed16ByteAlignment = (_hints & FunctionHints.Assume16ByteAlignment) != 0; _isPerformed16ByteAlignment = (_hints & FunctionHints.Perform16ByteAlignment) != 0; _isNaked = (_hints & FunctionHints.Naked) != 0; _pePushPop = (_hints & FunctionHints.PushPopSequence) != 0; _emitEMMS = (_hints & FunctionHints.Emms) != 0; _emitSFence = (_hints & FunctionHints.StoreFence) != 0; _emitLFence = (_hints & FunctionHints.LoadFence) != 0; // Updated to respect comment from issue #47, align also when using MMX code. if (!_isAssumed16ByteAlignment && !_isNaked && (cc.Mem16BlocksCount + cc.Mem8BlocksCount > 0)) { // Have to align stack to 16-bytes. _isPerformed16ByteAlignment = true; _isEspAdjusted = true; } _modifiedAndPreservedGP = cc.ModifiedGPRegisters & _functionPrototype.PreservedGP & ~RegisterMask.FromIndex(RegIndex.Esp); _modifiedAndPreservedMM = cc.ModifiedMMRegisters & _functionPrototype.PreservedMM; _modifiedAndPreservedXMM = cc.ModifiedXMMRegisters & _functionPrototype.PreservedXMM; _movDqInstruction = (IsAssumed16ByteAlignment || IsPerformed16ByteAlignment) ? InstructionCode.Movdqa : InstructionCode.Movdqu; // Prolog & Epilog stack size. { int memGpSize = _modifiedAndPreservedGP.RegisterCount * IntPtr.Size; int memMmSize = _modifiedAndPreservedMM.RegisterCount * 8; int memXmmSize = _modifiedAndPreservedXMM.RegisterCount * 16; if (_pePushPop) { _pePushPopStackSize = memGpSize; _peMovStackSize = memXmmSize + Util.AlignTo16(memMmSize); } else { _pePushPopStackSize = 0; _peMovStackSize = memXmmSize + Util.AlignTo16(memMmSize + memGpSize); } } if (IsPerformed16ByteAlignment) { _peAdjustStackSize += Util.DeltaTo16(_pePushPopStackSize); } else { int v = 16 - IntPtr.Size; if (!_isNaked) v -= IntPtr.Size; v -= _pePushPopStackSize & 15; if (v < 0) v += 16; _peAdjustStackSize = v; //_peAdjustStackSize += deltaTo16(_pePushPopStackSize + v); } // Memory stack size. _memStackSize = cc.MemBytesTotal; _memStackSize16 = Util.AlignTo16(_memStackSize); if (_isNaked) { cc.ArgumentsBaseReg = RegIndex.Esp; cc.ArgumentsBaseOffset = (_isEspAdjusted) ? (_functionCallStackSize + _memStackSize16 + _peMovStackSize + _pePushPopStackSize + _peAdjustStackSize) : (_pePushPopStackSize); } else { cc.ArgumentsBaseReg = RegIndex.Ebp; cc.ArgumentsBaseOffset = IntPtr.Size; } cc.VariablesBaseReg = RegIndex.Esp; cc.VariablesBaseOffset = _functionCallStackSize; if (!_isEspAdjusted) cc.VariablesBaseOffset = -_memStackSize16 - _peMovStackSize - _peAdjustStackSize; }
public RegisterUsage(BasicBlock entryBlock, int blocksCount) { _inputs = new RegisterMask[blocksCount]; _outputs = new RegisterMask[blocksCount]; HashSet <BasicBlock> visited = new HashSet <BasicBlock>(); Stack <BasicBlock> blockStack = new Stack <BasicBlock>(); List <BasicBlock> postOrderBlocks = new List <BasicBlock>(blocksCount); visited.Add(entryBlock); blockStack.Push(entryBlock); while (blockStack.TryPop(out BasicBlock block)) { if (block.Next != null && visited.Add(block.Next)) { blockStack.Push(block); blockStack.Push(block.Next); } else if (block.Branch != null && visited.Add(block.Branch)) { blockStack.Push(block); blockStack.Push(block.Branch); } else { postOrderBlocks.Add(block); } } RegisterMask[] cmnOutputMasks = new RegisterMask[blocksCount]; bool modified; bool firstPass = true; do { modified = false; for (int blkIndex = postOrderBlocks.Count - 1; blkIndex >= 0; blkIndex--) { BasicBlock block = postOrderBlocks[blkIndex]; if (block.Predecessors.Count != 0 && !block.HasStateLoad) { BasicBlock predecessor = block.Predecessors[0]; RegisterMask cmnOutputs = predecessor.RegOutputs | cmnOutputMasks[predecessor.Index]; RegisterMask outputs = _outputs[predecessor.Index]; for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++) { predecessor = block.Predecessors[pIndex]; cmnOutputs &= predecessor.RegOutputs | cmnOutputMasks[predecessor.Index]; outputs |= _outputs[predecessor.Index]; } _inputs[block.Index] |= outputs & ~cmnOutputs; if (!firstPass) { cmnOutputs &= cmnOutputMasks[block.Index]; } if (Exchange(cmnOutputMasks, block.Index, cmnOutputs)) { modified = true; } outputs |= block.RegOutputs; if (Exchange(_outputs, block.Index, _outputs[block.Index] | outputs)) { modified = true; } } else if (Exchange(_outputs, block.Index, block.RegOutputs)) { modified = true; } } firstPass = false; }while (modified); do { modified = false; for (int blkIndex = 0; blkIndex < postOrderBlocks.Count; blkIndex++) { BasicBlock block = postOrderBlocks[blkIndex]; RegisterMask inputs = block.RegInputs; if (block.Next != null) { inputs |= _inputs[block.Next.Index]; } if (block.Branch != null) { inputs |= _inputs[block.Branch.Index]; } inputs &= ~cmnOutputMasks[block.Index]; if (Exchange(_inputs, block.Index, _inputs[block.Index] | inputs)) { modified = true; } } }while (modified); }
public void AllocXMMVar(CompilerVar var, RegisterMask regMask, VariableAlloc vflags) { Contract.Requires(var != null); Contract.Requires(Function != null); AllocNonGPVar(var, regMask, vflags, RegNum.XMM, var.Scope.Declaration.PreservedXMM, _state.UsedXMM, _state.XMM, AllocatedXMMRegister, SpillXMMVar, FreedXMMRegister); }
// TODO: Find code which uses this and improve. internal void NewRegisterHomeMask(CompilerVar var, RegisterMask mask) { Contract.Requires(var != null); var.PreferredRegisterMask |= mask; }
internal void MarkXMMRegisterModified(RegIndex index) { _modifiedXMMRegisters |= RegisterMask.FromIndex(index); }
internal void Clear() { Contract.Ensures(Function == null); //_zone.clear(); _function = null; _start = null; _stop = null; _state.Clear(); _active = null; _forwardJumps = null; _currentOffset = 0; //_unreachable = 0; _modifiedGPRegisters = RegisterMask.Zero; _modifiedMMRegisters = RegisterMask.Zero; _modifiedXMMRegisters = RegisterMask.Zero; _allocableEBP = false; _adjustESP = 0; _argumentsBaseReg = RegIndex.Invalid; _argumentsBaseOffset = 0; _argumentsActualDisp = 0; _variablesBaseReg = RegIndex.Invalid; _variablesBaseOffset = 0; _variablesActualDisp = 0; _memUsed = null; _memFree = null; _mem4BlocksCount = 0; _mem8BlocksCount = 0; _mem16BlocksCount = 0; _memBytesTotal = 0; _backCode.Clear(); _backPos = 0; }
internal void AllocatedXMMRegister(RegIndex index) { _state.UsedXMM |= RegisterMask.FromIndex(index); _modifiedXMMRegisters |= RegisterMask.FromIndex(index); }
private CallingConventionInfo(CallingConvention callingConvention) { _callingConvention = callingConvention; _argumentsDirection = ArgumentsDirection.RightToLeft; RegIndex[] argumentsGPList = new RegIndex[(int)RegNum.GP]; RegIndex[] argumentsXMMList = new RegIndex[(int)RegNum.XMM]; for (int i = 0; i < argumentsGPList.Length; i++) argumentsGPList[i] = RegIndex.Invalid; for (int i = 0; i < argumentsXMMList.Length; i++) argumentsXMMList[i] = RegIndex.Invalid; if (Util.IsX86) { _preservedGP = RegisterMask.FromIndex(RegIndex.Ebx) | RegisterMask.FromIndex(RegIndex.Esp) | RegisterMask.FromIndex(RegIndex.Ebp) | RegisterMask.FromIndex(RegIndex.Esi) | RegisterMask.FromIndex(RegIndex.Edi); _preservedXMM = RegisterMask.Zero; switch (_callingConvention) { case NAsmJit.CallingConvention.Cdecl: _calleePopsStack = false; break; case NAsmJit.CallingConvention.StdCall: _calleePopsStack = true; break; case NAsmJit.CallingConvention.MsThisCall: _calleePopsStack = true; argumentsGPList[0] = RegIndex.Ecx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Ecx); break; case NAsmJit.CallingConvention.MsFastCall: _calleePopsStack = true; argumentsGPList[0] = RegIndex.Ecx; argumentsGPList[1] = RegIndex.Edx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Ecx) | RegisterMask.FromIndex(RegIndex.Edx); break; case NAsmJit.CallingConvention.BorlandFastCall: _calleePopsStack = true; _argumentsDirection = ArgumentsDirection.LeftToRight; argumentsGPList[0] = RegIndex.Eax; argumentsGPList[1] = RegIndex.Edx; argumentsGPList[2] = RegIndex.Ecx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Edx) | RegisterMask.FromIndex(RegIndex.Ecx); break; case NAsmJit.CallingConvention.GccFastCall: _calleePopsStack = true; argumentsGPList[0] = RegIndex.Ecx; argumentsGPList[1] = RegIndex.Edx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Ecx) | RegisterMask.FromIndex(RegIndex.Edx); break; case NAsmJit.CallingConvention.GccRegParm1: _calleePopsStack = false; argumentsGPList[0] = RegIndex.Eax; _argumentsGP = RegisterMask.FromIndex(RegIndex.Eax); break; case NAsmJit.CallingConvention.GccRegParm2: _calleePopsStack = false; argumentsGPList[0] = RegIndex.Eax; argumentsGPList[1] = RegIndex.Edx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Edx); break; case NAsmJit.CallingConvention.GccRegParm3: _calleePopsStack = false; argumentsGPList[0] = RegIndex.Eax; argumentsGPList[1] = RegIndex.Edx; argumentsGPList[2] = RegIndex.Ecx; _argumentsGP = RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Edx) | RegisterMask.FromIndex(RegIndex.Ecx); break; default: throw new NotSupportedException(string.Format("The calling convention '{0}' is not supported on this platform.", callingConvention)); } } else if (Util.IsX64) { switch (_callingConvention) { case CallingConvention.X64W: argumentsGPList[0] = RegIndex.Rcx; argumentsGPList[1] = RegIndex.Rdx; argumentsGPList[2] = RegIndex.R8; argumentsGPList[3] = RegIndex.R9; argumentsXMMList[0] = RegIndex.Xmm0; argumentsXMMList[1] = RegIndex.Xmm1; argumentsXMMList[2] = RegIndex.Xmm2; argumentsXMMList[3] = RegIndex.Xmm3; _argumentsGP = RegisterMask.FromIndex(RegIndex.Rcx) | RegisterMask.FromIndex(RegIndex.Rdx) | RegisterMask.FromIndex(RegIndex.R8) | RegisterMask.FromIndex(RegIndex.R9); _argumentsXMM = RegisterMask.FromIndex(RegIndex.Xmm0) | RegisterMask.FromIndex(RegIndex.Xmm1) | RegisterMask.FromIndex(RegIndex.Xmm2) | RegisterMask.FromIndex(RegIndex.Xmm3); _preservedGP = RegisterMask.FromIndex(RegIndex.Rbx) | RegisterMask.FromIndex(RegIndex.Rsp) | RegisterMask.FromIndex(RegIndex.Rbp) | RegisterMask.FromIndex(RegIndex.Rsi) | RegisterMask.FromIndex(RegIndex.Rdi) | RegisterMask.FromIndex(RegIndex.R12) | RegisterMask.FromIndex(RegIndex.R13) | RegisterMask.FromIndex(RegIndex.R14) | RegisterMask.FromIndex(RegIndex.R15); _preservedXMM = RegisterMask.FromIndex(RegIndex.Xmm6) | RegisterMask.FromIndex(RegIndex.Xmm7) | RegisterMask.FromIndex(RegIndex.Xmm8) | RegisterMask.FromIndex(RegIndex.Xmm9) | RegisterMask.FromIndex(RegIndex.Xmm10) | RegisterMask.FromIndex(RegIndex.Xmm11) | RegisterMask.FromIndex(RegIndex.Xmm12) | RegisterMask.FromIndex(RegIndex.Xmm13) | RegisterMask.FromIndex(RegIndex.Xmm14) | RegisterMask.FromIndex(RegIndex.Xmm15); break; case CallingConvention.X64U: argumentsGPList[0] = RegIndex.Rdi; argumentsGPList[1] = RegIndex.Rsi; argumentsGPList[2] = RegIndex.Rdx; argumentsGPList[3] = RegIndex.Rcx; argumentsGPList[4] = RegIndex.R8; argumentsGPList[5] = RegIndex.R9; argumentsXMMList[0] = RegIndex.Xmm0; argumentsXMMList[1] = RegIndex.Xmm1; argumentsXMMList[2] = RegIndex.Xmm2; argumentsXMMList[3] = RegIndex.Xmm3; argumentsXMMList[4] = RegIndex.Xmm4; argumentsXMMList[5] = RegIndex.Xmm5; argumentsXMMList[6] = RegIndex.Xmm6; argumentsXMMList[7] = RegIndex.Xmm7; _argumentsGP = RegisterMask.FromIndex(RegIndex.Rdi) | RegisterMask.FromIndex(RegIndex.Rsi) | RegisterMask.FromIndex(RegIndex.Rdx) | RegisterMask.FromIndex(RegIndex.Rcx) | RegisterMask.FromIndex(RegIndex.R8) | RegisterMask.FromIndex(RegIndex.R9); _argumentsXMM = RegisterMask.FromIndex(RegIndex.Xmm0) | RegisterMask.FromIndex(RegIndex.Xmm1) | RegisterMask.FromIndex(RegIndex.Xmm2) | RegisterMask.FromIndex(RegIndex.Xmm3) | RegisterMask.FromIndex(RegIndex.Xmm4) | RegisterMask.FromIndex(RegIndex.Xmm5) | RegisterMask.FromIndex(RegIndex.Xmm6) | RegisterMask.FromIndex(RegIndex.Xmm7); _preservedGP = RegisterMask.FromIndex(RegIndex.Rbx) | RegisterMask.FromIndex(RegIndex.Rsp) | RegisterMask.FromIndex(RegIndex.Rbp) | RegisterMask.FromIndex(RegIndex.R12) | RegisterMask.FromIndex(RegIndex.R13) | RegisterMask.FromIndex(RegIndex.R14) | RegisterMask.FromIndex(RegIndex.R15); break; default: throw new NotSupportedException(string.Format("The calling convention '{0}' is not supported on this platform.", callingConvention)); } } else { throw new NotImplementedException(); } _argumentsGPList = new ReadOnlyCollection<RegIndex>(argumentsGPList); _argumentsXMMList = new ReadOnlyCollection<RegIndex>(argumentsXMMList); }
protected override void PrepareImpl(CompilerContext cc) { // Prepare is similar to EInstruction::prepare(). We collect unique variables // and update statistics, but we don't use standard alloc/free register calls. // // The calling function is also unique in variable allocator point of view, // because we need to alloc some variables that may be destroyed be the // callee (okay, may not, but this is not guaranteed). Offset = cc.CurrentOffset; // Tell EFunction that another function will be called inside. It needs this // information to reserve stack for the call and to mark esp adjustable. Caller.ReserveStackForFunctionCall(Declaration.ArgumentsStackSize); int i; int argumentsCount = Declaration.Arguments.Length; int operandsCount = argumentsCount; int variablesCount = 0; // Create registers used as arguments mask. for (i = 0; i < argumentsCount; i++) { FunctionDeclaration.Argument fArg = Declaration.Arguments[i]; if (fArg._registerIndex != RegIndex.Invalid) { switch (fArg._variableType) { case VariableType.GPD: case VariableType.GPQ: _gpParams |= RegisterMask.FromIndex(fArg._registerIndex); break; case VariableType.MM: _mmParams |= RegisterMask.FromIndex(fArg._registerIndex); break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: _xmmParams |= RegisterMask.FromIndex(fArg._registerIndex); break; default: throw new CompilerException("Invalid variable type in function call argument."); } } else { cc.Function.IsEspAdjusted = true; } } // Call address. operandsCount++; // The first return value. if (_ret[0] != null && !_ret[0].IsNone) operandsCount++; // The second return value. if (_ret[1] != null && !_ret[1].IsNone) operandsCount++; for (i = 0; i < operandsCount; i++) { Operand o = (i < argumentsCount) ? (_args[i]) : (i == argumentsCount ? _target : _ret[i - argumentsCount - 1]); if (o.IsVar) { if (o.Id == InvalidValue) throw new CompilerException(); CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); if (vdata.WorkOffset == Offset) continue; if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } else if (o.IsMem) { if ((o.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); cc.MarkMemoryUsed(vdata); if (!cc.IsActive(vdata)) cc.AddActive(vdata); continue; } else if (((int)((Mem)o).Base & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)((Mem)o).Base); Contract.Assert(vdata != null); if (vdata.WorkOffset == Offset) continue; if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } if (((int)((Mem)o).Index & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)((Mem)o).Index); Contract.Assert(vdata != null); if (vdata.WorkOffset == Offset) continue; if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } } else { if (o != _target) throw new NotImplementedException(); } } _variables = new VarCallRecord[variablesCount]; // Traverse all active variables and set their firstCallable pointer to this // call. This information can be used to choose between the preserved-first // and preserved-last register allocation. if (cc.Active != null) { CompilerVar first = cc.Active; CompilerVar active = first; do { if (active.FirstCallable == null) active.FirstCallable = this; active = active.NextActive; } while (active != first); } if (variablesCount == 0) { cc.CurrentOffset++; return; } for (int j = 0; j < variablesCount; j++) _variables[j] = new VarCallRecord(); VarCallRecord var = null; int curIndex = 0; int varIndex = -1; Action<CompilerVar> __GET_VARIABLE = __vardata__ => { CompilerVar _candidate = __vardata__; for (varIndex = curIndex; ; ) { if (varIndex == 0) { varIndex = curIndex++; _variables[varIndex].vdata = _candidate; break; } varIndex--; if (_variables[varIndex].vdata == _candidate) break; } var = _variables[varIndex]; Contract.Assert(var != null); }; for (i = 0; i < operandsCount; i++) { Operand o = (i < argumentsCount) ? (_args[i]) : (i == argumentsCount ? _target : _ret[i - argumentsCount - 1]); if (o.IsVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); __GET_VARIABLE(vdata); _argumentToVarRecord[i] = var; if (i < argumentsCount) { FunctionDeclaration.Argument fArg = Declaration.Arguments[i]; if (fArg._registerIndex != RegIndex.Invalid) { cc.NewRegisterHomeIndex(vdata, fArg._registerIndex); switch (fArg._variableType) { case VariableType.GPD: case VariableType.GPQ: var.Flags |= VarCallFlags.IN_GP; var.InCount++; break; case VariableType.MM: var.Flags |= VarCallFlags.IN_MM; var.InCount++; break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: var.Flags |= VarCallFlags.IN_XMM; var.InCount++; break; default: throw new CompilerException("Invalid variable type in function call argument."); } } else { var.InCount++; } vdata.RegisterReadCount++; } else if (i == argumentsCount) { RegisterMask mask = ~Declaration.PreservedGP & ~Declaration.PassedGP & (RegisterMask.MaskToIndex(RegNum.GP) & ~RegisterMask.FromIndex(RegIndex.Eax)); cc.NewRegisterHomeIndex(vdata, mask.FirstRegister); cc.NewRegisterHomeMask(vdata, mask); var.Flags |= VarCallFlags.CALL_OPERAND_REG; vdata.RegisterReadCount++; } else { switch (vdata.Type) { case VariableType.GPD: case VariableType.GPQ: if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_EAX; else var.Flags |= VarCallFlags.OUT_EDX; break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: if (Util.IsX86) { if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_ST0; else var.Flags |= VarCallFlags.OUT_ST1; } else if (Util.IsX64) { if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_XMM0; else var.Flags |= VarCallFlags.OUT_XMM1; } else { throw new NotImplementedException(); } break; case VariableType.MM: var.Flags |= VarCallFlags.OUT_MM0; break; case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_XMM0; else var.Flags |= VarCallFlags.OUT_XMM1; break; case VariableType.XMM_1F: case VariableType.XMM_1D: if (Util.IsX86) { if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_ST0; else var.Flags |= VarCallFlags.OUT_ST1; } else if (Util.IsX64) { if (i == argumentsCount + 1) var.Flags |= VarCallFlags.OUT_XMM0; else var.Flags |= VarCallFlags.OUT_XMM1; } else { throw new NotImplementedException(); } break; default: throw new CompilerException("Invalid variable type in function call argument."); } vdata.RegisterWriteCount++; } } else if (o.IsMem) { if (i != argumentsCount) throw new CompilerException(); if ((o.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); vdata.MemoryReadCount++; } else if (((int)((Mem)o).Base & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)((Mem)o).Base); Contract.Assert(vdata != null); vdata.RegisterReadCount++; __GET_VARIABLE(vdata); var.Flags |= VarCallFlags.CALL_OPERAND_REG | VarCallFlags.CALL_OPERAND_MEM; } if (((int)((Mem)o).Index & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)((Mem)o).Index); Contract.Assert(vdata != null); vdata.RegisterReadCount++; __GET_VARIABLE(vdata); var.Flags |= VarCallFlags.CALL_OPERAND_REG | VarCallFlags.CALL_OPERAND_MEM; } } else { if (o != _target) throw new NotImplementedException(); } } // Traverse all variables and update firstItem / lastItem. This // function is called from iterator that scans items using forward // direction so we can use this knowledge to optimize the process. // // Same code is in EInstruction::prepare(). for (i = 0; i < _variables.Length; i++) { CompilerVar v = _variables[i].vdata; // First item (begin of variable scope). if (v.FirstItem == null) v.FirstItem = this; // Last item (end of variable scope). v.LastItem = this; } cc.CurrentOffset++; }
private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value) { ref RegisterMask curValue = ref masks[blkIndex];
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode) { // Compute local register inputs and outputs used inside blocks. RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count]; for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { for (Operation node = block.Operations.First; node != default; node = node.ListNext) { for (int index = 0; index < node.SourcesCount; index++) { Operand source = node.GetSource(index); if (source.Kind == OperandKind.Register) { Register register = source.GetRegister(); localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index]; } } if (node.Destination != default && node.Destination.Kind == OperandKind.Register) { localOutputs[block.Index] |= GetMask(node.Destination.GetRegister()); } } } // Compute global register inputs and outputs used across blocks. RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count]; bool modified; bool firstPass = true; do { modified = false; // Compute register outputs. for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; if (block.Predecessors.Count != 0 && !HasContextLoad(block)) { BasicBlock predecessor = block.Predecessors[0]; RegisterMask cmnOutputs = localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index]; RegisterMask outputs = globalOutputs[predecessor.Index]; for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++) { predecessor = block.Predecessors[pIndex]; cmnOutputs &= localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index]; outputs |= globalOutputs[predecessor.Index]; } globalInputs[block.Index] |= outputs & ~cmnOutputs; if (!firstPass) { cmnOutputs &= globalCmnOutputs[block.Index]; } modified |= Exchange(globalCmnOutputs, block.Index, cmnOutputs); outputs |= localOutputs[block.Index]; modified |= Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs); } else { modified |= Exchange(globalOutputs, block.Index, localOutputs[block.Index]); } } // Compute register inputs. for (int index = 0; index < cfg.PostOrderBlocks.Length; index++) { BasicBlock block = cfg.PostOrderBlocks[index]; RegisterMask inputs = localInputs[block.Index]; for (int i = 0; i < block.SuccessorsCount; i++) { inputs |= globalInputs[block.GetSuccessor(i).Index]; } inputs &= ~globalCmnOutputs[block.Index]; modified |= Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs); } firstPass = false; }while (modified); // Insert load and store context instructions where needed. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { bool hasContextLoad = HasContextLoad(block); if (hasContextLoad) { block.Operations.Remove(block.Operations.First); } Operand arg = default; // The only block without any predecessor should be the entry block. // It always needs a context load as it is the first block to run. if (block.Predecessors.Count == 0 || hasContextLoad) { arg = Local(OperandType.I64); Operation loadArg = block.Operations.AddFirst(Operation(Instruction.LoadArgument, arg, Const(0))); LoadLocals(block, globalInputs[block.Index].VecMask, RegisterType.Vector, mode, loadArg, arg); LoadLocals(block, globalInputs[block.Index].IntMask, RegisterType.Integer, mode, loadArg, arg); } bool hasContextStore = HasContextStore(block); if (hasContextStore) { block.Operations.Remove(block.Operations.Last); } if (EndsWithReturn(block) || hasContextStore) { if (arg == default) { arg = Local(OperandType.I64); block.Append(Operation(Instruction.LoadArgument, arg, Const(0))); } StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer, mode, arg); StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector, mode, arg); } } }
public void AllocGPVar(CompilerVar var, RegisterMask regMask, VariableAlloc variableAlloc) { Contract.Requires(var != null); Contract.Requires(Function != null); RegisterMask fullMask = RegisterMask.MaskToIndex(RegNum.GP) & ~RegisterMask.FromIndex(RegIndex.Esp); if (!_allocableEBP) fullMask &= ~RegisterMask.FromIndex(RegIndex.Ebp); // Fix the regMask (0 or full bit-array means that any register may be used). if (regMask == RegisterMask.Zero) regMask = RegisterMask.MaskToIndex(RegNum.GP); regMask &= fullMask; int i; RegisterMask mask; // Last register code (aka home). RegIndex home = var.HomeRegisterIndex; // New register code. RegIndex idx = RegIndex.Invalid; // Preserved GP variables. RegisterMask preservedGP = var.Scope.Declaration.PreservedGP; // Spill candidate. CompilerVar spillCandidate = null; // spill caused by direct jump to L_Spill bool doSpill = false; // Whether to alloc the non-preserved variables first. bool nonPreservedFirst = true; if (Function.IsCaller) { nonPreservedFirst = var.FirstCallable == null || var.FirstCallable.Offset >= var.LastItem.Offset; } // -------------------------------------------------------------------------- // [Already Allocated] // -------------------------------------------------------------------------- // Go away if variable is already allocated. if (var.State == VariableState.Register) { RegIndex oldIndex = var.RegisterIndex; // Already allocated in the right register. if ((RegisterMask.FromIndex(oldIndex) & regMask) != RegisterMask.Zero) return; // Try to find unallocated register first. mask = regMask & ~_state.UsedGP; if (mask != RegisterMask.Zero) { idx = ((nonPreservedFirst && (mask & ~preservedGP) != RegisterMask.Zero) ? mask & ~preservedGP : mask).FirstRegister; } // Then find the allocated and exchange later. else { idx = (regMask & _state.UsedGP).FirstRegister; } Contract.Assert(idx != RegIndex.Invalid); CompilerVar other = _state.GP[(int)idx]; EmitExchangeVar(var, idx, variableAlloc, other); _state.GP[(int)oldIndex] = other; _state.GP[(int)idx] = var; if (other != null) other.RegisterIndex = oldIndex; else FreedGPRegister(oldIndex); // Update VarData. var.State = VariableState.Register; var.RegisterIndex = idx; var.HomeRegisterIndex = idx; AllocatedGPRegister(idx); return; } // -------------------------------------------------------------------------- // [Find Unused GP] // -------------------------------------------------------------------------- // Home register code. if (idx == RegIndex.Invalid && home != RegIndex.Invalid && (regMask & RegisterMask.FromIndex(home)) != RegisterMask.Zero && (State.UsedGP & RegisterMask.FromIndex(home)) == RegisterMask.Zero) { idx = home; goto _Alloc; } // We start from 1, because EAX/RAX register is sometimes explicitly // needed. So we trying to prevent reallocation in near future. if (idx == RegIndex.Invalid) { for (i = 1, mask = RegisterMask.FromIndex((RegIndex)i); i < (int)RegNum.GP; i++, mask = RegisterMask.FromIndex((RegIndex)i)) { if ((regMask & mask) != RegisterMask.Zero && (_state.UsedGP & mask) == RegisterMask.Zero) { // Convenience to alloc non-preserved first or non-preserved last. if (nonPreservedFirst) { if (idx != RegIndex.Invalid && (preservedGP & mask) != RegisterMask.Zero) continue; idx = (RegIndex)i; // If current register is preserved, we should try to find different // one that is not. This can save one push / pop in prolog / epilog. if ((preservedGP & mask) == RegisterMask.Zero) break; } else { if (idx != RegIndex.Invalid && (preservedGP & mask) == RegisterMask.Zero) continue; idx = (RegIndex)i; // The opposite. if ((preservedGP & mask) != RegisterMask.Zero) break; } } } } // If not found, try EAX/RAX. if (idx == RegIndex.Invalid && (regMask & RegisterMask.FromIndex(RegIndex.Eax)) != RegisterMask.Zero && (_state.UsedGP & RegisterMask.FromIndex(RegIndex.Eax)) == RegisterMask.Zero) { idx = RegIndex.Eax; goto _Alloc; } // If regMask contains restricted registers which may be used then everything // is handled inside this block. if (idx == RegIndex.Invalid && regMask != fullMask) { // Try to find unallocated register first. mask = regMask & ~_state.UsedGP; if (mask != RegisterMask.Zero) { idx = ((nonPreservedFirst && (mask & ~preservedGP) != RegisterMask.Zero) ? (mask & ~preservedGP) : mask).FirstRegister; Contract.Assert(idx != RegIndex.Invalid); } // Then find the allocated and spill later. else { idx = (regMask & _state.UsedGP).FirstRegister; Contract.Assert(idx != RegIndex.Invalid); // Spill register we need. spillCandidate = _state.GP[(int)idx]; // Jump to spill part of allocation. doSpill = true; goto L_Spill; } } // -------------------------------------------------------------------------- // [Spill] // -------------------------------------------------------------------------- // If register is still not found, spill other variable. if (idx == RegIndex.Invalid) { if (spillCandidate == null) { spillCandidate = GetSpillCandidateGP(); } // Spill candidate not found? if (spillCandidate == null) { throw new CompilerException("Not enough registers."); } } L_Spill: if (idx == RegIndex.Invalid || doSpill) { // Prevented variables can't be spilled. _getSpillCandidate() never returns // prevented variables, but when jumping to L_Spill it can happen. if (spillCandidate.WorkOffset == _currentOffset) { throw new CompilerException("Registers overlap."); } idx = spillCandidate.RegisterIndex; SpillGPVar(spillCandidate); } // -------------------------------------------------------------------------- // [Alloc] // -------------------------------------------------------------------------- _Alloc: if (var.State == VariableState.Memory && (variableAlloc & VariableAlloc.Read) != 0) { EmitLoadVar(var, idx); } // Update VarData. var.State = VariableState.Register; var.RegisterIndex = idx; var.HomeRegisterIndex = idx; // Update StateData. AllocatedVariable(var); }
public static void RunPass(ControlFlowGraph cfg, bool isCompleteFunction) { // Compute local register inputs and outputs used inside blocks. RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count]; for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { for (Node node = block.Operations.First; node != null; node = node.ListNext) { Operation operation = node as Operation; for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) { Operand source = operation.GetSource(srcIndex); if (source.Kind != OperandKind.Register) { continue; } Register register = source.GetRegister(); localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index]; } if (operation.Destination != null && operation.Destination.Kind == OperandKind.Register) { localOutputs[block.Index] |= GetMask(operation.Destination.GetRegister()); } } } // Compute global register inputs and outputs used across blocks. RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count]; RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count]; bool modified; bool firstPass = true; do { modified = false; // Compute register outputs. for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; if (block.Predecessors.Count != 0 && !HasContextLoad(block)) { BasicBlock predecessor = block.Predecessors[0]; RegisterMask cmnOutputs = localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index]; RegisterMask outputs = globalOutputs[predecessor.Index]; for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++) { predecessor = block.Predecessors[pIndex]; cmnOutputs &= localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index]; outputs |= globalOutputs[predecessor.Index]; } globalInputs[block.Index] |= outputs & ~cmnOutputs; if (!firstPass) { cmnOutputs &= globalCmnOutputs[block.Index]; } if (Exchange(globalCmnOutputs, block.Index, cmnOutputs)) { modified = true; } outputs |= localOutputs[block.Index]; if (Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs)) { modified = true; } } else if (Exchange(globalOutputs, block.Index, localOutputs[block.Index])) { modified = true; } } // Compute register inputs. for (int index = 0; index < cfg.PostOrderBlocks.Length; index++) { BasicBlock block = cfg.PostOrderBlocks[index]; RegisterMask inputs = localInputs[block.Index]; if (block.Next != null) { inputs |= globalInputs[block.Next.Index]; } if (block.Branch != null) { inputs |= globalInputs[block.Branch.Index]; } inputs &= ~globalCmnOutputs[block.Index]; if (Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs)) { modified = true; } } firstPass = false; }while (modified); // Insert load and store context instructions where needed. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { bool hasContextLoad = HasContextLoad(block); if (hasContextLoad) { block.Operations.Remove(block.Operations.First); } // The only block without any predecessor should be the entry block. // It always needs a context load as it is the first block to run. if (block.Predecessors.Count == 0 || hasContextLoad) { LoadLocals(block, globalInputs[block.Index].VecMask, RegisterType.Vector); LoadLocals(block, globalInputs[block.Index].IntMask, RegisterType.Integer); } bool hasContextStore = HasContextStore(block); if (hasContextStore) { block.Operations.Remove(block.Operations.Last); } if (EndsWithReturn(block) || hasContextStore) { StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer, isCompleteFunction); StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector, isCompleteFunction); } } }
public void AllocVar(CompilerVar var, RegisterMask regMask, VariableAlloc variableAlloc) { Contract.Requires(var != null); switch (var.Type) { case VariableType.GPD: case VariableType.GPQ: if (var.Type == VariableType.GPQ && !Util.IsX64) throw new NotSupportedException(); AllocGPVar(var, regMask, variableAlloc); break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: // TODO: X87 VARIABLES NOT IMPLEMENTED. break; case VariableType.MM: AllocMMVar(var, regMask, variableAlloc); break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: AllocXMMVar(var, regMask, variableAlloc); break; } PostAlloc(var, variableAlloc); }