예제 #1
0
        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;
        }
예제 #2
0
        protected override CompilerItem TranslateImpl(CompilerContext cc)
        {
            int i;
            int variablesCount = (_variables != null) ? _variables.Length : 0;

            if (variablesCount > 0)
            {
                // These variables are used by the instruction and we set current offset
                // to their work offsets -> getSpillCandidate never return the variable
                // used this instruction.
                for (i = 0; i < variablesCount; i++)
                {
                    _variables[i].VarData.WorkOffset = cc.CurrentOffset;
                }

                // Alloc variables used by the instruction (special first).
                for (i = 0; i < variablesCount; i++)
                {
                    VarAllocRecord r = _variables[i];
                    // Alloc variables with specific register first.
                    if ((r.VarFlags & VariableAlloc.Special) != 0)
                        cc.AllocVar(r.VarData, r.RegMask, r.VarFlags);
                }

                for (i = 0; i < variablesCount; i++)
                {
                    VarAllocRecord r = _variables[i];
                    // Alloc variables without specific register last.
                    if ((r.VarFlags & VariableAlloc.Special) == 0)
                        cc.AllocVar(r.VarData, r.RegMask, r.VarFlags);
                }

                cc.TranslateOperands(_operands);
            }

            if (_memoryOperand != null && (_memoryOperand.Id & Operand.OperandIdTypeMask) == Operand.OperandIdTypeVar)
            {
                CompilerVar vdata = Compiler.GetVarData(_memoryOperand.Id);
                Contract.Assert(vdata != null);

                switch (vdata.State)
                {
                case VariableState.Unused:
                    vdata.State = VariableState.Memory;
                    break;

                case VariableState.Register:
                    vdata.Changed = false;
                    cc.UnuseVar(vdata, VariableState.Memory);
                    break;
                }
            }

            for (i = 0; i < variablesCount; i++)
            {
                cc.UnuseVarOnEndOfScope(this, _variables[i]);
            }

            return Next;
        }
예제 #3
0
        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;
        }