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 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; }