LLVM 23.0.0git
M68kISelDAGToDAG.cpp
Go to the documentation of this file.
1//===-- M68kISelDAGToDAG.cpp - M68k Dag to Dag Inst Selector ----*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file defines an instruction selector for the M68K target.
11///
12//===----------------------------------------------------------------------===//
13
14#include "M68k.h"
15
16#include "M68kMachineFunction.h"
17#include "M68kRegisterInfo.h"
19#include "M68kTargetMachine.h"
27#include "llvm/IR/CFG.h"
28#include "llvm/IR/GlobalValue.h"
30#include "llvm/IR/Intrinsics.h"
31#include "llvm/IR/Type.h"
33#include "llvm/Support/Debug.h"
38
39using namespace llvm;
40
41#define DEBUG_TYPE "m68k-isel"
42#define PASS_NAME "M68k DAG->DAG Pattern Instruction Selection"
43
44namespace {
45
46// For reference, the full order of operands for memory references is:
47// (Operand), Displacement, Base, Index, Scale
48struct M68kISelAddressMode {
49 enum class AddrType {
50 ARI, // Address Register Indirect
51 ARIPI, // Address Register Indirect with Postincrement
52 ARIPD, // Address Register Indirect with Postdecrement
53 ARID, // Address Register Indirect with Displacement
54 ARII, // Address Register Indirect with Index
55 PCD, // Program Counter Indirect with Displacement
56 PCI, // Program Counter Indirect with Index
57 AL, // Absolute
58 };
59 AddrType AM;
60
61 enum class Base { RegBase, FrameIndexBase };
63
64 int64_t Disp;
65
66 // This is really a union, discriminated by BaseType!
67 SDValue BaseReg;
68 int BaseFrameIndex;
69
70 SDValue IndexReg;
71 unsigned Scale;
72
73 const GlobalValue *GV;
74 const Constant *CP;
75 const BlockAddress *BlockAddr;
76 const char *ES;
77 MCSymbol *MCSym;
78 int JT;
79 Align Alignment; // CP alignment.
80
81 unsigned char SymbolFlags; // M68kII::MO_*
82
83 M68kISelAddressMode(AddrType AT)
84 : AM(AT), BaseType(Base::RegBase), Disp(0), BaseFrameIndex(0), IndexReg(),
85 Scale(1), GV(nullptr), CP(nullptr), BlockAddr(nullptr), ES(nullptr),
86 MCSym(nullptr), JT(-1), Alignment(), SymbolFlags(M68kII::MO_NO_FLAG) {}
87
88 bool hasSymbolicDisplacement() const {
89 return GV != nullptr || CP != nullptr || ES != nullptr ||
90 MCSym != nullptr || JT != -1 || BlockAddr != nullptr;
91 }
92
93 bool hasBase() const {
94 return BaseType == Base::FrameIndexBase || BaseReg.getNode() != nullptr;
95 }
96
97 bool hasFrameIndex() const { return BaseType == Base::FrameIndexBase; }
98
99 bool hasBaseReg() const {
100 return BaseType == Base::RegBase && BaseReg.getNode() != nullptr;
101 }
102
103 bool hasIndexReg() const {
104 return BaseType == Base::RegBase && IndexReg.getNode() != nullptr;
105 }
106
107 /// True if address mode type supports displacement
108 bool isDispAddrType() const {
109 return AM == AddrType::ARII || AM == AddrType::PCI ||
110 AM == AddrType::ARID || AM == AddrType::PCD || AM == AddrType::AL;
111 }
112
113 unsigned getDispSize() const {
114 switch (AM) {
115 default:
116 return 0;
117 case AddrType::ARII:
118 case AddrType::PCI:
119 return 8;
120 // These two in the next chip generations can hold upto 32 bit
121 case AddrType::ARID:
122 case AddrType::PCD:
123 return 16;
124 case AddrType::AL:
125 return 32;
126 }
127 }
128
129 bool hasDisp() const { return getDispSize() != 0; }
130 bool isDisp8() const { return getDispSize() == 8; }
131 bool isDisp16() const { return getDispSize() == 16; }
132 bool isDisp32() const { return getDispSize() == 32; }
133
134 /// Return true if this addressing mode is already PC-relative.
135 bool isPCRelative() const {
136 if (BaseType != Base::RegBase)
137 return false;
138 if (auto *RegNode = dyn_cast_or_null<RegisterSDNode>(BaseReg.getNode()))
139 return RegNode->getReg() == M68k::PC;
140 return false;
141 }
142
143 void setBaseReg(SDValue Reg) {
144 BaseType = Base::RegBase;
145 BaseReg = Reg;
146 }
147
148 void setIndexReg(SDValue Reg) { IndexReg = Reg; }
149
150#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
151 void dump() {
152 dbgs() << "M68kISelAddressMode " << this;
153 dbgs() << "\nDisp: " << Disp;
154 dbgs() << ", BaseReg: ";
155 if (BaseReg.getNode())
156 BaseReg.getNode()->dump();
157 else
158 dbgs() << "null";
159 dbgs() << ", BaseFI: " << BaseFrameIndex;
160 dbgs() << ", IndexReg: ";
161 if (IndexReg.getNode()) {
162 IndexReg.getNode()->dump();
163 } else {
164 dbgs() << "null";
165 dbgs() << ", Scale: " << Scale;
166 }
167 dbgs() << '\n';
168 }
169#endif
170};
171} // end anonymous namespace
172
173namespace {
174
175// Helper type used by isSafeStoreLoad. Used to determine if
176// it is safe to fold a load and store into a single operation.
177struct CallSeqChainInfo {
178 // The nearest callseq_{start/end} (or lowered equivalent)
179 // in the chain of the load or store currently being analyzed.
180 SDNode *Node = nullptr;
181 // True when a TokenFactor introduces a dependency on more than one
182 // chain with a callseq_{start/end} (or lowered equivalent) to the load
183 // or store currently being analyzed
184 bool Multiple = false;
185};
186
187static bool isCallSeqNode(const SDNode *N) {
188 if (N->getOpcode() == ISD::CALLSEQ_START ||
189 N->getOpcode() == ISD::CALLSEQ_END)
190 return true;
191 if (N->isMachineOpcode()) {
192 unsigned Opc = N->getMachineOpcode();
193 return Opc == M68k::ADJCALLSTACKDOWN || Opc == M68k::ADJCALLSTACKUP;
194 }
195 return false;
196}
197
198static CallSeqChainInfo getCallSeqChainInfo(SDValue Chain) {
199 SmallVector<SDValue, 8> Worklist = {Chain};
201 SDNode *Found = nullptr;
202
203 while (!Worklist.empty()) {
204 SDNode *CN = Worklist.pop_back_val().getNode();
205 if (!CN || !Visited.insert(CN).second)
206 continue;
207
208 if (isCallSeqNode(CN)) {
209 if (!Found)
210 Found = CN;
211 else if (Found != CN)
212 return CallSeqChainInfo{nullptr, true};
213 }
214
215 if (CN->getOpcode() == ISD::TokenFactor) {
216 for (const SDValue &Op : CN->op_values())
217 if (Op.getValueType() == MVT::Other)
218 Worklist.push_back(Op);
219 continue;
220 }
221
222 for (const SDValue &Op : CN->op_values()) {
223 if (Op.getValueType() == MVT::Other) {
224 if (Worklist.size() == 8) {
225 // We can't actually evaluate all branches,
226 // be pessimistic and fail out.
227 return CallSeqChainInfo{nullptr, true};
228 }
229 Worklist.push_back(Op);
230 break;
231 }
232 }
233 }
234
235 return CallSeqChainInfo{Found, false};
236}
237
238// Helper for use in TableGen. We can't safely use a combined load/store in the
239// case where a token factor can cause a chain dep on a different call sequence.
240// Look for that case and return false if we can't confirm it's safe. This is
241// necessary due to the nesting level tracking in
242// ScheduleDAGRRList::FindCallSeqStart.
243static bool isSafeStoreLoad(SDNode *N) {
244 auto *ST = dyn_cast<StoreSDNode>(N);
245 if (!ST)
246 return false;
247 auto *LD = dyn_cast<LoadSDNode>(ST->getValue());
248 if (!LD)
249 return false;
250 // Load and store chains can be unrelated; guard against either side
251 // depending on a different call sequence boundary.
252 CallSeqChainInfo LoadInfo = getCallSeqChainInfo(LD->getChain());
253 CallSeqChainInfo StoreInfo = getCallSeqChainInfo(ST->getChain());
254 if (LoadInfo.Multiple || StoreInfo.Multiple)
255 return false;
256 if (!LoadInfo.Node && !StoreInfo.Node)
257 return true;
258 return LoadInfo.Node && StoreInfo.Node && LoadInfo.Node == StoreInfo.Node;
259}
260
261class M68kDAGToDAGISel : public SelectionDAGISel {
262public:
263 M68kDAGToDAGISel() = delete;
264
265 explicit M68kDAGToDAGISel(M68kTargetMachine &TM)
266 : SelectionDAGISel(TM), Subtarget(nullptr) {}
267
268 bool runOnMachineFunction(MachineFunction &MF) override;
269 bool IsProfitableToFold(SDValue N, SDNode *U, SDNode *Root) const override;
270
271private:
272 /// Keep a pointer to the M68kSubtarget around so that we can
273 /// make the right decision when generating code for different targets.
274 const M68kSubtarget *Subtarget;
275
276// Include the pieces autogenerated from the target description.
277#include "M68kGenDAGISel.inc"
278
279 /// getTargetMachine - Return a reference to the TargetMachine, casted
280 /// to the target-specific type.
281 const M68kTargetMachine &getTargetMachine() {
282 return static_cast<const M68kTargetMachine &>(TM);
283 }
284
285 void Select(SDNode *N) override;
286
287 // Insert instructions to initialize the global base register in the
288 // first MBB of the function.
289 // HMM... do i need this?
290 void initGlobalBaseReg(MachineFunction &MF);
291
292 bool foldOffsetIntoAddress(uint64_t Offset, M68kISelAddressMode &AM);
293
294 bool matchLoadInAddress(LoadSDNode *N, M68kISelAddressMode &AM);
295 bool matchAddress(SDValue N, M68kISelAddressMode &AM);
296 bool matchAddressBase(SDValue N, M68kISelAddressMode &AM);
297 bool matchAddressRecursively(SDValue N, M68kISelAddressMode &AM,
298 unsigned Depth);
299 bool matchADD(SDValue &N, M68kISelAddressMode &AM, unsigned Depth);
300 bool matchWrapper(SDValue N, M68kISelAddressMode &AM);
301
302 std::pair<bool, SDNode *> selectNode(SDNode *Node);
303
304 bool SelectARI(SDNode *Parent, SDValue N, SDValue &Base);
305 bool SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base);
306 bool SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base);
307 bool SelectARID(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base);
308 bool SelectARII(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base,
309 SDValue &Index);
310 bool SelectAL(SDNode *Parent, SDValue N, SDValue &Sym);
311 bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm);
312 bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index);
313 bool SelectPCIBD(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index,
314 SDValue &Scale);
315
316 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
317 InlineAsm::ConstraintCode ConstraintID,
318 std::vector<SDValue> &OutOps) override;
319
320 // If Address Mode represents Frame Index store FI in Disp and
321 // Displacement bit size in Base. These values are read symmetrically by
322 // M68kRegisterInfo::eliminateFrameIndex method
323 inline bool getFrameIndexAddress(M68kISelAddressMode &AM, const SDLoc &DL,
324 SDValue &Disp, SDValue &Base) {
325 if (AM.BaseType == M68kISelAddressMode::Base::FrameIndexBase) {
326 Disp = getI32Imm(AM.Disp, DL);
327 Base = CurDAG->getTargetFrameIndex(
328 AM.BaseFrameIndex, TLI->getPointerTy(CurDAG->getDataLayout()));
329 return true;
330 }
331
332 return false;
333 }
334
335 // Gets a symbol plus optional displacement
336 inline bool getSymbolicDisplacement(M68kISelAddressMode &AM, const SDLoc &DL,
337 SDValue &Sym) {
338 if (AM.GV) {
339 Sym = CurDAG->getTargetGlobalAddress(AM.GV, SDLoc(), MVT::i32, AM.Disp,
340 AM.SymbolFlags);
341 return true;
342 }
343
344 if (AM.CP) {
345 Sym = CurDAG->getTargetConstantPool(AM.CP, MVT::i32, AM.Alignment,
346 AM.Disp, AM.SymbolFlags);
347 return true;
348 }
349
350 if (AM.ES) {
351 assert(!AM.Disp && "Non-zero displacement is ignored with ES.");
352 Sym = CurDAG->getTargetExternalSymbol(AM.ES, MVT::i32, AM.SymbolFlags);
353 return true;
354 }
355
356 if (AM.MCSym) {
357 assert(!AM.Disp && "Non-zero displacement is ignored with MCSym.");
358 assert(AM.SymbolFlags == 0 && "oo");
359 Sym = CurDAG->getMCSymbol(AM.MCSym, MVT::i32);
360 return true;
361 }
362
363 if (AM.JT != -1) {
364 assert(!AM.Disp && "Non-zero displacement is ignored with JT.");
365 Sym = CurDAG->getTargetJumpTable(AM.JT, MVT::i32, AM.SymbolFlags);
366 return true;
367 }
368
369 if (AM.BlockAddr) {
370 Sym = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp,
371 AM.SymbolFlags);
372 return true;
373 }
374
375 return false;
376 }
377
378 /// Return a target constant with the specified value of type i8.
379 inline SDValue getI8Imm(int64_t Imm, const SDLoc &DL) {
380 return CurDAG->getSignedTargetConstant(Imm, DL, MVT::i8);
381 }
382
383 /// Return a target constant with the specified value of type i8.
384 inline SDValue getI16Imm(int64_t Imm, const SDLoc &DL) {
385 return CurDAG->getSignedTargetConstant(Imm, DL, MVT::i16);
386 }
387
388 /// Return a target constant with the specified value, of type i32.
389 inline SDValue getI32Imm(int64_t Imm, const SDLoc &DL) {
390 return CurDAG->getSignedTargetConstant(Imm, DL, MVT::i32);
391 }
392
393 /// Return a reference to the TargetInstrInfo, casted to the target-specific
394 /// type.
395 const M68kInstrInfo *getInstrInfo() const {
396 return Subtarget->getInstrInfo();
397 }
398
399 /// Return an SDNode that returns the value of the global base register.
400 /// Output instructions required to initialize the global base register,
401 /// if necessary.
402 SDNode *getGlobalBaseReg();
403};
404
405class M68kDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
406public:
407 static char ID;
408 explicit M68kDAGToDAGISelLegacy(M68kTargetMachine &TM)
409 : SelectionDAGISelLegacy(ID, std::make_unique<M68kDAGToDAGISel>(TM)) {}
410};
411
412char M68kDAGToDAGISelLegacy::ID;
413
414} // namespace
415
416INITIALIZE_PASS(M68kDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
417
418bool M68kDAGToDAGISel::IsProfitableToFold(SDValue N, SDNode *U,
419 SDNode *Root) const {
420 if (OptLevel == CodeGenOptLevel::None)
421 return false;
422
423 if (U == Root) {
424 switch (U->getOpcode()) {
425 default:
426 return true;
427 case M68kISD::SUB:
428 case ISD::SUB:
429 // Prefer NEG instruction when zero subtracts a value.
430 // e.g.
431 // move.l #0, %d0
432 // sub.l (4,%sp), %d0
433 // vs.
434 // move.l (4,%sp), %d0
435 // neg.l %d0
436 if (llvm::isNullConstant(U->getOperand(0)))
437 return false;
438 break;
439 }
440 }
441
442 return true;
443}
444
445bool M68kDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
446 Subtarget = &MF.getSubtarget<M68kSubtarget>();
448}
449
450/// This pass converts a legalized DAG into a M68k-specific DAG,
451/// ready for instruction scheduling.
453 return new M68kDAGToDAGISelLegacy(TM);
454}
455
456static bool doesDispFitFI(M68kISelAddressMode &AM) {
457 if (!AM.isDispAddrType())
458 return false;
459 // -1 to make sure that resolved FI will fit into Disp field
460 return isIntN(AM.getDispSize() - 1, AM.Disp);
461}
462
463static bool doesDispFit(M68kISelAddressMode &AM, int64_t Val) {
464 if (!AM.isDispAddrType())
465 return false;
466 return isIntN(AM.getDispSize(), Val);
467}
468
469/// Return an SDNode that returns the value of the global base register.
470/// Output instructions required to initialize the global base register,
471/// if necessary.
472SDNode *M68kDAGToDAGISel::getGlobalBaseReg() {
473 unsigned GlobalBaseReg = getInstrInfo()->getGlobalBaseReg(MF);
474 auto &DL = MF->getDataLayout();
475 return CurDAG->getRegister(GlobalBaseReg, TLI->getPointerTy(DL)).getNode();
476}
477
478bool M68kDAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
479 M68kISelAddressMode &AM) {
480 // Cannot combine ExternalSymbol displacements with integer offsets.
481 if (Offset != 0 && (AM.ES || AM.MCSym))
482 return false;
483
484 int64_t Val = AM.Disp + Offset;
485
486 if (doesDispFit(AM, Val)) {
487 AM.Disp = Val;
488 return true;
489 }
490
491 return false;
492}
493
494//===----------------------------------------------------------------------===//
495// Matchers
496//===----------------------------------------------------------------------===//
497
498/// Helper for MatchAddress. Add the specified node to the
499/// specified addressing mode without any further recursion.
500bool M68kDAGToDAGISel::matchAddressBase(SDValue N, M68kISelAddressMode &AM) {
501 // Is the base register already occupied?
502 if (AM.hasBase()) {
503 // If so, check to see if the scale index register is set.
504 if (!AM.hasIndexReg()) {
505 AM.IndexReg = N;
506 AM.Scale = 1;
507 return true;
508 }
509
510 // Otherwise, we cannot select it.
511 return false;
512 }
513
514 // Default, generate it as a register.
515 AM.BaseType = M68kISelAddressMode::Base::RegBase;
516 AM.BaseReg = N;
517 return true;
518}
519
520/// TODO Add TLS support
521bool M68kDAGToDAGISel::matchLoadInAddress(LoadSDNode *N,
522 M68kISelAddressMode &AM) {
523 return false;
524}
525
526bool M68kDAGToDAGISel::matchAddressRecursively(SDValue N,
527 M68kISelAddressMode &AM,
528 unsigned Depth) {
529 SDLoc DL(N);
530
531 // Limit recursion.
532 if (Depth > 5)
533 return matchAddressBase(N, AM);
534
535 // If this is already a %PC relative address, we can only merge immediates
536 // into it. Instead of handling this in every case, we handle it here.
537 // PC relative addressing: %PC + 16-bit displacement!
538 if (AM.isPCRelative()) {
539 // FIXME JumpTable and ExternalSymbol address currently don't like
540 // displacements. It isn't very important, but should be fixed for
541 // consistency.
542
543 if (ConstantSDNode *Cst = dyn_cast<ConstantSDNode>(N))
544 if (foldOffsetIntoAddress(Cst->getSExtValue(), AM))
545 return true;
546 return false;
547 }
548
549 switch (N.getOpcode()) {
550 default:
551 break;
552
553 case ISD::Constant: {
554 uint64_t Val = cast<ConstantSDNode>(N)->getSExtValue();
555 if (foldOffsetIntoAddress(Val, AM))
556 return true;
557 break;
558 }
559
560 case M68kISD::Wrapper:
561 case M68kISD::WrapperPC:
562 if (matchWrapper(N, AM))
563 return true;
564 break;
565
566 case ISD::LOAD:
567 if (matchLoadInAddress(cast<LoadSDNode>(N), AM))
568 return true;
569 break;
570
571 case ISD::OR:
572 // We want to look through a transform in InstCombine and DAGCombiner that
573 // turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'.
574 // Example: (or (and x, 1), (shl y, 3)) --> (add (and x, 1), (shl y, 3))
575 // An 'lea' can then be used to match the shift (multiply) and add:
576 // and $1, %esi
577 // lea (%rsi, %rdi, 8), %rax
578 if (CurDAG->haveNoCommonBitsSet(N.getOperand(0), N.getOperand(1)) &&
579 matchADD(N, AM, Depth))
580 return true;
581 break;
582
583 case ISD::ADD:
584 if (matchADD(N, AM, Depth))
585 return true;
586 break;
587
588 case ISD::FrameIndex:
589 if (AM.isDispAddrType() &&
590 AM.BaseType == M68kISelAddressMode::Base::RegBase &&
591 AM.BaseReg.getNode() == nullptr && doesDispFitFI(AM)) {
592 AM.BaseType = M68kISelAddressMode::Base::FrameIndexBase;
593 AM.BaseFrameIndex = cast<FrameIndexSDNode>(N)->getIndex();
594 return true;
595 }
596 break;
597
599 GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(N);
600 AM.GV = GA->getGlobal();
601 AM.SymbolFlags = GA->getTargetFlags();
602 return true;
603 }
604 }
605
606 return matchAddressBase(N, AM);
607}
608
609/// Add the specified node to the specified addressing mode, returning true if
610/// it cannot be done. This just pattern matches for the addressing mode.
611bool M68kDAGToDAGISel::matchAddress(SDValue N, M68kISelAddressMode &AM) {
612 // TODO: Post-processing: Convert lea(,%reg,2) to lea(%reg,%reg), which has
613 // a smaller encoding and avoids a scaled-index.
614 // And make sure it is an indexed mode
615
616 // TODO: Post-processing: Convert foo to foo(%pc), even in non-PIC mode,
617 // because it has a smaller encoding.
618 // Make sure this must be done only if PC* modes are currently being matched
619 return matchAddressRecursively(N, AM, 0);
620}
621
622bool M68kDAGToDAGISel::matchADD(SDValue &N, M68kISelAddressMode &AM,
623 unsigned Depth) {
624 // Add an artificial use to this node so that we can keep track of
625 // it if it gets CSE'd with a different node.
626 HandleSDNode Handle(N);
627
628 M68kISelAddressMode Backup = AM;
629 if (matchAddressRecursively(N.getOperand(0), AM, Depth + 1) &&
630 matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1)) {
631 return true;
632 }
633 AM = Backup;
634
635 // Try again after commuting the operands.
636 if (matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1) &&
637 matchAddressRecursively(Handle.getValue().getOperand(0), AM, Depth + 1)) {
638 return true;
639 }
640 AM = Backup;
641
642 // If we couldn't fold both operands into the address at the same time,
643 // see if we can just put each operand into a register and fold at least
644 // the add.
645 if (!AM.hasBase() && !AM.hasIndexReg()) {
646 N = Handle.getValue();
647 AM.BaseReg = N.getOperand(0);
648 AM.IndexReg = N.getOperand(1);
649 AM.Scale = 1;
650 return true;
651 }
652
653 N = Handle.getValue();
654 return false;
655}
656
657/// Try to match M68kISD::Wrapper and M68kISD::WrapperPC nodes into an
658/// addressing mode. These wrap things that will resolve down into a symbol
659/// reference. If no match is possible, this returns true, otherwise it returns
660/// false.
661bool M68kDAGToDAGISel::matchWrapper(SDValue N, M68kISelAddressMode &AM) {
662 // If the addressing mode already has a symbol as the displacement, we can
663 // never match another symbol.
664 if (AM.hasSymbolicDisplacement())
665 return false;
666
667 SDValue N0 = N.getOperand(0);
668
669 if (N.getOpcode() == M68kISD::WrapperPC) {
670
671 // If cannot match here just restore the old version
672 M68kISelAddressMode Backup = AM;
673
674 if (AM.hasBase()) {
675 return false;
676 }
677
678 if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) {
679 AM.GV = G->getGlobal();
680 AM.SymbolFlags = G->getTargetFlags();
681 if (!foldOffsetIntoAddress(G->getOffset(), AM)) {
682 AM = Backup;
683 return false;
684 }
685 } else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) {
686 AM.CP = CP->getConstVal();
687 AM.Alignment = CP->getAlign();
688 AM.SymbolFlags = CP->getTargetFlags();
689 if (!foldOffsetIntoAddress(CP->getOffset(), AM)) {
690 AM = Backup;
691 return false;
692 }
693 } else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) {
694 AM.ES = S->getSymbol();
695 AM.SymbolFlags = S->getTargetFlags();
696 } else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) {
697 AM.MCSym = S->getMCSymbol();
698 } else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) {
699 AM.JT = J->getIndex();
700 AM.SymbolFlags = J->getTargetFlags();
701 } else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) {
702 AM.BlockAddr = BA->getBlockAddress();
703 AM.SymbolFlags = BA->getTargetFlags();
704 if (!foldOffsetIntoAddress(BA->getOffset(), AM)) {
705 AM = Backup;
706 return false;
707 }
708 } else
709 llvm_unreachable("Unhandled symbol reference node.");
710
711 AM.setBaseReg(CurDAG->getRegister(M68k::PC, MVT::i32));
712 return true;
713 }
714
715 // This wrapper requires 32bit disp/imm field for Medium CM
716 if (!AM.isDisp32()) {
717 return false;
718 }
719
720 if (N.getOpcode() == M68kISD::Wrapper) {
721 if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) {
722 AM.GV = G->getGlobal();
723 AM.Disp += G->getOffset();
724 AM.SymbolFlags = G->getTargetFlags();
725 } else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) {
726 AM.CP = CP->getConstVal();
727 AM.Alignment = CP->getAlign();
728 AM.Disp += CP->getOffset();
729 AM.SymbolFlags = CP->getTargetFlags();
730 } else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) {
731 AM.ES = S->getSymbol();
732 AM.SymbolFlags = S->getTargetFlags();
733 } else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) {
734 AM.MCSym = S->getMCSymbol();
735 } else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) {
736 AM.JT = J->getIndex();
737 AM.SymbolFlags = J->getTargetFlags();
738 } else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) {
739 AM.BlockAddr = BA->getBlockAddress();
740 AM.Disp += BA->getOffset();
741 AM.SymbolFlags = BA->getTargetFlags();
742 } else
743 llvm_unreachable("Unhandled symbol reference node.");
744 return true;
745 }
746
747 return false;
748}
749
750//===----------------------------------------------------------------------===//
751// Selectors
752//===----------------------------------------------------------------------===//
753
754void M68kDAGToDAGISel::Select(SDNode *Node) {
755 unsigned Opcode = Node->getOpcode();
756 SDLoc DL(Node);
757
758 LLVM_DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << '\n');
759
760 if (Node->isMachineOpcode()) {
761 LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n');
762 Node->setNodeId(-1);
763 return; // Already selected.
764 }
765
766 switch (Opcode) {
767 default:
768 break;
769
771 SDValue GOT = CurDAG->getTargetExternalSymbol(
772 "_GLOBAL_OFFSET_TABLE_", MVT::i32, M68kII::MO_GOTPCREL);
773 MachineSDNode *Res =
774 CurDAG->getMachineNode(M68k::LEA32q, DL, MVT::i32, GOT);
775 ReplaceNode(Node, Res);
776 return;
777 }
778
779 case M68kISD::GLOBAL_BASE_REG:
780 ReplaceNode(Node, getGlobalBaseReg());
781 return;
782 }
783
784 SelectCode(Node);
785}
786
787bool M68kDAGToDAGISel::SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base) {
788 LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPI: ");
789 LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n");
790 return false;
791}
792
793bool M68kDAGToDAGISel::SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base) {
794 LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPD: ");
795 LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n");
796 return false;
797}
798
799[[maybe_unused]] static bool allowARIDWithDisp(SDNode *Parent) {
800 if (!Parent)
801 return false;
802 switch (Parent->getOpcode()) {
803 case ISD::LOAD:
804 case ISD::STORE:
805 case ISD::ATOMIC_LOAD:
807 return true;
808 default:
809 return false;
810 }
811}
812
813bool M68kDAGToDAGISel::SelectARID(SDNode *Parent, SDValue N, SDValue &Disp,
814 SDValue &Base) {
815 LLVM_DEBUG(dbgs() << "Selecting AddrType::ARID: ");
816 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARID);
817
818 if (!matchAddress(N, AM))
819 return false;
820
821 if (AM.isPCRelative()) {
822 LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
823 return false;
824 }
825
826 // If this is a frame index, grab it
827 if (getFrameIndexAddress(AM, SDLoc(N), Disp, Base)) {
828 LLVM_DEBUG(dbgs() << "SUCCESS matched FI\n");
829 return true;
830 }
831
832 if (AM.hasIndexReg()) {
833 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
834 return false;
835 }
836
837 if (!AM.hasBaseReg()) {
838 LLVM_DEBUG(dbgs() << "REJECT: No Base reg\n");
839 return false;
840 }
841
842 Base = AM.BaseReg;
843
844 if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
845 assert((!AM.Disp || allowARIDWithDisp(Parent)) &&
846 "Should not be any displacement");
847 LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
848 return true;
849 }
850
851 // Give a chance to AddrType::ARI
852 if (AM.Disp == 0) {
853 LLVM_DEBUG(dbgs() << "REJECT: No displacement\n");
854 return false;
855 }
856
857 Disp = getI16Imm(AM.Disp, SDLoc(N));
858
859 LLVM_DEBUG(dbgs() << "SUCCESS\n");
860 return true;
861}
862
863static bool isAddressBase(const SDValue &N) {
864 switch (N.getOpcode()) {
865 case ISD::ADD:
866 case ISD::ADDC:
867 return llvm::any_of(N.getNode()->ops(),
868 [](const SDUse &U) { return isAddressBase(U.get()); });
869 case M68kISD::Wrapper:
870 case M68kISD::WrapperPC:
871 case M68kISD::GLOBAL_BASE_REG:
872 return true;
873 default:
874 return false;
875 }
876}
877
878static bool AllowARIIWithZeroDisp(SDNode *Parent) {
879 if (!Parent)
880 return false;
881 switch (Parent->getOpcode()) {
882 case ISD::LOAD:
883 case ISD::STORE:
884 case ISD::ATOMIC_LOAD:
887 return true;
888 default:
889 return false;
890 }
891}
892
893bool M68kDAGToDAGISel::SelectARII(SDNode *Parent, SDValue N, SDValue &Disp,
894 SDValue &Base, SDValue &Index) {
895 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARII);
896 LLVM_DEBUG(dbgs() << "Selecting AddrType::ARII: ");
897
898 if (!matchAddress(N, AM))
899 return false;
900
901 if (AM.isPCRelative()) {
902 LLVM_DEBUG(dbgs() << "REJECT: PC relative\n");
903 return false;
904 }
905
906 if (!AM.hasIndexReg()) {
907 LLVM_DEBUG(dbgs() << "REJECT: No Index\n");
908 return false;
909 }
910
911 if (!AM.hasBaseReg()) {
912 LLVM_DEBUG(dbgs() << "REJECT: No Base\n");
913 return false;
914 }
915
916 if (!isAddressBase(AM.BaseReg) && isAddressBase(AM.IndexReg)) {
917 Base = AM.IndexReg;
918 Index = AM.BaseReg;
919 } else {
920 Base = AM.BaseReg;
921 Index = AM.IndexReg;
922 }
923
924 if (AM.hasSymbolicDisplacement()) {
925 LLVM_DEBUG(dbgs() << "REJECT, Cannot match symbolic displacement\n");
926 return false;
927 }
928
929 // The idea here is that we want to use AddrType::ARII without displacement
930 // only if necessary like memory operations, otherwise this must be lowered
931 // into addition
932 if (AM.Disp == 0 && !AllowARIIWithZeroDisp(Parent)) {
933 LLVM_DEBUG(dbgs() << "REJECT: Displacement is Zero\n");
934 return false;
935 }
936
937 Disp = getI8Imm(AM.Disp, SDLoc(N));
938
939 LLVM_DEBUG(dbgs() << "SUCCESS\n");
940 return true;
941}
942
943bool M68kDAGToDAGISel::SelectAL(SDNode *Parent, SDValue N, SDValue &Sym) {
944 LLVM_DEBUG(dbgs() << "Selecting AddrType::AL: ");
945 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::AL);
946
947 if (!matchAddress(N, AM)) {
948 LLVM_DEBUG(dbgs() << "REJECT: Match failed\n");
949 return false;
950 }
951
952 if (AM.isPCRelative()) {
953 LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
954 return false;
955 }
956
957 if (AM.hasBase()) {
958 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Base\n");
959 return false;
960 }
961
962 if (AM.hasIndexReg()) {
963 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
964 return false;
965 }
966
967 if (getSymbolicDisplacement(AM, SDLoc(N), Sym)) {
968 LLVM_DEBUG(dbgs() << "SUCCESS: Matched symbol\n");
969 return true;
970 }
971
972 if (AM.Disp) {
973 Sym = getI32Imm(AM.Disp, SDLoc(N));
974 LLVM_DEBUG(dbgs() << "SUCCESS\n");
975 return true;
976 }
977
978 LLVM_DEBUG(dbgs() << "REJECT: Not Symbol or Disp\n");
979 return false;
980 ;
981}
982
983bool M68kDAGToDAGISel::SelectPCD(SDNode *Parent, SDValue N, SDValue &Disp) {
984 LLVM_DEBUG(dbgs() << "Selecting AddrType::PCD: ");
985 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCD);
986
987 if (!matchAddress(N, AM))
988 return false;
989
990 if (!AM.isPCRelative()) {
991 LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n");
992 return false;
993 }
994
995 if (AM.hasIndexReg()) {
996 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
997 return false;
998 }
999
1000 if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
1001 LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
1002 return true;
1003 }
1004
1005 Disp = getI16Imm(AM.Disp, SDLoc(N));
1006
1007 LLVM_DEBUG(dbgs() << "SUCCESS\n");
1008 return true;
1009}
1010
1011bool M68kDAGToDAGISel::SelectPCI(SDNode *Parent, SDValue N, SDValue &Disp,
1012 SDValue &Index) {
1013 LLVM_DEBUG(dbgs() << "Selecting AddrType::PCI: ");
1014 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCI);
1015
1016 if (!matchAddress(N, AM))
1017 return false;
1018
1019 if (!AM.isPCRelative()) {
1020 LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n");
1021 return false;
1022 }
1023
1024 if (!AM.hasIndexReg()) {
1025 LLVM_DEBUG(dbgs() << "REJECT: No Index\n");
1026 return false;
1027 }
1028
1029 Index = AM.IndexReg;
1030
1031 if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
1032 assert(!AM.Disp && "Should not be any displacement");
1033 LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
1034 return true;
1035 }
1036
1037 Disp = getI8Imm(AM.Disp, SDLoc(N));
1038
1039 LLVM_DEBUG(dbgs() << "SUCCESS\n");
1040 return true;
1041}
1042
1043bool M68kDAGToDAGISel::SelectPCIBD(SDNode *Parent, SDValue N, SDValue &Disp,
1044 SDValue &Index, SDValue &Scale) {
1045 // TODO: Implement the actual selection logic on SCALE. Currently this is
1046 // just a placeholder.
1047 if (SelectPCI(Parent, N, Disp, Index)) {
1048 Scale = CurDAG->getTargetConstant(1, SDLoc(N), MVT::i8);
1049 return true;
1050 }
1051 return false;
1052}
1053bool M68kDAGToDAGISel::SelectARI(SDNode *Parent, SDValue N, SDValue &Base) {
1054 LLVM_DEBUG(dbgs() << "Selecting AddrType::ARI: ");
1055 M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARI);
1056
1057 if (!matchAddress(N, AM)) {
1058 LLVM_DEBUG(dbgs() << "REJECT: Match failed\n");
1059 return false;
1060 }
1061
1062 if (AM.isPCRelative()) {
1063 LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
1064 return false;
1065 }
1066
1067 // AddrType::ARI does not use these
1068 if (AM.hasIndexReg() || AM.Disp != 0) {
1069 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index or Disp\n");
1070 return false;
1071 }
1072
1073 // Must be matched by AddrType::AL
1074 if (AM.hasSymbolicDisplacement()) {
1075 LLVM_DEBUG(dbgs() << "REJECT: Cannot match Symbolic Disp\n");
1076 return false;
1077 }
1078
1079 if (AM.hasBaseReg()) {
1080 Base = AM.BaseReg;
1081 LLVM_DEBUG(dbgs() << "SUCCESS\n");
1082 return true;
1083 }
1084
1085 return false;
1086}
1087
1088bool M68kDAGToDAGISel::SelectInlineAsmMemoryOperand(
1089 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
1090 std::vector<SDValue> &OutOps) {
1091 // In order to tell AsmPrinter the exact addressing mode we select here, which
1092 // might comprise of multiple SDValues (hence MachineOperands), a 32-bit
1093 // immediate value is prepended to the list of selected SDValues to indicate
1094 // the addressing mode kind.
1095 using AMK = M68k::MemAddrModeKind;
1096 auto addKind = [this](SDValue &Opnd, AMK Kind) -> bool {
1097 Opnd = CurDAG->getTargetConstant(unsigned(Kind), SDLoc(), MVT::i32);
1098 return true;
1099 };
1100
1101 switch (ConstraintID) {
1102 // Generic memory operand.
1103 case InlineAsm::ConstraintCode::m: {
1104 // Try every supported (memory) addressing modes.
1105 SDValue Operands[4];
1106
1107 // TODO: The ordering of the following SelectXXX is relatively...arbitrary,
1108 // right now we simply sort them by descending complexity. Maybe we should
1109 // adjust this by code model and/or relocation mode in the future.
1110 if (SelectARII(nullptr, Op, Operands[1], Operands[2], Operands[3]) &&
1111 addKind(Operands[0], AMK::f)) {
1112 OutOps.insert(OutOps.end(), &Operands[0], Operands + 4);
1113 return false;
1114 }
1115
1116 if ((SelectPCI(nullptr, Op, Operands[1], Operands[2]) &&
1117 addKind(Operands[0], AMK::k)) ||
1118 (SelectARID(nullptr, Op, Operands[1], Operands[2]) &&
1119 addKind(Operands[0], AMK::p))) {
1120 OutOps.insert(OutOps.end(), &Operands[0], Operands + 3);
1121 return false;
1122 }
1123
1124 if ((SelectPCD(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::q)) ||
1125 (SelectARI(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::j)) ||
1126 (SelectAL(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::b))) {
1127 OutOps.insert(OutOps.end(), {Operands[0], Operands[1]});
1128 return false;
1129 }
1130
1131 return true;
1132 }
1133 // 'Q': Address register indirect addressing.
1134 case InlineAsm::ConstraintCode::Q: {
1135 SDValue AMKind, Base;
1136 // 'j' addressing mode.
1137 // TODO: Add support for 'o' and 'e' after their
1138 // select functions are implemented.
1139 if (SelectARI(nullptr, Op, Base) && addKind(AMKind, AMK::j)) {
1140 OutOps.insert(OutOps.end(), {AMKind, Base});
1141 return false;
1142 }
1143 return true;
1144 }
1145 // 'U': Address register indirect w/ constant offset addressing.
1146 case InlineAsm::ConstraintCode::Um: {
1147 SDValue AMKind, Base, Offset;
1148 // 'p' addressing mode.
1149 if (SelectARID(nullptr, Op, Offset, Base) && addKind(AMKind, AMK::p)) {
1150 OutOps.insert(OutOps.end(), {AMKind, Offset, Base});
1151 return false;
1152 }
1153 return true;
1154 }
1155 default:
1156 return true;
1157 }
1158}
return SDValue()
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define DEBUG_TYPE
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
static bool doesDispFit(M68kISelAddressMode &AM, int64_t Val)
static bool allowARIDWithDisp(SDNode *Parent)
static bool doesDispFitFI(M68kISelAddressMode &AM)
static bool isAddressBase(const SDValue &N)
static bool AllowARIIWithZeroDisp(SDNode *Parent)
This file declares the M68k specific subclass of MachineFunctionInfo.
This file contains the M68k implementation of the TargetRegisterInfo class.
This file declares the M68k specific subclass of TargetMachine.
This file contains the entry points for global functions defined in the M68k target library,...
#define G(x, y, z)
Definition MD5.cpp:55
This file declares the MachineConstantPool class which is an abstract constant pool to keep track of ...
Register Reg
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
BaseType
A given derived pointer can have multiple base pointers through phi/selects.
#define LLVM_DEBUG(...)
Definition Debug.h:119
#define PASS_NAME
static bool initGlobalBaseReg(MachineFunction &MF)
The address of a basic block.
Definition Constants.h:1082
This is an important base class in LLVM.
Definition Constant.h:43
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
const GlobalValue * getGlobal() const
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Represents one node in the SelectionDAG.
LLVM_ABI void dump() const
Dump this node, for debugging.
unsigned getOpcode() const
Return the SelectionDAG opcode value for this node.
iterator_range< value_op_iterator > op_values() const
Represents a use of a SDNode.
Unlike LLVM values, Selection DAG nodes may return multiple values as the result of a computation.
SDNode * getNode() const
get the SDNode which holds the desired result
const SDValue & getOperand(unsigned i) const
SelectionDAGISel - This is the common base class used for SelectionDAG-based pattern-matching instruc...
virtual bool runOnMachineFunction(MachineFunction &mf)
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ ATOMIC_STORE
OUTCHAIN = ATOMIC_STORE(INCHAIN, val, ptr) This corresponds to "store atomic" instruction.
@ ADDC
Carry-setting nodes for multiple precision addition and subtraction.
Definition ISDOpcodes.h:294
@ ADD
Simple integer binary arithmetic operators.
Definition ISDOpcodes.h:264
@ LOAD
LOAD and STORE have token chains as their first operand, then the same operands as an LLVM load/store...
@ ATOMIC_LOAD
Val, OUTCHAIN = ATOMIC_LOAD(INCHAIN, ptr) This corresponds to "load atomic" instruction.
@ ATOMIC_CMP_SWAP
Val, OUTCHAIN = ATOMIC_CMP_SWAP(INCHAIN, ptr, cmp, swap) For double-word atomic operations: ValLo,...
@ GLOBAL_OFFSET_TABLE
The address of the GOT.
Definition ISDOpcodes.h:103
@ TokenFactor
TokenFactor - This node takes multiple tokens as input and produces a single token result.
Definition ISDOpcodes.h:53
@ CALLSEQ_START
CALLSEQ_START/CALLSEQ_END - These operators mark the beginning and end of a call sequence,...
@ TargetGlobalTLSAddress
Definition ISDOpcodes.h:186
@ MO_GOTPCREL
On a symbol operand this indicates that the immediate is offset to the GOT entry for the symbol name ...
@ GlobalBaseReg
The result of the mflr at function entry, used for PIC code.
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
This is an optimization pass for GlobalISel generic memory operations.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
@ Offset
Definition DWP.cpp:558
LLVM_ABI bool isNullConstant(SDValue V)
Returns true if V is a constant integer zero.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1745
FunctionPass * createM68kISelDag(M68kTargetMachine &TM)
This pass converts a legalized DAG into a M68k-specific DAG, ready for instruction scheduling.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
constexpr bool isIntN(unsigned N, int64_t x)
Checks if an signed integer fits into the given (dynamic) bit width.
Definition MathExtras.h:248
#define N
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39