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