예제 #1
0
        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++;
        }