internal void DoJump(CompilerContext cc) { Contract.Requires(cc != null); // The state have to be already known. The _doJump() method is called by // translate() or by Compiler in case that it's forward jump. if (_jumpTarget.State == null) throw new CompilerException("Cannot jump to a target without knowing its state."); if ((Code == InstructionCode.Jmp) || (IsTaken && _jumpTarget.Offset < Offset)) { // Instruction type is JMP or conditional jump that should be taken (likely). // We can set state here instead of jumping out, setting state and jumping // to _jumpTarget. // // NOTE: We can't use this technique if instruction is forward conditional // jump. The reason is that when generating code we can't change state here, // because the next instruction depends on it. cc.RestoreState(_jumpTarget.State, _jumpTarget.Offset); } else { // Instruction type is JMP or conditional jump that should be not normally // taken. If we need add code that will switch between different states we // add it after the end of function body (after epilog, using 'ExtraBlock'). Compiler compiler = cc.Compiler; CompilerItem ext = cc.ExtraBlock; CompilerItem old = compiler.CurrentItem; compiler.CurrentItem = ext; cc.RestoreState(_jumpTarget.State, _jumpTarget.Offset); if (compiler.CurrentItem != ext) { // Add the jump to the target. compiler.Jmp(_jumpTarget.Label); ext = compiler.CurrentItem; // The cc._restoreState() method emitted some instructions so we need to // patch the jump. Label L = compiler.DefineLabel(); compiler.CurrentItem = cc.ExtraBlock; compiler.MarkLabel(L); // Finally, patch the jump target. if (Operands.Length == 0) throw new CompilerException(); Operands[0] = L; // Operand part (Label). _jumpTarget = compiler.GetTarget(L.Id); // Item part (ETarget). } cc.ExtraBlock = ext; compiler.CurrentItem = old; // Assign state back. cc.AssignState(_state); } }
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; } }
private RegIndex FindTemporaryGpRegister(CompilerContext cc, bool spillIfNecessary) { RegisterMask passedGP = Declaration.PassedGP; RegIndex candidate = RegIndex.Invalid; RegisterMask reserved = RegisterMask.FromIndex(RegIndex.Esp); if (!cc.AllocableEbp) reserved |= RegisterMask.FromIndex(RegIndex.Ebp); // Find all registers used to pass function arguments. We shouldn't use these // if possible. for (int i = 0; i < (int)RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((reserved & mask).RegisterCount != 0) continue; if (cc.State.GP[i] == null) { // If this register is used to pass arguments to function, we will mark // it and use it only if there is no other one. if ((passedGP & mask) != RegisterMask.Zero) candidate = (RegIndex)i; else return (RegIndex)i; } } if (candidate == RegIndex.Invalid && spillIfNecessary) { for (int i = 0; i < (int)RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((reserved & mask).RegisterCount != 0) continue; candidate = (RegIndex)i; if ((passedGP & mask) == RegisterMask.Zero) break; // If this register is used to pass arguments to function, we will mark // it and use it only if there is no other one. if ((passedGP & mask) != RegisterMask.Zero) candidate = (RegIndex)i; else return (RegIndex)i; } Contract.Assert(cc.State.GP[(int)candidate] != null); cc.SpillGPVar(cc.State.GP[(int)candidate]); } return candidate; }
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) { Offset = cc.CurrentOffset; // Update _isTaken to true if this is conditional backward jump. This behavior // can be overridden by using HINT_NOT_TAKEN when using the instruction. if ((Code != InstructionCode.Jmp) && Operands.Length == 1 && _jumpTarget.Offset < Offset) { _isTaken = true; } // Now patch all variables where jump location is in the active range. if (_jumpTarget.Offset != InvalidValue && cc.Active != null) { CompilerVar first = cc.Active; CompilerVar var = first; int jumpOffset = _jumpTarget.Offset; do { if (var.FirstItem != null) { if (var.LastItem == null) throw new CompilerException(); int start = var.FirstItem.Offset; int end = var.LastItem.Offset; if (jumpOffset >= start && jumpOffset <= end) var.LastItem = this; } var = var.NextActive; } while (var != first); } cc.CurrentOffset++; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { switch (_hintKind) { case VariableHintKind.Alloc: cc.AllocVar(_varData, new RegisterMask(_hintValue), VariableAlloc.Read); break; case VariableHintKind.Spill: if (_varData.State == VariableState.Register) cc.SpillVar(_varData); break; case VariableHintKind.Save: case VariableHintKind.SaveAndUnuse: if (_varData.State == VariableState.Register && _varData.Changed) { cc.EmitSaveVar(_varData, _varData.RegisterIndex); _varData.Changed = false; } if (_hintKind == VariableHintKind.SaveAndUnuse) goto case VariableHintKind.Unuse; break; case VariableHintKind.Unuse: cc.UnuseVar(_varData, VariableState.Unused); goto end; } cc.UnuseVarOnEndOfScope(this, _varData); end: return Next; }
protected override void PrepareImpl(CompilerContext cc) { Offset = cc.CurrentOffset++; PrepareVariables(this); }
internal void EmitProlog(CompilerContext cc) { RegisterMask preservedGP = _modifiedAndPreservedGP; RegisterMask preservedMM = _modifiedAndPreservedMM; RegisterMask preservedXMM = _modifiedAndPreservedXMM; int stackOffset = RequiredStackOffset; int stackPos; // -------------------------------------------------------------------------- // [Prolog] // -------------------------------------------------------------------------- if (Compiler.Logger != null) Compiler.Comment("Prolog"); // Emit standard prolog entry code (but don't do it if function is set to be // naked). // // Also see the _prologEpilogStackAdjust variable. If function is naked (so // prolog and epilog will not contain "push ebp" and "mov ebp, esp", we need // to adjust stack by 8 bytes in 64-bit mode (this will give us that stack // will remain aligned to 16 bytes). if (!_isNaked) { Compiler.Emit(InstructionCode.Push, Register.nbp); Compiler.Emit(InstructionCode.Mov, Register.nbp, Register.nsp); } // Align manually stack-pointer to 16-bytes. if (_isPerformed16ByteAlignment) { if (_isNaked) throw new CompilerException(); Compiler.Emit(InstructionCode.And, Register.nsp, (Imm)(-16)); } // -------------------------------------------------------------------------- // [Save Gp - Push/Pop] // -------------------------------------------------------------------------- if (preservedGP != RegisterMask.Zero && _pePushPop) { for (int i = 0; i < RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedGP & mask) != RegisterMask.Zero) Compiler.Emit(InstructionCode.Push, Register.gpn((RegIndex)i)); } } // -------------------------------------------------------------------------- // [Adjust Scack] // -------------------------------------------------------------------------- if (_isEspAdjusted) { stackPos = _memStackSize16 + _functionCallStackSize; if (stackOffset != 0) Compiler.Emit(InstructionCode.Sub, Register.nsp, (Imm)stackOffset); } else { stackPos = -(_peMovStackSize + _peAdjustStackSize); //if (_pePushPop) stackPos += bitCount(preservedGP) * sizeof(sysint_t); } // -------------------------------------------------------------------------- // [Save Xmm - MovDqa/MovDqu] // -------------------------------------------------------------------------- if (preservedXMM != RegisterMask.Zero) { for (int i = 0; i < RegNum.XMM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedXMM & mask) != RegisterMask.Zero) { Compiler.Emit(_movDqInstruction, Mem.dqword_ptr(Register.nsp, stackPos), Register.xmm((RegIndex)i)); stackPos += 16; } } } // -------------------------------------------------------------------------- // [Save Mm - MovQ] // -------------------------------------------------------------------------- if (preservedMM != RegisterMask.Zero) { for (int i = 0; i < 8; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedMM & mask) != RegisterMask.Zero) { Compiler.Emit(InstructionCode.Movq, Mem.qword_ptr(Register.nsp, stackPos), Register.mm((RegIndex)i)); stackPos += 8; } } } // -------------------------------------------------------------------------- // [Save Gp - Mov] // -------------------------------------------------------------------------- if (preservedGP != RegisterMask.Zero && !_pePushPop) { for (int i = 0; i < RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedGP & mask) != RegisterMask.Zero) { Compiler.Emit(InstructionCode.Mov, Mem.sysint_ptr(Register.nsp, stackPos), Register.gpn((RegIndex)i)); stackPos += IntPtr.Size; } } } // -------------------------------------------------------------------------- // [...] // -------------------------------------------------------------------------- if (Compiler.Logger != null) Compiler.Comment("Body"); }
protected override CompilerItem TranslateImpl(CompilerContext cc) { RegisterMask preserved; RegIndex temporaryGpReg; RegIndex temporaryXmmReg; int offset = cc.CurrentOffset; Compiler compiler = cc.Compiler; // Constants. FunctionDeclaration.Argument[] targs = Declaration.Arguments; int argumentsCount = Declaration.Arguments.Length; int variablesCount = _variables.Length; // Processed arguments. bool[] processed = new bool[FUNC_MAX_ARGS]; compiler.Comment("Call"); // These variables are used by the instruction so we set current offset // to their work offsets -> The SpillCandidate method never returns // the variable used by this instruction. for (int i = 0; i < variablesCount; i++) { _variables[i].vdata.WorkOffset = offset; // Init back-reference to VarCallRecord. _variables[i].vdata.Temp = _variables[i]; } // -------------------------------------------------------------------------- // STEP 1: // // Spill variables which are not used by the function call and have to // be destroyed. These registers may be used by callee. // -------------------------------------------------------------------------- preserved = Declaration.PreservedGP; for (int i = 0; i < RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.GP[i]; if (vdata != null && vdata.WorkOffset != offset && (preserved & mask) == RegisterMask.Zero) cc.SpillGPVar(vdata); } preserved = Declaration.PreservedMM; for (int i = 0; i < RegNum.MM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.MM[i]; if (vdata != null && vdata.WorkOffset != offset && (preserved & mask) == RegisterMask.Zero) cc.SpillMMVar(vdata); } preserved = Declaration.PreservedXMM; for (int i = 0; i < RegNum.XMM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.XMM[i]; if (vdata != null && vdata.WorkOffset != offset && (preserved & mask) == RegisterMask.Zero) cc.SpillXMMVar(vdata); } // -------------------------------------------------------------------------- // STEP 2: // // Move all arguments to the stack which are already in registers. // -------------------------------------------------------------------------- for (int i = 0; i < argumentsCount; i++) { if (processed[i]) continue; FunctionDeclaration.Argument argType = targs[i]; if (argType._registerIndex != RegIndex.Invalid) continue; Operand operand = _args[i]; if (operand.IsVar) { VarCallRecord rec = _argumentToVarRecord[i]; CompilerVar vdata = compiler.GetVarData(operand.Id); Contract.Assert(vdata != null); if (vdata.RegisterIndex != RegIndex.Invalid) { MoveAllocatedVariableToStack(cc, vdata, argType); rec.InDone++; processed[i] = true; } } } // -------------------------------------------------------------------------- // STEP 3: // // Spill all non-preserved variables we moved to stack in STEP #2. // -------------------------------------------------------------------------- for (int i = 0; i < argumentsCount; i++) { VarCallRecord rec = _argumentToVarRecord[i]; if (rec == null || processed[i]) continue; if (rec.InDone >= rec.InCount) { CompilerVar vdata = rec.vdata; if (vdata.RegisterIndex == RegIndex.Invalid) continue; if (rec.OutCount != 0) { // Variable will be rewritten by function return value, it's not needed // to spill it. It will be allocated again by ECall. cc.UnuseVar(rec.vdata, VariableState.Unused); } else { switch (vdata.Type) { case VariableType.GPD: case VariableType.GPQ: if ((Declaration.PreservedGP & RegisterMask.FromIndex(vdata.RegisterIndex)) == RegisterMask.Zero) cc.SpillGPVar(vdata); break; case VariableType.MM: if ((Declaration.PreservedMM & RegisterMask.FromIndex(vdata.RegisterIndex)) == RegisterMask.Zero) cc.SpillMMVar(vdata); break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_1D: case VariableType.XMM_4F: case VariableType.XMM_2D: if ((Declaration.PreservedXMM & RegisterMask.FromIndex(vdata.RegisterIndex)) == RegisterMask.Zero) cc.SpillXMMVar(vdata); break; default: throw new CompilerException(); } } } } // -------------------------------------------------------------------------- // STEP 4: // // Get temporary register that we can use to pass input function arguments. // Now it's safe to do, because the non-needed variables should be spilled. // -------------------------------------------------------------------------- temporaryGpReg = FindTemporaryGpRegister(cc, true); temporaryXmmReg = FindTemporaryXmmRegister(cc); // If failed to get temporary register then we need just to pick one. if (temporaryGpReg == RegIndex.Invalid) { throw new NotImplementedException(); } if (temporaryXmmReg == RegIndex.Invalid) { // TODO. throw new NotImplementedException(); } // -------------------------------------------------------------------------- // STEP 5: // // Move all remaining arguments to the stack (we can use temporary register). // or allocate it to the primary register. Also move immediates. // -------------------------------------------------------------------------- for (int i = 0; i < argumentsCount; i++) { if (processed[i]) continue; FunctionDeclaration.Argument argType = targs[i]; if (argType._registerIndex != RegIndex.Invalid) continue; Operand operand = _args[i]; if (operand.IsVar) { VarCallRecord rec = _argumentToVarRecord[i]; CompilerVar vdata = compiler.GetVarData(operand.Id); Contract.Assert(vdata != null); MoveSpilledVariableToStack(cc, vdata, argType, temporaryGpReg, temporaryXmmReg); rec.InDone++; processed[i] = true; } else if (operand.IsImm) { Mem dst; switch (argType._variableType) { case VariableType.GPD: dst = Mem.dword_ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); break; case VariableType.GPQ: dst = Mem.qword_ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); break; case VariableType.X87: case VariableType.X87_1D: case VariableType.X87_1F: throw new NotImplementedException(); case VariableType.MM: dst = Mem.mmword_ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); break; case VariableType.XMM: case VariableType.XMM_1D: case VariableType.XMM_1F: case VariableType.XMM_2D: case VariableType.XMM_4F: dst = Mem.xmmword_ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); break; default: throw new NotImplementedException(); } compiler.Mov(dst, (Imm)operand); } } // -------------------------------------------------------------------------- // STEP 6: // // Allocate arguments to registers. // -------------------------------------------------------------------------- bool didWork; do { didWork = false; for (int i = 0; i < argumentsCount; i++) { if (processed[i]) continue; VarCallRecord rsrc = _argumentToVarRecord[i]; Operand osrc = _args[i]; if (!osrc.IsVar) throw new CompilerException(); CompilerVar vsrc = compiler.GetVarData(osrc.Id); Contract.Assert(vsrc != null); FunctionDeclaration.Argument srcArgType = targs[i]; CompilerVar vdst = GetOverlappingVariable(cc, srcArgType); if (vsrc == vdst) { rsrc.InDone++; processed[i] = true; didWork = true; continue; } else if (vdst != null) { VarCallRecord rdst = (VarCallRecord)(vdst.Temp); if (rdst == null) { cc.SpillVar(vdst); vdst = null; } else if (rdst.InDone >= rdst.InCount && (rdst.Flags & VarCallFlags.CALL_OPERAND_REG) == 0) { // Safe to spill. if (rdst.OutCount != 0 || vdst.LastItem == this) cc.UnuseVar(vdst, VariableState.Unused); else cc.SpillVar(vdst); vdst = null; } else { int x = Declaration.FindArgumentByRegisterCode(VariableInfo.GetVariableRegisterCode(vsrc.Type, vsrc.RegisterIndex)); bool doSpill = true; if ((VariableInfo.GetVariableClass(vdst.Type) & VariableClass.GP) != 0) { // Try to emit mov to register which is possible for call() operand. if (x == InvalidValue && (rdst.Flags & VarCallFlags.CALL_OPERAND_REG) != 0) { int rIndex; // The mask which contains registers which are not-preserved // (these that might be clobbered by the callee) and which are // not used to pass function arguments. Each register contained // in this mask is ideal to be used by call() instruction. RegisterMask possibleMask = ~Declaration.PreservedGP & ~Declaration.PassedGP & (RegisterMask.MaskToIndex(RegNum.GP) & ~RegisterMask.FromIndex(RegIndex.Eax)); if (possibleMask != RegisterMask.Zero) { for (rIndex = 0; rIndex < RegNum.GP; rIndex++) { RegisterMask rBit = RegisterMask.FromIndex((RegIndex)rIndex); if ((possibleMask & rBit) != RegisterMask.Zero) { if (cc.State.GP[rIndex] == null) { // This is the best possible solution, the register is // free. We do not need to continue with this loop, the // rIndex will be used by the call(). break; } else { // Wait until the register is freed or try to find another. doSpill = false; didWork = true; } } } } else { // Try to find a register which is free and which is not used // to pass a function argument. possibleMask = Declaration.PreservedGP; for (rIndex = 0; rIndex < RegNum.GP; rIndex++) { RegisterMask rBit = RegisterMask.FromIndex((RegIndex)rIndex); if ((possibleMask & rBit) != RegisterMask.Zero) { // Found one. if (cc.State.GP[rIndex] == null) break; } } } if (rIndex < RegNum.GP) { if (temporaryGpReg == vsrc.RegisterIndex) temporaryGpReg = (RegIndex)rIndex; compiler.Emit(InstructionCode.Mov, Register.gpn((RegIndex)rIndex), Register.gpn(vsrc.RegisterIndex)); cc.State.GP[(int)vsrc.RegisterIndex] = null; cc.State.GP[rIndex] = vsrc; vsrc.RegisterIndex = (RegIndex)rIndex; cc.AllocatedGPRegister((RegIndex)rIndex); doSpill = false; didWork = true; } } // Emit xchg instead of spill/alloc if possible. else if (x != InvalidValue) { FunctionDeclaration.Argument dstArgType = targs[x]; if (VariableInfo.GetVariableInfo(dstArgType._variableType).Class == VariableInfo.GetVariableInfo(srcArgType._variableType).Class) { RegIndex dstIndex = vdst.RegisterIndex; RegIndex srcIndex = vsrc.RegisterIndex; if (srcIndex == dstArgType._registerIndex) { compiler.Emit(InstructionCode.Xchg, Register.gpn(dstIndex), Register.gpd(srcIndex)); if (Util.IsX64) { if (vdst.Type != VariableType.GPD || vsrc.Type != VariableType.GPD) compiler.Emit(InstructionCode.Xchg, Register.gpq(dstIndex), Register.gpq(srcIndex)); else compiler.Emit(InstructionCode.Xchg, Register.gpd(dstIndex), Register.gpd(srcIndex)); } else { compiler.Emit(InstructionCode.Xchg, Register.gpd(dstIndex), Register.gpd(srcIndex)); } cc.State.GP[(int)srcIndex] = vdst; cc.State.GP[(int)dstIndex] = vsrc; vdst.RegisterIndex = srcIndex; vsrc.RegisterIndex = dstIndex; rdst.InDone++; rsrc.InDone++; processed[i] = true; processed[x] = true; doSpill = false; } } } } if (doSpill) { cc.SpillVar(vdst); vdst = null; } } } if (vdst == null) { VarCallRecord rec = (VarCallRecord)(vsrc.Temp); MoveSrcVariableToRegister(cc, vsrc, srcArgType); switch (srcArgType._variableType) { case VariableType.GPD: case VariableType.GPQ: cc.MarkGPRegisterModified(srcArgType._registerIndex); break; case VariableType.MM: cc.MarkMMRegisterModified(srcArgType._registerIndex); break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_1D: case VariableType.XMM_4F: case VariableType.XMM_2D: cc.MarkXMMRegisterModified(srcArgType._registerIndex); break; } rec.InDone++; processed[i] = true; } } } while (didWork); // -------------------------------------------------------------------------- // STEP 7: // // Allocate operand used by CALL instruction. // -------------------------------------------------------------------------- for (int i = 0; i < variablesCount; i++) { VarCallRecord r = _variables[i]; if ((r.Flags & VarCallFlags.CALL_OPERAND_REG) != 0 && (r.vdata.RegisterIndex == RegIndex.Invalid)) { // If the register is not allocated and the call form is 'call reg' then // it's possible to keep it in memory. if ((r.Flags & VarCallFlags.CALL_OPERAND_MEM) == 0) { _target = GPVar.FromData(r.vdata).ToMem(); break; } if (temporaryGpReg == RegIndex.Invalid) temporaryGpReg = FindTemporaryGpRegister(cc, true); cc.AllocGPVar(r.vdata, RegisterMask.FromIndex(temporaryGpReg), VariableAlloc.Register | VariableAlloc.Read); } } { Operand[] operands = { _target }; cc.TranslateOperands(operands); _target = operands[0]; } // -------------------------------------------------------------------------- // STEP 8: // // Spill all preserved variables. // -------------------------------------------------------------------------- preserved = Declaration.PreservedGP; for (int i = 0; i < (int)RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.GP[i]; if (vdata != null && (preserved & mask) == RegisterMask.Zero) { VarCallRecord rec = (VarCallRecord)(vdata.Temp); if (rec != null && (rec.OutCount != 0 || (rec.Flags & VarCallFlags.UnuseAfterUse) != 0 || vdata.LastItem == this)) cc.UnuseVar(vdata, VariableState.Unused); else cc.SpillGPVar(vdata); } } preserved = Declaration.PreservedMM; for (int i = 0; i < (int)RegNum.MM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.MM[i]; if (vdata != null && (preserved & mask) == RegisterMask.Zero) { VarCallRecord rec = (VarCallRecord)(vdata.Temp); if (rec != null && (rec.OutCount != 0 || vdata.LastItem == this)) cc.UnuseVar(vdata, VariableState.Unused); else cc.SpillMMVar(vdata); } } preserved = Declaration.PreservedXMM; for (int i = 0; i < (int)RegNum.XMM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); CompilerVar vdata = cc.State.XMM[i]; if (vdata != null && (preserved & mask) == RegisterMask.Zero) { VarCallRecord rec = (VarCallRecord)(vdata.Temp); if (rec != null && (rec.OutCount != 0 || vdata.LastItem == this)) cc.UnuseVar(vdata, VariableState.Unused); else cc.SpillXMMVar(vdata); } } // -------------------------------------------------------------------------- // STEP 9: // // Emit CALL instruction. // -------------------------------------------------------------------------- compiler.Emit(InstructionCode.Call, _target); // Restore the stack offset. if (Declaration.CalleePopsStack) { int s = Declaration.ArgumentsStackSize; if (s != 0) compiler.Emit(InstructionCode.Sub, Register.nsp, (Imm)s); } // -------------------------------------------------------------------------- // STEP 10: // // Prepare others for return value(s) and cleanup. // -------------------------------------------------------------------------- // Clear temp data, see AsmJit::VarData::temp why it's needed. for (int i = 0; i < variablesCount; i++) { VarCallRecord rec = _variables[i]; CompilerVar vdata = rec.vdata; if ((rec.Flags & (VarCallFlags.OUT_EAX | VarCallFlags.OUT_EDX)) != 0) { if ((VariableInfo.GetVariableInfo(vdata.Type).Class & VariableClass.GP) != 0) { cc.AllocGPVar(vdata, (rec.Flags & VarCallFlags.OUT_EAX) != 0 ? RegisterMask.FromIndex(RegIndex.Eax) : RegisterMask.FromIndex(RegIndex.Edx), VariableAlloc.Register | VariableAlloc.Write); vdata.Changed = true; } } if ((rec.Flags & (VarCallFlags.OUT_MM0)) != 0) { if ((VariableInfo.GetVariableInfo(vdata.Type).Class & VariableClass.MM) != 0) { cc.AllocMMVar(vdata, RegisterMask.FromIndex(RegIndex.Mm0), VariableAlloc.Register | VariableAlloc.Write); vdata.Changed = true; } } if ((rec.Flags & (VarCallFlags.OUT_XMM0 | VarCallFlags.OUT_XMM1)) != 0) { if ((VariableInfo.GetVariableInfo(vdata.Type).Class & VariableClass.XMM) != 0) { cc.AllocXMMVar(vdata, RegisterMask.FromIndex((rec.Flags & VarCallFlags.OUT_XMM0) != 0 ? RegIndex.Xmm0 : RegIndex.Xmm1), VariableAlloc.Register | VariableAlloc.Write); vdata.Changed = true; } } if ((rec.Flags & (VarCallFlags.OUT_ST0 | VarCallFlags.OUT_ST1)) != 0) { if ((VariableInfo.GetVariableInfo(vdata.Type).Class & VariableClass.XMM) != 0) { Mem mem = cc.GetVarMem(vdata); cc.UnuseVar(vdata, VariableState.Memory); switch (vdata.Type) { case VariableType.XMM_1F: case VariableType.XMM_4F: { //mem.Size = 4; if (mem.Size != 4) throw new NotImplementedException("Size is now an immutable property."); compiler.Emit(InstructionCode.Fstp, mem); break; } case VariableType.XMM_1D: case VariableType.XMM_2D: { //mem.Size = 8; if (mem.Size != 4) throw new NotImplementedException("Size is now an immutable property."); compiler.Emit(InstructionCode.Fstp, mem); break; } default: { compiler.Comment("*** WARNING: Can't convert float return value to untyped XMM\n"); break; } } } } // Cleanup. vdata.Temp = null; } for (int i = 0; i < variablesCount; i++) { cc.UnuseVarOnEndOfScope(this, _variables[i]); } return Next; }
private void MoveSrcVariableToRegister(CompilerContext cc, CompilerVar vdata, FunctionDeclaration.Argument argType) { Contract.Requires(cc != null); Contract.Requires(vdata != null); Contract.Requires(argType != null); RegIndex dst = argType._registerIndex; RegIndex src = vdata.RegisterIndex; Compiler compiler = cc.Compiler; if (src != RegIndex.Invalid) { switch (argType._variableType) { case VariableType.GPD: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(dst), Register.gpd(src)); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Mov, Register.gpq(dst), Register.gpq(src)); return; case VariableType.MM: compiler.Emit(InstructionCode.Movd, Register.gpd(dst), Register.mm(src)); return; } break; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(dst), Register.gpd(src)); return; case VariableType.GPQ: compiler.Emit(InstructionCode.Mov, Register.gpq(dst), Register.gpq(src)); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.gpq(dst), Register.mm(src)); return; } break; case VariableType.MM: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Movd, Register.gpd(dst), Register.gpd(src)); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Movq, Register.gpq(dst), Register.gpq(src)); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.mm(dst), Register.mm(src)); return; } break; case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Movd, Register.xmm(dst), Register.gpd(src)); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Movq, Register.xmm(dst), Register.gpq(src)); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), Register.mm(src)); return; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), Register.xmm(src)); return; } break; case VariableType.XMM_1F: switch (vdata.Type) { case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), Register.mm(src)); return; case VariableType.XMM: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), Register.xmm(src)); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Movss, Register.xmm(dst), Register.xmm(src)); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Cvtsd2ss, Register.xmm(dst), Register.xmm(src)); return; } break; case VariableType.XMM_1D: switch (vdata.Type) { case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), Register.mm(src)); return; case VariableType.XMM: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), Register.xmm(src)); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Cvtss2sd, Register.xmm(dst), Register.xmm(src)); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movsd, Register.xmm(dst), Register.xmm(src)); return; } break; } } else { Mem mem = cc.GetVarMem(vdata); switch (argType._variableType) { case VariableType.GPD: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(dst), mem); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Mov, Register.gpq(dst), mem); return; case VariableType.MM: compiler.Emit(InstructionCode.Movd, Register.gpd(dst), mem); return; } break; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(dst), mem); return; case VariableType.GPQ: compiler.Emit(InstructionCode.Mov, Register.gpq(dst), mem); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.gpq(dst), mem); return; } break; case VariableType.MM: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Movd, Register.gpd(dst), mem); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Movq, Register.gpq(dst), mem); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.mm(dst), mem); return; } break; case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: switch (vdata.Type) { case VariableType.GPD: compiler.Emit(InstructionCode.Movd, Register.xmm(dst), mem); return; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Movq, Register.xmm(dst), mem); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), mem); return; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), mem); return; } break; case VariableType.XMM_1F: switch (vdata.Type) { case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), mem); return; case VariableType.XMM: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), mem); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Movss, Register.xmm(dst), mem); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Cvtsd2ss, Register.xmm(dst), mem); return; } break; case VariableType.XMM_1D: switch (vdata.Type) { case VariableType.MM: compiler.Emit(InstructionCode.Movq, Register.xmm(dst), mem); return; case VariableType.XMM: compiler.Emit(InstructionCode.Movdqa, Register.xmm(dst), mem); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Cvtss2sd, Register.xmm(dst), mem); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movsd, Register.xmm(dst), mem); return; } break; } } throw new ArgumentException("Incompatible argument."); }
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 void MoveSpilledVariableToStack(CompilerContext cc, CompilerVar vdata, FunctionDeclaration.Argument argType, RegIndex temporaryGpReg, RegIndex temporaryXmmReg) { Contract.Requires(cc != null); Contract.Requires(vdata != null); Contract.Requires(argType != null); if (argType._registerIndex != RegIndex.Invalid) throw new ArgumentException(); if (vdata.RegisterIndex != RegIndex.Invalid) throw new ArgumentException(); Compiler compiler = cc.Compiler; Mem src = cc.GetVarMem(vdata); Mem dst = Mem.ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); switch (vdata.Type) { case VariableType.GPD: switch (argType._variableType) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(temporaryGpReg), src); compiler.Emit(InstructionCode.Mov, dst, Register.gpd(temporaryGpReg)); return; case VariableType.GPQ: case VariableType.MM: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Mov, Register.gpd(temporaryGpReg), src); compiler.Emit(InstructionCode.Mov, dst, Register.gpq(temporaryGpReg)); return; default: throw new CompilerException(); } case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); switch (argType._variableType) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, Register.gpd(temporaryGpReg), src); compiler.Emit(InstructionCode.Mov, dst, Register.gpd(temporaryGpReg)); return; case VariableType.GPQ: case VariableType.MM: compiler.Emit(InstructionCode.Mov, Register.gpq(temporaryGpReg), src); compiler.Emit(InstructionCode.Mov, dst, Register.gpq(temporaryGpReg)); return; default: throw new CompilerException(); } case VariableType.MM: switch (argType._variableType) { case VariableType.GPD: case VariableType.X87_1F: case VariableType.XMM_1F: compiler.Emit(InstructionCode.Mov, Register.gpd(temporaryGpReg), src); compiler.Emit(InstructionCode.Mov, dst, Register.gpd(temporaryGpReg)); return; case VariableType.GPQ: case VariableType.MM: case VariableType.X87_1D: case VariableType.XMM_1D: // TODO return; default: throw new CompilerException(); } // We allow incompatible types here, because the called can convert them // to correct format before function is called. case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: switch (argType._variableType) { case VariableType.XMM: compiler.Emit(InstructionCode.Movdqu, Register.xmm(temporaryXmmReg), src); compiler.Emit(InstructionCode.Movdqu, dst, Register.xmm(temporaryXmmReg)); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Movups, Register.xmm(temporaryXmmReg), src); compiler.Emit(InstructionCode.Movups, dst, Register.xmm(temporaryXmmReg)); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movupd, Register.xmm(temporaryXmmReg), src); compiler.Emit(InstructionCode.Movupd, dst, Register.xmm(temporaryXmmReg)); return; default: throw new CompilerException(); } case VariableType.XMM_1F: switch (argType._variableType) { case VariableType.X87_1F: case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movss, Register.xmm(temporaryXmmReg), src); compiler.Emit(InstructionCode.Movss, dst, Register.xmm(temporaryXmmReg)); return; default: throw new CompilerException(); } case VariableType.XMM_1D: switch (argType._variableType) { case VariableType.X87_1D: case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movsd, Register.xmm(temporaryXmmReg), src); compiler.Emit(InstructionCode.Movsd, dst, Register.xmm(temporaryXmmReg)); return; default: throw new CompilerException(); } default: throw new CompilerException("Incompatible argument."); } }
private void MoveAllocatedVariableToStack(CompilerContext cc, CompilerVar vdata, FunctionDeclaration.Argument argType) { Contract.Requires(cc != null); Contract.Requires(vdata != null); Contract.Requires(argType != null); if (argType._registerIndex != RegIndex.Invalid) throw new ArgumentException(); if (vdata.RegisterIndex == RegIndex.Invalid) throw new ArgumentException(); Compiler compiler = cc.Compiler; RegIndex src = vdata.RegisterIndex; Mem dst = Mem.ptr(Register.nsp, -IntPtr.Size + argType._stackOffset); switch (vdata.Type) { case VariableType.GPD: switch (argType._variableType) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, dst, Register.gpd(src)); return; case VariableType.GPQ: case VariableType.MM: if (!Util.IsX64) throw new NotSupportedException(); compiler.Emit(InstructionCode.Mov, dst, Register.gpq(src)); return; } break; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); switch (argType._variableType) { case VariableType.GPD: compiler.Emit(InstructionCode.Mov, dst, Register.gpd(src)); return; case VariableType.GPQ: compiler.Emit(InstructionCode.Mov, dst, Register.gpq(src)); return; case VariableType.MM: compiler.Emit(InstructionCode.Movq, dst, Register.gpq(src)); return; } break; case VariableType.MM: switch (argType._variableType) { case VariableType.GPD: case VariableType.X87_1F: case VariableType.XMM_1F: compiler.Emit(InstructionCode.Movd, dst, Register.mm(src)); return; case VariableType.GPQ: case VariableType.MM: case VariableType.X87_1D: case VariableType.XMM_1D: compiler.Emit(InstructionCode.Movq, dst, Register.mm(src)); return; } break; // We allow incompatible types here, because the called can convert them // to correct format before function is called. case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: switch (argType._variableType) { case VariableType.XMM: compiler.Emit(InstructionCode.Movdqu, dst, Register.xmm(src)); return; case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Movups, dst, Register.xmm(src)); return; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movupd, dst, Register.xmm(src)); return; } break; case VariableType.XMM_1F: switch (argType._variableType) { case VariableType.X87_1F: case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movss, dst, Register.xmm(src)); return; } break; case VariableType.XMM_1D: switch (argType._variableType) { case VariableType.X87_1D: case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Movsd, dst, Register.xmm(src)); return; } break; } throw new ArgumentException("Incompatible argument."); }
private CompilerVar GetOverlappingVariable(CompilerContext cc, FunctionDeclaration.Argument argType) { Contract.Requires(cc != null); Contract.Requires(argType != null); if (argType._variableType == VariableType.Invalid) throw new ArgumentException(); switch (argType._variableType) { case VariableType.GPD: case VariableType.GPQ: return cc.State.GP[(int)argType._registerIndex]; case VariableType.MM: return cc.State.MM[(int)argType._registerIndex]; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_1D: case VariableType.XMM_4F: case VariableType.XMM_2D: return cc.State.XMM[(int)argType._registerIndex]; default: throw new CompilerException(); } }
private RegIndex FindTemporaryXmmRegister(CompilerContext cc) { Contract.Requires(cc != null); RegisterMask passedXMM = Declaration.PassedXMM; RegIndex candidate = RegIndex.Invalid; // Find all registers used to pass function arguments. We shouldn't use these // if possible. for (int i = 0; i < RegNum.XMM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if (cc.State.XMM[i] == null) { // If this register is used to pass arguments to function, we will mark // it and use it only if there is no other one. if ((passedXMM & mask) != RegisterMask.Zero) candidate = (RegIndex)i; else return (RegIndex)i; } } return candidate; }
internal void DumpFunction(CompilerContext cc) { Logger logger = Compiler.Logger; if (logger == null) throw new InvalidOperationException("Cannot dump a function without a logger."); int i; StringBuilder buffer = new StringBuilder(); // Log function prototype. { int argumentsCount = _functionPrototype.Arguments.Length; bool first = true; logger.LogString("; Function Prototype:" + Environment.NewLine); logger.LogString(";" + Environment.NewLine); for (i = 0; i < argumentsCount; i++) { FunctionDeclaration.Argument a = _functionPrototype.Arguments[i]; CompilerVar vdata = _argumentVariables[i]; if (first) { logger.LogString("; IDX| Type | Sz | Home |" + Environment.NewLine); logger.LogString("; ---+----------+----+----------------+" + Environment.NewLine); } buffer.Clear(); if (a._registerIndex != RegIndex.Invalid) { var regOp = new GPReg(Register.NativeRegisterType, a._registerIndex); Assembler.DumpOperand(buffer, regOp, Register.NativeRegisterType); } else { Mem memOp = new Mem(); memOp.Base = RegIndex.Esp; memOp.Displacement = (IntPtr)a._stackOffset; Assembler.DumpOperand(buffer, memOp, Register.NativeRegisterType); } // original format string: "; %-3u| %-9s| %-3u| %-15s|\n" logger.LogFormat("; {0,-3}| {1,-9}| {2,-3}| {3,-15}|" + Environment.NewLine, // Argument index. i, // Argument type. (int)vdata.Type < _variableTypeCount ? VariableInfo.GetVariableInfo(vdata.Type).Name : "invalid", // Argument size. vdata.Size, // Argument memory home. buffer ); first = false; } logger.LogString(";" + Environment.NewLine); } // Log variables. { int variablesCount = Compiler.Variables.Count; bool first = true; logger.LogString("; Variables:" + Environment.NewLine); logger.LogString(";" + Environment.NewLine); for (i = 0; i < variablesCount; i++) { CompilerVar vdata = Compiler.GetVarData(i); Contract.Assert(vdata != null); // If this variable is not related to this function then skip it. if (vdata.Scope != this) continue; // Get some information about variable type. VariableInfo vinfo = VariableInfo.GetVariableInfo(vdata.Type); if (first) { logger.LogString("; ID | Type | Sz | Home | Register Access | Memory Access |" + Environment.NewLine); logger.LogString("; ---+----------+----+----------------+-------------------+-------------------+" + Environment.NewLine); } buffer.Clear(); buffer.Append("[None]"); if (vdata.HomeMemoryData != null) { buffer.Clear(); Mem memOp = new Mem(); if (vdata.IsMemArgument) { FunctionDeclaration.Argument a = _functionPrototype.Arguments[i]; memOp.Base = cc.ArgumentsBaseReg; memOp.Displacement += cc.ArgumentsBaseOffset; memOp.Displacement += a._stackOffset; } else { VarMemBlock memBlock = vdata.HomeMemoryData; memOp.Base = cc.VariablesBaseReg; memOp.Displacement += cc.VariablesBaseOffset; memOp.Displacement += memBlock.Offset; } Assembler.DumpOperand(buffer, memOp, Register.NativeRegisterType); } string registerAccess = string.Format("r={0}w={1}x={2}", vdata.RegisterReadCount, vdata.RegisterWriteCount, vdata.RegisterRWCount); string memoryAccess = string.Format("r={0}w={1}x={2}", vdata.MemoryReadCount, vdata.MemoryWriteCount, vdata.MemoryRWCount); logger.LogFormat("; {0,-3}| {1,-9}| {2,-3}| {3,-15}| {4,-18}| {5,-18}|" + Environment.NewLine, // Variable id. (uint)(i & Operand.OperandIdValueMask), // Variable type. (int)vdata.Type < _variableTypeCount ? vinfo.Name : "invalid", // Variable size. vdata.Size, // Variable memory home. buffer, // Register access count. registerAccess, // Memory access count. memoryAccess ); first = false; } logger.LogString(";" + Environment.NewLine); } // Log modified registers. { buffer.Clear(); int r; int modifiedRegisters = 0; for (r = 0; r < 3; r++) { bool first = true; RegisterMask regs; RegType type; switch (r) { case 0: regs = cc.ModifiedGPRegisters; type = Register.NativeRegisterType; buffer.Append("; GP : "); break; case 1: regs = cc.ModifiedMMRegisters; type = RegType.MM; buffer.Append("; MM : "); break; case 2: regs = cc.ModifiedXMMRegisters; type = RegType.XMM; buffer.Append("; XMM: "); break; default: Contract.Assert(false, ""); continue; } for (i = 0; i < RegNum.Base; i++) { if ((regs & RegisterMask.FromIndex((RegIndex)i)) != RegisterMask.Zero) { if (!first) { buffer.Append(','); buffer.Append(' '); } DumpRegister(buffer, type, i); first = false; modifiedRegisters++; } } buffer.AppendLine(); } logger.LogFormat("; Modified registers ({0}):" + Environment.NewLine, modifiedRegisters); logger.LogString(buffer.ToString()); } logger.LogString(Environment.NewLine); }
internal void EmitEpilog(CompilerContext cc) { // -------------------------------------------------------------------------- // [Init] // -------------------------------------------------------------------------- RegisterMask preservedGP = _modifiedAndPreservedGP; RegisterMask preservedMM = _modifiedAndPreservedMM; RegisterMask preservedXMM = _modifiedAndPreservedXMM; int stackOffset = RequiredStackOffset; int stackPos; if (IsEspAdjusted) stackPos = _memStackSize16 + _functionCallStackSize; else stackPos = -(_peMovStackSize + _peAdjustStackSize); // -------------------------------------------------------------------------- // [Epilog] // -------------------------------------------------------------------------- if (Compiler.Logger != null) Compiler.Comment("Epilog"); // -------------------------------------------------------------------------- // [Restore Xmm - MovDqa/ModDqu] // -------------------------------------------------------------------------- if (preservedXMM != RegisterMask.Zero) { for (int i = 0; i < RegNum.XMM; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedXMM & mask) != RegisterMask.Zero) { Compiler.Emit(_movDqInstruction, Register.xmm((RegIndex)i), Mem.dqword_ptr(Register.nsp, stackPos)); stackPos += 16; } } } // -------------------------------------------------------------------------- // [Restore Mm - MovQ] // -------------------------------------------------------------------------- if (preservedMM != RegisterMask.Zero) { for (int i = 0; i < 8; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedMM & mask) != RegisterMask.Zero) { Compiler.Emit(InstructionCode.Movq, Register.mm((RegIndex)i), Mem.qword_ptr(Register.nsp, stackPos)); stackPos += 8; } } } // -------------------------------------------------------------------------- // [Restore Gp - Mov] // -------------------------------------------------------------------------- if (preservedGP != RegisterMask.Zero && !_pePushPop) { for (int i = 0; i < (int)RegNum.GP; i++) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedGP & mask) != RegisterMask.Zero) { Compiler.Emit(InstructionCode.Mov, Register.gpn((RegIndex)i), Mem.sysint_ptr(Register.nsp, stackPos)); stackPos += IntPtr.Size; } } } // -------------------------------------------------------------------------- // [Adjust Stack] // -------------------------------------------------------------------------- if (_isEspAdjusted && stackOffset != 0) Compiler.Emit(InstructionCode.Add, Register.nsp, (Imm)stackOffset); // -------------------------------------------------------------------------- // [Restore Gp - Push/Pop] // -------------------------------------------------------------------------- if (preservedGP != RegisterMask.Zero && _pePushPop) { for (int i = RegNum.GP - 1; i >= 0; i--) { RegisterMask mask = RegisterMask.FromIndex((RegIndex)i); if ((preservedGP & mask) != RegisterMask.Zero) { Compiler.Emit(InstructionCode.Pop, Register.gpn((RegIndex)i)); } } } // -------------------------------------------------------------------------- // [Emms] // -------------------------------------------------------------------------- if (_emitEMMS) Compiler.Emit(InstructionCode.Emms); // -------------------------------------------------------------------------- // [MFence/SFence/LFence] // -------------------------------------------------------------------------- if (_emitSFence && _emitLFence) Compiler.Emit(InstructionCode.Mfence); // MFence == SFence & LFence. else if (_emitSFence) Compiler.Emit(InstructionCode.Sfence); // Only SFence. else if (_emitLFence) Compiler.Emit(InstructionCode.Lfence); // Only LFence. // -------------------------------------------------------------------------- // [Epilog] // -------------------------------------------------------------------------- if (!_isNaked) { CpuInfo cpuInfo = CpuInfo.Instance; // AMD seems to prefer LEAVE instead of MOV/POP sequence. if (cpuInfo.VendorId == CpuVendor.Amd) { Compiler.Emit(InstructionCode.Leave); } else { Compiler.Emit(InstructionCode.Mov, Register.nsp, Register.nbp); Compiler.Emit(InstructionCode.Pop, Register.nbp); } } // Emit return. if (_functionPrototype.CalleePopsStack) { Compiler.Emit(InstructionCode.Ret, (Imm)((short)_functionPrototype.ArgumentsStackSize)); } else { Compiler.Emit(InstructionCode.Ret); } }
protected override void PrepareImpl(CompilerContext cc) { Offset = cc.CurrentOffset++; }
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; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { // If this Target was already translated, it's needed to change the current // state and return null to tell CompilerContext to process next untranslated // item. if (IsTranslated) { cc.RestoreState(_state); return null; } if (cc.Unreachable) { // If the context has "isUnreachable" flag set and there is no state then // it means that this code will be never called. This is a problem, because // we are unable to assign a state to current location so we can't allocate // registers for variables used inside. So instead of doing anything wrong // we remove the unreachable code. if (_state == null) return RemoveUnreachableItems(); // Assign state to the compiler context. cc.Unreachable = false; cc.AssignState(_state); } else { _state = cc.SaveState(); } return Next; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { AllocVariables(cc); return base.TranslateImpl(cc); }
public void Prepare(CompilerContext cc) { Contract.Requires(cc != null); PrepareImpl(cc); }
public CompilerItem Translate(CompilerContext cc) { Contract.Requires(cc != null); CompilerItem next = TranslateImpl(cc); Contract.Assert(!_isTranslated || next == null); _isTranslated = true; return next; }
protected virtual void PrepareImpl(CompilerContext cc) { Contract.Requires(cc != null); _offset = cc.CurrentOffset; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { // translate using Instruction CompilerItem ret = base.TranslateImpl(cc); // we jump with item if it's InstructionCode.JMP (unconditional) and points to yet unknown location. if (Code == InstructionCode.Jmp && !JumpTarget.IsTranslated) { cc.AddBackwardCode(this); ret = JumpTarget; } else { _state = cc.SaveState(); if (JumpTarget.IsTranslated) { DoJump(cc); } else { // state is not known, so we need to call DoJump() later. Compiler will do it for us. cc.AddForwardJump(this); JumpTarget.State = _state; } // Mark next code as unrecheable, cleared by a next label (ETarget). if (Code == InstructionCode.Jmp) { cc.Unreachable = true; } } // Need to traverse over all active variables and unuse them if their scope ends here if (cc.Active != null) { CompilerVar first = cc.Active; CompilerVar var = first; do { cc.UnuseVarOnEndOfScope(this, var); var = var.NextActive; } while (var != first); } return ret; }
protected virtual CompilerItem TranslateImpl(CompilerContext cc) { Contract.Requires(cc != null); return Next; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { int i; int variablesCount = (_variables != null) ? _variables.Length : 0; if (variablesCount > 0) { // These variables are used by the instruction and we set current offset // to their work offsets -> getSpillCandidate never return the variable // used this instruction. for (i = 0; i < variablesCount; i++) { _variables[i].VarData.WorkOffset = cc.CurrentOffset; } // Alloc variables used by the instruction (special first). for (i = 0; i < variablesCount; i++) { VarAllocRecord r = _variables[i]; // Alloc variables with specific register first. if ((r.VarFlags & VariableAlloc.Special) != 0) cc.AllocVar(r.VarData, r.RegMask, r.VarFlags); } for (i = 0; i < variablesCount; i++) { VarAllocRecord r = _variables[i]; // Alloc variables without specific register last. if ((r.VarFlags & VariableAlloc.Special) == 0) cc.AllocVar(r.VarData, r.RegMask, r.VarFlags); } cc.TranslateOperands(_operands); } if (_memoryOperand != null && (_memoryOperand.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar) { CompilerVar vdata = Compiler.GetVarData(_memoryOperand.Id); Contract.Assert(vdata != null); switch (vdata.State) { case VariableState.Unused: vdata.State = VariableState.Memory; break; case VariableState.Register: vdata.Changed = false; cc.UnuseVar(vdata, VariableState.Memory); break; } } for (i = 0; i < variablesCount; i++) { cc.UnuseVarOnEndOfScope(this, _variables[i]); } return Next; }
internal void AllocVariables(CompilerContext cc) { int i; int count = _functionPrototype.Arguments.Length; if (count == 0) return; for (i = 0; i < count; i++) { CompilerVar vdata = _argumentVariables[i]; if (vdata.FirstItem != null || vdata.IsRegArgument || vdata.IsMemArgument) { // Variable is used. if (vdata.RegisterIndex != RegIndex.Invalid) { vdata.State = VariableState.Register; // If variable is in register -> mark it as changed so it will not be // lost by first spill. vdata.Changed = true; cc.AllocatedVariable(vdata); } else if (vdata.IsMemArgument) { vdata.State = VariableState.Memory; } } else { // Variable is not used. vdata.RegisterIndex = RegIndex.Invalid; } } }
protected override CompilerItem TranslateImpl(CompilerContext cc) { return null; }
protected override CompilerItem TranslateImpl(CompilerContext cc) { Compiler compiler = cc.Compiler; Operand[] ret = { _first, _second }; // Check whether the return value is compatible. VariableType retValType = Function.Declaration.ReturnValue; int i; switch (retValType) { case VariableType.GPD: case VariableType.GPQ: for (i = 0; i < 2; i++) { if (ret[i] == null) continue; RegIndex dsti = (i == 0) ? RegIndex.Eax : RegIndex.Edx; RegIndex srci; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsGPVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Mov, Register.gpn(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(InstructionCode.Mov, Register.gpn(dsti), Register.gpn(srci)); } } else if (ret[i].IsImm) { compiler.Emit(InstructionCode.Mov, Register.gpn(dsti), ret[i]); } } break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: // There is case that we need to return two values (Unix-ABI specific): // - FLD #2 //- FLD #1 i = 2; do { i--; int dsti = i; RegIndex srci; if (ret[i] == null) continue; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsX87Var) { // TODO: X87. throw new NotImplementedException("X87 support is not yet implemented."); } else if (((BaseVar)ret[i]).IsXMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; if (srci != RegIndex.Invalid) cc.SaveVar(vdata); switch (vdata.Type) { case VariableType.XMM_1F: case VariableType.XMM_4F: compiler.Emit(InstructionCode.Fld, BaseVarMem(((BaseVar)ret[i]), 4)); break; case VariableType.XMM_1D: case VariableType.XMM_2D: compiler.Emit(InstructionCode.Fld, BaseVarMem(((BaseVar)ret[i]), 8)); break; } } } } while (i != 0); break; case VariableType.MM: for (i = 0; i < 2; i++) { RegIndex dsti = (RegIndex)i; RegIndex srci; if (ret[i] == null) continue; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsGPVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; InstructionCode inst = ret[i].IsRegType(RegType.GPQ) ? InstructionCode.Movq : InstructionCode.Movd; if (srci == RegIndex.Invalid) { compiler.Emit(inst, Register.mm(dsti), cc.GetVarMem(vdata)); } else { if (Util.IsX86) compiler.Emit(inst, Register.mm(dsti), Register.gpd(srci)); else if (Util.IsX64) compiler.Emit(inst, Register.mm(dsti), ret[i].IsRegType(RegType.GPQ) ? Register.gpq(srci) : Register.gpd(srci)); else throw new NotImplementedException(); } } else if (((BaseVar)ret[i]).IsMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; InstructionCode inst = InstructionCode.Movq; if (srci == RegIndex.Invalid) compiler.Emit(inst, Register.mm(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(inst, Register.mm(dsti), Register.mm(srci)); } else if (((BaseVar)ret[i]).IsXMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; InstructionCode inst = InstructionCode.Movq; if (((BaseVar)ret[i]).VariableType == VariableType.XMM_1F) inst = InstructionCode.Movd; if (srci == RegIndex.Invalid) compiler.Emit(inst, Register.mm(dsti), cc.GetVarMem(vdata)); else compiler.Emit(inst, Register.mm(dsti), Register.xmm(srci)); } } } break; case VariableType.XMM: case VariableType.XMM_4F: case VariableType.XMM_2D: for (i = 0; i < 2; i++) { RegIndex dsti = (RegIndex)i; RegIndex srci; if (ret[i] == null) continue; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsGPVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; InstructionCode inst = ret[i].IsRegType(RegType.GPQ) ? InstructionCode.Movq : InstructionCode.Movd; if (srci == RegIndex.Invalid) { compiler.Emit(inst, Register.xmm(dsti), cc.GetVarMem(vdata)); } else { if (Util.IsX86) compiler.Emit(inst, Register.xmm(dsti), Register.gpd(srci)); else if (Util.IsX64) compiler.Emit(inst, Register.xmm(dsti), ret[i].IsRegType(RegType.GPQ) ? Register.gpq(srci) : Register.gpd(srci)); else throw new NotImplementedException(); } } else if (((BaseVar)ret[i]).IsX87Var) { // TODO: X87. throw new NotImplementedException("X87 support is not yet implemented."); } else if (((BaseVar)ret[i]).IsMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movq, Register.xmm(dsti), cc.GetVarMem(vdata)); else compiler.Emit(InstructionCode.Movq, Register.xmm(dsti), Register.mm(srci)); } else if (((BaseVar)ret[i]).IsXMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), Register.xmm(srci)); } } } break; case VariableType.XMM_1F: for (i = 0; i < 2; i++) { RegIndex dsti = (RegIndex)i; RegIndex srci; if (ret[i] == null) continue; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsX87Var) { // TODO: X87. } else if (((BaseVar)ret[i]).IsXMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; switch (vdata.Type) { case VariableType.XMM: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), Register.xmm(srci)); break; case VariableType.XMM_1F: case VariableType.XMM_4F: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movss, Register.xmm(dsti), cc.GetVarMem(vdata)); else compiler.Emit(InstructionCode.Movss, Register.xmm(dsti), Register.xmm(srci)); break; case VariableType.XMM_1D: case VariableType.XMM_2D: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Cvtsd2ss, Register.xmm(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(InstructionCode.Cvtsd2ss, Register.xmm(dsti), Register.xmm(srci)); break; } } } } break; case VariableType.XMM_1D: for (i = 0; i < 2; i++) { RegIndex dsti = (RegIndex)i; RegIndex srci; if (ret[i] == null) continue; if (ret[i].IsVar) { if (((BaseVar)ret[i]).IsX87Var) { // TODO: X87. } else if (((BaseVar)ret[i]).IsXMMVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); srci = vdata.RegisterIndex; switch (vdata.Type) { case VariableType.XMM: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), cc.GetVarMem(vdata)); else if (dsti != srci) compiler.Emit(InstructionCode.Movdqa, Register.xmm(dsti), Register.xmm(srci)); break; case VariableType.XMM_1F: case VariableType.XMM_4F: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Cvtss2sd, Register.xmm(dsti), cc.GetVarMem(vdata)); else compiler.Emit(InstructionCode.Cvtss2sd, Register.xmm(dsti), Register.xmm(srci)); break; case VariableType.XMM_1D: case VariableType.XMM_2D: if (srci == RegIndex.Invalid) compiler.Emit(InstructionCode.Movsd, Register.xmm(dsti), cc.GetVarMem(vdata)); else compiler.Emit(InstructionCode.Movsd, Register.xmm(dsti), Register.xmm(srci)); break; } } } } break; case VariableType.Invalid: default: break; } if (ShouldEmitJumpToEpilog()) { cc.Unreachable = true; } for (i = 0; i < 2; i++) { if (ret[i] != null && ret[i].IsVar) { CompilerVar vdata = compiler.GetVarData(ret[i].Id); Contract.Assert(vdata != null); cc.UnuseVarOnEndOfScope(this, vdata); } } return Next; }