public void EmitMoveVar(CompilerVar var, RegIndex regIndex, VariableAlloc vflags) { Contract.Requires(var != null); if (var.RegisterIndex == RegIndex.Invalid) throw new ArgumentException("Caller must ensure that variable is allocated."); if ((vflags & VariableAlloc.Read) == 0) return; switch (var.Type) { case VariableType.GPD: _compiler.Emit(InstructionCode.Mov, Register.gpd(regIndex), Register.gpd(var.RegisterIndex)); break; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); _compiler.Emit(InstructionCode.Mov, Register.gpq(regIndex), Register.gpq(var.RegisterIndex)); break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: // TODO: X87 VARIABLES NOT IMPLEMENTED. throw new NotImplementedException("X87 variables are not yet implemented."); case VariableType.MM: _compiler.Emit(InstructionCode.Movq, Register.mm(regIndex), Register.mm(var.RegisterIndex)); break; case VariableType.XMM: _compiler.Emit(InstructionCode.Movdqa, Register.xmm(regIndex), Register.xmm(var.RegisterIndex)); break; case VariableType.XMM_1F: _compiler.Emit(InstructionCode.Movss, Register.xmm(regIndex), Register.xmm(var.RegisterIndex)); break; case VariableType.XMM_1D: _compiler.Emit(InstructionCode.Movsd, Register.xmm(regIndex), Register.xmm(var.RegisterIndex)); break; case VariableType.XMM_4F: _compiler.Emit(InstructionCode.Movaps, Register.xmm(regIndex), Register.xmm(var.RegisterIndex)); break; case VariableType.XMM_2D: _compiler.Emit(InstructionCode.Movapd, Register.xmm(regIndex), Register.xmm(var.RegisterIndex)); break; default: throw new CompilerException("Invalid variable type."); } }
public void EmitExchangeVar(CompilerVar var, RegIndex regIndex, VariableAlloc vflags, CompilerVar other) { Contract.Requires(var != null); Contract.Requires(other != null); if (var.RegisterIndex == RegIndex.Invalid) throw new CompilerException("Caller must ensure that variable is allocated."); // If other is not valid then we can just emit MOV (or other similar instruction). if (other == null) { EmitMoveVar(var, regIndex, vflags); return; } // If we need to alloc for write-only operation then we can move other // variable away instead of exchanging them. if ((vflags & VariableAlloc.Read) == 0) { EmitMoveVar(other, var.RegisterIndex, VariableAlloc.Read); return; } switch (var.Type) { case VariableType.GPD: _compiler.Emit(InstructionCode.Xchg, Register.gpd(regIndex), Register.gpd(var.RegisterIndex)); break; case VariableType.GPQ: if (!Util.IsX64) throw new NotSupportedException(); _compiler.Emit(InstructionCode.Xchg, Register.gpq(regIndex), Register.gpq(var.RegisterIndex)); break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: // TODO: X87 VARIABLES NOT IMPLEMENTED. break; // NOTE: MM and XMM registers shoudln't be exchanged using this way, it's // correct, but it sucks. case VariableType.MM: { MMReg a = Register.mm(regIndex); MMReg b = Register.mm(var.RegisterIndex); _compiler.Emit(InstructionCode.Pxor, a, b); _compiler.Emit(InstructionCode.Pxor, b, a); _compiler.Emit(InstructionCode.Pxor, a, b); break; } case VariableType.XMM_1F: case VariableType.XMM_4F: { XMMReg a = Register.xmm(regIndex); XMMReg b = Register.xmm(var.RegisterIndex); _compiler.Emit(InstructionCode.Xorps, a, b); _compiler.Emit(InstructionCode.Xorps, b, a); _compiler.Emit(InstructionCode.Xorps, a, b); break; } case VariableType.XMM_1D: case VariableType.XMM_2D: { XMMReg a = Register.xmm(regIndex); XMMReg b = Register.xmm(var.RegisterIndex); _compiler.Emit(InstructionCode.Xorpd, a, b); _compiler.Emit(InstructionCode.Xorpd, b, a); _compiler.Emit(InstructionCode.Xorpd, a, b); break; } case VariableType.XMM: { XMMReg a = Register.xmm(regIndex); XMMReg b = Register.xmm(var.RegisterIndex); _compiler.Emit(InstructionCode.Pxor, a, b); _compiler.Emit(InstructionCode.Pxor, b, a); _compiler.Emit(InstructionCode.Pxor, a, b); break; } } }
public void AllocVar(CompilerVar var, RegisterMask regMask, VariableAlloc variableAlloc) { Contract.Requires(var != null); switch (var.Type) { case VariableType.GPD: case VariableType.GPQ: if (var.Type == VariableType.GPQ && !Util.IsX64) throw new NotSupportedException(); AllocGPVar(var, regMask, variableAlloc); break; case VariableType.X87: case VariableType.X87_1F: case VariableType.X87_1D: // TODO: X87 VARIABLES NOT IMPLEMENTED. break; case VariableType.MM: AllocMMVar(var, regMask, variableAlloc); break; case VariableType.XMM: case VariableType.XMM_1F: case VariableType.XMM_4F: case VariableType.XMM_1D: case VariableType.XMM_2D: AllocXMMVar(var, regMask, variableAlloc); break; } PostAlloc(var, variableAlloc); }
public void AllocXMMVar(CompilerVar var, RegisterMask regMask, VariableAlloc vflags) { Contract.Requires(var != null); Contract.Requires(Function != null); AllocNonGPVar(var, regMask, vflags, RegNum.XMM, var.Scope.Declaration.PreservedXMM, _state.UsedXMM, _state.XMM, AllocatedXMMRegister, SpillXMMVar, FreedXMMRegister); }
public void AllocGPVar(CompilerVar var, RegisterMask regMask, VariableAlloc variableAlloc) { Contract.Requires(var != null); Contract.Requires(Function != null); RegisterMask fullMask = RegisterMask.MaskToIndex(RegNum.GP) & ~RegisterMask.FromIndex(RegIndex.Esp); if (!_allocableEBP) fullMask &= ~RegisterMask.FromIndex(RegIndex.Ebp); // Fix the regMask (0 or full bit-array means that any register may be used). if (regMask == RegisterMask.Zero) regMask = RegisterMask.MaskToIndex(RegNum.GP); regMask &= fullMask; int i; RegisterMask mask; // Last register code (aka home). RegIndex home = var.HomeRegisterIndex; // New register code. RegIndex idx = RegIndex.Invalid; // Preserved GP variables. RegisterMask preservedGP = var.Scope.Declaration.PreservedGP; // Spill candidate. CompilerVar spillCandidate = null; // spill caused by direct jump to L_Spill bool doSpill = false; // Whether to alloc the non-preserved variables first. bool nonPreservedFirst = true; if (Function.IsCaller) { nonPreservedFirst = var.FirstCallable == null || var.FirstCallable.Offset >= var.LastItem.Offset; } // -------------------------------------------------------------------------- // [Already Allocated] // -------------------------------------------------------------------------- // Go away if variable is already allocated. if (var.State == VariableState.Register) { RegIndex oldIndex = var.RegisterIndex; // Already allocated in the right register. if ((RegisterMask.FromIndex(oldIndex) & regMask) != RegisterMask.Zero) return; // Try to find unallocated register first. mask = regMask & ~_state.UsedGP; if (mask != RegisterMask.Zero) { idx = ((nonPreservedFirst && (mask & ~preservedGP) != RegisterMask.Zero) ? mask & ~preservedGP : mask).FirstRegister; } // Then find the allocated and exchange later. else { idx = (regMask & _state.UsedGP).FirstRegister; } Contract.Assert(idx != RegIndex.Invalid); CompilerVar other = _state.GP[(int)idx]; EmitExchangeVar(var, idx, variableAlloc, other); _state.GP[(int)oldIndex] = other; _state.GP[(int)idx] = var; if (other != null) other.RegisterIndex = oldIndex; else FreedGPRegister(oldIndex); // Update VarData. var.State = VariableState.Register; var.RegisterIndex = idx; var.HomeRegisterIndex = idx; AllocatedGPRegister(idx); return; } // -------------------------------------------------------------------------- // [Find Unused GP] // -------------------------------------------------------------------------- // Home register code. if (idx == RegIndex.Invalid && home != RegIndex.Invalid && (regMask & RegisterMask.FromIndex(home)) != RegisterMask.Zero && (State.UsedGP & RegisterMask.FromIndex(home)) == RegisterMask.Zero) { idx = home; goto _Alloc; } // We start from 1, because EAX/RAX register is sometimes explicitly // needed. So we trying to prevent reallocation in near future. if (idx == RegIndex.Invalid) { for (i = 1, mask = RegisterMask.FromIndex((RegIndex)i); i < (int)RegNum.GP; i++, mask = RegisterMask.FromIndex((RegIndex)i)) { if ((regMask & mask) != RegisterMask.Zero && (_state.UsedGP & mask) == RegisterMask.Zero) { // Convenience to alloc non-preserved first or non-preserved last. if (nonPreservedFirst) { if (idx != RegIndex.Invalid && (preservedGP & mask) != RegisterMask.Zero) continue; idx = (RegIndex)i; // If current register is preserved, we should try to find different // one that is not. This can save one push / pop in prolog / epilog. if ((preservedGP & mask) == RegisterMask.Zero) break; } else { if (idx != RegIndex.Invalid && (preservedGP & mask) == RegisterMask.Zero) continue; idx = (RegIndex)i; // The opposite. if ((preservedGP & mask) != RegisterMask.Zero) break; } } } } // If not found, try EAX/RAX. if (idx == RegIndex.Invalid && (regMask & RegisterMask.FromIndex(RegIndex.Eax)) != RegisterMask.Zero && (_state.UsedGP & RegisterMask.FromIndex(RegIndex.Eax)) == RegisterMask.Zero) { idx = RegIndex.Eax; goto _Alloc; } // If regMask contains restricted registers which may be used then everything // is handled inside this block. if (idx == RegIndex.Invalid && regMask != fullMask) { // Try to find unallocated register first. mask = regMask & ~_state.UsedGP; if (mask != RegisterMask.Zero) { idx = ((nonPreservedFirst && (mask & ~preservedGP) != RegisterMask.Zero) ? (mask & ~preservedGP) : mask).FirstRegister; Contract.Assert(idx != RegIndex.Invalid); } // Then find the allocated and spill later. else { idx = (regMask & _state.UsedGP).FirstRegister; Contract.Assert(idx != RegIndex.Invalid); // Spill register we need. spillCandidate = _state.GP[(int)idx]; // Jump to spill part of allocation. doSpill = true; goto L_Spill; } } // -------------------------------------------------------------------------- // [Spill] // -------------------------------------------------------------------------- // If register is still not found, spill other variable. if (idx == RegIndex.Invalid) { if (spillCandidate == null) { spillCandidate = GetSpillCandidateGP(); } // Spill candidate not found? if (spillCandidate == null) { throw new CompilerException("Not enough registers."); } } L_Spill: if (idx == RegIndex.Invalid || doSpill) { // Prevented variables can't be spilled. _getSpillCandidate() never returns // prevented variables, but when jumping to L_Spill it can happen. if (spillCandidate.WorkOffset == _currentOffset) { throw new CompilerException("Registers overlap."); } idx = spillCandidate.RegisterIndex; SpillGPVar(spillCandidate); } // -------------------------------------------------------------------------- // [Alloc] // -------------------------------------------------------------------------- _Alloc: if (var.State == VariableState.Memory && (variableAlloc & VariableAlloc.Read) != 0) { EmitLoadVar(var, idx); } // Update VarData. var.State = VariableState.Register; var.RegisterIndex = idx; var.HomeRegisterIndex = idx; // Update StateData. AllocatedVariable(var); }