예제 #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++;
        }
예제 #2
0
        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++;
        }
예제 #3
0
        protected override void PrepareImpl(CompilerContext cc)
        {
            Offset = cc.CurrentOffset;

            VariableType retValType = Function.Declaration.ReturnValue;
            if (retValType != VariableType.Invalid)
            {
                for (int i = 0; i < 2; i++)
                {
                    Operand o = (i == 0 ? _first : _second);
                    if (o == null)
                        continue;

                    if (o.IsVar)
                    {
                        if (o.Id == Operand.InvalidValue)
                            throw new CompilerException();

                        CompilerVar vdata = Compiler.GetVarData(o.Id);
                        Contract.Assert(vdata != null);

                        // First item (begin of variable scope).
                        if (vdata.FirstItem == null)
                            vdata.FirstItem = this;

                        // Last item (end of variable scope).
                        vdata.LastItem = this;

                        if (vdata.WorkOffset == Offset)
                            continue;
                        if (!cc.IsActive(vdata))
                            cc.AddActive(vdata);

                        vdata.WorkOffset = Offset;
                        vdata.RegisterReadCount++;

                        if (VariableInfo.IsVariableInteger(vdata.Type) && VariableInfo.IsVariableInteger(retValType))
                        {
                            cc.NewRegisterHomeIndex(vdata, (i == 0) ? RegIndex.Eax : RegIndex.Edx);
                        }
                    }
                }
            }

            cc.CurrentOffset++;
        }