Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / jit / lowerriscv64.cpp
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
4 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XX                                                                           XX
7 XX             Lowering for RISCV64 common code                              XX
8 XX                                                                           XX
9 XX  This encapsulates common logic for lowering trees for the RISCV64        XX
10 XX  architectures.  For a more detailed view of what is lowering, please     XX
11 XX  take a look at Lower.cpp                                                 XX
12 XX                                                                           XX
13 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 */
16
17 #include "jitpch.h"
18 #ifdef _MSC_VER
19 #pragma hdrstop
20 #endif
21
22 #ifdef TARGET_RISCV64 // This file is ONLY used for RISCV64 architectures
23
24 #include "jit.h"
25 #include "sideeffects.h"
26 #include "lower.h"
27 #include "lsra.h"
28
29 #ifdef FEATURE_HW_INTRINSICS
30 #include "hwintrinsic.h"
31 #endif
32
33 //------------------------------------------------------------------------
34 // IsCallTargetInRange: Can a call target address be encoded in-place?
35 //
36 // Return Value:
37 //    True if the addr fits into the range.
38 //
39 bool Lowering::IsCallTargetInRange(void* addr)
40 {
41     // TODO-RISCV64: using B/BL for optimization.
42     return false;
43 }
44
45 //------------------------------------------------------------------------
46 // IsContainableImmed: Is an immediate encodable in-place?
47 //
48 // Return Value:
49 //    True if the immediate can be folded into an instruction,
50 //    for example small enough and non-relocatable.
51 //
52 bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const
53 {
54     if (!varTypeIsFloating(parentNode->TypeGet()))
55     {
56         // Make sure we have an actual immediate
57         if (!childNode->IsCnsIntOrI())
58             return false;
59         if (childNode->AsIntCon()->ImmedValNeedsReloc(comp))
60             return false;
61
62         // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntCon::gtIconVal had target_ssize_t type.
63         target_ssize_t immVal = (target_ssize_t)childNode->AsIntCon()->gtIconVal;
64
65         switch (parentNode->OperGet())
66         {
67             case GT_ADD:
68             case GT_EQ:
69             case GT_NE:
70             case GT_LT:
71             case GT_LE:
72             case GT_GE:
73             case GT_GT:
74                 return emitter::isValidSimm12(immVal);
75             case GT_AND:
76             case GT_OR:
77             case GT_XOR:
78                 return emitter::isValidUimm11(immVal);
79             case GT_JCMP:
80                 return true;
81
82             case GT_STORE_LCL_FLD:
83             case GT_STORE_LCL_VAR:
84                 if (immVal == 0)
85                     return true;
86                 break;
87
88             default:
89                 break;
90         }
91     }
92
93     return false;
94 }
95
96 //------------------------------------------------------------------------
97 // LowerMul: Lower a GT_MUL/GT_MULHI/GT_MUL_LONG node.
98 //
99 // Performs contaiment checks.
100 //
101 // TODO-RISCV64-CQ: recognize GT_MULs that can be turned into MUL_LONGs,
102 // as those are cheaper.
103 //
104 // Arguments:
105 //    mul - The node to lower
106 //
107 // Return Value:
108 //    The next node to lower.
109 //
110 GenTree* Lowering::LowerMul(GenTreeOp* mul)
111 {
112     assert(mul->OperIsMul());
113
114     ContainCheckMul(mul);
115
116     return mul->gtNext;
117 }
118
119 //------------------------------------------------------------------------
120 // Lowering::LowerJTrue: Lowers a JTRUE node.
121 //
122 // Arguments:
123 //    jtrue - the JTRUE node
124 //
125 // Return Value:
126 //    The next node to lower (usually nullptr).
127 //
128 GenTree* Lowering::LowerJTrue(GenTreeOp* jtrue)
129 {
130     GenTree*     op = jtrue->gtGetOp1();
131     GenCondition cond;
132     GenTree*     cmpOp1;
133     GenTree*     cmpOp2;
134
135     if (op->OperIsCompare() && !varTypeIsFloating(op->gtGetOp1()))
136     {
137         // We do not expect any other relops on RISCV64
138         assert(op->OperIs(GT_EQ, GT_NE, GT_LT, GT_LE, GT_GE, GT_GT));
139
140         cond = GenCondition::FromRelop(op);
141
142         cmpOp1 = op->gtGetOp1();
143         cmpOp2 = op->gtGetOp2();
144
145         // We will fall through and turn this into a JCMP(op1, op2, kind), but need to remove the relop here.
146         BlockRange().Remove(op);
147     }
148     else
149     {
150         cond = GenCondition(GenCondition::NE);
151
152         cmpOp1 = op;
153         cmpOp2 = comp->gtNewZeroConNode(cmpOp1->TypeGet());
154
155         BlockRange().InsertBefore(jtrue, cmpOp2);
156
157         // Fall through and turn this into a JCMP(op1, 0, NE).
158     }
159
160     // for RISCV64's compare and condition-branch instructions,
161     // it's very similar to the IL instructions.
162     jtrue->ChangeOper(GT_JCMP);
163     jtrue->gtOp1                 = cmpOp1;
164     jtrue->gtOp2                 = cmpOp2;
165     jtrue->AsOpCC()->gtCondition = cond;
166
167     if (cmpOp2->IsCnsIntOrI())
168     {
169         cmpOp2->SetContained();
170     }
171
172     return jtrue->gtNext;
173 }
174
175 //------------------------------------------------------------------------
176 // LowerBinaryArithmetic: lowers the given binary arithmetic node.
177 //
178 // Arguments:
179 //    node - the arithmetic node to lower
180 //
181 // Returns:
182 //    The next node to lower.
183 //
184 GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp)
185 {
186     ContainCheckBinary(binOp);
187
188     return binOp->gtNext;
189 }
190
191 //------------------------------------------------------------------------
192 // LowerStoreLoc: Lower a store of a lclVar
193 //
194 // Arguments:
195 //    storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR)
196 //
197 // Notes:
198 //    This involves:
199 //    - Widening operations of unsigneds.
200 //
201 void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
202 {
203     if (storeLoc->OperIs(GT_STORE_LCL_FLD))
204     {
205         // We should only encounter this for lclVars that are lvDoNotEnregister.
206         verifyLclFldDoNotEnregister(storeLoc->GetLclNum());
207     }
208     ContainCheckStoreLoc(storeLoc);
209 }
210
211 //------------------------------------------------------------------------
212 // LowerStoreIndir: Determine addressing mode for an indirection, and whether operands are contained.
213 //
214 // Arguments:
215 //    node       - The indirect store node (GT_STORE_IND) of interest
216 //
217 // Return Value:
218 //    None.
219 //
220 void Lowering::LowerStoreIndir(GenTreeStoreInd* node)
221 {
222     ContainCheckStoreIndir(node);
223 }
224
225 //------------------------------------------------------------------------
226 // LowerBlockStore: Set block store type
227 //
228 // Arguments:
229 //    blkNode       - The block store node of interest
230 //
231 // Return Value:
232 //    None.
233 //
234 void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
235 {
236     GenTree* dstAddr = blkNode->Addr();
237     GenTree* src     = blkNode->Data();
238     unsigned size    = blkNode->Size();
239
240     if (blkNode->OperIsInitBlkOp())
241     {
242         if (src->OperIs(GT_INIT_VAL))
243         {
244             src->SetContained();
245             src = src->AsUnOp()->gtGetOp1();
246         }
247
248         if (!blkNode->OperIs(GT_STORE_DYN_BLK) && (size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset)) &&
249             src->OperIs(GT_CNS_INT))
250         {
251             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
252
253             // The fill value of an initblk is interpreted to hold a
254             // value of (unsigned int8) however a constant of any size
255             // may practically reside on the evaluation stack. So extract
256             // the lower byte out of the initVal constant and replicate
257             // it to a larger constant whose size is sufficient to support
258             // the largest width store of the desired inline expansion.
259
260             ssize_t fill = src->AsIntCon()->IconValue() & 0xFF;
261             if (fill == 0)
262             {
263                 src->SetContained();
264             }
265             else if (size >= REGSIZE_BYTES)
266             {
267                 fill *= 0x0101010101010101LL;
268                 src->gtType = TYP_LONG;
269             }
270             else
271             {
272                 fill *= 0x01010101;
273             }
274             src->AsIntCon()->SetIconValue(fill);
275
276             ContainBlockStoreAddress(blkNode, size, dstAddr, nullptr);
277         }
278         else
279         {
280             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
281         }
282     }
283     else
284     {
285         assert(src->OperIs(GT_IND, GT_LCL_VAR, GT_LCL_FLD));
286         src->SetContained();
287
288         if (src->OperIs(GT_LCL_VAR))
289         {
290             // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register.
291             const unsigned srcLclNum = src->AsLclVar()->GetLclNum();
292             comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DoNotEnregisterReason::BlockOp));
293         }
294
295         ClassLayout* layout               = blkNode->GetLayout();
296         bool         doCpObj              = !blkNode->OperIs(GT_STORE_DYN_BLK) && layout->HasGCPtr();
297         unsigned     copyBlockUnrollLimit = comp->getUnrollThreshold(Compiler::UnrollKind::Memcpy);
298
299         if (doCpObj && (size <= copyBlockUnrollLimit))
300         {
301             // No write barriers are needed on the stack.
302             // If the layout contains a byref, then we know it must live on the stack.
303             if (dstAddr->OperIs(GT_LCL_ADDR) || layout->HasGCByRef())
304             {
305                 // If the size is small enough to unroll then we need to mark the block as non-interruptible
306                 // to actually allow unrolling. The generated code does not report GC references loaded in the
307                 // temporary register(s) used for copying.
308                 doCpObj                  = false;
309                 blkNode->gtBlkOpGcUnsafe = true;
310             }
311         }
312
313         // CopyObj or CopyBlk
314         if (doCpObj)
315         {
316             assert((dstAddr->TypeGet() == TYP_BYREF) || (dstAddr->TypeGet() == TYP_I_IMPL));
317             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindCpObjUnroll;
318         }
319         else if (blkNode->OperIs(GT_STORE_BLK) && (size <= copyBlockUnrollLimit))
320         {
321             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
322
323             if (src->OperIs(GT_IND))
324             {
325                 ContainBlockStoreAddress(blkNode, size, src->AsIndir()->Addr(), src->AsIndir());
326             }
327
328             ContainBlockStoreAddress(blkNode, size, dstAddr, nullptr);
329         }
330         else
331         {
332             assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK));
333
334             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
335         }
336     }
337 }
338
339 //------------------------------------------------------------------------
340 // ContainBlockStoreAddress: Attempt to contain an address used by an unrolled block store.
341 //
342 // Arguments:
343 //    blkNode - the block store node
344 //    size - the block size
345 //    addr - the address node to try to contain
346 //    addrParent - the parent of addr, in case this is checking containment of the source address.
347 //
348 void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenTree* addr, GenTree* addrParent)
349 {
350     assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
351     assert(size < INT32_MAX);
352
353     if (addr->OperIs(GT_LCL_ADDR))
354     {
355         addr->SetContained();
356         return;
357     }
358
359     if (!addr->OperIs(GT_ADD) || addr->gtOverflow() || !addr->AsOp()->gtGetOp2()->OperIs(GT_CNS_INT))
360     {
361         return;
362     }
363
364     GenTreeIntCon* offsetNode = addr->AsOp()->gtGetOp2()->AsIntCon();
365     ssize_t        offset     = offsetNode->IconValue();
366
367     // TODO-RISCV64: not including the ldptr and SIMD offset which not used right now.
368     if (!emitter::isValidSimm12(offset) || !emitter::isValidSimm12(offset + static_cast<int>(size)))
369     {
370         return;
371     }
372
373     if (!IsSafeToContainMem(blkNode, addrParent, addr))
374     {
375         return;
376     }
377
378     BlockRange().Remove(offsetNode);
379
380     addr->ChangeOper(GT_LEA);
381     addr->AsAddrMode()->SetIndex(nullptr);
382     addr->AsAddrMode()->SetScale(0);
383     addr->AsAddrMode()->SetOffset(static_cast<int>(offset));
384     addr->SetContained();
385 }
386
387 //------------------------------------------------------------------------
388 // LowerPutArgStkOrSplit: Lower a GT_PUTARG_STK/GT_PUTARG_SPLIT.
389 //
390 // Arguments:
391 //    putArgNode - The node to lower
392 //
393 void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
394 {
395     GenTree* src = putArgNode->Data();
396
397     if (src->TypeIs(TYP_STRUCT))
398     {
399         // STRUCT args (FIELD_LIST / BLK / LCL_VAR / LCL_FLD) will always be contained.
400         MakeSrcContained(putArgNode, src);
401
402         if (src->OperIs(GT_LCL_VAR))
403         {
404             // TODO-1stClassStructs: support struct enregistration here by retyping "src" to its register type for
405             // the non-split case.
406             comp->lvaSetVarDoNotEnregister(src->AsLclVar()->GetLclNum() DEBUGARG(DoNotEnregisterReason::IsStructArg));
407         }
408     }
409 }
410
411 //------------------------------------------------------------------------
412 // LowerCast: Lower GT_CAST(srcType, DstType) nodes.
413 //
414 // Arguments:
415 //    tree - GT_CAST node to be lowered
416 //
417 // Return Value:
418 //    None.
419 //
420 // Notes:
421 //    Casts from float/double to a smaller int type are transformed as follows:
422 //    GT_CAST(float/double, byte)     =   GT_CAST(GT_CAST(float/double, int32), byte)
423 //    GT_CAST(float/double, sbyte)    =   GT_CAST(GT_CAST(float/double, int32), sbyte)
424 //    GT_CAST(float/double, int16)    =   GT_CAST(GT_CAST(double/double, int32), int16)
425 //    GT_CAST(float/double, uint16)   =   GT_CAST(GT_CAST(double/double, int32), uint16)
426 //
427 //    Note that for the overflow conversions we still depend on helper calls and
428 //    don't expect to see them here.
429 //    i) GT_CAST(float/double, int type with overflow detection)
430 //
431
432 void Lowering::LowerCast(GenTree* tree)
433 {
434     assert(tree->OperGet() == GT_CAST);
435
436     JITDUMP("LowerCast for: ");
437     DISPNODE(tree);
438     JITDUMP("\n");
439
440     GenTree*  op1     = tree->AsOp()->gtOp1;
441     var_types dstType = tree->CastToType();
442     var_types srcType = genActualType(op1->TypeGet());
443
444     if (varTypeIsFloating(srcType))
445     {
446         noway_assert(!tree->gtOverflow());
447         assert(!varTypeIsSmall(dstType)); // fgMorphCast creates intermediate casts when converting from float to small
448                                           // int.
449     }
450
451     assert(!varTypeIsSmall(srcType));
452
453     // Now determine if we have operands that should be contained.
454     ContainCheckCast(tree->AsCast());
455 }
456
457 //------------------------------------------------------------------------
458 // LowerRotate: Lower GT_ROL and GT_ROR nodes.
459 //
460 // Arguments:
461 //    tree - the node to lower
462 //
463 // Return Value:
464 //    None.
465 //
466 void Lowering::LowerRotate(GenTree* tree)
467 {
468     ContainCheckShiftRotate(tree->AsOp());
469 }
470
471 #ifdef FEATURE_SIMD
472 //----------------------------------------------------------------------------------------------
473 // Lowering::LowerSIMD: Perform containment analysis for a SIMD intrinsic node.
474 //
475 //  Arguments:
476 //     simdNode - The SIMD intrinsic node.
477 //
478 void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
479 {
480     NYI_RISCV64("LowerSIMD");
481 }
482 #endif // FEATURE_SIMD
483
484 #ifdef FEATURE_HW_INTRINSICS
485 //----------------------------------------------------------------------------------------------
486 // Lowering::LowerHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
487 //
488 //  Arguments:
489 //     node - The hardware intrinsic node.
490 //
491 void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
492 {
493     NYI_RISCV64("LowerHWIntrinsic");
494 }
495
496 //----------------------------------------------------------------------------------------------
497 // Lowering::IsValidConstForMovImm: Determines if the given node can be replaced by a mov/fmov immediate instruction
498 //
499 //  Arguments:
500 //     node - The hardware intrinsic node.
501 //
502 //  Returns:
503 //     true if the node can be replaced by a mov/fmov immediate instruction; otherwise, false
504 //
505 //  IMPORTANT:
506 //     This check may end up modifying node->gtOp1 if it is a cast node that can be removed
507 bool Lowering::IsValidConstForMovImm(GenTreeHWIntrinsic* node)
508 {
509     NYI_RISCV64("IsValidConstForMovImm");
510     return false;
511 }
512
513 //----------------------------------------------------------------------------------------------
514 // Lowering::LowerHWIntrinsicCmpOp: Lowers a Vector128 or Vector256 comparison intrinsic
515 //
516 //  Arguments:
517 //     node  - The hardware intrinsic node.
518 //     cmpOp - The comparison operation, currently must be GT_EQ or GT_NE
519 //
520 void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
521 {
522     NYI_RISCV64("LowerHWIntrinsicCmpOp");
523 }
524
525 //----------------------------------------------------------------------------------------------
526 // Lowering::LowerHWIntrinsicCreate: Lowers a Vector64 or Vector128 Create call
527 //
528 //  Arguments:
529 //     node - The hardware intrinsic node.
530 //
531 void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
532 {
533     NYI_RISCV64("LowerHWIntrinsicCreate");
534 }
535
536 //----------------------------------------------------------------------------------------------
537 // Lowering::LowerHWIntrinsicDot: Lowers a Vector64 or Vector128 Dot call
538 //
539 //  Arguments:
540 //     node - The hardware intrinsic node.
541 //
542 void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
543 {
544     NYI_RISCV64("LowerHWIntrinsicDot");
545 }
546
547 #endif // FEATURE_HW_INTRINSICS
548
549 //------------------------------------------------------------------------
550 // Containment analysis
551 //------------------------------------------------------------------------
552
553 //------------------------------------------------------------------------
554 // ContainCheckCallOperands: Determine whether operands of a call should be contained.
555 //
556 // Arguments:
557 //    call       - The call node of interest
558 //
559 // Return Value:
560 //    None.
561 //
562 void Lowering::ContainCheckCallOperands(GenTreeCall* call)
563 {
564     // There are no contained operands for RISCV64.
565 }
566
567 //------------------------------------------------------------------------
568 // ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained.
569 //
570 // Arguments:
571 //    node - pointer to the node
572 //
573 void Lowering::ContainCheckStoreIndir(GenTreeStoreInd* node)
574 {
575     GenTree* src = node->Data();
576     if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
577     {
578         // an integer zero for 'src' can be contained.
579         MakeSrcContained(node, src);
580     }
581
582     ContainCheckIndir(node);
583 }
584
585 //------------------------------------------------------------------------
586 // ContainCheckIndir: Determine whether operands of an indir should be contained.
587 //
588 // Arguments:
589 //    indirNode - The indirection node of interest
590 //
591 // Notes:
592 //    This is called for both store and load indirections.
593 //
594 // Return Value:
595 //    None.
596 //
597 void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
598 {
599     // If this is the rhs of a block copy it will be handled when we handle the store.
600     if (indirNode->TypeGet() == TYP_STRUCT)
601     {
602         return;
603     }
604
605 #ifdef FEATURE_SIMD
606     NYI_RISCV64("ContainCheckIndir-SIMD");
607 #endif // FEATURE_SIMD
608
609     GenTree* addr = indirNode->Addr();
610     if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirNode, addr))
611     {
612         MakeSrcContained(indirNode, addr);
613     }
614     else if (addr->OperIs(GT_LCL_ADDR))
615     {
616         // These nodes go into an addr mode:
617         // - GT_LCL_ADDR is a stack addr mode.
618         MakeSrcContained(indirNode, addr);
619     }
620     else if (addr->OperIs(GT_CLS_VAR_ADDR))
621     {
622         // These nodes go into an addr mode:
623         // - GT_CLS_VAR_ADDR turns into a constant.
624         // make this contained, it turns into a constant that goes into an addr mode
625         MakeSrcContained(indirNode, addr);
626     }
627 }
628
629 //------------------------------------------------------------------------
630 // ContainCheckBinary: Determine whether a binary op's operands should be contained.
631 //
632 // Arguments:
633 //    node - the node we care about
634 //
635 void Lowering::ContainCheckBinary(GenTreeOp* node)
636 {
637     // Check and make op2 contained (if it is a containable immediate)
638     CheckImmedAndMakeContained(node, node->gtOp2);
639 }
640
641 //------------------------------------------------------------------------
642 // ContainCheckMul: Determine whether a mul op's operands should be contained.
643 //
644 // Arguments:
645 //    node - the node we care about
646 //
647 void Lowering::ContainCheckMul(GenTreeOp* node)
648 {
649     ContainCheckBinary(node);
650 }
651
652 //------------------------------------------------------------------------
653 // ContainCheckDivOrMod: determine which operands of a div/mod should be contained.
654 //
655 // Arguments:
656 //    node - the node we care about
657 //
658 void Lowering::ContainCheckDivOrMod(GenTreeOp* node)
659 {
660     assert(node->OperIs(GT_MOD, GT_UMOD, GT_DIV, GT_UDIV));
661 }
662
663 //------------------------------------------------------------------------
664 // ContainCheckShiftRotate: Determine whether a mul op's operands should be contained.
665 //
666 // Arguments:
667 //    node - the node we care about
668 //
669 void Lowering::ContainCheckShiftRotate(GenTreeOp* node)
670 {
671     GenTree* shiftBy = node->gtOp2;
672     assert(node->OperIsShiftOrRotate());
673
674     if (shiftBy->IsCnsIntOrI())
675     {
676         MakeSrcContained(node, shiftBy);
677     }
678 }
679
680 //------------------------------------------------------------------------
681 // ContainCheckStoreLoc: determine whether the source of a STORE_LCL* should be contained.
682 //
683 // Arguments:
684 //    node - pointer to the node
685 //
686 void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const
687 {
688     assert(storeLoc->OperIsLocalStore());
689     GenTree* op1 = storeLoc->gtGetOp1();
690
691     if (op1->OperIs(GT_BITCAST))
692     {
693         // If we know that the source of the bitcast will be in a register, then we can make
694         // the bitcast itself contained. This will allow us to store directly from the other
695         // type if this node doesn't get a register.
696         GenTree* bitCastSrc = op1->gtGetOp1();
697         if (!bitCastSrc->isContained() && !bitCastSrc->IsRegOptional())
698         {
699             op1->SetContained();
700             return;
701         }
702     }
703
704     const LclVarDsc* varDsc = comp->lvaGetDesc(storeLoc);
705
706 #ifdef FEATURE_SIMD
707     if (storeLoc->TypeIs(TYP_SIMD8, TYP_SIMD12))
708     {
709         // If this is a store to memory, we can initialize a zero vector in memory from REG_ZR.
710         if ((op1->IsIntegralConst(0) || op1->IsVectorZero()) && varDsc->lvDoNotEnregister)
711         {
712             // For an InitBlk we want op1 to be contained
713             MakeSrcContained(storeLoc, op1);
714         }
715         return;
716     }
717 #endif // FEATURE_SIMD
718
719     if (IsContainableImmed(storeLoc, op1))
720     {
721         MakeSrcContained(storeLoc, op1);
722     }
723
724     // If the source is a containable immediate, make it contained, unless it is
725     // an int-size or larger store of zero to memory, because we can generate smaller code
726     // by zeroing a register and then storing it.
727     var_types type = varDsc->GetRegisterType(storeLoc);
728     if (IsContainableImmed(storeLoc, op1) && (!op1->IsIntegralConst(0) || varTypeIsSmall(type)))
729     {
730         MakeSrcContained(storeLoc, op1);
731     }
732 }
733
734 //------------------------------------------------------------------------
735 // ContainCheckCast: determine whether the source of a CAST node should be contained.
736 //
737 // Arguments:
738 //    node - pointer to the node
739 //
740 void Lowering::ContainCheckCast(GenTreeCast* node)
741 {
742     // There are no contained operands for RISCV64.
743 }
744
745 //------------------------------------------------------------------------
746 // ContainCheckCompare: determine whether the sources of a compare node should be contained.
747 //
748 // Arguments:
749 //    node - pointer to the node
750 //
751 void Lowering::ContainCheckCompare(GenTreeOp* cmp)
752 {
753     CheckImmedAndMakeContained(cmp, cmp->gtOp2);
754 }
755
756 //------------------------------------------------------------------------
757 // ContainCheckSelect : determine whether the source of a select should be contained.
758 //
759 // Arguments:
760 //    node - pointer to the node
761 //
762 void Lowering::ContainCheckSelect(GenTreeOp* node)
763 {
764     noway_assert(!"GT_SELECT nodes are not supported on riscv64");
765 }
766
767 //------------------------------------------------------------------------
768 // ContainCheckBoundsChk: determine whether any source of a bounds check node should be contained.
769 //
770 // Arguments:
771 //    node - pointer to the node
772 //
773 void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
774 {
775     assert(node->OperIs(GT_BOUNDS_CHECK));
776     if (!CheckImmedAndMakeContained(node, node->GetIndex()))
777     {
778         CheckImmedAndMakeContained(node, node->GetArrayLength());
779     }
780 }
781
782 #ifdef FEATURE_SIMD
783 //----------------------------------------------------------------------------------------------
784 // ContainCheckSIMD: Perform containment analysis for a SIMD intrinsic node.
785 //
786 //  Arguments:
787 //     simdNode - The SIMD intrinsic node.
788 //
789 void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
790 {
791     NYI_RISCV64("ContainCheckSIMD");
792 }
793 #endif // FEATURE_SIMD
794
795 #ifdef FEATURE_HW_INTRINSICS
796 //----------------------------------------------------------------------------------------------
797 // ContainCheckHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
798 //
799 //  Arguments:
800 //     node - The hardware intrinsic node.
801 //
802 void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
803 {
804     NYI_RISCV64("ContainCheckHWIntrinsic");
805 }
806 #endif // FEATURE_HW_INTRINSICS
807
808 #endif // TARGET_RISCV64