protected override void PrepareImpl(CompilerContext cc) { Offset = cc.CurrentOffset; InstructionDescription id = InstructionDescription.FromInstruction(_code); int i; int len = _operands.Length; int variablesCount = 0; for (i = 0; i < len; i++) { Operand o = _operands[i]; BaseVar vo = o as BaseVar; if (vo != null) { if (o.Id == InvalidValue) throw new CompilerException(); CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); if (vo.IsGPVar) { if (((GPVar)vo).IsGPBLo) { _isGPBLoUsed = true; vdata.RegisterGPBLoCount++; } if (((GPVar)vo).IsGPBHi) { _isGPBHiUsed = true; vdata.RegisterGPBHiCount++; } } if (vdata.WorkOffset != Offset) { if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } } else { Mem mem = o as Mem; if (mem != null) { if ((o.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); cc.MarkMemoryUsed(vdata); if (vdata.WorkOffset != Offset) { if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } } else if (((int)mem.Base & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)mem.Base); Contract.Assert(vdata != null); if (vdata.WorkOffset != Offset) { if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } } if (((int)mem.Index & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)mem.Index); Contract.Assert(vdata != null); if (vdata.WorkOffset != Offset) { if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; variablesCount++; } } } } } if (variablesCount == 0) { cc.CurrentOffset++; return; } _variables = new VarAllocRecord[variablesCount]; for (int j = 0; j < variablesCount; j++) _variables[j] = new VarAllocRecord(); int curIndex = 0; VarAllocRecord var = null; int varIndex = -1; Action<CompilerVar> __GET_VARIABLE = __vardata__ => { CompilerVar candidate = __vardata__; for (varIndex = curIndex; ; ) { if (varIndex == 0) { varIndex = curIndex++; _variables[varIndex].VarData = candidate; _variables[varIndex].VarFlags = 0; _variables[varIndex].RegMask = RegisterMask.All; break; } varIndex--; if (_variables[varIndex].VarData == candidate) break; } var = _variables[varIndex]; if (var == null) throw new CompilerException(); }; bool _isGPBUsed = _isGPBLoUsed | _isGPBHiUsed; RegisterMask gpRestrictMask = RegisterMask.MaskToIndex(RegNum.GP); if (_isGPBHiUsed && Util.IsX64) { gpRestrictMask &= RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Ebx) | RegisterMask.FromIndex(RegIndex.Ecx) | RegisterMask.FromIndex(RegIndex.Edx) | RegisterMask.FromIndex(RegIndex.Ebp) | RegisterMask.FromIndex(RegIndex.Esi) | RegisterMask.FromIndex(RegIndex.Edi); } for (i = 0; i < len; i++) { Operand o = _operands[i]; if (o.IsVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); __GET_VARIABLE(vdata); var.VarFlags |= VariableAlloc.Register; if (_isGPBUsed) { if (Util.IsX64) { if (((GPVar)o).IsGPB) { var.RegMask &= RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Ebx) | RegisterMask.FromIndex(RegIndex.Ecx) | RegisterMask.FromIndex(RegIndex.Edx); } } else { // Restrict all BYTE registers to RAX/RBX/RCX/RDX if HI BYTE register // is used (REX prefix makes HI BYTE addressing unencodable). if (_isGPBHiUsed) { if (((GPVar)o).IsGPB) { var.RegMask &= RegisterMask.FromIndex(RegIndex.Eax) | RegisterMask.FromIndex(RegIndex.Ebx) | RegisterMask.FromIndex(RegIndex.Ecx) | RegisterMask.FromIndex(RegIndex.Edx); } } } } if (IsSpecial) { // ${SPECIAL_INSTRUCTION_HANDLING_BEGIN} switch (_code) { case InstructionCode.Cpuid: switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ebx); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; case 3: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Cbw: case InstructionCode.Cdqe: case InstructionCode.Cwde: switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Cdq: case InstructionCode.Cqo: case InstructionCode.Cwd: switch (i) { case 0: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Cmpxchg: switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite; break; case 2: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Cmpxchg8b: case InstructionCode.Cmpxchg16b: if (_code == InstructionCode.Cmpxchg16b && !Util.IsX64) throw new NotSupportedException(string.Format("The '{0}' instruction is only supported on X64.", _code)); switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edx); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; case 3: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ebx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Daa: case InstructionCode.Das: if (!Util.IsX86) throw new NotSupportedException(string.Format("The '{0}' instruction is only supported on X86.", _code)); if (i != 0) throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case InstructionCode.Imul: case InstructionCode.Mul: case InstructionCode.Idiv: case InstructionCode.Div: switch (i) { case 0: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edx); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.MovPtr: switch (i) { case 0: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Lahf: if (i != 0) throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case InstructionCode.Sahf: if (i != 0) throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case InstructionCode.Maskmovdqu: case InstructionCode.Maskmovq: switch (i) { case 0: vdata.MemoryReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edi); gpRestrictMask &= ~var.RegMask; break; case 1: case 2: vdata.MemoryReadCount++; var.VarFlags |= VariableAlloc.Read; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Enter: case InstructionCode.Leave: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Ret: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Monitor: case InstructionCode.Mwait: // TODO: MONITOR/MWAIT (COMPILER). throw new NotImplementedException(); case InstructionCode.Pop: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Popad: case InstructionCode.Popfd: case InstructionCode.Popfq: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Push: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Pushad: case InstructionCode.Pushfd: case InstructionCode.Pushfq: // TODO: SPECIAL INSTRUCTION. throw new NotImplementedException(); case InstructionCode.Rcl: case InstructionCode.Rcr: case InstructionCode.Rol: case InstructionCode.Ror: case InstructionCode.Sal: case InstructionCode.Sar: case InstructionCode.Shl: case InstructionCode.Shr: switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Shld: case InstructionCode.Shrd: switch (i) { case 0: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read; break; case 2: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.Rdtsc: case InstructionCode.Rdtscp: switch (i) { case 0: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edx); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 2: if (_code != InstructionCode.Rdtscp) throw new CompilerException(); vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.RepLodsb: case InstructionCode.RepLodsd: case InstructionCode.RepLodsq: case InstructionCode.RepLodsw: switch (i) { case 0: vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Esi); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.RepMovsb: case InstructionCode.RepMovsd: case InstructionCode.RepMovsq: case InstructionCode.RepMovsw: case InstructionCode.RepeCmpsb: case InstructionCode.RepeCmpsd: case InstructionCode.RepeCmpsq: case InstructionCode.RepeCmpsw: case InstructionCode.RepneCmpsb: case InstructionCode.RepneCmpsd: case InstructionCode.RepneCmpsq: case InstructionCode.RepneCmpsw: switch (i) { case 0: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edi); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Esi); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.RepStosb: case InstructionCode.RepStosd: case InstructionCode.RepStosq: case InstructionCode.RepStosw: switch (i) { case 0: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edi); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; case InstructionCode.RepeScasb: case InstructionCode.RepeScasd: case InstructionCode.RepeScasq: case InstructionCode.RepeScasw: case InstructionCode.RepneScasb: case InstructionCode.RepneScasd: case InstructionCode.RepneScasq: case InstructionCode.RepneScasw: switch (i) { case 0: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Edi); gpRestrictMask &= ~var.RegMask; break; case 1: vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Eax); gpRestrictMask &= ~var.RegMask; break; case 2: vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite | VariableAlloc.Special; var.RegMask = RegisterMask.FromIndex(RegIndex.Ecx); gpRestrictMask &= ~var.RegMask; break; default: throw new NotSupportedException(string.Format("The {0} instruction does not support {1} arguments.", InstructionDescription.FromInstruction(_code).Name, i)); } break; default: throw new NotImplementedException(string.Format("Handling for special instruction {0} is not yet implemented.", InstructionDescription.FromInstruction(_code).Name)); } // ${SPECIAL_INSTRUCTION_HANDLING_END} } else { if (i == 0) { // CMP/TEST instruction. if (id.Code == InstructionCode.Cmp || id.Code == InstructionCode.Test) { // Read-only case. vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read; } // CVTTSD2SI/CVTTSS2SI instructions. else if (id.Code == InstructionCode.Cvttsd2si || id.Code == InstructionCode.Cvttss2si) { // In 32-bit mode the whole destination is replaced. In 64-bit mode // we need to check whether the destination operand size is 64-bits. if (Util.IsX86 || _operands[0].IsRegType(RegType.GPQ)) { // Write-only case. vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write; } else if (Util.IsX64) { // Read/Write. vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite; } } // MOV/MOVSS/MOVSD instructions. // // If instruction is MOV (source replaces the destination) or // MOVSS/MOVSD and source operand is memory location then register // allocator should know that previous destination value is lost // (write only operation). else if ((id.IsMov) || ((id.Code == InstructionCode.Movss || id.Code == InstructionCode.Movsd) /* && _operands[1].isMem() */) || (id.Code == InstructionCode.Imul && _operands.Length == 3 && !IsSpecial)) { // Write-only case. vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write; } else if (id.Code == InstructionCode.Lea) { // Write. vdata.RegisterWriteCount++; var.VarFlags |= VariableAlloc.Write; } else { // Read/Write. vdata.RegisterRWCount++; var.VarFlags |= VariableAlloc.ReadWrite; } } else { // Second, third, ... operands are read-only. vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Read; } if (_memoryOperand == null && i < 2 && (id.OperandFlags[i] & OperandFlags.MEM) != 0) { var.VarFlags |= VariableAlloc.Memory; } } // If variable must be in specific register we could add some hint to allocator. if ((var.VarFlags & VariableAlloc.Special) != 0) { vdata.PreferredRegisterMask |= var.RegMask; cc.NewRegisterHomeIndex(vdata, var.RegMask.FirstRegister); } } else if (o.IsMem) { Mem mem = (Mem)o; if ((o.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); __GET_VARIABLE(vdata); if (i == 0) { // If variable is MOV instruction type (source replaces the destination) // or variable is MOVSS/MOVSD instruction then register allocator should // know that previous destination value is lost (write only operation). if (id.IsMov || ((id.Code == InstructionCode.Movss || id.Code == InstructionCode.Movsd))) { // Write only case. vdata.MemoryWriteCount++; } else { vdata.MemoryRWCount++; } } else { vdata.MemoryReadCount++; } } else if (((int)mem.Base & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)mem.Base); Contract.Assert(vdata != null); __GET_VARIABLE(vdata); vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Register | VariableAlloc.Read; gpRestrictMask &= ~var.RegMask; } if (((int)mem.Index & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData((int)mem.Index); Contract.Assert(vdata != null); __GET_VARIABLE(vdata); vdata.RegisterReadCount++; var.VarFlags |= VariableAlloc.Register | VariableAlloc.Read; gpRestrictMask &= ~var.RegMask; } } } // 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. // // Similar to ECall::prepare(). for (i = 0; i < _variables.Length; i++) { CompilerVar v = _variables[i].VarData; // Update GP register allocator restrictions. if (VariableInfo.IsVariableInteger(v.Type)) { if (_variables[i].RegMask == RegisterMask.All) _variables[i].RegMask &= gpRestrictMask; } // Update first/last item (begin of variable scope). if (v.FirstItem == null) v.FirstItem = this; v.LastItem = this; } // There are some instructions that can be used to clear or to set all bits // in a register: // // - andn reg, reg ; Set all bits in reg to 0. // - xor/pxor reg, reg ; Set all bits in reg to 0. // - sub/psub reg, reg ; Set all bits in reg to 0. // - pcmpgt reg, reg ; Set all bits in reg to 0. // - pcmpeq reg, reg ; Set all bits in reg to 1. // // There are also combinations which do nothing: // // - and reg, reg ; Nop. // - or reg, reg ; Nop. // - xchg reg, reg ; Nop. if (_variables.Length == 1 && _operands.Length > 1 && _operands[0].IsVar && _operands[1].IsVar && _memoryOperand == null) { switch ((InstructionCode)_code) { // ---------------------------------------------------------------------- // [Zeros/Ones] // ---------------------------------------------------------------------- // ANDN Instructions. case InstructionCode.Pandn: // XOR Instructions. case InstructionCode.Xor: case InstructionCode.Xorpd: case InstructionCode.Xorps: case InstructionCode.Pxor: // SUB Instructions. case InstructionCode.Sub: case InstructionCode.Psubb: case InstructionCode.Psubw: case InstructionCode.Psubd: case InstructionCode.Psubq: case InstructionCode.Psubsb: case InstructionCode.Psubsw: case InstructionCode.Psubusb: case InstructionCode.Psubusw: // PCMPEQ Instructions. case InstructionCode.Pcmpeqb: case InstructionCode.Pcmpeqw: case InstructionCode.Pcmpeqd: case InstructionCode.Pcmpeqq: // PCMPGT Instructions. case InstructionCode.Pcmpgtb: case InstructionCode.Pcmpgtw: case InstructionCode.Pcmpgtd: case InstructionCode.Pcmpgtq: // Clear the read flag. This prevents variable alloc/spill. _variables[0].VarFlags = VariableAlloc.Write; _variables[0].VarData.RegisterReadCount--; break; // ---------------------------------------------------------------------- // [Nop] // ---------------------------------------------------------------------- // AND Instructions. case InstructionCode.And: case InstructionCode.Andpd: case InstructionCode.Andps: case InstructionCode.Pand: // OR Instructions. case InstructionCode.Or: case InstructionCode.Orpd: case InstructionCode.Orps: case InstructionCode.Por: // XCHG Instruction. case InstructionCode.Xchg: // Clear the write flag. _variables[0].VarFlags = VariableAlloc.Read; _variables[0].VarData.RegisterWriteCount--; break; } } cc.CurrentOffset++; }
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++; }
protected override void PrepareImpl(CompilerContext cc) { Offset = cc.CurrentOffset; // First item (begin of variable scope). if (_varData.FirstItem == null) _varData.FirstItem = this; CompilerItem oldLast = _varData.LastItem; // Last item (end of variable scope). _varData.LastItem = this; switch (_hintKind) { case VariableHintKind.Alloc: case VariableHintKind.Spill: case VariableHintKind.Save: if (!cc.IsActive(_varData)) cc.AddActive(_varData); break; case VariableHintKind.SaveAndUnuse: if (!cc.IsActive(_varData)) cc.AddActive(_varData); break; case VariableHintKind.Unuse: if (oldLast != null) oldLast.TryUnuseVar(_varData); break; } }
protected override void PrepareImpl(CompilerContext cc) { Offset = cc.CurrentOffset; VariableType retValType = Function.Declaration.ReturnValue; if (retValType != VariableType.Invalid) { for (int i = 0; i < 2; i++) { Operand o = (i == 0 ? _first : _second); if (o == null) continue; if (o.IsVar) { if (o.Id == Operand.InvalidValue) throw new CompilerException(); CompilerVar vdata = Compiler.GetVarData(o.Id); Contract.Assert(vdata != null); // First item (begin of variable scope). if (vdata.FirstItem == null) vdata.FirstItem = this; // Last item (end of variable scope). vdata.LastItem = this; if (vdata.WorkOffset == Offset) continue; if (!cc.IsActive(vdata)) cc.AddActive(vdata); vdata.WorkOffset = Offset; vdata.RegisterReadCount++; if (VariableInfo.IsVariableInteger(vdata.Type) && VariableInfo.IsVariableInteger(retValType)) { cc.NewRegisterHomeIndex(vdata, (i == 0) ? RegIndex.Eax : RegIndex.Edx); } } } } cc.CurrentOffset++; }