1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX Lower XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | */ |
13 | |
14 | #ifndef _LOWER_H_ |
15 | #define _LOWER_H_ |
16 | |
17 | #include "compiler.h" |
18 | #include "phase.h" |
19 | #include "lsra.h" |
20 | #include "sideeffects.h" |
21 | |
22 | class Lowering : public Phase |
23 | { |
24 | public: |
25 | inline Lowering(Compiler* compiler, LinearScanInterface* lsra) |
26 | : Phase(compiler, "Lowering" , PHASE_LOWERING), vtableCallTemp(BAD_VAR_NUM) |
27 | { |
28 | m_lsra = (LinearScan*)lsra; |
29 | assert(m_lsra); |
30 | } |
31 | virtual void DoPhase() override; |
32 | |
33 | // This variant of LowerRange is called from outside of the main Lowering pass, |
34 | // so it creates its own instance of Lowering to do so. |
35 | void LowerRange(BasicBlock* block, LIR::ReadOnlyRange& range) |
36 | { |
37 | Lowering lowerer(comp, m_lsra); |
38 | lowerer.m_block = block; |
39 | |
40 | lowerer.LowerRange(range); |
41 | } |
42 | |
43 | private: |
44 | // LowerRange handles new code that is introduced by or after Lowering. |
45 | void LowerRange(LIR::ReadOnlyRange& range) |
46 | { |
47 | for (GenTree* newNode : range) |
48 | { |
49 | LowerNode(newNode); |
50 | } |
51 | } |
52 | void LowerRange(GenTree* firstNode, GenTree* lastNode) |
53 | { |
54 | LIR::ReadOnlyRange range(firstNode, lastNode); |
55 | LowerRange(range); |
56 | } |
57 | |
58 | // ContainCheckRange handles new code that is introduced by or after Lowering, |
59 | // and that is known to be already in Lowered form. |
60 | void ContainCheckRange(LIR::ReadOnlyRange& range) |
61 | { |
62 | for (GenTree* newNode : range) |
63 | { |
64 | ContainCheckNode(newNode); |
65 | } |
66 | } |
67 | void ContainCheckRange(GenTree* firstNode, GenTree* lastNode) |
68 | { |
69 | LIR::ReadOnlyRange range(firstNode, lastNode); |
70 | ContainCheckRange(range); |
71 | } |
72 | |
73 | void InsertTreeBeforeAndContainCheck(GenTree* insertionPoint, GenTree* tree) |
74 | { |
75 | LIR::Range range = LIR::SeqTree(comp, tree); |
76 | ContainCheckRange(range); |
77 | BlockRange().InsertBefore(insertionPoint, std::move(range)); |
78 | } |
79 | |
80 | void ContainCheckNode(GenTree* node); |
81 | |
82 | void ContainCheckDivOrMod(GenTreeOp* node); |
83 | void ContainCheckReturnTrap(GenTreeOp* node); |
84 | void ContainCheckArrOffset(GenTreeArrOffs* node); |
85 | void ContainCheckLclHeap(GenTreeOp* node); |
86 | void ContainCheckRet(GenTreeOp* node); |
87 | void ContainCheckJTrue(GenTreeOp* node); |
88 | |
89 | void ContainCheckCallOperands(GenTreeCall* call); |
90 | void ContainCheckIndir(GenTreeIndir* indirNode); |
91 | void ContainCheckStoreIndir(GenTreeIndir* indirNode); |
92 | void ContainCheckMul(GenTreeOp* node); |
93 | void ContainCheckShiftRotate(GenTreeOp* node); |
94 | void ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc); |
95 | void ContainCheckCast(GenTreeCast* node); |
96 | void ContainCheckCompare(GenTreeOp* node); |
97 | void ContainCheckBinary(GenTreeOp* node); |
98 | void ContainCheckBoundsChk(GenTreeBoundsChk* node); |
99 | #ifdef _TARGET_XARCH_ |
100 | void ContainCheckFloatBinary(GenTreeOp* node); |
101 | void ContainCheckIntrinsic(GenTreeOp* node); |
102 | #endif // _TARGET_XARCH_ |
103 | #ifdef FEATURE_SIMD |
104 | void ContainCheckSIMD(GenTreeSIMD* simdNode); |
105 | #endif // FEATURE_SIMD |
106 | #ifdef FEATURE_HW_INTRINSICS |
107 | void ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node); |
108 | #endif // FEATURE_HW_INTRINSICS |
109 | |
110 | #ifdef DEBUG |
111 | static void CheckCallArg(GenTree* arg); |
112 | static void CheckCall(GenTreeCall* call); |
113 | static void CheckNode(Compiler* compiler, GenTree* node); |
114 | static bool CheckBlock(Compiler* compiler, BasicBlock* block); |
115 | #endif // DEBUG |
116 | |
117 | void LowerBlock(BasicBlock* block); |
118 | GenTree* LowerNode(GenTree* node); |
119 | |
120 | void CheckVSQuirkStackPaddingNeeded(GenTreeCall* call); |
121 | |
122 | // ------------------------------ |
123 | // Call Lowering |
124 | // ------------------------------ |
125 | void LowerCall(GenTree* call); |
126 | #ifndef _TARGET_64BIT_ |
127 | GenTree* DecomposeLongCompare(GenTree* cmp); |
128 | #endif |
129 | GenTree* OptimizeConstCompare(GenTree* cmp); |
130 | GenTree* LowerCompare(GenTree* cmp); |
131 | GenTree* LowerJTrue(GenTreeOp* jtrue); |
132 | void LowerJmpMethod(GenTree* jmp); |
133 | void LowerRet(GenTree* ret); |
134 | GenTree* LowerDelegateInvoke(GenTreeCall* call); |
135 | GenTree* LowerIndirectNonvirtCall(GenTreeCall* call); |
136 | GenTree* LowerDirectCall(GenTreeCall* call); |
137 | GenTree* LowerNonvirtPinvokeCall(GenTreeCall* call); |
138 | GenTree* LowerTailCallViaHelper(GenTreeCall* callNode, GenTree* callTarget); |
139 | void LowerFastTailCall(GenTreeCall* callNode); |
140 | void InsertProfTailCallHook(GenTreeCall* callNode, GenTree* insertionPoint); |
141 | GenTree* LowerVirtualVtableCall(GenTreeCall* call); |
142 | GenTree* LowerVirtualStubCall(GenTreeCall* call); |
143 | void LowerArgsForCall(GenTreeCall* call); |
144 | void ReplaceArgWithPutArgOrBitcast(GenTree** ppChild, GenTree* newNode); |
145 | GenTree* NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* info, var_types type); |
146 | void LowerArg(GenTreeCall* call, GenTree** ppTree); |
147 | #ifdef _TARGET_ARMARCH_ |
148 | GenTree* LowerFloatArg(GenTree** pArg, fgArgTabEntry* info); |
149 | GenTree* LowerFloatArgReg(GenTree* arg, regNumber regNum); |
150 | #endif |
151 | |
152 | void InsertPInvokeCallProlog(GenTreeCall* call); |
153 | void InsertPInvokeCallEpilog(GenTreeCall* call); |
154 | void InsertPInvokeMethodProlog(); |
155 | void InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree* lastExpr)); |
156 | GenTree* SetGCState(int cns); |
157 | GenTree* CreateReturnTrapSeq(); |
158 | enum FrameLinkAction |
159 | { |
160 | PushFrame, |
161 | PopFrame |
162 | }; |
163 | GenTree* CreateFrameLinkUpdate(FrameLinkAction); |
164 | GenTree* AddrGen(ssize_t addr); |
165 | GenTree* AddrGen(void* addr); |
166 | |
167 | GenTree* Ind(GenTree* tree) |
168 | { |
169 | return comp->gtNewOperNode(GT_IND, TYP_I_IMPL, tree); |
170 | } |
171 | |
172 | GenTree* PhysReg(regNumber reg, var_types type = TYP_I_IMPL) |
173 | { |
174 | return comp->gtNewPhysRegNode(reg, type); |
175 | } |
176 | |
177 | GenTree* ThisReg(GenTreeCall* call) |
178 | { |
179 | return PhysReg(comp->codeGen->genGetThisArgReg(call), TYP_REF); |
180 | } |
181 | |
182 | GenTree* Offset(GenTree* base, unsigned offset) |
183 | { |
184 | var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet(); |
185 | return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, nullptr, 0, offset); |
186 | } |
187 | |
188 | GenTree* OffsetByIndex(GenTree* base, GenTree* index) |
189 | { |
190 | var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet(); |
191 | return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, index, 0, 0); |
192 | } |
193 | |
194 | GenTree* OffsetByIndexWithScale(GenTree* base, GenTree* index, unsigned scale) |
195 | { |
196 | var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet(); |
197 | return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, index, scale, 0); |
198 | } |
199 | |
200 | // Replace the definition of the given use with a lclVar, allocating a new temp |
201 | // if 'tempNum' is BAD_VAR_NUM. Returns the LclVar node. |
202 | GenTreeLclVar* ReplaceWithLclVar(LIR::Use& use, unsigned tempNum = BAD_VAR_NUM) |
203 | { |
204 | GenTree* oldUseNode = use.Def(); |
205 | if ((oldUseNode->gtOper != GT_LCL_VAR) || (tempNum != BAD_VAR_NUM)) |
206 | { |
207 | use.ReplaceWithLclVar(comp, m_block->getBBWeight(comp), tempNum); |
208 | GenTree* newUseNode = use.Def(); |
209 | ContainCheckRange(oldUseNode->gtNext, newUseNode); |
210 | return newUseNode->AsLclVar(); |
211 | } |
212 | return oldUseNode->AsLclVar(); |
213 | } |
214 | |
215 | // return true if this call target is within range of a pc-rel call on the machine |
216 | bool IsCallTargetInRange(void* addr); |
217 | |
218 | #if defined(_TARGET_XARCH_) |
219 | GenTree* PreferredRegOptionalOperand(GenTree* tree); |
220 | |
221 | // ------------------------------------------------------------------ |
222 | // SetRegOptionalBinOp - Indicates which of the operands of a bin-op |
223 | // register requirement is optional. Xarch instruction set allows |
224 | // either of op1 or op2 of binary operation (e.g. add, mul etc) to be |
225 | // a memory operand. This routine provides info to register allocator |
226 | // which of its operands optionally require a register. Lsra might not |
227 | // allocate a register to RefTypeUse positions of such operands if it |
228 | // is beneficial. In such a case codegen will treat them as memory |
229 | // operands. |
230 | // |
231 | // Arguments: |
232 | // tree - Gentree of a binary operation. |
233 | // isSafeToMarkOp1 True if it's safe to mark op1 as register optional |
234 | // isSafeToMarkOp2 True if it's safe to mark op2 as register optional |
235 | // |
236 | // Returns |
237 | // The caller is expected to get isSafeToMarkOp1 and isSafeToMarkOp2 |
238 | // by calling IsSafeToContainMem. |
239 | // |
240 | // Note: On xarch at most only one of the operands will be marked as |
241 | // reg optional, even when both operands could be considered register |
242 | // optional. |
243 | void SetRegOptionalForBinOp(GenTree* tree, bool isSafeToMarkOp1, bool isSafeToMarkOp2) |
244 | { |
245 | assert(GenTree::OperIsBinary(tree->OperGet())); |
246 | |
247 | GenTree* const op1 = tree->gtGetOp1(); |
248 | GenTree* const op2 = tree->gtGetOp2(); |
249 | |
250 | const unsigned operatorSize = genTypeSize(tree->TypeGet()); |
251 | |
252 | const bool op1Legal = |
253 | isSafeToMarkOp1 && tree->OperIsCommutative() && (operatorSize == genTypeSize(op1->TypeGet())); |
254 | const bool op2Legal = isSafeToMarkOp2 && (operatorSize == genTypeSize(op2->TypeGet())); |
255 | |
256 | GenTree* regOptionalOperand = nullptr; |
257 | if (op1Legal) |
258 | { |
259 | regOptionalOperand = op2Legal ? PreferredRegOptionalOperand(tree) : op1; |
260 | } |
261 | else if (op2Legal) |
262 | { |
263 | regOptionalOperand = op2; |
264 | } |
265 | if (regOptionalOperand != nullptr) |
266 | { |
267 | regOptionalOperand->SetRegOptional(); |
268 | } |
269 | } |
270 | #endif // defined(_TARGET_XARCH_) |
271 | |
272 | // Per tree node member functions |
273 | void LowerStoreIndir(GenTreeIndir* node); |
274 | GenTree* LowerAdd(GenTree* node); |
275 | bool LowerUnsignedDivOrMod(GenTreeOp* divMod); |
276 | GenTree* LowerConstIntDivOrMod(GenTree* node); |
277 | GenTree* LowerSignedDivOrMod(GenTree* node); |
278 | void LowerBlockStore(GenTreeBlk* blkNode); |
279 | void LowerPutArgStk(GenTreePutArgStk* tree); |
280 | |
281 | GenTree* TryCreateAddrMode(LIR::Use&& use, bool isIndir); |
282 | void AddrModeCleanupHelper(GenTreeAddrMode* addrMode, GenTree* node); |
283 | |
284 | GenTree* LowerSwitch(GenTree* node); |
285 | bool TryLowerSwitchToBitTest( |
286 | BasicBlock* jumpTable[], unsigned jumpCount, unsigned targetCount, BasicBlock* bbSwitch, GenTree* switchValue); |
287 | |
288 | void LowerCast(GenTree* node); |
289 | |
290 | #if !CPU_LOAD_STORE_ARCH |
291 | bool IsRMWIndirCandidate(GenTree* operand, GenTree* storeInd); |
292 | bool IsBinOpInRMWStoreInd(GenTree* tree); |
293 | bool IsRMWMemOpRootedAtStoreInd(GenTree* storeIndTree, GenTree** indirCandidate, GenTree** indirOpSource); |
294 | bool LowerRMWMemOp(GenTreeIndir* storeInd); |
295 | #endif |
296 | |
297 | void WidenSIMD12IfNecessary(GenTreeLclVarCommon* node); |
298 | void LowerStoreLoc(GenTreeLclVarCommon* tree); |
299 | GenTree* LowerArrElem(GenTree* node); |
300 | void LowerRotate(GenTree* tree); |
301 | void LowerShift(GenTreeOp* shift); |
302 | #ifdef FEATURE_SIMD |
303 | void LowerSIMD(GenTreeSIMD* simdNode); |
304 | #endif // FEATURE_SIMD |
305 | #ifdef FEATURE_HW_INTRINSICS |
306 | void LowerHWIntrinsic(GenTreeHWIntrinsic* node); |
307 | #endif // FEATURE_HW_INTRINSICS |
308 | |
309 | // Utility functions |
310 | public: |
311 | static bool IndirsAreEquivalent(GenTree* pTreeA, GenTree* pTreeB); |
312 | |
313 | // return true if 'childNode' is an immediate that can be contained |
314 | // by the 'parentNode' (i.e. folded into an instruction) |
315 | // for example small enough and non-relocatable |
316 | bool IsContainableImmed(GenTree* parentNode, GenTree* childNode); |
317 | |
318 | // Return true if 'node' is a containable memory op. |
319 | bool IsContainableMemoryOp(GenTree* node) |
320 | { |
321 | return m_lsra->isContainableMemoryOp(node); |
322 | } |
323 | |
324 | #ifdef FEATURE_HW_INTRINSICS |
325 | // Return true if 'node' is a containable HWIntrinsic op. |
326 | bool IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, GenTree* node, bool* supportsRegOptional); |
327 | #endif // FEATURE_HW_INTRINSICS |
328 | |
329 | private: |
330 | static bool NodesAreEquivalentLeaves(GenTree* candidate, GenTree* storeInd); |
331 | |
332 | bool AreSourcesPossiblyModifiedLocals(GenTree* addr, GenTree* base, GenTree* index); |
333 | |
334 | // Makes 'childNode' contained in the 'parentNode' |
335 | void MakeSrcContained(GenTree* parentNode, GenTree* childNode); |
336 | |
337 | // Checks and makes 'childNode' contained in the 'parentNode' |
338 | bool CheckImmedAndMakeContained(GenTree* parentNode, GenTree* childNode); |
339 | |
340 | // Checks for memory conflicts in the instructions between childNode and parentNode, and returns true if childNode |
341 | // can be contained. |
342 | bool IsSafeToContainMem(GenTree* parentNode, GenTree* childNode); |
343 | |
344 | inline LIR::Range& BlockRange() const |
345 | { |
346 | return LIR::AsRange(m_block); |
347 | } |
348 | |
349 | // Any tracked lclVar accessed by a LCL_FLD or STORE_LCL_FLD should be marked doNotEnregister. |
350 | // This method checks, and asserts in the DEBUG case if it is not so marked, |
351 | // but in the non-DEBUG case (asserts disabled) set the flag so that we don't generate bad code. |
352 | // This ensures that the local's value is valid on-stack as expected for a *LCL_FLD. |
353 | void verifyLclFldDoNotEnregister(unsigned lclNum) |
354 | { |
355 | LclVarDsc* varDsc = &(comp->lvaTable[lclNum]); |
356 | // Do a couple of simple checks before setting lvDoNotEnregister. |
357 | // This may not cover all cases in 'isRegCandidate()' but we don't want to |
358 | // do an expensive check here. For non-candidates it is not harmful to set lvDoNotEnregister. |
359 | if (varDsc->lvTracked && !varDsc->lvDoNotEnregister) |
360 | { |
361 | assert(!m_lsra->isRegCandidate(varDsc)); |
362 | comp->lvaSetVarDoNotEnregister(lclNum DEBUG_ARG(Compiler::DNER_LocalField)); |
363 | } |
364 | } |
365 | |
366 | LinearScan* m_lsra; |
367 | unsigned vtableCallTemp; // local variable we use as a temp for vtable calls |
368 | SideEffectSet m_scratchSideEffects; // SideEffectSet used for IsSafeToContainMem and isRMWIndirCandidate |
369 | BasicBlock* m_block; |
370 | }; |
371 | |
372 | #endif // _LOWER_H_ |
373 | |