summaryrefslogtreecommitdiff
path: root/src/jit/lower.h
blob: bcc2bafdab64e1e7e4c26810f7df9fc4fb5e82db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                               Lower                                       XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#ifndef _LOWER_H_
#define _LOWER_H_

#include "compiler.h"
#include "phase.h"
#include "lsra.h"
#include "sideeffects.h"

class Lowering : public Phase
{
public:
    inline Lowering(Compiler* compiler, LinearScanInterface* lsra)
        : Phase(compiler, "Lowering", PHASE_LOWERING), vtableCallTemp(BAD_VAR_NUM)
    {
        m_lsra = (LinearScan*)lsra;
        assert(m_lsra);
    }
    virtual void DoPhase();

    // If requiresOverflowCheck is false, all other values will be unset
    struct CastInfo
    {
        bool requiresOverflowCheck; // Will the cast require an overflow check
        bool unsignedSource;        // Is the source unsigned
        bool unsignedDest;          // is the dest unsigned

        // All other fields are only meaningful if requiresOverflowCheck is set.

        ssize_t typeMin;       // Lowest storable value of the dest type
        ssize_t typeMax;       // Highest storable value of the dest type
        ssize_t typeMask;      // For converting from/to unsigned
        bool    signCheckOnly; // For converting between unsigned/signed int
    };

    static void getCastDescription(GenTreePtr treeNode, CastInfo* castInfo);

private:
#ifdef DEBUG
    static void CheckCallArg(GenTree* arg);
    static void CheckCall(GenTreeCall* call);
    static void CheckNode(GenTree* node);
    static bool CheckBlock(Compiler* compiler, BasicBlock* block);
#endif // DEBUG

    void LowerBlock(BasicBlock* block);
    GenTree* LowerNode(GenTree* node);
    void CheckVSQuirkStackPaddingNeeded(GenTreeCall* call);

    // ------------------------------
    // Call Lowering
    // ------------------------------
    void LowerCall(GenTree* call);
    void LowerCompare(GenTree* tree);
    void LowerJmpMethod(GenTree* jmp);
    void LowerRet(GenTree* ret);
    GenTree* LowerDelegateInvoke(GenTreeCall* call);
    GenTree* LowerIndirectNonvirtCall(GenTreeCall* call);
    GenTree* LowerDirectCall(GenTreeCall* call);
    GenTree* LowerNonvirtPinvokeCall(GenTreeCall* call);
    GenTree* LowerTailCallViaHelper(GenTreeCall* callNode, GenTree* callTarget);
    void LowerFastTailCall(GenTreeCall* callNode);
    void InsertProfTailCallHook(GenTreeCall* callNode, GenTree* insertionPoint);
    GenTree* LowerVirtualVtableCall(GenTreeCall* call);
    GenTree* LowerVirtualStubCall(GenTreeCall* call);
    void LowerArgsForCall(GenTreeCall* call);
    void ReplaceArgWithPutArgOrCopy(GenTreePtr* ppChild, GenTreePtr newNode);
    GenTree* NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryPtr info, var_types type);
    void LowerArg(GenTreeCall* call, GenTreePtr* ppTree);
    void InsertPInvokeCallProlog(GenTreeCall* call);
    void InsertPInvokeCallEpilog(GenTreeCall* call);
    void InsertPInvokeMethodProlog();
    void InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTreePtr lastExpr));
    GenTree* SetGCState(int cns);
    GenTree* CreateReturnTrapSeq();
    enum FrameLinkAction
    {
        PushFrame,
        PopFrame
    };
    GenTree* CreateFrameLinkUpdate(FrameLinkAction);
    GenTree* AddrGen(ssize_t addr, regNumber reg = REG_NA);
    GenTree* AddrGen(void* addr, regNumber reg = REG_NA);

    GenTree* Ind(GenTree* tree)
    {
        return comp->gtNewOperNode(GT_IND, TYP_I_IMPL, tree);
    }

    GenTree* PhysReg(regNumber reg, var_types type = TYP_I_IMPL)
    {
        return comp->gtNewPhysRegNode(reg, type);
    }

    GenTree* PhysRegDst(regNumber reg, GenTree* src)
    {
        return comp->gtNewPhysRegNode(reg, src);
    }

    GenTree* ThisReg(GenTreeCall* call)
    {
        return PhysReg(comp->codeGen->genGetThisArgReg(call), TYP_REF);
    }

    GenTree* Offset(GenTree* base, unsigned offset)
    {
        var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet();
        return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, nullptr, 0, offset);
    }

    // returns true if the tree can use the read-modify-write memory instruction form
    bool isRMWRegOper(GenTreePtr tree);

    // return true if this call target is within range of a pc-rel call on the machine
    bool IsCallTargetInRange(void* addr);

#ifdef _TARGET_X86_
    bool ExcludeNonByteableRegisters(GenTree* tree);
#endif

    void TreeNodeInfoInit(GenTree* stmt);

    void TreeNodeInfoInitCheckByteable(GenTree* tree);

#if defined(_TARGET_XARCH_)
    void TreeNodeInfoInitSimple(GenTree* tree);

    //----------------------------------------------------------------------
    // SetRegOptional - sets a bit to indicate to LSRA that register
    // for a given tree node is optional for codegen purpose.  If no
    // register is allocated to such a tree node, its parent node treats
    // it as a contained memory operand during codegen.
    //
    // Arguments:
    //    tree    -   GenTree node
    //
    // Returns
    //    None
    void SetRegOptional(GenTree* tree)
    {
        tree->gtLsraInfo.regOptional = true;
    }

    GenTree* PreferredRegOptionalOperand(GenTree* tree);

    // ------------------------------------------------------------------
    // SetRegOptionalBinOp - Indicates which of the operands of a bin-op
    // register requirement is optional. Xarch instruction set allows
    // either of op1 or op2 of binary operation (e.g. add, mul etc) to be
    // a memory operand.  This routine provides info to register allocator
    // which of its operands optionally require a register.  Lsra might not
    // allocate a register to RefTypeUse positions of such operands if it
    // is beneficial. In such a case codegen will treat them as memory
    // operands.
    //
    // Arguments:
    //     tree  -  Gentree of a binary operation.
    //
    // Returns
    //     None.
    //
    // Note: On xarch at most only one of the operands will be marked as
    // reg optional, even when both operands could be considered register
    // optional.
    void SetRegOptionalForBinOp(GenTree* tree)
    {
        assert(GenTree::OperIsBinary(tree->OperGet()));

        GenTree* op1 = tree->gtGetOp1();
        GenTree* op2 = tree->gtGetOp2();

        if (tree->OperIsCommutative() && tree->TypeGet() == op1->TypeGet())
        {
            GenTree* preferredOp = PreferredRegOptionalOperand(tree);
            SetRegOptional(preferredOp);
        }
        else if (tree->TypeGet() == op2->TypeGet())
        {
            SetRegOptional(op2);
        }
    }
#endif // defined(_TARGET_XARCH_)
    void TreeNodeInfoInitStoreLoc(GenTree* tree);
    void TreeNodeInfoInitReturn(GenTree* tree);
    void TreeNodeInfoInitShiftRotate(GenTree* tree);
    void TreeNodeInfoInitPutArgReg(
        GenTreeUnOp* node, regNumber argReg, TreeNodeInfo& info, bool isVarArgs, bool* callHasFloatRegArgs);
    void TreeNodeInfoInitCall(GenTreeCall* call);
    void TreeNodeInfoInitCmp(GenTreePtr tree);
    void TreeNodeInfoInitStructArg(GenTreePtr structArg);
    void TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode);
    void TreeNodeInfoInitLogicalOp(GenTree* tree);
    void TreeNodeInfoInitModDiv(GenTree* tree);
    void TreeNodeInfoInitIntrinsic(GenTree* tree);
    void TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* tree);
    void TreeNodeInfoInitIndir(GenTree* indirTree);
    void TreeNodeInfoInitGCWriteBarrier(GenTree* tree);
#if !CPU_LOAD_STORE_ARCH
    bool TreeNodeInfoInitIfRMWMemOp(GenTreePtr storeInd);
#endif
#ifdef FEATURE_SIMD
    void TreeNodeInfoInitSIMD(GenTree* tree);
#endif // FEATURE_SIMD
    void TreeNodeInfoInitCast(GenTree* tree);
#ifdef _TARGET_ARM64_
    void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
    void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
#ifdef _TARGET_ARM_
    void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
    void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
#ifdef FEATURE_PUT_STRUCT_ARG_STK
    void LowerPutArgStk(GenTreePutArgStk* tree);
    void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* tree);
#endif // FEATURE_PUT_STRUCT_ARG_STK
    void TreeNodeInfoInitLclHeap(GenTree* tree);

    void DumpNodeInfoMap();

    // Per tree node member functions
    void LowerStoreInd(GenTree* node);
    GenTree* LowerAdd(GenTree* node);
    void LowerUnsignedDivOrMod(GenTree* node);
    GenTree* LowerSignedDivOrMod(GenTree* node);
    void LowerBlockStore(GenTreeBlk* blkNode);

    GenTree* TryCreateAddrMode(LIR::Use&& use, bool isIndir);
    void AddrModeCleanupHelper(GenTreeAddrMode* addrMode, GenTree* node);

    GenTree* LowerSwitch(GenTree* node);
    void LowerCast(GenTree* node);

#if defined(_TARGET_XARCH_)
    void TreeNodeInfoInitMul(GenTreePtr tree);
    void SetContainsAVXFlags(bool isFloatingPointType = true, unsigned sizeOfSIMDVector = 0);
#endif // defined(_TARGET_XARCH_)

#if !CPU_LOAD_STORE_ARCH
    bool IsRMWIndirCandidate(GenTree* operand, GenTree* storeInd);
    bool IsBinOpInRMWStoreInd(GenTreePtr tree);
    bool IsRMWMemOpRootedAtStoreInd(GenTreePtr storeIndTree, GenTreePtr* indirCandidate, GenTreePtr* indirOpSource);
#endif
    void LowerStoreLoc(GenTreeLclVarCommon* tree);
    GenTree* LowerArrElem(GenTree* node);
    void LowerRotate(GenTree* tree);

    // Utility functions
    void MorphBlkIntoHelperCall(GenTreePtr pTree, GenTreePtr treeStmt);

public:
    static bool IndirsAreEquivalent(GenTreePtr pTreeA, GenTreePtr pTreeB);

private:
    static bool NodesAreEquivalentLeaves(GenTreePtr candidate, GenTreePtr storeInd);

    bool AreSourcesPossiblyModifiedLocals(GenTree* addr, GenTree* base, GenTree* index);

    // return true if 'childNode' is an immediate that can be contained
    //  by the 'parentNode' (i.e. folded into an instruction)
    //  for example small enough and non-relocatable
    bool IsContainableImmed(GenTree* parentNode, GenTree* childNode);

    // Makes 'childNode' contained in the 'parentNode'
    void MakeSrcContained(GenTreePtr parentNode, GenTreePtr childNode);

    // Checks and makes 'childNode' contained in the 'parentNode'
    bool CheckImmedAndMakeContained(GenTree* parentNode, GenTree* childNode);

    // Checks for memory conflicts in the instructions between childNode and parentNode, and returns true if childNode
    // can be contained.
    bool IsSafeToContainMem(GenTree* parentNode, GenTree* childNode);

    inline LIR::Range& BlockRange() const
    {
        return LIR::AsRange(m_block);
    }

    LinearScan*   m_lsra;
    unsigned      vtableCallTemp;       // local variable we use as a temp for vtable calls
    SideEffectSet m_scratchSideEffects; // SideEffectSet used for IsSafeToContainMem and isRMWIndirCandidate
    BasicBlock*   m_block;
};

#endif // _LOWER_H_